diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4b192568..a3047e0a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,25 @@ stages: - build - test + - test_ngsolve - deploy - cleanup +push_github: + stage: build + tags: + - linux + - docker + - bash + script: + - git remote add github git@github.com:NGSolve/netgen.git || true + - git remote update + - git checkout --track origin/master + - git pull origin master + - git push github master --tags + only: + - master + ############################################ # Windows ############################################ @@ -14,7 +30,7 @@ stages: - x64 before_script: - "echo off" - - 'call "%VS2017INSTALLDIR%\VC\Auxiliary\Build\vcvars64"' + - call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64" - set CI_DIR=C:\ci\%CI_PIPELINE_ID% - set CLCACHE_BASEDIR=C:\ci\%CI_PIPELINE_ID% - set NETGEN_BUILD_DIR=%CI_DIR%\build @@ -22,6 +38,7 @@ stages: - set SRC_DIR=%CI_DIR%\src - set NETGENDIR=%INSTALL_DIR%\bin - set PYTHONPATH=%INSTALL_DIR%\lib\site-packages + - set PATH=%NETGENDIR%;%PATH% build_win: <<: *win @@ -38,8 +55,11 @@ build_win: cmake %SRC_DIR% -G Ninja -DCMAKE_INSTALL_PREFIX=%INSTALL_DIR% + -DCHECK_RANGE=ON + -DUSE_CGNS=ON -DUSE_OCC=ON -DUSE_CCACHE=ON + -DENABLE_UNIT_TESTS=ON -DCMAKE_BUILD_TYPE=Release - cmake --build . --target install --config Release @@ -47,9 +67,12 @@ test_win: <<: *win stage: test script: + - pip install pytest-check + - cd tests\pytest - cd %NETGEN_BUILD_DIR%\netgen - - ctest -C Release -V + - ctest -C Release -V --output-on-failure - cd .. + needs: ["build_win"] cleanup_win: <<: *win @@ -62,6 +85,7 @@ cleanup_win: - rd /s /q %CI_DIR% when: always allow_failure: true + needs: ["test_win"] ############################################ # Ubuntu/Linux @@ -70,24 +94,48 @@ cleanup_win: .template_ubuntu: &ubuntu tags: - linux + - bash before_script: - pwd - ls - docker info variables: - UBUNTU_VERSION: "18.04" + UBUNTU_VERSION: "22.04" -build_ubuntu: +build_ubuntu_debug: <<: *ubuntu stage: build script: - - docker build -t netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} -f tests/dockerfile . - - rm -f netgen_${CI_BUILD_REF_NAME}_$UBUNTU_VERSION.id - - docker run --cidfile netgen_${CI_BUILD_REF_NAME}_${UBUNTU_VERSION}.id -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build.sh - - docker commit `cat netgen_${CI_BUILD_REF_NAME}_${UBUNTU_VERSION}.id` netgen_${CI_BUILD_REF_NAME}_installed:${UBUNTU_VERSION} - - rm netgen_${CI_BUILD_REF_NAME}_${UBUNTU_VERSION}.id + - docker build -t netgen_${CI_PIPELINE_ID}:${UBUNTU_VERSION} -f tests/dockerfile . + - rm -f netgen_${CI_PIPELINE_ID}_$UBUNTU_VERSION.id + - >- + docker run + --cidfile netgen_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id + -e CCACHE_DIR=/ccache + -v /mnt/ccache:/ccache + netgen_${CI_PIPELINE_ID}:${UBUNTU_VERSION} + bash /root/src/netgen/tests/build_debug.sh + - docker commit `cat netgen_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id` netgen_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} + - rm netgen_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id -test_ubuntu: +build_ubuntu_mpi: + <<: *ubuntu + stage: build + script: + - docker build -t netgen_mpi_${CI_PIPELINE_ID}:${UBUNTU_VERSION} -f tests/dockerfile_mpi . + - rm -f netgen_mpi_${CI_PIPELINE_ID}_$UBUNTU_VERSION.id_mpi + - >- + docker run>- + --cidfile netgen_mpi_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id>- + -e CCACHE_DIR=/ccache + -e RUN_SLOW_TESTS=${RUN_SLOW_TESTS} + -v /mnt/ccache:/ccache + netgen_mpi_${CI_PIPELINE_ID}:${UBUNTU_VERSION} + bash /root/src/netgen/tests/build_mpi.sh + - docker commit `cat netgen_mpi_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id` netgen_mpi_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} + - rm netgen_mpi_${CI_PIPELINE_ID}_${UBUNTU_VERSION}.id + +test_ubuntu_debug: <<: *ubuntu stage: test script: @@ -95,36 +143,72 @@ test_ubuntu: docker run -e NETGENDIR=/opt/netgen/bin -e PYTHONPATH=/opt/netgen/lib/python3/dist-packages - netgen_${CI_BUILD_REF_NAME}_installed:${UBUNTU_VERSION} - bash -c 'cd /root/build/netgen && make test_netgen ARGS="-V"' + netgen_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} + bash -c 'cd /root/build/netgen && make test_netgen ARGS="-V --output-on-failure"' + needs: ["build_ubuntu_debug"] -# cpp guideline checks -test_guidelines: +test_ubuntu_mpi: <<: *ubuntu stage: test script: - - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_guidelines.sh - when: always + - >- + docker run + -e RUN_SLOW_TESTS=${RUN_SLOW_TESTS} + -e NETGENDIR=/opt/netgen/bin + -e PYTHONPATH=/opt/netgen/lib/python3/dist-packages + netgen_mpi_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} + bash -c 'cd /root/build/netgen && make test_netgen ARGS="-V --output-on-failure"' + needs: ["build_ubuntu_mpi"] + +test_build_ngsolve: + <<: *ubuntu allow_failure: true + stage: test_ngsolve + script: + - >- + docker run + -e NETGENDIR=/opt/netgen/bin + -e PYTHONPATH=/opt/netgen/lib/python3/dist-packages + -e MKLROOT=/opt/intel/mkl + -v /opt/intel:/opt/intel + -e CCACHE_DIR=/ccache + -v /mnt/ccache:/ccache + netgen_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} + bash -c 'cd /root/src/netgen/tests/ && ./build_ngsolve.sh' + +# cpp guideline checks +# test_guidelines: +# <<: *ubuntu +# stage: test +# script: +# - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_PIPELINE_ID}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_guidelines.sh +# when: always +# allow_failure: true # check if it compiles without spdlog test_noSpdlog: <<: *ubuntu stage: test script: - - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_nospdlog.sh + - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_PIPELINE_ID}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_nospdlog.sh cleanup_ubuntu: stage: cleanup tags: - linux + - bash script: # remove intermediate and old docker images and containers - docker rm -f `docker ps --no-trunc -aq` - docker images --no-trunc -aqf "dangling=true" | xargs docker rmi -f || true + - docker rmi -f netgen_${CI_PIPELINE_ID}:${UBUNTU_VERSION} || true + - docker rmi -f netgen_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} || true + - docker rmi -f netgen_mpi_${CI_PIPELINE_ID}:${UBUNTU_VERSION} || true + - docker rmi -f netgen_mpi_${CI_PIPELINE_ID}_installed:${UBUNTU_VERSION} || true when: always allow_failure: true + ############################################ # MacOSX ############################################ @@ -132,6 +216,7 @@ cleanup_ubuntu: .template_mac: &mac tags: - mac + - x64 before_script: - export ROOT_DIR=/tmp/$CI_PIPELINE_ID - export SRC_DIR=$ROOT_DIR/src @@ -155,11 +240,14 @@ 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.9 + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_OSX_SYSROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk + -DUSE_CGNS=ON + -DUSE_OCC=ON - make -j5 install test_mac: @@ -167,7 +255,8 @@ test_mac: stage: test script: - cd $BUILD_DIR/netgen - - ctest . --output-on-failure + - ctest . -V --output-on-failure + needs: ["build_mac"] cleanup_mac: <<: *mac @@ -176,23 +265,40 @@ cleanup_mac: - rm -rf $ROOT_DIR when: always allow_failure: true + needs: ["test_mac"] -############################################ -# Deploy stage -############################################ - -deploy_sourceforge: - stage: deploy +pip_linux: + image: quay.io/pypa/manylinux2014_x86_64 + stage: build tags: + - pip - linux - docker script: - - git remote add sourceforge ssh://mhochste@git.code.sf.net/p/netgen-mesher/git || true - - git remote add github git@github.com:NGSolve/netgen.git || true - - git remote update - - git checkout master - - git pull origin master - - git push sourceforge master - - git push github master - only: - - master + - ./tests/build_pip.sh + when: manual + +pip_windows: + stage: build + tags: + - pip + - windows + script: + - .\tests\build_pip.ps1 C:\Python38 + - .\tests\build_pip.ps1 C:\Python39 + - .\tests\build_pip.ps1 C:\Python310 + - .\tests\build_pip.ps1 C:\Python311 + when: manual + +pip_macos: + stage: build + tags: + - pip + - macosx + - m1 + script: + - ./tests/build_pip_mac.sh 3.8 + - ./tests/build_pip_mac.sh 3.9 + - ./tests/build_pip_mac.sh 3.10 + - ./tests/build_pip_mac.sh 3.11 + when: manual 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 bf7980e3..9a18972a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,23 +2,26 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING INTERNAL) endif(NOT CMAKE_BUILD_TYPE) -if(WIN32) - # we are linking to object libraries on Windows - cmake_minimum_required(VERSION 3.12) -else(WIN32) - cmake_minimum_required(VERSION 3.1.3) -endif(WIN32) +cmake_minimum_required(VERSION 3.13) +cmake_policy(VERSION 3.13) -if(NOT WIN32) - option( USE_NATIVE_ARCH "build which -march=native" ON) -endif(NOT WIN32) +include (CMakeDependentOption) +option( USE_NATIVE_ARCH "build for native cpu architecture" ON) -option( USE_GUI "don't build netgen with GUI" ON ) +option( USE_GUI "build with GUI" ON ) option( USE_PYTHON "build with python interface" ON ) +cmake_dependent_option( PREFER_SYSTEM_PYBIND11 "Use system wide PyBind11" OFF "USE_PYTHON" OFF) option( USE_MPI "enable mpi parallelization" OFF ) -option( USE_OCC "(not supported) compile with OpenCascade geometry kernel" OFF) +option( USE_MPI4PY "enable mpi4py interface" ON ) +option( USE_OCC "build with OpenCascade geometry kernel interface" OFF) +option( USE_STLGEOM "build with STL geometry support" ON) +option( USE_CSG "build with CSG kernel" ON) +option( USE_INTERFACE "build nginterface" ON) +option( USE_GEOM2D "build 2d geometry kernels" ON) 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") @@ -28,8 +31,17 @@ option( ENABLE_CPP_CORE_GUIDELINES_CHECK "Enable cpp core guideline checks on ng option( USE_SPDLOG "Enable spd log logging" OFF) option( DEBUG_LOG "Enable more debug output (may increase computation time) - only works with USE_SPDLOG=ON" OFF) option( CHECK_RANGE "Check array range access, automatically enabled if built in debug mode" OFF) +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( USE_SUPERBUILD "build dependencies automatically" ON) +option( TRACE_MEMORY "Enable memory tracing" OFF) + +set(NG_COMPILE_FLAGS "" CACHE STRING "Additional compile flags") + +set(NGLIB_LIBRARY_TYPE SHARED CACHE STRING "nglib library type") +set(NGCORE_LIBRARY_TYPE SHARED CACHE STRING "ngcore library type") +set(NGGUI_LIBRARY_TYPE SHARED CACHE STRING "nggui library type") set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_modules") @@ -48,14 +60,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) @@ -74,12 +86,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}") ####################################################################### @@ -90,11 +100,6 @@ if(USE_CCACHE) endif(CCACHE_FOUND) endif(USE_CCACHE) -####################################################################### -if(USE_NATIVE_ARCH) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") -endif(USE_NATIVE_ARCH) - ####################################################################### if(INTEL_MIC) set(MKL_ARCH "mic") @@ -114,6 +119,7 @@ endif (ADDITIONAL_PATHS) ####################################################################### # build options include_directories ("${PROJECT_SOURCE_DIR}/include") +include_directories ("${PROJECT_SOURCE_DIR}/libsrc") include_directories ("${PROJECT_SOURCE_DIR}/libsrc/include") include_directories ("${PROJECT_BINARY_DIR}") @@ -121,7 +127,9 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) if(USE_PYTHON) find_package(PythonInterp 3 REQUIRED) - find_package(PythonLibs 3 REQUIRED) + if(NOT BUILD_FOR_CONDA) + find_package(PythonLibs 3 REQUIRED) + endif() execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1,0,''))" OUTPUT_VARIABLE PYTHON_PACKAGES_INSTALL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) file(TO_CMAKE_PATH ${PYTHON_PACKAGES_INSTALL_DIR} PYTHON_PACKAGES_INSTALL_DIR) @@ -189,13 +197,16 @@ install(EXPORT netgen-targets DESTINATION ${NG_INSTALL_DIR_CMAKE} COMPONENT netg set(CMAKE_MACOSX_RPATH TRUE) set(CMAKE_INSTALL_RPATH "${NG_RPATH_TOKEN};${NG_RPATH_TOKEN}/${NETGEN_RPATH}") +if(BUILD_FOR_CONDA) + file(RELATIVE_PATH py_rpath "/bin" "/${NG_INSTALL_DIR_LIB}") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_RPATH};${py_rpath}") +endif(BUILD_FOR_CONDA) include (CheckIncludeFiles) 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}) @@ -215,90 +226,193 @@ macro(get_dll_from_lib dll_path lib_path) get_filename_component(lib_name ${lib} name) endmacro() -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_CXX_STANDARD 17) if(WIN32) - get_WIN32_WINNT(ver) - add_definitions(-D_WIN32_WINNT=${ver} -DWNT -DWNT_WINDOW -DNOMINMAX) set(CMAKE_MFC_FLAG 0) - add_definitions(-DMSVC_EXPRESS -D_CRT_SECURE_NO_WARNINGS -DHAVE_STRUCT_TIMESPEC) - # build convenience (aka object) libraries in windows) - set(NG_LIB_TYPE OBJECT) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /wd4244 /wd4800") -else(WIN32) - # build shared libraries - set(NG_LIB_TYPE SHARED) endif(WIN32) if(APPLE) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup") endif(APPLE) +####################################################################### +add_library(nglib ${NGLIB_LIBRARY_TYPE}) +if(USE_GUI) + add_library(nggui ${NGGUI_LIBRARY_TYPE}) + if(WIN32) + set_target_properties( nggui PROPERTIES OUTPUT_NAME "libnggui") + endif(WIN32) +endif(USE_GUI) + ####################################################################### if(NOT ZLIB_INCLUDE_DIRS) find_package(ZLIB REQUIRED) endif(NOT ZLIB_INCLUDE_DIRS) -include_directories(${ZLIB_INCLUDE_DIRS}) +target_include_directories(nglib PRIVATE ${ZLIB_INCLUDE_DIRS}) +if(USE_GUI) + target_include_directories(nggui PRIVATE ${ZLIB_INCLUDE_DIRS}) +endif(USE_GUI) +target_link_libraries(nglib PRIVATE ${ZLIB_LIBRARIES}) ####################################################################### +if(WIN32) + add_library(netgen_gui INTERFACE IMPORTED) +else() + add_library(netgen_gui INTERFACE) +endif() + if (USE_GUI) find_package(TCL 8.5 REQUIRED) + find_package(TclStub 8.5 REQUIRED) find_package(Threads REQUIRED) if(APPLE) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework AppKit") + target_link_libraries(netgen_gui INTERFACE "-framework AppKit") else(APPLE) find_package(X11 REQUIRED) + target_link_libraries( netgen_gui INTERFACE ${X11_Xmu_LIB} ${X11_X11_LIB}) endif(APPLE) find_package(OpenGL REQUIRED) - add_definitions(-DTCL -DOPENGL -DUSE_TOGL_2) - include_directories(${TCL_INCLUDE_PATH}) - include_directories(${TK_INCLUDE_PATH}) - set(LIBTOGL togl) + target_compile_definitions(netgen_gui INTERFACE -DTCL -DOPENGL -DUSE_TOGL_2 -DUSE_TCL_STUBS -DUSE_TK_STUBS) + target_include_directories(netgen_gui INTERFACE ${TCL_INCLUDE_PATH} ${TK_INCLUDE_PATH}) + target_link_libraries(netgen_gui INTERFACE ${TCL_STUB_LIBRARY} ${TK_STUB_LIBRARY}) + if(NOT EXISTS ${TK_INCLUDE_PATH}/tkWin.h AND EXISTS ${TK_INCLUDE_PATH}/../win/tkWin.h) + target_include_directories(netgen_gui INTERFACE ${TK_INCLUDE_PATH}/../win) + endif() + if(NOT EXISTS ${TK_INCLUDE_PATH}/x11/Xlib.h AND EXISTS ${TK_INCLUDE_PATH}/../xlib/X11/Xlib.h) + target_include_directories(netgen_gui INTERFACE ${TK_INCLUDE_PATH}/../xlib) + endif() + + target_link_libraries(nggui PUBLIC nglib togl PRIVATE "$" ) if(WIN32) - add_definitions(-DTOGL_WGL) - else(WIN32) - if(APPLE) - ADD_DEFINITIONS(-DTOGL_NSOPENGL) - else(APPLE) - ADD_DEFINITIONS(-DTOGL_X11) - endif(APPLE) - endif(WIN32) + target_compile_definitions(netgen_gui INTERFACE -DTOGL_WGL) + endif() + if(APPLE) + target_compile_definitions(netgen_gui INTERFACE -DTOGL_NSOPENGL) + endif() + if(UNIX AND NOT APPLE) + target_compile_definitions(netgen_gui INTERFACE -DTOGL_X11) + endif() + endif (USE_GUI) ####################################################################### -if (USE_PYTHON) - add_subdirectory(external_dependencies/pybind11) - add_definitions(-DNG_PYTHON) - find_path(PYBIND_INCLUDE_DIR pybind11/pybind11.h HINTS ${PYTHON_INCLUDE_DIR}) - if( PYBIND_INCLUDE_DIR ) - message(STATUS "Found Pybind11: ${PYBIND_INCLUDE_DIR}") - else( PYBIND_INCLUDE_DIR ) - message(FATAL_ERROR "Could NOT find pybind11!") - endif( PYBIND_INCLUDE_DIR ) +if(WIN32) + add_library(netgen_python INTERFACE IMPORTED) +else() + add_library(netgen_python INTERFACE) +endif() - include_directories(${PYBIND_INCLUDE_DIR}) - include_directories(${PYTHON_INCLUDE_DIRS}) +if (USE_PYTHON) + if (PREFER_SYSTEM_PYBIND11) + set(NG_INSTALL_PYBIND OFF) + find_package(pybind11 CONFIG REQUIRED) + else() + add_subdirectory(external_dependencies/pybind11) + endif() + + target_include_directories(netgen_python INTERFACE ${pybind11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) + target_include_directories(nglib PRIVATE ${pybind11_INCLUDE_DIR} ${PYTHON_INCLUDE_DIRS}) + if(NOT ${BUILD_FOR_CONDA} OR WIN32) + # Don't link python libraries in conda environments + target_link_libraries(netgen_python INTERFACE ${PYTHON_LIBRARIES}) + endif() if(NG_INSTALL_PYBIND) - install(DIRECTORY ${PYBIND_INCLUDE_DIR}/pybind11 DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel) - install(FILES ${PYBIND_INCLUDE_DIR}/../LICENSE DESTINATION ${NG_INSTALL_DIR_INCLUDE}/pybind11 COMPONENT netgen_devel) + install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel) + install(FILES ${pybind11_INCLUDE_DIR}/../LICENSE DESTINATION ${NG_INSTALL_DIR_INCLUDE}/pybind11 COMPONENT netgen_devel) endif(NG_INSTALL_PYBIND) endif (USE_PYTHON) ####################################################################### +add_library(netgen_mpi INTERFACE) +add_library(netgen_metis INTERFACE) if (USE_MPI) find_package(MPI REQUIRED) + target_include_directories(netgen_mpi INTERFACE ${MPI_CXX_INCLUDE_PATH}) + target_link_libraries(netgen_mpi INTERFACE ${MPI_mpi_LIBRARY} ${MPI_CXX_LIBRARIES} ) + target_compile_definitions(netgen_mpi INTERFACE PARALLEL ) + find_package(METIS REQUIRED) - add_definitions(-DPARALLEL -DMETIS) - include_directories(${MPI_CXX_INCLUDE_PATH}) - include_directories(${METIS_INCLUDE_DIR}) + 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}) ####################################################################### if (USE_OCC) - find_package(OpenCasCade REQUIRED) - add_definitions(-DOCCGEOMETRY -D_OCC64) - include_directories(${OCC_INCLUDE_DIR}) + find_package(OpenCascade NAMES OpenCASCADE opencascade REQUIRED CMAKE_FIND_ROOT_PATH_BOTH) + add_definitions(-DOCCGEOMETRY) + set(OCC_LIBRARIES + TKBO + TKBRep + TKBool + TKCAF + TKCDF + TKFillet + TKG2d + TKG3d + TKGeomAlgo + TKGeomBase + TKHLR + TKIGES + TKLCAF + TKMath + TKMesh + TKOffset + TKPrim + TKSTEP + TKSTEP209 + TKSTEPAttr + TKSTEPBase + TKSTL + TKService + TKShHealing + TKTopAlgo + TKV3d + TKVCAF + TKXCAF + TKXDEIGES + TKXDESTEP + TKXSBase + TKernel + ) + include_directories(${OpenCASCADE_INCLUDE_DIR}) + if(NOT OpenCASCADE_BUILD_SHARED_LIBS) + if(OpenCASCADE_WITH_FREETYPE) + find_library( FREETYPE NAMES freetype HINTS ${OpenCASCADE_INSTALL_PREFIX}/lib) + list(APPEND OCC_LIBRARIES ${FREETYPE}) + if(UNIX AND NOT APPLE) + find_package(Fontconfig REQUIRED) + list(APPEND OCC_LIBRARIES ${Fontconfig_LIBRARIES}) + endif() + endif(OpenCASCADE_WITH_FREETYPE) + if(UNIX AND NOT APPLE) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads REQUIRED) + list(APPEND OCC_LIBRARIES Threads::Threads) + list(PREPEND OCC_LIBRARIES -Wl,--start-group) + list(APPEND OCC_LIBRARIES -Wl,--end-group) + endif() + if(WIN32) + list(APPEND OCC_LIBRARIES Ws2_32.lib) + endif() + endif() + message(STATUS "OCC DIRS ${OpenCASCADE_INCLUDE_DIR}") + if(WIN32 AND USE_GUI) + target_link_libraries(nggui PRIVATE ${OCC_LIBRARIES}) + endif(WIN32 AND USE_GUI) endif (USE_OCC) ####################################################################### @@ -315,6 +429,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") @@ -379,18 +499,91 @@ 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) + +# include instead of add_subdirectory to recognize the generated source files properly +include(rules/CMakeLists.txt) + +add_subdirectory(windows) add_subdirectory(libsrc) add_subdirectory(ng) add_subdirectory(tutorials) -if (USE_PYTHON) - add_subdirectory(python) -endif (USE_PYTHON) add_subdirectory(py_tutorials) add_subdirectory(doc) -add_subdirectory(windows) add_subdirectory(nglib) add_subdirectory(tests) +####################################################################### +if(USE_NATIVE_ARCH) + if(WIN32) + include(CheckCXXSourceRuns) + check_cxx_source_runs(" + #include + int main() + { + __m256d a{1.,2.,3.,4.}; + __m256d b{2.,0.,3.,5.}; + __m256d c = _mm256_mul_pd(a,b); + return 0; + } " NG_HAVE_AVX) + check_cxx_source_runs(" + #include + int main() + { + __m256i a{1,2,3,4}; + __m256i b{2,0,3,5}; + __m256i c = _mm256_cmpgt_epi64 (a,b); + return 0; + } " NG_HAVE_AVX2) + check_cxx_source_runs(" + #include + int main() + { + __m512d a{1.,2.,3.,4.}; + __m512d b{5.,6.,7.,8.}; + __m512d c = _mm512_mul_pd(a,b); + return 0; + } " NG_HAVE_AVX512) + + if(NG_HAVE_AVX512) + target_compile_options(ngcore PUBLIC "/arch:AVX512") + message(STATUS "Build for AVX512 CPU") + elseif(NG_HAVE_AVX2) + target_compile_options(ngcore PUBLIC "/arch:AVX2") + message(STATUS "Build for AVX2 CPU") + elseif(NG_HAVE_AVX) + target_compile_options(ngcore PUBLIC "/arch:AVX") + message(STATUS "Build for AVX CPU") + 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) + if(APPLE) + # work-around for bug in Xcode 11.3: https://forums.developer.apple.com/thread/121887 + target_compile_options(ngcore PUBLIC "-fno-stack-check") + endif(APPLE) +endif(USE_NATIVE_ARCH) + +if (USE_PYTHON) + add_subdirectory(python) +endif (USE_PYTHON) ####################################################################### # Debian packager @@ -428,7 +621,7 @@ if(UNIX) endif(temp) endif(UNIX) -if(APPLE) +if(APPLE AND NOT SKBUILD) # create some auxiliary files set(mac_startup ${CMAKE_CURRENT_BINARY_DIR}/startup.sh) file(WRITE ${mac_startup} "\ @@ -450,7 +643,7 @@ $Netgen_MACOS/netgen #!/bin/sh Netgen_BUNDLE=\"`echo \"$0\" | sed -e 's/\\/Contents\\/MacOS\\/Netgen1//'`\" Netgen_MACOS=\"$Netgen_BUNDLE/Contents/MacOS\" -open -a /Applications/Utilities/Terminal.app $Netgen_MACOS/startup.sh +open -a /Applications/Utilities/Terminal.app $Netgen_MACOS/startup.sh || open -a /System/Applications/Utilities/Terminal.app $Netgen_MACOS/startup.sh ") install(PROGRAMS ${mac_ngsuite} DESTINATION ${NG_INSTALL_DIR_BIN} RENAME Netgen1) @@ -473,7 +666,7 @@ open -a /Applications/Utilities/Terminal.app $Netgen_MACOS/startup.sh install(FILES ${mac_plist} DESTINATION ${NG_INSTALL_DIR_BIN}/../) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/netgen.icns DESTINATION ${NG_INSTALL_DIR_RES}/../ RENAME Netgen.icns) -endif(APPLE) +endif(APPLE AND NOT SKBUILD) if(NOT APPLE) include(CPack) 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/README.md b/README.md index e9457377..e7d2e8fd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ Netgen mesh generator -NETGEN is an automatic 3d tetrahedral mesh generator. It accepts input from constructive solid geometry (CSG) or boundary representation (BRep) from STL file format. The connection to a geometry kernel allows the handling of IGES and STEP files. NETGEN contains modules for mesh optimization and hierarchical mesh refinement. Netgen 6.x supports scripting via a Python interface. Netgen is open source based on the LGPL license. It is available for Unix/Linux, Windows, and OSX. \ No newline at end of file +NETGEN is an automatic 3d tetrahedral mesh generator. It accepts input from constructive solid geometry (CSG) or boundary representation (BRep) from STL file format. The connection to a geometry kernel allows the handling of IGES and STEP files. NETGEN contains modules for mesh optimization and hierarchical mesh refinement. Netgen 6.x supports scripting via a Python interface. Netgen is open source based on the LGPL license. It is available for Unix/Linux, Windows, and OSX. + +Find the Open Source Community on https://ngsolve.org +Support & Services: https://cerbsim.com diff --git a/cmake/NetgenConfig.cmake.in b/cmake/NetgenConfig.cmake.in index 4c5217b8..ddb8850b 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) @@ -14,6 +18,8 @@ get_filename_component(NETGEN_RESOURCE_DIR "${NETGEN_CMAKE_DIR}/@NETGEN_RESOURCE set(NETGEN_SOURCE_DIR "@PROJECT_SOURCE_DIR@") +set(NETGEN_BUILD_FOR_CONDA "@BUILD_FOR_CONDA@") +set(NETGEN_CHECK_RANGE "@CHECK_RANGE@") set(NETGEN_INCLUDE_DIRS "${NETGEN_INCLUDE_DIR}/include;${NETGEN_INCLUDE_DIR}") set(NETGEN_CMAKE_THREAD_LIBS_INIT "@CMAKE_THREAD_LIBS_INIT@") set(NETGEN_FFMPEG_LIBRARIES "@FFMPEG_LIBRARIES@") @@ -25,19 +31,21 @@ 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_OCC_INCLUDE_DIR "@OCC_INCLUDE_DIR@") -set(NETGEN_OCC_LIBRARIES_BIN "@OCC_LIBRARIES_BIN@") +set(NETGEN_NUMA_LIBRARY "@NUMA_LIBRARY@") +set(NETGEN_OCC_DIR "@OpenCasCade_DIR@") +set(NETGEN_OCC_INCLUDE_DIR "@OpenCASCADE_INCLUDE_DIR@") +set(NETGEN_OCC_LIBRARIES_BIN "@OpenCASCADE_BINARY_DIR@") set(NETGEN_OCC_LIBRARIES "@OCC_LIBRARIES@") -set(NETGEN_OCC_LIBRARY_DIR "@OCC_LIBRARY_DIR@") +set(NETGEN_OCC_LIBRARY_DIR "@OpenCASCADE_LIBRARY_DIR@") set(NETGEN_OPENGL_LIBRARIES "@OPENGL_LIBRARIES@") set(NETGEN_PYTHON_EXECUTABLE "@PYTHON_EXECUTABLE@") set(NETGEN_PYTHON_INCLUDE_DIRS "@PYTHON_INCLUDE_DIRS@") set(NETGEN_PYTHON_LIBRARIES "@PYTHON_LIBRARIES@") set(NETGEN_TCL_INCLUDE_PATH "@TCL_INCLUDE_PATH@") -set(NETGEN_TCL_LIBRARY "@TCL_LIBRARY@") +set(NETGEN_TCL_LIBRARY "@TCL_STUB_LIBRARY@") set(NETGEN_TK_DND_LIBRARY "@TK_DND_LIBRARY@") set(NETGEN_TK_INCLUDE_PATH "@TK_INCLUDE_PATH@") -set(NETGEN_TK_LIBRARY "@TK_LIBRARY@") +set(NETGEN_TK_LIBRARY "@TK_STUB_LIBRARY@") set(NETGEN_X11_X11_LIB "@X11_X11_LIB@") set(NETGEN_X11_Xmu_LIB "@X11_Xmu_LIB@") set(NETGEN_ZLIB_INCLUDE_DIRS "@ZLIB_INCLUDE_DIRS@") @@ -49,9 +57,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 646de088..387f7b29 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -1,38 +1,72 @@ include (ExternalProject) +option( BUILD_ZLIB "Build and link static version of zlib (useful for pip binaries)" OFF ) +option( BUILD_OCC "Build and link static version of occ (useful for pip binaries)" OFF ) set_property (DIRECTORY PROPERTY EP_PREFIX dependencies) set (NETGEN_DEPENDENCIES) set (LAPACK_DEPENDENCIES) set (NETGEN_CMAKE_ARGS "" CACHE INTERNAL "") +set (SUBPROJECT_CMAKE_ARGS "" CACHE INTERNAL "") + +set (SUBPROJECT_ARGS + LIST_SEPARATOR | + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dependencies +) + +# only show output on failure in ci-builds +if(DEFINED ENV{CI}) + set (SUBPROJECT_ARGS + LOG_DOWNLOAD ON + LOG_BUILD ON + LOG_INSTALL ON + LOG_CONFIGURE ON + ) + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14.0") + set (SUBPROJECT_ARGS + ${SUBPROJECT_ARGS} + LOG_OUTPUT_ON_FAILURE ON + LOG_MERGED_STDOUTERR ON + ) + endif() +endif() + + +set (NETGEN_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ) macro(set_vars VAR_OUT) foreach(varname ${ARGN}) if(NOT "${${varname}}" STREQUAL "") - string(REPLACE ";" "$" varvalue "${${varname}}" ) - set(${VAR_OUT} ${${VAR_OUT}};-D${varname}=${varvalue} CACHE INTERNAL "") + string(REPLACE ";" "|" varvalue "${${varname}}" ) + set(${VAR_OUT} "${${VAR_OUT}};-D${varname}=${varvalue}" CACHE INTERNAL "") endif() 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) - if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") - string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_CXX_FLAGS_NEW ${CMAKE_CXX_FLAGS}) - set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS_NEW} CACHE STRING "compile flags" FORCE) - string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_CXX_FLAGS_NEW ${CMAKE_CXX_FLAGS_RELEASE}) - set(CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_NEW} CACHE STRING "compile flags" FORCE) - string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_SHARED_LINKER_FLAGS_NEW ${CMAKE_SHARED_LINKER_FLAGS}) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_NEW} /IGNORE:4217,4049" CACHE STRING "compile flags" FORCE) - string(REGEX REPLACE "/W[0-4]" "/W0" CMAKE_EXE_LINKER_FLAGS_NEW ${CMAKE_EXE_LINKER_FLAGS}) - set(CMAKE_EXE_LINKER_FLAGS"${CMAKE_EXE_LINKER_FLAGS_NEW}/IGNORE:4217,4049" CACHE STRING "compile flags" FORCE) +set_vars(SUBPROJECT_CMAKE_ARGS CMAKE_OSX_DEPLOYMENT_TARGET) +set_vars(SUBPROJECT_CMAKE_ARGS CMAKE_OSX_SYSROOT) +set_vars(SUBPROJECT_CMAKE_ARGS CMAKE_C_COMPILER) +set_vars(SUBPROJECT_CMAKE_ARGS CMAKE_CXX_COMPILER) +set_vars(SUBPROJECT_CMAKE_ARGS CMAKE_BUILD_TYPE) + +set(SUBPROJECT_CMAKE_ARGS "${SUBPROJECT_CMAKE_ARGS};-DCMAKE_POSITION_INDEPENDENT_CODE=ON" CACHE INTERNAL "") + +if(USE_CCACHE) + find_program(CCACHE_FOUND NAMES ccache ccache.bat) + if(CCACHE_FOUND) + set(SUBPROJECT_CMAKE_ARGS "${SUBPROJECT_CMAKE_ARGS};-DCMAKE_CXX_COMPILER_LAUNCHER=${CCACHE_FOUND}" CACHE INTERNAL "") + endif() +endif() + +####################################################################### +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}/occ75_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) - endif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") -endif(WIN32) if(UNIX) message("Checking for write permissions in install directory...") @@ -43,10 +77,79 @@ if(UNIX) endif() endif(UNIX) -if(NOT WIN32) - find_package(ZLIB REQUIRED) - set_vars(NETGEN_CMAKE_ARGS ZLIB_INCLUDE_DIRS ZLIB_LIBRARIES) -endif(NOT WIN32) +if(USE_OCC) +if(BUILD_OCC) + set(OCC_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/occ) + + ExternalProject_Add(project_occ + URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V7_6_1.zip + URL_MD5 e891d85cad61c5cc7ccba3d0110f0c8c + DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies + ${SUBPROJECT_ARGS} + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${OCC_DIR} + -DCMAKE_PREFIX_PATH=${OCC_DIR} + -DBUILD_LIBRARY_TYPE:STRING=Static + -DBUILD_MODULE_FoundationClasses:BOOL=ON + -DBUILD_MODULE_ModelingData:BOOL=ON + -DBUILD_MODULE_ModelingAlgorithms:BOOL=ON + -DBUILD_MODULE_DataExchange:BOOL=ON + -DBUILD_MODULE_Visualization:BOOL=OFF + -DBUILD_MODULE_ApplicationFramework:BOOL=OFF + -DBUILD_MODULE_Draw:BOOL=OFF + -DUSE_FREETYPE:BOOL=OFF + -DUSE_OPENGL:BOOL=OFF + -DUSE_XLIB:BOOL=OFF + -DBUILD_DOC_Overview:BOOL=OFF + ${SUBPROJECT_CMAKE_ARGS} + UPDATE_COMMAND "" + ) + + list(APPEND NETGEN_DEPENDENCIES project_occ) + set(OpenCascade_ROOT ${OCC_DIR}) +else(BUILD_OCC) + if(WIN32 AND NOT OCC_INCLUDE_DIR AND NOT OpenCASCADE_DIR) + # we can download prebuilt occ binaries for windows + ExternalProject_Add(win_download_occ + ${SUBPROJECT_ARGS} + URL ${OCC_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} + ) + list(APPEND NETGEN_DEPENDENCIES win_download_occ) + else() + find_package(OpenCascade NAMES OpenCasCade OpenCASCADE opencascade REQUIRED) + endif() +endif(BUILD_OCC) +endif(USE_OCC) + +if(BUILD_ZLIB) + set(ZLIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/zlib) + ExternalProject_Add(project_zlib + ${SUBPROJECT_ARGS} + URL https://github.com/madler/zlib/archive/refs/tags/v1.2.11.zip + URL_MD5 9d6a627693163bbbf3f26403a3a0b0b1 + DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${ZLIB_DIR} + ${SUBPROJECT_CMAKE_ARGS} + UPDATE_COMMAND "" # Disable update + BUILD_IN_SOURCE 1 + ) + + list(APPEND NETGEN_DEPENDENCIES project_zlib) + list(APPEND NETGEN_CMAKE_PREFIX_PATH ${ZLIB_DIR}) + if(WIN32) + # force linking the static library + set(ZLIB_INCLUDE_DIRS ${ZLIB_DIR}/include) + set(ZLIB_LIBRARIES ${ZLIB_DIR}/lib/zlibstatic.lib) + endif(WIN32) +else() + include(cmake/external_projects/zlib.cmake) +endif() ####################################################################### if (USE_PYTHON) @@ -63,7 +166,9 @@ if (USE_PYTHON) message(FATAL_ERROR "Could NOT find pybind11!") endif( PYBIND_INCLUDE_DIR ) find_package(PythonInterp 3 REQUIRED) - find_package(PythonLibs 3 REQUIRED) + if(NOT BUILD_FOR_CONDA) + find_package(PythonLibs 3 REQUIRED) + endif() set_vars(NETGEN_CMAKE_ARGS PYTHON_INCLUDE_DIRS @@ -77,35 +182,27 @@ endif (USE_PYTHON) ####################################################################### -if(USE_OCC AND WIN32 AND NOT OCC_INCLUDE_DIR) - ExternalProject_Add(win_download_occ - PREFIX ${CMAKE_CURRENT_BINARY_DIR}/tcl - URL ${OCC_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 win_download_occ) -endif(USE_OCC AND WIN32 AND NOT OCC_INCLUDE_DIR) - -####################################################################### - -include(cmake/external_projects/zlib.cmake) 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) - find_package(METIS QUIET) - if(NOT METIS_FOUND) - message(STATUS "Could not find METIS, it will be built from source") - include(cmake/external_projects/metis.cmake) - endif() + if (METIS_DIR) + message(STATUS "Using external METIS at: ${METIS_DIR}") + else (METIS_DIR) + message(STATUS "Looking for system METIS") + find_package(METIS QUIET) + if(NOT METIS_FOUND) + message(WARNING "Could not find METIS, it will be built from source (this might conflict with NGSolve MUMPS)!") + include(cmake/external_projects/metis.cmake) + endif(NOT METIS_FOUND) + endif(METIS_DIR) else(UNIX) find_package(METIS REQUIRED) endif(UNIX) @@ -115,14 +212,10 @@ endif(USE_MPI) ####################################################################### # propagate cmake variables to Netgen subproject set_vars( NETGEN_CMAKE_ARGS - CMAKE_CXX_COMPILER - CMAKE_BUILD_TYPE CMAKE_SHARED_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS_RELEASE CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_RELEASE - CMAKE_OSX_DEPLOYMENT_TARGET - CMAKE_OSX_SYSROOT USE_GUI USE_PYTHON @@ -135,16 +228,27 @@ set_vars( NETGEN_CMAKE_ARGS USE_OCC USE_MPEG USE_JPEG + USE_CGNS USE_INTERNAL_TCL INSTALL_PROFILES INTEL_MIC - CMAKE_PREFIX_PATH CMAKE_INSTALL_PREFIX ENABLE_UNIT_TESTS ENABLE_CPP_CORE_GUIDELINES_CHECK USE_SPDLOG DEBUG_LOG CHECK_RANGE + TRACE_MEMORY + BUILD_STUB_FILES + BUILD_FOR_CONDA + NG_COMPILE_FLAGS + OpenCascade_ROOT + ZLIB_INCLUDE_DIRS + ZLIB_LIBRARIES + + NGLIB_LIBRARY_TYPE + NGCORE_LIBRARY_TYPE + NGGUI_LIBRARY_TYPE ) # propagate all variables set on the command line using cmake -DFOO=BAR @@ -152,9 +256,10 @@ set_vars( NETGEN_CMAKE_ARGS get_cmake_property(CACHE_VARS CACHE_VARIABLES) foreach(CACHE_VAR ${CACHE_VARS}) get_property(CACHE_VAR_HELPSTRING CACHE ${CACHE_VAR} PROPERTY HELPSTRING) - if(CACHE_VAR_HELPSTRING STREQUAL "No help, variable specified on the command line.") + if(CACHE_VAR_HELPSTRING STREQUAL "No help, variable specified on the command line." AND NOT CACHE_VAR STREQUAL "CMAKE_OSX_ARCHITECTURES") get_property(CACHE_VAR_TYPE CACHE ${CACHE_VAR} PROPERTY TYPE) - set(NETGEN_CMAKE_ARGS ${NETGEN_CMAKE_ARGS};-D${CACHE_VAR}:${CACHE_VAR_TYPE}=${${CACHE_VAR}} CACHE INTERNAL "") + string(REPLACE ";" "|" varvalue "${${CACHE_VAR}}" ) + set(NETGEN_CMAKE_ARGS ${NETGEN_CMAKE_ARGS};-D${CACHE_VAR}:${CACHE_VAR_TYPE}=${varvalue} CACHE INTERNAL "") endif() endforeach() @@ -164,10 +269,17 @@ else() set(NETGEN_BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/netgen --config ${CMAKE_BUILD_TYPE}) endif() + +string(REPLACE ";" "|" NETGEN_CMAKE_PREFIX_PATH_ALT_SEP "${NETGEN_CMAKE_PREFIX_PATH}") ExternalProject_Add (netgen + ${SUBPROJECT_ARGS} DEPENDS ${NETGEN_DEPENDENCIES} SOURCE_DIR ${PROJECT_SOURCE_DIR} - CMAKE_ARGS -DUSE_SUPERBUILD=OFF ${NETGEN_CMAKE_ARGS} + CMAKE_ARGS + -DUSE_SUPERBUILD=OFF + ${NETGEN_CMAKE_ARGS} + ${SUBPROJECT_CMAKE_ARGS} + -DCMAKE_PREFIX_PATH=${NETGEN_CMAKE_PREFIX_PATH_ALT_SEP} INSTALL_COMMAND "" BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/netgen BUILD_COMMAND ${NETGEN_BUILD_COMMAND} @@ -191,7 +303,7 @@ ExternalProject_Add (netgen ) -install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build . --target install --config ${CMAKE_BUILD_TYPE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/netgen)") +install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" --build . --target install --config ${CMAKE_BUILD_TYPE} WORKING_DIRECTORY \"${CMAKE_CURRENT_BINARY_DIR}/netgen\")") add_custom_target(test_netgen ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/netgen diff --git a/cmake/cmake_modules/FindOpenCasCade.cmake b/cmake/cmake_modules/FindOpenCasCade.cmake deleted file mode 100644 index a981af7b..00000000 --- a/cmake/cmake_modules/FindOpenCasCade.cmake +++ /dev/null @@ -1,97 +0,0 @@ -# Try to find OCC -# Once done this will define -# -# OCC_FOUND - system has OCC - OpenCASCADE -# OCC_INCLUDE_DIR - where the OCC include directory can be found -# OCC_LIBRARY_DIR - where the OCC library directory can be found -# OCC_LIBRARIES - Link this to use OCC - -if(WIN32) - find_path(OCC_INCLUDE_DIR Standard_Version.hxx PATH_SUFFIXES inc ../inc) - find_library(OCC_LIBRARY TKernel) -else(WIN32) - find_path(OCC_INCLUDE_DIR Standard_Version.hxx - /usr/include/opencascade - /usr/local/include/opencascade - /usr/include/oce - /usr/local/include/oce - /opt/opencascade/include - /opt/opencascade/inc - ) - find_library(OCC_LIBRARY TKernel - /usr/lib - /usr/local/lib - /opt/opencascade/lib - ) -endif(WIN32) - -if(OCC_LIBRARY) - get_filename_component(OCC_LIBRARY_DIR ${OCC_LIBRARY} PATH) -endif(OCC_LIBRARY) - -if(OCC_INCLUDE_DIR) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAJOR - REGEX "#define OCC_VERSION_MAJOR.*" - ) - string(REGEX MATCH "[0-9]+" OCC_MAJOR ${OCC_MAJOR}) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MINOR - REGEX "#define OCC_VERSION_MINOR.*" - ) - string(REGEX MATCH "[0-9]+" OCC_MINOR ${OCC_MINOR}) - file(STRINGS ${OCC_INCLUDE_DIR}/Standard_Version.hxx OCC_MAINT - REGEX "#define OCC_VERSION_MAINTENANCE.*" - ) - string(REGEX MATCH "[0-9]+" OCC_MAINT ${OCC_MAINT}) - - set(OCC_VERSION_STRING "${OCC_MAJOR}.${OCC_MINOR}.${OCC_MAINT}") -endif(OCC_INCLUDE_DIR) - - -set(OCC_LIBRARY_NAMES - TKBO - TKBool - TKBRep - TKCAF - TKCDF - TKernel - TKG2d - TKG3d - TKGeomAlgo - TKGeomBase - TKHLR - TKIGES - TKLCAF - TKMath - TKMesh - TKOffset - TKPrim - TKService - TKShHealing - TKSTEP - TKSTEP209 - TKSTEPAttr - TKSTEPBase - TKSTL - TKTopAlgo - TKV3d - TKXCAF - TKXDEIGES - TKXDESTEP - TKXSBase -) - -foreach( libname ${OCC_LIBRARY_NAMES} ) - find_library( ${libname} ${libname} ${OCC_LIBRARY_DIR} ) - set(OCC_LIBRARIES ${OCC_LIBRARIES} ${${libname}}) -endforeach() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OCC REQUIRED_VARS OCC_INCLUDE_DIR VERSION_VAR OCC_VERSION_STRING ${OCC_LIBRARIY_NAMES}) - -if(OCC_FOUND) - message(STATUS "-- Found OpenCASCADE version: ${OCC_VERSION_STRING}") - message(STATUS "-- OpenCASCADE include directory: ${OCC_INCLUDE_DIR}") - message(STATUS "-- OpenCASCADE shared libraries directory: ${OCC_LIBRARY_DIR}") - message(STATUS "-- OpenCASCADE shared libraries :\n ${OCC_LIBRARIES}") -endif(OCC_FOUND) - diff --git a/cmake/external_projects/catch.cmake b/cmake/external_projects/catch.cmake index 0f79e6c7..57127b22 100644 --- a/cmake/external_projects/catch.cmake +++ b/cmake/external_projects/catch.cmake @@ -4,7 +4,7 @@ ExternalProject_Add( project_catch PREFIX ${CMAKE_BINARY_DIR}/catch GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v2.0.1 + GIT_TAG v2.13.7 TIMEOUT 10 UPDATE_COMMAND "" # ${GIT_EXECUTABLE} pull CONFIGURE_COMMAND "" 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/external_projects/metis.cmake b/cmake/external_projects/metis.cmake index 0e5f4711..1711b5df 100644 --- a/cmake/external_projects/metis.cmake +++ b/cmake/external_projects/metis.cmake @@ -3,12 +3,15 @@ set(METIS_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/metis) ExternalProject_Add(project_metis PREFIX ${CMAKE_CURRENT_BINARY_DIR}/dependencies - URL "http://ftp.mcs.anl.gov/pub/petsc/externalpackages/metis-5.1.0-p3.tar.gz" - URL_MD5 09d2d771c63a2efb3499882688100088 + URL https://bitbucket.org/petsc/pkg-metis/get/v5.1.0-p6.tar.gz + URL_MD5 55fc654bb838846b856ba898795143f1 DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies CMAKE_ARGS -DGKLIB_PATH=${METIS_SRC_DIR}/GKlib -DCMAKE_INSTALL_PREFIX=${METIS_DIR} + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} UPDATE_COMMAND "" # Disable update BUILD_IN_SOURCE 1 ) diff --git a/cmake/external_projects/tcltk.cmake b/cmake/external_projects/tcltk.cmake index b071e495..637c1899 100644 --- a/cmake/external_projects/tcltk.cmake +++ b/cmake/external_projects/tcltk.cmake @@ -1,61 +1,179 @@ +if(UNIX AND NOT APPLE) + set (LINUX TRUE) +endif() +if(LINUX) + find_package(TclStub 8.5 REQUIRED) +else(LINUX) +if(SKBUILD) +# we are building a pip package - download the tcl/tk sources matching the tkinter version (for private headers not shipped with python) + +execute_process(COMMAND ${PYTHON_EXECUTABLE} -c +"import tkinter;print(tkinter.Tcl().eval('info patchlevel').replace('.','-'))" +OUTPUT_VARIABLE PYTHON_TCL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + +set(TCL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tcl) +set(TK_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tk) + +ExternalProject_Add(project_tcl + URL "https://github.com/tcltk/tcl/archive/refs/tags/core-${PYTHON_TCL_VERSION}.zip" + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + ${SUBPROJECT_ARGS} + DOWNLOAD_DIR download_tcl +) +ExternalProject_Add(project_tk + URL "https://github.com/tcltk/tk/archive/refs/tags/core-${PYTHON_TCL_VERSION}.zip" + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" + BUILD_COMMAND ${CMAKE_COMMAND} -E copy_directory macosx generic + ${SUBPROJECT_ARGS} + DOWNLOAD_DIR download_tk + BUILD_IN_SOURCE 1 +) + +set(TCL_INCLUDE_PATH ${TCL_DIR}/generic) +set(TK_INCLUDE_PATH ${TK_DIR}/generic) +list(APPEND NETGEN_DEPENDENCIES project_tcl project_tk) + +if(APPLE OR WIN32) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import sys; print(sys.prefix)" OUTPUT_VARIABLE PYTHON_PREFIX OUTPUT_STRIP_TRAILING_WHITESPACE) + file(TO_CMAKE_PATH ${PYTHON_PREFIX} PYTHON_PREFIX) + + set(tcl_find_args + REQUIRED + NO_DEFAULT_PATH + NO_PACKAGE_ROOT_PATH + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_FIND_ROOT_PATH + HINTS ${PYTHON_PREFIX}/lib ${PYTHON_PREFIX}/tcl + ) + find_library(TCL_STUB_LIBRARY NAMES tclstub85 tclstub8.5 tclstub86 tclstub8.6 ${tcl_find_args}) + find_library(TK_STUB_LIBRARY NAMES tkstub85 tkstub8.5 tkstub86 tkstub8.6 ${tcl_find_args}) + find_library(TCL_LIBRARY NAMES tcl85 tcl8.5 tcl86 tcl8.6 tcl86t ${tcl_find_args}) + find_library(TK_LIBRARY NAMES tk85 tk8.5 tk86 tk8.6 tk86t ${tcl_find_args}) +else() + # use system tcl/tk on linux + find_package(TclStub REQUIRED) +endif() + +else(SKBUILD) if(APPLE) - # use system tcl/tk - if((${PYTHON_VERSION_STRING} VERSION_EQUAL "3.7") OR (${PYTHON_VERSION_STRING} VERSION_GREATER "3.7")) - # fetch tcl/tk sources to match the one used in Python 3.7 - ExternalProject_Add(project_tcl - URL "https://prdownloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz" - URL_MD5 81656d3367af032e0ae6157eff134f89 - DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies - UPDATE_COMMAND "" # Disable update - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - ) - ExternalProject_Add(project_tk - URL "https://prdownloads.sourceforge.net/tcl/tk8.6.8-src.tar.gz" - URL_MD5 5e0faecba458ee1386078fb228d008ba - DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies - UPDATE_COMMAND "" # Disable update - CONFIGURE_COMMAND "" - BUILD_COMMAND "" - INSTALL_COMMAND "" - ) - - get_filename_component(PYTHON_LIB_DIR ${PYTHON_LIBRARY} DIRECTORY) - find_library(TCL_LIBRARY libtcl8.6.dylib PATHS ${PYTHON_LIB_DIR} NO_DEFAULT_PATH) - find_library(TK_LIBRARY libtk8.6.dylib PATHS ${PYTHON_LIB_DIR} NO_DEFAULT_PATH) - - set(TCL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tcl) - set(TK_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tk) - set(TCL_INCLUDE_PATH "${TCL_DIR}/generic;${TCL_DIR}/macosx") - set(TK_INCLUDE_PATH "${TK_DIR}/generic;${TK_DIR}/macosx;${TK_DIR}/xlib") - string(REPLACE ";" "$" TCL_INC "${TCL_INCLUDE_PATH}") - string(REPLACE ";" "$" TK_INC "${TK_INCLUDE_PATH}") - - ExternalProject_Add(project_tkdnd - URL "http://sourceforge.net/projects/tkdnd/files/TkDND/TkDND%202.8/tkdnd2.8-src.tar.gz" - URL_MD5 a6d47a996ea957416469b12965d4db91 - DEPENDS project_tcl project_tk - DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies - PATCH_COMMAND patch < ${CMAKE_CURRENT_LIST_DIR}/tkdnd_macosx.patch - UPDATE_COMMAND "" # Disable update - BUILD_IN_SOURCE 1 - CMAKE_ARGS - -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/Contents/MacOS - -DTCL_INCLUDE_PATH=${TCL_INC} - -DTK_INCLUDE_PATH=${TK_INC} - -DTK_LIBRARY=${TK_LIBRARY} - -DTCL_LIBRARY=${TCL_LIBRARY} - LOG_DOWNLOAD 1 - LOG_CONFIGURE 1 - LOG_BUILD 1 - LOG_INSTALL 1 + set(tcl_prefix ${CMAKE_INSTALL_PREFIX}) + # URL "http://sourceforge.net/projects/tcl/files/Tcl/8.6.9/tcl8.6.9-src.tar.gz" + # URL_MD5 aa0a121d95a0e7b73a036f26028538d4 + ExternalProject_Add(project_tcl + URL "https://github.com/NGSolve/tcl/archive/7769161.zip" + URL_MD5 1131f188dd26944df557913c475d43b4 + DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies + UPDATE_COMMAND "" + CONFIGURE_COMMAND ../project_tcl/macosx/configure --enable-threads --enable-framework --prefix=${tcl_prefix} --libdir=${tcl_prefix}/Contents/Frameworks --bindir=${tcl_prefix}/Contents/Frameworks/Tcl.framework/bin + BUILD_COMMAND make -j4 binaries libraries + INSTALL_COMMAND make install-binaries install-headers install-libraries install-private-headers + ${SUBPROJECT_ARGS} ) - -list(APPEND NETGEN_DEPENDENCIES project_tkdnd) - else() - find_package(TCL 8.5 REQUIRED) - endif() + + # URL "http://sourceforge.net/projects/tcl/files/Tcl/8.6.9/tk8.6.9.1-src.tar.gz" + # URL_MD5 9efe3976468352dc894dae0c4e785a8e + ExternalProject_Add(project_tk + DEPENDS project_tcl + URL "https://github.com/NGSolve/tk/archive/e7c2bc7.zip" + URL_MD5 94044140d4826069c22f1c60cedb6e59 + DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies + UPDATE_COMMAND "" + CONFIGURE_COMMAND ../project_tk/macosx/configure --enable-aqua=yes --enable-threads --enable-framework --prefix=${tcl_prefix} --libdir=${tcl_prefix}/Contents/Frameworks --bindir=${tcl_prefix}/Contents/Frameworks/Tcl.framework/bin --with-tcl=${tcl_prefix}/Contents/Frameworks/Tcl.framework + BUILD_COMMAND make -j4 binaries libraries + INSTALL_COMMAND make install-binaries install-headers install-libraries install-private-headers + ${SUBPROJECT_ARGS} + ) + + ExternalProject_Add(project_tkdnd + URL "http://sourceforge.net/projects/tkdnd/files/TkDND/TkDND%202.8/tkdnd2.8-src.tar.gz" + URL_MD5 a6d47a996ea957416469b12965d4db91 + DEPENDS project_tcl project_tk + DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies + PATCH_COMMAND patch < ${CMAKE_CURRENT_LIST_DIR}/tkdnd_macosx.patch + UPDATE_COMMAND "" # Disable update + BUILD_IN_SOURCE 1 + CMAKE_ARGS + -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/Contents/MacOS + -DTCL_INCLUDE_PATH=${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tcl.framework/Headers + -DTK_INCLUDE_PATH=${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tk.framework/Headers + ${SUBPROJECT_ARGS} + ) + + list(APPEND NETGEN_DEPENDENCIES project_tcl project_tk project_tkdnd) + list(APPEND CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks) + set(TCL_INCLUDE_PATH ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tcl.framework/Headers) + set(TCL_LIBRARY ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tcl.framework) + set(TK_LIBRARY ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tk.framework) + set(TK_INCLUDE_PATH ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tk.framework/Headers) + + set(TCL_STUB_LIBRARY ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tcl.framework/libtclstub8.6.a) + set(TK_STUB_LIBRARY ${CMAKE_INSTALL_PREFIX}/Contents/Frameworks/Tk.framework/libtkstub8.6.a) + +# # use system tcl/tk +# if((${PYTHON_VERSION_STRING} VERSION_EQUAL "3.7") OR (${PYTHON_VERSION_STRING} VERSION_GREATER "3.7")) +# # fetch tcl/tk sources to match the one used in Python 3.7 +# ExternalProject_Add(project_tcl +# URL "https://prdownloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz" +# URL_MD5 81656d3367af032e0ae6157eff134f89 +# DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies +# UPDATE_COMMAND "" # Disable update +# CONFIGURE_COMMAND "" +# BUILD_COMMAND "" +# INSTALL_COMMAND "" +# ) +# ExternalProject_Add(project_tk +# URL "https://prdownloads.sourceforge.net/tcl/tk8.6.8-src.tar.gz" +# URL_MD5 5e0faecba458ee1386078fb228d008ba +# DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies +# UPDATE_COMMAND "" # Disable update +# CONFIGURE_COMMAND "" +# BUILD_COMMAND "" +# INSTALL_COMMAND "" +# ) +# +# get_filename_component(PYTHON_LIB_DIR ${PYTHON_LIBRARY} DIRECTORY) +# find_library(TCL_LIBRARY libtcl8.6.dylib PATHS ${PYTHON_LIB_DIR} NO_DEFAULT_PATH) +# find_library(TK_LIBRARY libtk8.6.dylib PATHS ${PYTHON_LIB_DIR} NO_DEFAULT_PATH) +# +# set(TCL_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tcl) +# set(TK_DIR ${CMAKE_CURRENT_BINARY_DIR}/dependencies/src/project_tk) +# set(TCL_INCLUDE_PATH "${TCL_DIR}/generic;${TCL_DIR}/macosx") +# set(TK_INCLUDE_PATH "${TK_DIR}/generic;${TK_DIR}/macosx;${TK_DIR}/xlib") +# string(REPLACE ";" "$" TCL_INC "${TCL_INCLUDE_PATH}") +# string(REPLACE ";" "$" TK_INC "${TK_INCLUDE_PATH}") +# +# ExternalProject_Add(project_tkdnd +# URL "http://sourceforge.net/projects/tkdnd/files/TkDND/TkDND%202.8/tkdnd2.8-src.tar.gz" +# URL_MD5 a6d47a996ea957416469b12965d4db91 +# DEPENDS project_tcl project_tk +# DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies +# PATCH_COMMAND patch < ${CMAKE_CURRENT_LIST_DIR}/tkdnd_macosx.patch +# UPDATE_COMMAND "" # Disable update +# BUILD_IN_SOURCE 1 +# CMAKE_ARGS +# -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}/Contents/MacOS +# -DTCL_INCLUDE_PATH=${TCL_INC} +# -DTK_INCLUDE_PATH=${TK_INC} +# -DTK_LIBRARY=${TK_LIBRARY} +# -DTCL_LIBRARY=${TCL_LIBRARY} +# LOG_DOWNLOAD 1 +# LOG_CONFIGURE 1 +# LOG_BUILD 1 +# LOG_INSTALL 1 +# ) +# +# list(APPEND NETGEN_DEPENDENCIES project_tkdnd) +# else() +# find_package(TCL 8.5 REQUIRED) +# endif() elseif(WIN32) @@ -66,13 +184,15 @@ elseif(WIN32) CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory . ${CMAKE_INSTALL_PREFIX} - LOG_DOWNLOAD 1 + ${SUBPROJECT_ARGS} ) set (TK_INCLUDE_PATH ${CMAKE_INSTALL_PREFIX}/include) set (TCL_INCLUDE_PATH ${CMAKE_INSTALL_PREFIX}/include) set (TCL_LIBRARY ${CMAKE_INSTALL_PREFIX}/lib/tcl86t.lib) set (TK_LIBRARY ${CMAKE_INSTALL_PREFIX}/lib/tk86t.lib) + set (TCL_STUB_LIBRARY ${CMAKE_INSTALL_PREFIX}/lib/tclstub86.lib) + set (TK_STUB_LIBRARY ${CMAKE_INSTALL_PREFIX}/lib/tkstub86.lib) list(APPEND NETGEN_DEPENDENCIES project_win_tcltk) else(WIN32) @@ -89,6 +209,8 @@ else(WIN32) # ) # list(APPEND NETGEN_DEPENDENCIES project_tkdnd) endif(APPLE) +endif(SKBUILD) +endif(LINUX) # Propagate settings to Netgen subproject -set_vars(NETGEN_CMAKE_ARGS TCL_INCLUDE_PATH TCL_LIBRARY TK_LIBRARY TK_INCLUDE_PATH TCL_TCLSH TK_WISH) +set_vars(NETGEN_CMAKE_ARGS TCL_INCLUDE_PATH TCL_STUB_LIBRARY TCL_LIBRARY TK_STUB_LIBRARY TK_LIBRARY TK_INCLUDE_PATH TCL_TCLSH TK_WISH) diff --git a/cmake/external_projects/tkdnd_macosx.patch b/cmake/external_projects/tkdnd_macosx.patch index 7e97a39a..f9c273dc 100644 --- a/cmake/external_projects/tkdnd_macosx.patch +++ b/cmake/external_projects/tkdnd_macosx.patch @@ -1,6 +1,8 @@ ---- CMakeLists.txt 19:24:32.000000000 +0200 -+++ CMakeLists.txt 2018-12-05 11:34:59.000000000 +0100 -@@ -43,17 +43,18 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 4eb497c..cd22a67 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -43,17 +43,18 @@ IF ( WIN32 ) ELSE ( WIN32 ) ## Unix and OS X... IF ( APPLE ) @@ -24,7 +26,7 @@ ADD_DEFINITIONS ( -fno-objc-arc ) # ADD_DEFINITIONS ( -fobjc-arc ) LINK_LIBRARIES ( ${COCOA_LIBRARY} ) -@@ -125,8 +126,8 @@ +@@ -125,8 +126,8 @@ SET ( CP ${CMAKE_COMMAND} -E copy ) ## Locate Tcl/Tk ## =========================================================================== MESSAGE ( STATUS "Searching for Tcl/Tk..." ) @@ -35,7 +37,7 @@ ## Tcl/Tk info (useful for debug purposes)... # MESSAGE ( STATUS " TCL_TCLSH: " ${TCL_TCLSH} ) -@@ -139,13 +140,13 @@ +@@ -139,13 +140,14 @@ FIND_PACKAGE ( TclStub REQUIRED ) # MESSAGE ( STATUS " TK_LIBRARY: " ${TK_LIBRARY} ) ## Enable Tcl/Tk stubs globally... @@ -48,8 +50,9 @@ INCLUDE_DIRECTORIES ( ${TK_INCLUDE_PATH} ) -LINK_LIBRARIES ( ${TCL_STUB_LIBRARY} ) -LINK_LIBRARIES ( ${TK_STUB_LIBRARY} ) -+LINK_LIBRARIES ( ${TCL_LIBRARY} ) -+LINK_LIBRARIES ( ${TK_LIBRARY} ) ++#LINK_LIBRARIES ( ${TCL_LIBRARY} ) ++#LINK_LIBRARIES ( ${TK_LIBRARY} ) ++SET ( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined dynamic_lookup" ) IF ( WIN32 AND NO_MSVCRT ) STRING ( REPLACE /MD /MT CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE} ) diff --git a/cmake/generate_version_file.cmake b/cmake/generate_version_file.cmake new file mode 100644 index 00000000..c4a579d1 --- /dev/null +++ b/cmake/generate_version_file.cmake @@ -0,0 +1,111 @@ +if(NOT BDIR) + set(BDIR ${CMAKE_CURRENT_BINARY_DIR}) +endif() + +if(NETGEN_VERSION_GIT) + set(git_version_string ${NETGEN_VERSION_GIT}) +else() + 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 OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +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(STRIP ${git_version_string} git_version_string) + +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}) + +if(NOT NETGEN_VERSION_GIT) + set(NETGEN_VERSION_GIT ${NETGEN_VERSION_LONG}) +endif() + +if(NOT NETGEN_VERSION_PYTHON) + set(NETGEN_VERSION_PYTHON ${NETGEN_VERSION_TWEAK}) +endif() + + +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/doc/element_types.tex b/doc/element_types.tex new file mode 100644 index 00000000..7f05fec2 --- /dev/null +++ b/doc/element_types.tex @@ -0,0 +1,282 @@ +\documentclass[convert=pdf2svg]{standalone} +% \documentclass{article} + +\usepackage[T1]{fontenc} +\usepackage{lmodern} +\renewcommand{\familydefault}{\sfdefault} +\usepackage{tikz} +\usepackage{tikz-3dplot} +\usetikzlibrary{external} +\tikzset{external/force remake} +\tikzset{external/disable dependency files} +\tikzset{external/aux in dpth={false}} +% uncomment this to generate a figure for each cell type (and change documentclass to article) +% \tikzexternalize + +\tikzstyle{vertex} = [circle,draw=black,fill=black,scale = 0.5] +\tdplotsetmaincoords{70}{110} + +% cnode(tag,x,y,z,label,label_pos) +\def\cnode(#1,#2,#3,#4,#5,#6){ +\node (#1) at (#2,#3,#4) [vertex,label=#6:$\mathsf{#5}$] {}; +} + +\pagestyle{empty} + +\begin{document} + +\begin{tabular}{cc} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +SEGMENT & +SEGMENT3 +\\ +\tikzsetnextfilename{line} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\draw (n0) -- (n1); +\end{tikzpicture} +& +\tikzsetnextfilename{line3} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\cnode(n2,1,0,0,3,below right); +\draw (n0) -- (n2) -- (n1); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +TRIG & +TRIG6 +\\ +\tikzsetnextfilename{triangle} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\cnode(n2,0,2,0,3,right); +\draw (n0) -- (n1) -- (n2) -- (n0); +\end{tikzpicture} +& +\tikzsetnextfilename{triangle6} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\cnode(n2,0,2,0,3,right); +\cnode(n3,1,0,0,6,below right); +\cnode(n4,1,1,0,4,right); +\cnode(n5,0,1,0,5,below right); +\draw (n0) -- (n3) -- (n1) -- (n4) -- (n2) -- (n5) -- (n0); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +QUAD & +QUAD8 +\\ +\tikzsetnextfilename{quad} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,4,below right); +\draw (n0) -- (n1) -- (n2) -- (n3) -- (n0); +\end{tikzpicture} +& +\tikzsetnextfilename{quad8} +\begin{tikzpicture}[scale = 2] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,2,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,4,below right); +\cnode(n4,1,0,0,5,below right); +\cnode(n5,2,1,0,8,below right); +\cnode(n6,1,2,0,6,below right); +\cnode(n7,0,1,0,7,below right); +\draw (n0) -- (n4) -- (n1) -- (n5) -- (n2) -- (n6) -- (n3) -- (n7) -- (n0); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +TET & +TET10 +\\ +\tikzsetnextfilename{tetra} +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,3,below right); +\cnode(n2,0,2,0,2,below right); +\cnode(n3,0,0,2,4,right); +\draw (n0) -- (n1) -- (n2) -- (n0); +\draw (n0) -- (n3); +\draw (n1) -- (n3); +\draw (n2) -- (n3); +\end{tikzpicture} +& +\tikzsetnextfilename{tetra10} % VTK +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,3,below right); +\cnode(n2,0,2,0,2,below right); +\cnode(n3,0,0,2,4,right); +\cnode(n4,1,0,0,6,below right); +\cnode(n5,1,1,0,8,below right); +\cnode(n6,0,1,0,5,below right); +\cnode(n7,0,0,1,7,below right); +\cnode(n8,1,0,1,10,below right); +\cnode(n9,0,1,1,9,right); +\draw (n0) -- (n4) -- (n1) -- (n5) -- (n2) -- (n6) -- (n0); +\draw (n0) -- (n7) -- (n3); +\draw (n1) -- (n8) -- (n3); +\draw (n2) -- (n9) -- (n3); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +PYRAMID & +PYRAMID13 +\\ +\tikzsetnextfilename{pyramid} +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,4,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,2,below right); +\cnode(n4,1,1,2,5,right); +\draw (n0) -- (n1) -- (n2) -- (n3) -- (n0); +\draw (n0) -- (n4); +\draw (n1) -- (n4); +\draw (n2) -- (n4); +\draw (n3) -- (n4); +\end{tikzpicture} +& +\tikzsetnextfilename{pyramid13} % VTK != gmsh +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,4,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,2,below right); +\cnode(n4,1,1,2,5,right); +\cnode(n5,1,0,0,8,below right); +\cnode(n6,2,1,0,7,below right); +\cnode(n7,1,2,0,9,below right); +\cnode(n8,0,1,0,6,below right); +\cnode(n9,0.5,0.5,1,10,below right); +\cnode(n10,1.5,0.5,1,13,below right); +\cnode(n11,1.5,1.5,1,12,below right); +\cnode(n12,0.5,1.5,1,11,right); +\draw (n0) -- (n5) -- (n1) -- (n6) -- (n2) -- (n7) -- (n3) -- (n8) -- (n0); +\draw (n0) -- (n9) -- (n4); +\draw (n1) -- (n10) -- (n4); +\draw (n2) -- (n11) -- (n4); +\draw (n3) -- (n12) -- (n4); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +PRISM & +PRISM15 +\\ +\tikzsetnextfilename{wedge} % gmsh != VTK +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,3,below right); +\cnode(n2,0,2,0,2,below right); +\cnode(n3,0,0,2,4,below right); +\cnode(n4,2,0,2,6,below right); +\cnode(n5,0,2,2,5,below right); +\draw (n0) -- (n1) -- (n2) -- (n0); +\draw (n3) -- (n4) -- (n5) -- (n3); +\draw (n0) -- (n3); +\draw (n1) -- (n4); +\draw (n2) -- (n5); +\end{tikzpicture} +& +\tikzsetnextfilename{wedge15} % VTK != gmsh +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,3,below right); +\cnode(n2,0,2,0,2,below right); +\cnode(n3,0,0,2,4,below right); +\cnode(n4,2,0,2,6,below right); +\cnode(n5,0,2,2,5,below right); +\cnode(n6,1,0,0,8,below right); +\cnode(n7,1,1,0,9,below right); +\cnode(n8,0,1,0,7,below right); +\cnode(n9,1,0,2,14,below right); +\cnode(n10,1,1,2,15,below right); +\cnode(n11,0,1,2,13,below right); +\cnode(n12,0,0,1,10,below right); +\cnode(n13,2,0,1,12,below right); +\cnode(n14,0,2,1,11,below right); +\draw (n0) -- (n6) -- (n1) -- (n7) -- (n2) -- (n8) -- (n0); +\draw (n3) -- (n9) -- (n4) -- (n10) -- (n5) -- (n11) -- (n3); +\draw (n0) -- (n12) -- (n3); +\draw (n1) -- (n13) -- (n4); +\draw (n2) -- (n14) -- (n5); +\end{tikzpicture} +\\[1 em] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +HEX & +HEX20 +\\ +\tikzsetnextfilename{hexahedron} +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,4,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,2,below right); +\cnode(n4,0,0,2,5,below right); +\cnode(n5,2,0,2,8,below right); +\cnode(n6,2,2,2,7,below right); +\cnode(n7,0,2,2,6,below right); +\draw (n0) -- (n1) -- (n2) -- (n3) -- (n0); +\draw (n4) -- (n5) -- (n6) -- (n7) -- (n4); +\draw (n0) -- (n4); +\draw (n1) -- (n5); +\draw (n2) -- (n6); +\draw (n3) -- (n7); +\end{tikzpicture} +& +\tikzsetnextfilename{hexahedron20} % VTK != gmsh +\begin{tikzpicture}[scale = 2, tdplot_main_coords] +\cnode(n0,0,0,0,1,below right); +\cnode(n1,2,0,0,4,below right); +\cnode(n2,2,2,0,3,below right); +\cnode(n3,0,2,0,2,below right); +\cnode(n4,0,0,2,5,below right); +\cnode(n5,2,0,2,8,below right); +\cnode(n6,2,2,2,7,below right); +\cnode(n7,0,2,2,6,below right); +\cnode(n8,1,0,0,11,below right); +\cnode(n9,2,1,0,10,below right); +\cnode(n10,1,2,0,12,below right); +\cnode(n11,0,1,0,9,below right); +\cnode(n12,1,0,2,15,below right); +\cnode(n13,2,1,2,14,below right); +\cnode(n14,1,2,2,16,below right); +\cnode(n15,0,1,2,13,below right); +\cnode(n16,0,0,1,17,below right); +\cnode(n17,2,0,1,20,below right); +\cnode(n18,2,2,1,19,below right); +\cnode(n19,0,2,1,18,below right); +\draw (n0) -- (n8) -- (n1) -- (n9) -- (n2) -- (n10) -- (n3) -- (n11) -- (n0); +\draw (n4) -- (n12) -- (n5) -- (n13) -- (n6) -- (n14) -- (n7) -- (n15) -- (n4); +\draw (n0) -- (n16) -- (n4); +\draw (n1) -- (n17) -- (n5); +\draw (n2) -- (n18) -- (n6); +\draw (n3) -- (n19) -- (n7); +\end{tikzpicture} + + + +\end{tabular} + +\end{document} diff --git a/doc/ng4.tex b/doc/ng4.tex index a63e92e1..0a918fe5 100644 --- a/doc/ng4.tex +++ b/doc/ng4.tex @@ -176,7 +176,7 @@ Lines starting with $\#$ are comment lines. Every CSG file must contain the keyword {\tt algebraic3d} before any non-comment line. The keyword {\tt solid} defines a named solid, here the solid {\it cube} is defined. A solid is defined by the Eulerian operations applied to -primitives. Here, the solid is just the primitve defined by {\tt orthobrick}. +primitives. Here, the solid is just the primitive defined by {\tt orthobrick}. This is a brick parallel to the axis, specified by the minimal $x$, $y$, and $z$ coordinates, and the maximal $x$, $y$, and $z$ coordinates. The present definition gives the cube $[0,1]^3$. Finally, the definition {\tt tlo cube} @@ -260,7 +260,7 @@ amount of red, green and blue (RGB) values. The flag {\tt -transparent} makes the solid appear transparent. -It is possible to specify bounday condition numbers for individual +It is possible to specify boundary condition numbers for individual surfaces of a solid. The flag {\tt -bc} assigns the bc to all surfaces of that solid-tree. If several flags are given the one closest to the leaves of the tree dominates. The following file defines a @@ -536,7 +536,7 @@ STL is a standardized file format to describe (approximate) geometies by triangulated surfaces. It is useful to describe complicated parts which are modeled with some CAD programmes. Also, some users have written their own (C) programmes to define STL geometries, where was not so easy -to use the CSG format. The syntac of STL files is as follos +to use the CSG format. The syntax of STL files is as follows \begin{quote} (not available yet. please figure out the syntax from the examples) \end{quote} @@ -593,10 +593,10 @@ Finally, the refinement factor along the line follows. \chapter{Mesh and Solution Formats} You can export meshes to a couple of file formats. Some are self-defined, -some other are standard formats. The self-defined are the followings: +some other are standard formats. The self-defined are the following: \section{Mesh Size File} -By means of a mesh size file you can provide a local mesh size density. The file extension must be {\it .msz}. If you want to use the mesh size file, you specify it in the ``Meshing Options'', doalog box, page ``Mesh Size''. +By means of a mesh size file you can provide a local mesh size density. The file extension must be {\it .msz}. If you want to use the mesh size file, you specify it in the ``Meshing Options'', dialog box, page ``Mesh Size''. The syntay is: \begin{verbatim} @@ -643,7 +643,7 @@ The Fepp 2D format contains the following sections: \begin{enumerate} \item -boundary segmetns \\ +boundary segments \\ After the number of boundary segments there follows a list of segments. Each segment is specified by the spline - patch number, and the two node indices. Counting starts with 1 @@ -749,7 +749,7 @@ the bottom, and the large drawing window. The menu items will be explained in \item Quit \newline Terminate Netgen \item Generate mesh \newline -Performe mesh generation +Perform mesh generation \item Stop Meshing \newline Stop mesh generation \item Geometry/Edges/Mesh/Solution \newline diff --git a/external_dependencies/pybind11 b/external_dependencies/pybind11 index 2a150736..80dc998e 160000 --- a/external_dependencies/pybind11 +++ b/external_dependencies/pybind11 @@ -1 +1 @@ -Subproject commit 2a150736601bb3113877bb673fb934bb60d46ec5 +Subproject commit 80dc998efced8ceb2be59756668a7e90e8bef917 diff --git a/libsrc/CMakeLists.txt b/libsrc/CMakeLists.txt index eb67d688..9ee2a855 100644 --- a/libsrc/CMakeLists.txt +++ b/libsrc/CMakeLists.txt @@ -4,10 +4,21 @@ add_subdirectory(gprim) add_subdirectory(linalg) add_subdirectory(include) add_subdirectory(meshing) -add_subdirectory(visualization) -add_subdirectory(csg) -add_subdirectory(geom2d) -add_subdirectory(occ) -add_subdirectory(stlgeom) -add_subdirectory(interface) - +if(USE_OCC) + add_subdirectory(occ) +endif(USE_OCC) +if(USE_STLGEOM) + add_subdirectory(stlgeom) +endif(USE_STLGEOM) +if(USE_GUI) + add_subdirectory(visualization) +endif(USE_GUI) +if(USE_INTERFACE) + add_subdirectory(interface) +endif(USE_INTERFACE) +if(USE_CSG) + add_subdirectory(csg) +endif(USE_CSG) +if(USE_GEOM2D) + add_subdirectory(geom2d) +endif(USE_GEOM2D) diff --git a/libsrc/core/.clang-tidy b/libsrc/core/.clang-tidy index 5742a3d8..9ceddce8 100644 --- a/libsrc/core/.clang-tidy +++ b/libsrc/core/.clang-tidy @@ -1,5 +1,8 @@ -Checks: '*,-clang-analyzer-alpha.*,-*braces-around-statements,-fuchsia-*,-google-runtime-references,-readability-implicit-bool-conversion,-google-explicit-constructor,-hicpp-explicit-conversions,-google-runtime-int,-llvm-header-guard,-modernize-pass-by-value' +Checks: '*,-clang-analyzer-alpha.*,-*braces-around-statements,-fuchsia-*,-google-runtime-references,-readability-implicit-bool-conversion,-google-explicit-constructor,-hicpp-explicit-conversions,-google-runtime-int,-llvm-header-guard,-modernize-pass-by-value,-cppcoreguidelines-non-private-member-variables-in-classes,-misc-non-private-member-variables-in-classes,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers' CheckOptions: - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor value: 1 + - key: cppcoreguidelines-macro-usage.AllowedRegexp + value: NGCORE_*|NETGEN_*|NG_EXCEPTION* + WarningsAsErrors: '*' diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 4f7dfdc7..c4f4795e 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -1,17 +1,54 @@ -add_library(ngcore SHARED archive.cpp logging.cpp paje_trace.cpp utils.cpp profiler.cpp) +add_library(ngcore ${NGCORE_LIBRARY_TYPE} + archive.cpp + bitarray.cpp + exception.cpp + localheap.cpp + logging.cpp + flags.cpp + paje_trace.cpp + profiler.cpp + table.cpp + taskmanager.cpp + utils.cpp + version.cpp + ) + +string(REPLACE "|" ";" ng_compile_flags_replace_sep "${NG_COMPILE_FLAGS}") +target_compile_options(ngcore PUBLIC ${ng_compile_flags_replace_sep}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) + target_link_libraries(ngcore PUBLIC stdc++fs) +endif() + +if(USE_PYTHON) + target_sources(ngcore PRIVATE python_ngcore.cpp) + target_compile_definitions(ngcore PUBLIC NETGEN_PYTHON NG_PYTHON PYBIND11_SIMPLE_GIL_MANAGEMENT) +endif(USE_PYTHON) + +if(WIN32) + target_compile_options(ngcore PUBLIC /bigobj /MP /W1 /wd4068) + get_WIN32_WINNT(ver) + target_compile_definitions(ngcore PUBLIC _WIN32_WINNT=${ver} WNT WNT_WINDOW NOMINMAX MSVC_EXPRESS _CRT_SECURE_NO_WARNINGS HAVE_STRUCT_TIMESPEC WIN32) + target_link_options(ngcore PUBLIC /ignore:4273 /ignore:4217 /ignore:4049) +endif(WIN32) target_compile_definitions(ngcore PRIVATE NGCORE_EXPORTS) -if(NOT WIN32) - target_compile_options(ngcore PRIVATE -fvisibility=hidden) -endif(NOT WIN32) - -target_compile_definitions(ngcore PUBLIC $<$:NETGEN_ENABLE_CHECK_RANGE>) +target_include_directories(ngcore INTERFACE $ $) if(CHECK_RANGE) target_compile_definitions(ngcore PUBLIC NETGEN_ENABLE_CHECK_RANGE) endif(CHECK_RANGE) +if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "DEBUG") + target_compile_definitions(ngcore PUBLIC _DEBUG NETGEN_ENABLE_CHECK_RANGE) +endif(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "DEBUG") + +if(TRACE_MEMORY) + target_compile_definitions(ngcore PUBLIC NETGEN_TRACE_MEMORY) +endif(TRACE_MEMORY) + + if(USE_SPDLOG) include_directories(${SPDLOG_INCLUDE_DIR}) install(DIRECTORY ${SPDLOG_INCLUDE_DIR} @@ -24,26 +61,34 @@ 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) -if(USE_PYTHON) - target_compile_definitions(ngcore PUBLIC NETGEN_PYTHON) - target_include_directories(ngcore PUBLIC ${PYTHON_INCLUDE_DIRS}) - target_link_libraries(ngcore PUBLIC ${PYTHON_LIBRARIES}) -endif(USE_PYTHON) +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 +install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp memtracer.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 ranges.hpp ngstream.hpp + simd.hpp simd_avx.hpp simd_avx512.hpp simd_generic.hpp simd_sse.hpp simd_arm64.hpp + register_archive.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.cpp) - target_link_libraries(pyngcore PUBLIC ngcore ${PYTHON_LIBRARIES}) - set_target_properties(pyngcore PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/${NETGEN_PYTHON_RPATH}") - install(TARGETS pyngcore DESTINATION ${NG_INSTALL_DIR_PYTHON} COMPONENT netgen) + pybind11_add_module(pyngcore SHARED python_ngcore_export.cpp) + target_link_libraries(pyngcore PUBLIC ngcore netgen_python) + set_target_properties(pyngcore PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/../${NETGEN_PYTHON_RPATH}") + install(TARGETS pyngcore DESTINATION ${NG_INSTALL_DIR_PYTHON}/pyngcore COMPONENT netgen) endif(USE_PYTHON) diff --git a/libsrc/core/archive.cpp b/libsrc/core/archive.cpp index 9d7f2cd5..5454b929 100644 --- a/libsrc/core/archive.cpp +++ b/libsrc/core/archive.cpp @@ -1,5 +1,7 @@ #include "archive.hpp" +#include "register_archive.hpp" +#include "version.hpp" #ifndef WIN32 #include @@ -7,18 +9,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) @@ -39,4 +29,13 @@ namespace ngcore std::make_unique>(); return type_register->count(classname) != 0; } + +#ifdef NETGEN_PYTHON + pybind11::object CastAnyToPy(const std::any& a) + { + auto info = Archive::GetArchiveRegister(Demangle(a.type().name())); + return info.anyToPyCaster(a); + } +#endif // NETGEN_PYTHON + } // namespace ngcore diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index 4dfa6628..79c3fedd 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -1,14 +1,19 @@ #ifndef NETGEN_CORE_ARCHIVE_HPP #define NETGEN_CORE_ARCHIVE_HPP +#include +#include // for array #include // for complex #include // for size_t, strlen +#include // for path #include // for ifstream, ofstream #include // for function #include // for map #include // for shared_ptr +#include // for optional #include // for string #include // for declval, enable_if_t, false_type, is_co... +#include // for std::byte #include // for type_info #include // for move, swap, pair #include // for vector @@ -21,17 +26,20 @@ #include "version.hpp" // for VersionInfo #ifdef NETGEN_PYTHON -#include +namespace pybind11 +{ + class object; +} #endif // NETGEN_PYTHON 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); + +#ifdef NETGEN_PYTHON + pybind11::object CastAnyToPy(const std::any& a); +#endif // NETGEN_PYTHON class NGCORE_API Archive; - namespace detail { // create new pointer of type T if it is default constructible, else throw @@ -86,12 +94,27 @@ namespace ngcore // This caster takes a void* pointer to the (base)class type_info and returns void* pointing // to the type stored in this info std::function downcaster; + +#ifdef NETGEN_PYTHON + std::function anyToPyCaster; +#endif // NETGEN_PYTHON }; } // namespace detail template constexpr bool is_archivable = detail::is_Archivable_struct::value; + + template + constexpr size_t TotSize () + { + if constexpr (sizeof...(Trest) == 0) + return sizeof(T); + else + return sizeof(T) + TotSize (); + } + + // Base Archive class class NGCORE_API Archive { @@ -108,6 +131,9 @@ namespace ngcore std::map version_map = GetLibraryVersions(); std::shared_ptr logger = GetLogger("Archive"); public: + template + static constexpr bool is_archivable = detail::is_Archivable_struct::value; + Archive() = delete; Archive(const Archive&) = delete; Archive(Archive&&) = delete; @@ -120,28 +146,23 @@ namespace ngcore // once and put them together correctly afterwards. Therefore all objects that may live in // Python should be archived using this Shallow function. If Shallow is called from C++ code // it archives the object normally. +#ifdef NETGEN_PYTHON + template + Archive& Shallow(T& val); // implemented in python_ngcore.hpp +#else // NETGEN_PYTHON template Archive& Shallow(T& val) { static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); -#ifdef NETGEN_PYTHON - if(shallow_to_python) - { - if(is_output) - ShallowOutPython(pybind11::cast(val)); - else - val = pybind11::cast(ShallowInPython()); - } - else -#endif // NETGEN_PYTHON *this & val; return *this; } +#endif // NETGEN_PYTHON #ifdef NETGEN_PYTHON virtual void ShallowOutPython(const pybind11::object& /*unused*/) { throw UnreachableCodeException{}; } - virtual pybind11::object ShallowInPython() + virtual void ShallowInPython(pybind11::object &) { throw UnreachableCodeException{}; } #endif // NETGEN_PYTHON @@ -157,6 +178,8 @@ 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 & (std::byte & d) = 0; + virtual Archive & operator & (float & d) = 0; virtual Archive & operator & (double & d) = 0; virtual Archive & operator & (int & i) = 0; virtual Archive & operator & (long & i) = 0; @@ -208,7 +231,7 @@ namespace ngcore Do(&v[0], size); return (*this); } - + // archive implementation for enums template auto operator & (T& val) -> std::enable_if_t::value, Archive&> @@ -272,12 +295,33 @@ namespace ngcore } return (*this); } + template + Archive& operator& (std::optional& opt) + { + bool has_value = opt.has_value(); + (*this) & has_value; + if(has_value) + { + if(Output()) + (*this) << *opt; + else + { + T value; + (*this) & value; + opt = value; + } + } + return (*this); + } // Archive arrays ===================================================== // this functions can be overloaded in Archive implementations for more efficiency template >> Archive & Do (T * data, size_t n) { for (size_t j = 0; j < n; j++) { (*this) & data[j]; }; return *this; }; // NOLINT + virtual Archive & Do (std::byte * d, size_t n) + { for (size_t j = 0; j < n; j++) { (*this) & d[j]; }; return *this; }; // NOLINT + virtual Archive & Do (double * d, size_t n) { for (size_t j = 0; j < n; j++) { (*this) & d[j]; }; return *this; }; // NOLINT @@ -306,6 +350,55 @@ namespace ngcore val.DoArchive(*this); return *this; } + + + + // pack elements to binary + template + Archive & DoPacked (Types & ... args) + { + if (true) // (isbinary) + { + constexpr size_t totsize = TotSize(); // (args...); + std::byte mem[totsize]; + if (is_output) + { + CopyToBin (&mem[0], args...); + Do(&mem[0], totsize); + } + else + { + Do(&mem[0], totsize); + CopyFromBin (&mem[0], args...); + } + } + // else + // cout << "DoPacked of non-binary called --> individual pickling" << endl; + return *this; + } + + + template + constexpr void CopyToBin (std::byte * ptr, T & first, Trest & ...rest) const + { + memcpy (ptr, &first, sizeof(first)); + CopyToBin(ptr+sizeof(first), rest...); + } + constexpr void CopyToBin (std::byte * ptr) const { } + + template + constexpr void CopyFromBin (std::byte * ptr, T & first, Trest & ...rest) const + { + memcpy (&first, ptr, sizeof(first)); + CopyFromBin(ptr+sizeof(first), rest...); + } + constexpr void CopyFromBin (std::byte * ptr) const { } + + + + + + // Archive shared_ptrs ================================================= template Archive& operator & (std::shared_ptr& ptr) @@ -376,7 +469,7 @@ namespace ngcore // -1 restores a new shared ptr by restoring the inner pointer and creating a shared_ptr to it if (nr == -1) { - logger->debug("Createing new shared_ptr"); + logger->debug("Creating new shared_ptr"); T* p = nullptr; bool neededDowncast; (*this) & neededDowncast & p; @@ -571,13 +664,18 @@ 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; +#ifdef NETGEN_PYTHON + friend pybind11::object CastAnyToPy(const std::any&); +#endif // NETGEN_PYTHON + // Returns ClassArchiveInfo of Demangled typeid static const detail::ClassArchiveInfo& GetArchiveRegister(const std::string& classname); // Set ClassArchiveInfo for Demangled typeid, this is done by creating an instance of @@ -631,33 +729,11 @@ namespace ngcore }; }; - template - class RegisterClassForArchive - { - public: - RegisterClassForArchive() - { - static_assert(detail::all_of_tmpl::value...>, - "Variadic template arguments must be base classes of T"); - detail::ClassArchiveInfo info {}; - info.creator = [this,&info](const std::type_info& ti) -> void* - { return typeid(T) == ti ? detail::constructIfPossible() - : Archive::Caster::tryUpcast(ti, detail::constructIfPossible()); }; - info.upcaster = [this](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, static_cast(p)); }; - info.downcaster = [this](const std::type_info& ti, void* p) -> void* - { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; - Archive::SetArchiveRegister(std::string(Demangle(typeid(T).name())),info); - } - - - }; - // BinaryOutArchive ====================================================================== class NGCORE_API BinaryOutArchive : public Archive { static constexpr size_t BUFFERSIZE = 1024; - char buffer[BUFFERSIZE] = {}; + std::array buffer{}; size_t ptr = 0; protected: std::shared_ptr stream; @@ -668,7 +744,7 @@ namespace ngcore BinaryOutArchive(std::shared_ptr&& astream) : Archive(true), stream(std::move(astream)) { } - BinaryOutArchive(const std::string& filename) + BinaryOutArchive(const std::filesystem::path& filename) : BinaryOutArchive(std::make_shared(filename)) {} ~BinaryOutArchive () override { FlushBuffer(); } @@ -676,6 +752,10 @@ namespace ngcore BinaryOutArchive& operator=(BinaryOutArchive&&) = delete; using Archive::operator&; + Archive & operator & (std::byte & d) override + { return Write(d); } + Archive & operator & (float & f) override + { return Write(f); } Archive & operator & (double & d) override { return Write(d); } Archive & operator & (int & i) override @@ -683,13 +763,18 @@ 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 { return Write(i); } Archive & operator & (bool & b) override { return Write(b); } + Archive & operator & (std::string & str) override { int len = str.length(); @@ -701,7 +786,7 @@ namespace ngcore } Archive & operator & (char *& str) override { - long len = str ? strlen (str) : -1; + long len = str ? static_cast(strlen (str)) : -1; (*this) & len; FlushBuffer(); if(len > 0) @@ -716,19 +801,23 @@ namespace ngcore ptr = 0; } } + Archive & Do (std::byte * d, size_t n) override + { + FlushBuffer(); + stream->write(reinterpret_cast(d), n*sizeof(std::byte)); return *this; + } private: 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; } @@ -743,10 +832,14 @@ namespace ngcore BinaryInArchive (std::shared_ptr&& astream) : Archive(false), stream(std::move(astream)) { } - BinaryInArchive (const std::string& filename) + BinaryInArchive (const std::filesystem::path& filename) : BinaryInArchive(std::make_shared(filename)) { ; } using Archive::operator&; + Archive & operator & (std::byte & d) override + { Read(d); return *this; } + Archive & operator & (float & f) override + { Read(f); return *this; } Archive & operator & (double & d) override { Read(d); return *this; } Archive & operator & (int & i) override @@ -754,7 +847,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 @@ -785,6 +883,8 @@ namespace ngcore return *this; } + Archive & Do (std::byte * d, size_t n) override + { stream->read(reinterpret_cast(d), n*sizeof(std::byte)); return *this; } // NOLINT Archive & Do (double * d, size_t n) override { stream->read(reinterpret_cast(d), n*sizeof(double)); return *this; } // NOLINT Archive & Do (int * i, size_t n) override @@ -807,10 +907,14 @@ namespace ngcore TextOutArchive (std::shared_ptr&& astream) : Archive(true), stream(std::move(astream)) { } - TextOutArchive (const std::string& filename) : + TextOutArchive (const std::filesystem::path& filename) : TextOutArchive(std::make_shared(filename)) { } using Archive::operator&; + Archive & operator & (std::byte & d) override + { *stream << std::hex << int(d) << ' '; return *this; } + 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 @@ -838,7 +942,7 @@ namespace ngcore } Archive & operator & (char *& str) override { - long len = str ? strlen (str) : -1; + long len = str ? static_cast(strlen (str)) : -1; *this & len; if(len > 0) { @@ -858,10 +962,14 @@ namespace ngcore TextInArchive (std::shared_ptr&& astream) : Archive(false), stream(std::move(astream)) { } - TextInArchive (const std::string& filename) + TextInArchive (const std::filesystem::path& filename) : TextInArchive(std::make_shared(filename)) {} using Archive::operator&; + Archive & operator & (std::byte & d) override + { int tmp; *stream >> std::hex >> tmp; d = std::byte(tmp); return *this; } + Archive & operator & (float & f) override + { *stream >> f; return *this; } Archive & operator & (double & d) override { *stream >> d; return *this; } Archive & operator & (int & i) override @@ -882,6 +990,8 @@ namespace ngcore *stream >> len; char ch; stream->get(ch); // '\n' + if(ch == '\r') // windows line endings -> read \n as well + stream->get(ch); str.resize(len); if(len) stream->get(&str[0], len+1, '\0'); @@ -901,6 +1011,8 @@ namespace ngcore if(len) { stream->get(ch); // \n + if(ch == '\r') // windows line endings, read \n as well + stream->get(ch); stream->get(&str[0], len+1, '\0'); // NOLINT } str[len] = '\0'; // NOLINT @@ -908,105 +1020,56 @@ namespace ngcore } }; -#ifdef NETGEN_PYTHON + // HashArchive ================================================================= + // This class enables to easily create hashes for archivable objects by xoring + // threw its data - template - class PyArchive : public ARCHIVE + class NGCORE_API HashArchive : public Archive { - private: - pybind11::list lst; - size_t index = 0; - std::map version_needed; - protected: - using ARCHIVE::stream; - using ARCHIVE::version_map; - using ARCHIVE::logger; - using ARCHIVE::GetLibraryVersions; + size_t hash_value = 0; + char* h; + int offset = 0; public: - PyArchive(const pybind11::object& alst = pybind11::none()) : - ARCHIVE(std::make_shared()), - lst(alst.is_none() ? pybind11::list() : pybind11::cast(alst)) + HashArchive() : Archive(true) + { h = (char*)&hash_value; } + + using Archive::operator&; + Archive & operator & (std::byte & d) override { return ApplyHash(d); } + 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) { - ARCHIVE::shallow_to_python = true; - if(Input()) + size_t n = sizeof(T); + char* pval = (char*)&val; + for(size_t i = 0; i < n; i++) { - stream = std::make_shared - (pybind11::cast(lst[pybind11::len(lst)-1])); - *this & version_needed; - logger->debug("versions needed for unpickling = {}", version_needed); - for(auto& libversion : version_needed) - if(libversion.second > GetLibraryVersion(libversion.first)) - throw Exception("Error in unpickling data:\nLibrary " + libversion.first + - " must be at least " + libversion.second.to_string()); - stream = std::make_shared - (pybind11::cast(lst[pybind11::len(lst)-2])); - *this & version_map; - stream = std::make_shared - (pybind11::cast(lst[pybind11::len(lst)-3])); + h[offset++] ^= pval[i]; + offset %= 8; } - } - - void NeedsVersion(const std::string& library, const std::string& version) override - { - if(Output()) - { - logger->debug("Need version {} of library {}.", version, library); - version_needed[library] = version_needed[library] > version ? version_needed[library] : version; - } - } - - using ARCHIVE::Output; - using ARCHIVE::Input; - using ARCHIVE::FlushBuffer; - using ARCHIVE::operator&; - using ARCHIVE::operator<<; - using ARCHIVE::GetVersion; - void ShallowOutPython(const pybind11::object& val) override { lst.append(val); } - pybind11::object ShallowInPython() override { return lst[index++]; } - - pybind11::list WriteOut() - { - FlushBuffer(); - lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); - stream = std::make_shared(); - *this & GetLibraryVersions(); - FlushBuffer(); - lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); - stream = std::make_shared(); - logger->debug("Writeout version needed = {}", version_needed); - *this & version_needed; - FlushBuffer(); - lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); - return lst; + return *this; } }; - template - auto NGSPickle() - { - return pybind11::pickle([](T* self) - { - PyArchive ar; - 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; - }, - [](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; - }); - } - -#endif // NETGEN_PYTHON } // namespace ngcore #endif // NETGEN_CORE_ARCHIVE_HPP diff --git a/libsrc/core/array.hpp b/libsrc/core/array.hpp new file mode 100644 index 00000000..170f51b8 --- /dev/null +++ b/libsrc/core/array.hpp @@ -0,0 +1,1693 @@ +#ifndef NETGEN_CORE_ARRAY_HPP +#define NETGEN_CORE_ARRAY_HPP + +/**************************************************************************/ +/* File: array.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include +#include + +#include "exception.hpp" +#include "logging.hpp" // for logger +#include "ngcore_api.hpp" // for NGCORE_API +#include "type_traits.hpp" // for all_of_tmpl +#include "localheap.hpp" +#include "memtracer.hpp" +#include "utils.hpp" + +namespace ngcore +{ + using std::ostream; + + template class Tuple + { + public: + int Size() const { return 0; } + }; + + template + class Tuple : Tuple + { + typedef Tuple BASE; + HEAD head; + public: + Tuple () { ; } + Tuple (HEAD h, TAIL ... t) : Tuple (t...), head(h) { ; } + + HEAD Head() const { return head; } + Tuple Tail() const { return *this; } + + int Size() const { return BASE::Size()+1; } + }; + + template + ostream & operator<< (ostream & ost, Tuple /* tup */) + { + return ost; + } + + template + ostream & operator<< (ostream & ost, Tuple tup) + { + ost << tup.Head() << ", " << tup.Tail(); + return ost; + } + + + template + Tuple MakeTuple (ARGS ... args) + { + return Tuple (args...); + } + + + template + class AOWrapperIterator + { + const AO & ao; + size_t ind; + public: + NETGEN_INLINE AOWrapperIterator (const AO & aao, size_t ai) + : ao(aao), ind(ai) { ; } + NETGEN_INLINE AOWrapperIterator operator++ (int) + { return AOWrapperIterator(ao, ind++); } + NETGEN_INLINE AOWrapperIterator& operator++ () + { ++ind; return *this; } + NETGEN_INLINE auto operator*() const -> decltype(ao[ind]) { return ao[ind]; } + NETGEN_INLINE auto operator*() -> decltype(ao[ind]) { return ao[ind]; } + NETGEN_INLINE bool operator != (AOWrapperIterator d2) { return ind != d2.ind; } + NETGEN_INLINE bool operator == (AOWrapperIterator d2) { return ind == d2.ind; } + }; + + + + + /* + Some class which can be treated as array + */ + template // , typename TA = T> + class BaseArrayObject + { + public: + NETGEN_INLINE BaseArrayObject() { ; } + + NETGEN_INLINE const T & Spec() const { return static_cast (*this); } + NETGEN_INLINE size_t Size() const { return Spec().Size(); } + template + NETGEN_INLINE bool Contains(const T2 & el) const + { + for (size_t i = 0; i < Size(); i++) + if (Spec()[i] == el) + return true; + return false; + } + + static constexpr size_t ILLEGAL_POSITION = size_t(-1); + template + NETGEN_INLINE size_t Pos(const T2 & el) const + { + for (size_t i = 0; i < Size(); i++) + if (Spec()[i] == el) + return i; + return ILLEGAL_POSITION; + } + + template + NETGEN_INLINE size_t PosSure(const T2 & el) const + { + for (size_t i = 0; ; i++) + if (Spec()[i] == el) + return i; + } + + // NETGEN_INLINE auto & operator[] (size_t i) { return Spec()[i]; } + NETGEN_INLINE auto operator[] (size_t i) const { return Spec()[i]; } + // NETGEN_INLINE auto begin() const { return Spec().begin(); } + // NETGEN_INLINE auto end() const { return Spec().end(); } + NETGEN_INLINE auto begin () const { return AOWrapperIterator (*this, 0); } + NETGEN_INLINE auto end () const { return AOWrapperIterator (*this, Size()); } + }; + + + + template + class AOWrapper : public BaseArrayObject> + { + T ar; + public: + NETGEN_INLINE AOWrapper (T aar) : ar(aar) { ; } + NETGEN_INLINE operator T () const { return ar; } + NETGEN_INLINE size_t Size() const { return ar.Size(); } + NETGEN_INLINE auto operator[] (size_t i) { return ar[i]; } + NETGEN_INLINE auto operator[] (size_t i) const { return ar[i]; } + NETGEN_INLINE AOWrapperIterator begin () const { return AOWrapperIterator (*this, 0); } + NETGEN_INLINE AOWrapperIterator end () const { return AOWrapperIterator (*this, Size()); } + }; + + template + NETGEN_INLINE AOWrapper ArrayObject (const T & ar) + { + return AOWrapper (ar); + } + + template + NETGEN_INLINE AOWrapper ArrayObject (T && ar) + { + return AOWrapper (ar); + } + + template + auto ArrayObject (size_t s, FUNC f) + { + class Dummy + { + size_t s; + FUNC f; + public: + Dummy (size_t _s, FUNC _f) : s(_s), f(_f) { ; } + size_t Size() const { return s; } + auto operator[] (size_t i) const { return f(i); } + }; + return ArrayObject(Dummy(s,f)); + } + + template + auto Substitute (const BaseArrayObject & ao, FUNC f) + { + return ArrayObject(ao.Size(), + [&ao,f] (size_t i) { return f(ao[i]); }); + } + + + + + /** + nothing more but a new type for a C array. + return value for Addr - operator of array + */ + template + class CArray + { + protected: + /// the data + T * data; + public: + + /// initialize array + NETGEN_INLINE CArray () { data = 0; } + + /// provide size and memory + NETGEN_INLINE CArray (T * adata) + : data(adata) { ; } + + /// Access array + NETGEN_INLINE T & operator[] (size_t i) const + { + return data[i]; + } + + NETGEN_INLINE operator T* () const { return data; } + }; + + + 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; + + + template + class ArrayIterator + { + FlatArray ar; + IndexType ind; + public: + NETGEN_INLINE ArrayIterator (FlatArray aar, IndexType ai) + : ar(aar), ind(ai) { ; } + NETGEN_INLINE ArrayIterator operator++ (int) + { return ArrayIterator(ar, ind++); } + NETGEN_INLINE ArrayIterator operator++ () + { return ArrayIterator(ar, ++ind); } + // NETGEN_INLINE const TELEM & operator*() const { return ar[ind]; } + // NETGEN_INLINE TELEM & operator*() { return ar[ind]; } + NETGEN_INLINE auto operator*() const -> decltype(ar[ind]) { return ar[ind]; } + NETGEN_INLINE auto operator*() -> decltype(ar[ind]) { return ar[ind]; } + NETGEN_INLINE bool operator != (ArrayIterator d2) { return ind != d2.ind; } + NETGEN_INLINE bool operator == (ArrayIterator d2) { return ind == d2.ind; } + }; + + + + template + class ArrayRangeIterator + { + TSIZE ind; + public: + NETGEN_INLINE ArrayRangeIterator (TSIZE ai) : ind(ai) { ; } + NETGEN_INLINE ArrayRangeIterator operator++ (int) { return ind++; } + NETGEN_INLINE ArrayRangeIterator operator++ () { return ++ind; } + NETGEN_INLINE TSIZE operator*() const { return ind; } + NETGEN_INLINE TSIZE Index() { return ind; } + NETGEN_INLINE operator TSIZE () const { return ind; } + NETGEN_INLINE bool operator != (ArrayRangeIterator d2) { return ind != d2.ind; } + NETGEN_INLINE bool operator == (ArrayRangeIterator d2) { return ind == d2.ind; } + }; + + /// a range of integers + template + class T_Range : public BaseArrayObject > + { + T first, next; + public: + NETGEN_INLINE T_Range () { ; } + NETGEN_INLINE T_Range (T n) : first(0), next(n) {;} + NETGEN_INLINE T_Range (T f, T n) : first(f), next(n) {;} + template + NETGEN_INLINE T_Range(T_Range r2) : first(r2.First()), next(r2.Next()) { ; } + NETGEN_INLINE T First() const { return first; } + NETGEN_INLINE T Next() const { return next; } + NETGEN_INLINE T & First() { return first; } + NETGEN_INLINE T & Next() { return next; } + NETGEN_INLINE auto Size() const { return next-first; } + 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); } + NETGEN_INLINE ArrayRangeIterator begin() const { return first; } + NETGEN_INLINE ArrayRangeIterator end() const { return next; } + + NETGEN_INLINE T_Range Split (size_t nr, int tot) const + { + T diff = next-first; + return T_Range (first + nr * diff / tot, + first + (nr+1) * diff / tot); + } + // NETGEN_INLINE operator IntRange () const { return IntRange(first,next); } + }; + + using IntRange = T_Range; + + template + NETGEN_INLINE T_Range Range (T a, T b) + { + return T_Range(a,b); + } + + template + NETGEN_INLINE auto Range (const T& ao) + -> typename std::enable_if, decltype(std::declval().Range())>::type + { return ao.Range(); } + + template + NETGEN_INLINE T_Range Range_impl (T n, std::true_type) + { + return T_Range (0, n); + } + + template + NETGEN_INLINE auto Range_impl (const TA & ao, std::false_type) + -> T_Range> + { + return T_Range> (IndexBASE>(), + IndexBASE>() + index_type(ao.Size())); + } + + /* + Range(obj) will create a range in using the following steps: + + * if obj is an integral type it will create T_Range(0, obj) + * if obj has a function Range() it will return obj.Range() + * if obj has a typedef index_type it will return + T_Range(IndexBASE(), IndexBASE() + index_type(obj.Size())) + * else it will return T_Range (0, obj.Size()) + + */ + template + auto Range(const T & x) + -> typename std::enable_if || !has_range, + decltype(Range_impl(x, std::is_integral()))>::type { + return Range_impl(x, std::is_integral()); + } + + + NETGEN_INLINE IntRange operator+ (const IntRange & range, int shift) + { + return IntRange (range.First()+shift, range.Next()+shift); + } + + NETGEN_INLINE IntRange operator+ (int shift, const IntRange & range) + { + return IntRange (range.First()+shift, range.Next()+shift); + } + + NETGEN_INLINE IntRange operator* (int scale, const IntRange & range) + { + return IntRange (scale*range.First(), scale*range.Next()); + } + + NETGEN_INLINE IntRange operator* (const IntRange & range, int scale) + { + return IntRange (scale*range.First(), scale*range.Next()); + } + + template + inline ostream & operator<< (ostream & s, T_Range ir) + { + s << "[" << ir.First() << "," << ir.Next() << ")"; + return s; + } + + template + ostream & operator<< (ostream & ost, Tuple tup) + { + ost << tup.Head() << ", " << tup.Tail(); + return ost; + } + + + template + inline ostream & operator<< (ostream & ost, const BaseArrayObject & array) + { + for (auto i : Range(array.Size())) + ost << i << ":" << array[i] << std::endl; + return ost; + } + + + template + class IndirectArray : public BaseArrayObject > + { + FlatArray ba; + const INDEX_ARRAY & ia; + + public: + NETGEN_INLINE IndirectArray (FlatArray aba, + const INDEX_ARRAY & aia) + : ba(aba), ia(aia) { ; } + + NETGEN_INLINE size_t Size() const { return ia.Size(); } + NETGEN_INLINE T & operator[] (size_t i) const { return ba[ia[i]]; } + // NETGEN_INLINE T & operator[] (size_t i) { return ba[ia[i]]; } + + NETGEN_INLINE IndirectArray operator= (const T & val) + { + for (auto i : Range(Size())) + (*this)[i] = val; + return IndirectArray (ba, ia); + } + + template + NETGEN_INLINE IndirectArray operator= (const BaseArrayObject & a2) + { + for (auto i : Range(Size())) + (*this)[i] = a2[i]; + return IndirectArray (ba, ia); + } + + NETGEN_INLINE AOWrapperIterator begin() const { return { *this, 0 }; } + NETGEN_INLINE AOWrapperIterator end() const { return { *this, Size() }; } + }; + + + /** + A simple array container. + Array represented by size and data-pointer. + No memory allocation and deallocation, must be provided by user. + Helper functions for printing. + Optional range check by macro NETGEN_CHECK_RANGE + */ + template + class FlatArray : public BaseArrayObject > + { + protected: + static constexpr IndexType BASE = IndexBASE(); + /// the size + size_t size = 0; + /// the data + T * __restrict data = nullptr; + public: + typedef T value_type; + typedef IndexType index_type; + using BaseArrayObject::ILLEGAL_POSITION; + + /// initialize array + NETGEN_INLINE FlatArray () = default; + // { ; } // size = 0; data = 0; } + + /// copy constructor allows size-type conversion + NETGEN_INLINE FlatArray (const FlatArray & a2) = default; + // : size(a2.Size()), data(a2.data) { ; } + + /// provide size and memory + NETGEN_INLINE FlatArray (size_t asize, T * adata) + : size(asize), data(adata) { ; } + + /// memory from local heap + NETGEN_INLINE FlatArray(size_t asize, Allocator & lh) + : size(asize), data(new (lh) T[asize]) + { ; } + + NETGEN_INLINE FlatArray(size_t asize, LocalHeap & lh) + : size(asize), data (lh.Alloc (asize)) + { ; } + + /// the size + NETGEN_INLINE size_t Size() const { return size; } + + /// the data + NETGEN_INLINE T* Data() const { return data; } + + /// Fill array with value val + NETGEN_INLINE const FlatArray & operator= (const T & val) const + { + size_t hsize = size; + T * hdata = data; + for (size_t i = 0; i < hsize; i++) + hdata[i] = val; + return *this; + } + + /// copies array + NETGEN_INLINE const FlatArray & operator= (const FlatArray & a2) const + { + size_t hsize = size; + T * hdata = data; + for (size_t i = 0; i < hsize; i++) hdata[i] = a2.data[i]; + return *this; + } + + template + NETGEN_INLINE const FlatArray & operator= (const BaseArrayObject & a2) const + { + size_t hsize = size; + T * hdata = data; + auto p2 = a2.begin(); + for (size_t i = 0; i < hsize; i++, p2++) hdata[i] = *p2; + return *this; + } + + template ::value>> + NETGEN_INLINE const FlatArray & operator= (const T2 & func) const + { + for (size_t i = 0; i < size; i++) + data[i] = func(i+BASE); + return *this; + } + +// template +// const FlatArray operator= (ParallelValue val); +// template +// const FlatArray operator= (ParallelFunction val); + + /// copies pointers + NETGEN_INLINE const FlatArray & Assign (const FlatArray & a2) + { + size = a2.size; + data = a2.data; + return *this; + } + + /// assigns memory from local heap + NETGEN_INLINE const FlatArray & Assign (size_t asize, LocalHeap & lh) + { + size = asize; + data = lh.Alloc (asize); + return *this; + } + + /// Access array. range check by macro NETGEN_CHECK_RANGE + NETGEN_INLINE T & operator[] (IndexType i) const + { + NETGEN_CHECK_RANGE(i,BASE,size+BASE); + return data[i-BASE]; + } + + NETGEN_INLINE T_Range Range () const + { + return T_Range (BASE, size+BASE); + } + + NETGEN_INLINE const CArray Addr (size_t pos) const + { + return CArray (data+pos-BASE); + } + + // const CArray operator+ (int pos) + // { return CArray (data+pos); } + NETGEN_INLINE T * operator+ (size_t pos) const { return data+pos; } + + /// access last element. check by macro NETGEN_CHECK_RANGE + T & Last () const + { + NETGEN_CHECK_RANGE(size-1,0,size); + return data[size-1]; + } + + /// takes sub-array starting from position pos + NETGEN_INLINE const FlatArray Part (size_t pos) + { + return FlatArray (size-pos, data+pos); + } + + /// takes subsize elements starting from position pos + NETGEN_INLINE const FlatArray Part (size_t pos, size_t subsize) + { + return FlatArray (subsize, data+pos); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE FlatArray Range (size_t start, size_t end) const + { + 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 + { + return FlatArray (range.Size(), data+range.First()); + } + + /// takes range starting from position start of end-start elements + NETGEN_INLINE const FlatArray operator[] (T_Range range) const + { + return FlatArray (range.Size(), data+range.First()); + } + + template + auto operator[] (const BaseArrayObject & ind_array) const + { + return IndirectArray > (*this, ind_array); + } + + /// first position of element elem, returns -1 if element not contained in array + NETGEN_INLINE size_t Pos(const T & el) const + { + for (size_t i = 0; i < Size(); i++) + if (data[i] == el) + return i; + return ILLEGAL_POSITION; + } + + /// does the array contain element elem ? + NETGEN_INLINE bool Contains(const T & elem) const + { + return Pos(elem) != ILLEGAL_POSITION; + } + + //auto begin() const { return ArrayIterator (*this, BASE); } + // auto end() const { return ArrayIterator (*this, size+BASE); } + NETGEN_INLINE auto begin() const { return data; } + NETGEN_INLINE auto end() const { return data+Size(); } + }; + + template + FlatArray View (FlatArray fa) { return fa; } + + template + auto Max (FlatArray array, typename std::remove_const::type max = std::numeric_limits::min()) -> T + { + for (auto & v : array) + if (v > max) max = v; + return max; + } + + template + auto Min (FlatArray array, typename std::remove_const::type 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) + { + for (auto i : a.Range()) + s << i << ": " << a[i] << "\n"; + return s; + } + + /// have arrays the same contents ? + template + inline bool operator== (const FlatArray & a1, + const FlatArray & a2) + { + if (a1.Size () != a2.Size()) return 0; + for (size_t i = 0; i < a1.Size(); i++) + if (a1[i] != a2[i]) return false; + return true; + } + + template + inline bool operator!= (const FlatArray & a1, + const FlatArray & a2) + { + return !(a1==a2); + } + + + /** + Dynamic array container. + + Array is an automatically increasing array container. + The allocated memory doubles on overflow. + Either the container takes care of memory allocation and deallocation, + or the user provides one block of data. + */ + template + class Array : public FlatArray + { + protected: + /// physical size of array + size_t allocsize; + /// 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; + + public: + using index_type = typename FlatArray::index_type; + /// Generate array of logical and physical size asize + NETGEN_INLINE explicit Array() + : FlatArray (0, nullptr) + { + allocsize = 0; + mem_to_delete = nullptr; + } + + NETGEN_INLINE explicit Array(size_t asize) + : FlatArray (asize, new T[asize]) + { + allocsize = asize; + mem_to_delete = data; + } + + + /// Generate array in user data + NETGEN_INLINE Array(size_t asize, T* adata, bool ownMemory = false) + : FlatArray (asize, adata) + { + allocsize = asize; + if(ownMemory) + mem_to_delete = adata; + else + mem_to_delete = nullptr; + } + + /// Generate array in user data + template + NETGEN_INLINE Array(size_t asize, ALLOCATOR & lh) + : FlatArray (asize, lh) + { + allocsize = asize; + mem_to_delete = nullptr; + } + + 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; + mem_to_delete = a2.mem_to_delete; + a2.size = 0; + a2.allocsize = 0; + a2.data = nullptr; + a2.mem_to_delete = nullptr; + } + + /// array copy + NETGEN_INLINE explicit Array (const Array & a2) + : FlatArray (a2.Size(), a2.Size() ? new T[a2.Size()] : nullptr) + { + 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]; + } + +// #ifdef __cpp_exceptions +#ifndef __CUDA_ARCH__ + else + throw Exception(std::string("cannot copy-construct Array of type ") + typeid(T).name()); +#endif + } + + + template + explicit Array (const BaseArrayObject & 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[i]; + */ + auto p2 = a2.begin(); + for (size_t i = 0; i < size; i++, p2++) + data[i] = *p2; + + } + + Array (std::initializer_list list) + : FlatArray (list.size(), + list.size() ? new T[list.size()] : NULL) + { + allocsize = size; + mem_to_delete = data; + size_t cnt = 0; + for (auto val : list) + data[cnt++] = val; + } + + /// array merge-copy + explicit Array (const Array & a2, const Array & a3) + : FlatArray (a2.Size()+a3.Size(), + a2.Size()+a3.Size() ? new T[a2.Size()+a3.Size()] : 0) + { + allocsize = size; + mem_to_delete = data; + for(size_t i = 0; i < a2.Size(); i++) + data[i] = a2[i]; + for (size_t i = a2.Size(), j=0; i < size; i++,j++) + data[i] = a3[j]; + } + + /// if responsible, deletes memory + NETGEN_INLINE ~Array() + { + if(mem_to_delete) + mt.Free(sizeof(T)*allocsize); + delete [] mem_to_delete; + } + + // Only provide this function if T is archivable + template + auto DoArchive(ARCHIVE& archive) + -> typename std::enable_if_t, void> + { + if(archive.Output()) + archive << size; + else + { + size_t s; + archive & s; + SetSize(s); + } + archive.Do(data, size); + } + + /// we tell the compiler that there is no need for deleting the array .. + NETGEN_INLINE void NothingToDelete () + { + mem_to_delete = nullptr; + } + + /// Change logical size. If necessary, do reallocation. Keeps contents. + NETGEN_INLINE void SetSize(size_t nsize) + { + if (nsize > allocsize) ReSize (nsize); + size = nsize; + } + + /// + NETGEN_INLINE void SetSize0() + { + size = 0; + } + + /// Change physical size. Keeps logical size. Keeps contents. + NETGEN_INLINE void SetAllocSize (size_t nallocsize) + { + if (nallocsize > allocsize) + ReSize (nallocsize); + } + + /// Change physical size. Keeps logical size. Keeps contents. + NETGEN_INLINE size_t AllocSize () const + { + return allocsize; + } + + + /// 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); + mem_to_delete = nullptr; + return *this; + } + + /// Add element at end of array. reallocation if necessary. + /// Returns index of new element. + NETGEN_INLINE index_type Append (const T & el) + { + if (size == allocsize) + ReSize (size+1); + data[size] = el; + return BASE + size++; + } + + /// Add element at end of array. reallocation not necessary. + /// Returns index of new element. + NETGEN_INLINE index_type AppendHaveMem (const T & el) + { + NETGEN_CHECK_RANGE(size, 0, allocsize); + data[size] = el; + return BASE + size++; + } + + + /// Add element at end of array. reallocation if necessary. + /// Returns index of new element. + NETGEN_INLINE index_type Append (T && el) + { + if (size == allocsize) + ReSize (size+1); + data[size] = std::move(el); + return BASE + size++; + } + + // Add elements of initializer list to end of array. Reallocation if necessary. + NETGEN_INLINE void Append(std::initializer_list lst) + { + if(allocsize < size + lst.size()) + ReSize(size+lst.size()); + for(auto val : lst) + data[size++] = val; + } + + /// Add element at end of array. reallocation if necessary. + NETGEN_INLINE void Insert (size_t pos, const T & el) + { + if (size == allocsize) + ReSize (size+1); + for (size_t i = size; i > pos; i--) + data[i] = data[i-1]; + data[pos] = el; + size++; + } + + NETGEN_INLINE Array & operator += (const T & el) + { + Append (el); + return *this; + } + + + /// Append array at end of array. reallocation if necessary. + NETGEN_INLINE void Append (FlatArray source) + { + if(size + source.Size() >= allocsize) + ReSize (size + source.Size() + 1); + + for(size_t i = size, j=0; jsize-1; j++) + this->data[j] = this->data[j+1]; + this->size--; + } + + + /// Delete last element. + NETGEN_INLINE void DeleteLast () + { + NETGEN_CHECK_RANGE(size-1,0,size); + size--; + } + + /// 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; + size = allocsize = 0; + } + + /// Fill array with val + NETGEN_INLINE Array & operator= (const T & val) + { + FlatArray::operator= (val); + return *this; + } + + /// array copy + NETGEN_INLINE Array & operator= (const Array & a2) + { + 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); + ngcore::Swap (mem_to_delete, a2.mem_to_delete); + return *this; + } + + + /// array copy + NETGEN_INLINE Array & operator= (const FlatArray & a2) + { + SetSize (a2.Size()); + for (size_t i = 0; i < size; i++) + data[i] = a2[i]; + return *this; + } + + /* + /// fill array with first, first+1, ... + Array & operator= (const IntRange & range) + { + SetSize (range.Size()); + for (int i = 0; i < size; i++) + (*this)[i] = range.First()+i; + return *this; + } + */ + template + Array & operator= (const BaseArrayObject & a2) + { + size_t newsize = a2.Spec().Size(); + SetSize0 (); + SetSize (newsize); + // for (size_t i = 0; i < newsize; i++) + // (*this)[i] = a2.Spec()[i]; + size_t i = 0; + for (auto val : a2.Spec()) + (*this)[i++] = val; + + return *this; + } + + template + Array & operator= (Tuple tup) + { + SetSize (ArraySize (tup)); + StoreToArray (*this, tup); + return *this; + } + + Array & operator= (std::initializer_list list) + { + *this = Array (list); + return *this; + } + + +// template +// Array & operator= (ParallelValue val) +// { +// FlatArray::operator= (val); +// return *this; +// } +// template +// Array & operator= (ParallelFunction val) +// { +// FlatArray::operator= (val); +// return *this; +// } + + + 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; + }; + + + /// resize array, at least to size minsize. copy contents + template + NETGEN_INLINE void Array :: ReSize (size_t minsize) + { + size_t nsize = 2 * allocsize; + if (nsize < minsize) nsize = minsize; + + T * hdata = data; + data = new T[nsize]; + mt.Alloc(sizeof(T) * nsize); + + if (hdata) + { + size_t mins = (nsize < size) ? nsize : size; +#if defined(__GNUG__) && __GNUC__ < 5 && !defined(__clang__) + for (size_t i = 0; i < mins; i++) data[i] = std::move(hdata[i]); +#else + if (std::is_trivially_copyable::value) + memcpy ((void*)data, hdata, sizeof(T)*mins); + 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; + } + + mem_to_delete = data; + allocsize = nsize; + } + + //extern template class Array; + + + /** + Array with static and dynamic memory management. + Declares a static array which size is given by the template parameter. + If the dynamic size fits into the static size, use static memory, + otherwise perform dynamic allocation + */ + template + class ArrayMem : public Array + { + T mem[S]; + + using Array::size; + using Array::allocsize; + using Array::data; + using Array::mem_to_delete; + // using Array::ownmem; + + public: + /// Generate array of logical and physical size asize + explicit ArrayMem(size_t asize = 0) + : Array (S, mem) + { + size = asize; + if (asize > S) + { + data = new T[asize]; + allocsize = size; + mem_to_delete = data; + } + } + + /// copies from Array a2 + explicit ArrayMem(const Array & a2) + : Array (S, (T*)mem) + { + Array::operator= (a2); + } + + /// copies from ArrayMem a2 + explicit ArrayMem(const ArrayMem & a2) + : Array (S, (T*)mem) + { + Array::operator= (a2); + } + + ArrayMem(ArrayMem && a2) + : Array (a2.Size(), (T*)mem) + { + if (a2.mem_to_delete) + { + mem_to_delete = a2.mem_to_delete; + data = a2.data; + allocsize = a2.allocsize; + a2.mem_to_delete = nullptr; + a2.data = nullptr; + a2.size = 0; + } + else + { + allocsize = S; + for (auto i : ngcore::Range(size)) + mem[i] = a2.mem[i]; + } + } + + ArrayMem (std::initializer_list list) + : ArrayMem (list.size()) + { + size_t cnt = 0; + for (auto val : list) + 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) + { + FlatArray::operator= (val); + return *this; + } + + ArrayMem & operator= (ArrayMem && a2) + { + ngcore::Swap (mem_to_delete, a2.mem_to_delete); + ngcore::Swap (allocsize, a2.allocsize); + ngcore::Swap (size, a2.size); + + if (mem_to_delete==nullptr) + { + for (auto i : ngcore::Range(size)) + mem[i] = std::move(a2.mem[i]); + data = mem; + } + else + ngcore::Swap (data, a2.data); + + return *this; + } + + + /// array copy + ArrayMem & operator= (const FlatArray & a2) + { + this->SetSize (a2.Size()); + for (size_t i = 0; i < size; i++) + (*this)[i] = a2[i]; + return *this; + } + + + template + ArrayMem & operator= (const BaseArrayObject & a2) + { + this->SetSize (a2.Spec().Size()); + + size_t i = 0; + for (auto val : a2.Spec()) + (*this)[i++] = val; + + return *this; + } + + }; + + + + + + template + size_t ArraySize (Tuple /* tup */) + { return 0;} + + template + size_t ArraySize (Tuple tup) + { return 1+ArraySize(tup.Tail()); } + + template + size_t ArraySize (Tuple tup) + { return tup.Head().Size()+ArraySize(tup.Tail()); } + + + template + void StoreToArray (FlatArray /* a */, Tuple /* tup */) { ; } + + template + void StoreToArray (FlatArray a, Tuple tup) + { + a[0] = tup.Head(); + StoreToArray (a.Range(1, a.Size()), tup.Tail()); + } + + template + void StoreToArray (FlatArray a, Tuple tup) + { + IntRange r = tup.Head(); + a.Range(0,r.Size()) = r; + StoreToArray (a.Range(r.Size(), a.Size()), tup.Tail()); + } + + /* + template template + NETGEN_INLINE Array & Array :: operator= (Tuple tup) + { + SetSize (ArraySize (tup)); + StoreToArray (*this, tup); + } + */ + + /* + /// append integers to array + inline Array & operator+= (Array & array, const IntRange & range) + { + int oldsize = array.Size(); + int s = range.Next() - range.First(); + + array.SetSize (oldsize+s); + + for (int i = 0; i < s; i++) + array[oldsize+i] = range.First()+i; + + return array; + } + */ + + + /* + template + inline Array & operator+= (Array & array, const BaseArrayObject & a2) + { + size_t oldsize = array.Size(); + size_t s = a2.Spec().Size(); + + array.SetSize (oldsize+s); + + for (size_t i = 0; i < s; i++) + array[oldsize+i] = a2.Spec()[i]; + + return array; + } + */ + + template + inline Array & operator+= (Array & array, const BaseArrayObject & a2) + { + auto oldsize = array.Size(); + auto s = a2.Spec().Size(); + + array.SetSize (oldsize+s); + + for (auto val : a2.Spec()) + array[oldsize++] = val; + + return array; + } + + template + inline Array operator+= (Array && array, const BaseArrayObject & a2) + { + array += a2; + return std::move(array); + } + + + /// bubble sort array + template + inline void BubbleSort (FlatArray data) + { + T hv; + for (size_t i = 0; i < data.Size(); i++) + for (size_t j = i+1; j < data.Size(); j++) + if (data[i] > data[j]) + { + hv = data[i]; + data[i] = data[j]; + data[j] = hv; + } + } + + /// bubble sort array + template + 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++) + if (data[i] > data[j]) + { + T hv = data[i]; + data[i] = data[j]; + data[j] = hv; + + S hvs = index[i]; + index[i] = index[j]; + index[j] = hvs; + } + } + + + + + template + void QuickSort (FlatArray data, TLESS less) + { + if (data.Size() <= 1) return; + + ptrdiff_t i = 0; + ptrdiff_t j = data.Size()-1; + + T midval = data[ (i+j)/2 ]; + + do + { + while (less (data[i], midval)) i++; + while (less (midval, data[j])) j--; + + if (i <= j) + { + Swap (data[i], data[j]); + i++; j--; + } + } + while (i <= j); + + QuickSort (data.Range (0, j+1), less); + QuickSort (data.Range (i, data.Size()), less); + } + + template + NETGEN_INLINE bool DefaultLess (const T & a, const T & b) + { + return a < b; + } + + template + class DefaultLessCl + { + public: + bool operator() (const T & a, const T & b) const + { + return a < b; + } + }; + + + + template + NETGEN_INLINE void QuickSort (FlatArray data) + { + QuickSort (data, DefaultLessCl()); + } + + + + template + void QuickSortI (FlatArray data, FlatArray index, TLESS less) + { + if (index.Size() <= 1) return; + + ptrdiff_t i = 0; + ptrdiff_t j = index.Size()-1; + + int midval = index[ (i+j)/2 ]; + + do + { + while (less (data[index[i]],data[midval]) ) i++; + while (less (data[midval], data[index[j]])) j--; + + if (i <= j) + { + Swap (index[i], index[j]); + i++; j--; + } + } + while (i <= j); + + QuickSortI (data, index.Range (0, j+1), less); + QuickSortI (data, index.Range (i, index.Size()), less); + } + + + template + NETGEN_INLINE void QuickSortI (FlatArray data, FlatArray index) + { + QuickSortI (data, index, DefaultLessCl()); + } + + + + + + template + NETGEN_INLINE T xxxRemoveRef (const T & x) + { + return x; + } + + template + class SumArray : public BaseArrayObject> + { + const TA1 & a1; + const TA2 & a2; + public: + SumArray (const TA1 & aa1, const TA2 & aa2) : a1(aa1), a2(aa2) { ; } + size_t Size() const { return a1.Size()+a2.Size(); } + auto operator[] (size_t i) const -> decltype (xxxRemoveRef (a1[0])) + { + return (i < a1.Size()) ? a1[i] : a2[i-a1.Size()]; + } + }; + + template + SumArray operator+ (const BaseArrayObject & a1, + const BaseArrayObject & a2) + { + return SumArray (a1.Spec(), a2.Spec()); + } + + + // head-tail array + template + class HTArray + { + HTArray tail; + T head; + public: + HTArray () = default; + HTArray (const HTArray &) = default; + template + HTArray (const HTArray & a2) : tail(a2.Tail()), head(a2.Head()) { ; } + + HTArray & operator= (const HTArray &) = default; + + T * Ptr () { return tail.Ptr(); } + T & operator[] (size_t i) { return Ptr()[i]; } + + const T * Ptr () const { return tail.Ptr(); } + const T & operator[] (size_t i) const { return Ptr()[i]; } + template + T & Elem() { return (NR==S-1) ? head : tail.template Elem(); } + + auto Tail() const { return tail; } + auto Head() const { return head; } + }; + + template + class HTArray<1,T> + { + T head; + public: + HTArray () = default; + HTArray (const HTArray &) = default; + template + HTArray (const HTArray<1,T2> & a2) : head(a2.Head()) { ; } + + HTArray & operator= (const HTArray &) = default; + + T * Ptr () { return &head; } + T & operator[] (size_t i) { return Ptr()[i]; } + + const T * Ptr () const { return &head; } + const T & operator[] (size_t i) const { return Ptr()[i]; } + template + T & Elem() + { + // assert(NR==0, "HTArray index error"); + return head; + } + + auto Head() const { return head; } + }; + + template + class HTArray<0,T> + { + // T head; // dummy variable + public: + HTArray () = default; + HTArray (const HTArray &) = default; + template + HTArray (const HTArray<0,T2> & a2) { ; } + + HTArray & operator= (const HTArray &) = default; + + /* + T * Ptr () { return &head; } + T & operator[] (size_t i) { return Ptr()[i]; } + + const T * Ptr () const { return &head; } + const T & operator[] (size_t i) const { return Ptr()[i]; } + template + T & Elem() + { + // assert(false, "HTArray index error"); + return head; + } + */ + // T * Ptr () { return (T*)(void*)&head; } + T * Ptr () { return (T*)(void*)this; } + T & operator[] (size_t i) { return Ptr()[i]; } + // const T * Ptr () const { return (const T*)(const void*)&head; } + const T * Ptr () const { return (const T*)(const void*)this; } + const T & operator[] (size_t i) const { return Ptr()[i]; } + template + T & Elem() + { + throw Exception("illegal HTArray<0>::Elem<0>"); + } + + }; + + template + const T * operator+ (const HTArray & ar, size_t i) + { + return ar.Ptr()+i; + } + template + T * operator+ (HTArray & ar, size_t i) + { + return ar.Ptr()+i; + } + + + + + + + + + + + template + class IteratorPair + { + TIA a; + TIB b; + public: + IteratorPair (TIA _a, TIB _b) : a(_a), b(_b) { ; } + + IteratorPair & operator++() { ++a; ++b; return *this; } + bool operator!= (const IteratorPair & it2) { return a != it2.a; } + + auto operator*() + { + // return pair(*a,*b); + return std::pair (*a, *b); // keep reference + } + }; + + + template + class Zip + { + const TA & a; + const TB & b; + public: + Zip(const TA & _a, const TB & _b) : a(_a), b(_b) { ; } + auto begin() const { return IteratorPair(a.begin(), b.begin()); } + auto end() const { return IteratorPair(a.end(), b.end()); } + }; + + template + inline size_t size (const BaseArrayObject & ao) { return ao.Size(); } + + template + class Enumerate + { + IntRange r; + const TA & a; + public: + Enumerate(const TA & _a) : r(size(_a)), a(_a) { ; } + auto begin() const { return IteratorPair(r.begin(), a.begin()); } + auto end() const { return IteratorPair(r.end(), a.end()); } + }; + + + + + +} + + +#endif // NETGEN_CORE_ARRAY_HPP + diff --git a/libsrc/core/bitarray.cpp b/libsrc/core/bitarray.cpp new file mode 100644 index 00000000..8c76ba90 --- /dev/null +++ b/libsrc/core/bitarray.cpp @@ -0,0 +1,184 @@ +/**************************************************************************/ +/* File: bitarray.cpp */ +/* Autho: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +/* + data type BitArray +*/ + +#include "bitarray.hpp" +#include "archive.hpp" + +namespace ngcore +{ + BitArray :: BitArray (size_t asize) + { + size = 0; + data = NULL; + SetSize (asize); + } + + BitArray :: BitArray (size_t asize, LocalHeap & lh) + { + size = asize; + data = new (lh) unsigned char [Addr (size)+1]; + owns_data = false; + } + + BitArray :: BitArray (const BitArray & ba2) + { + size = 0; + data = NULL; + (*this) = ba2; + } + + void BitArray :: SetSize (size_t asize) + { + if (size == asize) return; + 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() + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = UCHAR_MAX; + return *this; + } + + BitArray & BitArray :: Clear () throw() + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = 0; + return *this; + } + + BitArray & BitArray :: Invert () + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] ^= 255; + return *this; + } + + BitArray & BitArray :: And (const BitArray & ba2) + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] &= ba2.data[i]; + return *this; + } + + + BitArray & BitArray :: Or (const BitArray & ba2) + { + if (!size) return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] |= ba2.data[i]; + 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) + { + SetSize (ba2.Size()); + if (!size) + return *this; + for (size_t i = 0; i <= Addr (size); i++) + data[i] = ba2.data[i]; + return *this; + } + + std::ostream & operator<<(std::ostream & s, const BitArray & ba) + { + size_t n = ba.Size(); + for (size_t i = 0; i < n; i++) + { + if (i % 50 == 0) s << i << ": "; + s << int(ba[i]); + if (i % 50 == 49) s << "\n"; + } + s << std::flush; + return s; + } + + size_t BitArray :: NumSet () const + { + size_t cnt = 0; + for (size_t i = 0; i < Size(); i++) + if (Test(i)) cnt++; + return cnt; + } + + void BitArray :: DoArchive(Archive& archive) + { + if(archive.GetVersion("netgen") >= "v6.2.2007-62") + { + 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 + { + if (archive.Output()) + { + 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); + } + } + } + } +} // namespace ngcore diff --git a/libsrc/core/bitarray.hpp b/libsrc/core/bitarray.hpp new file mode 100644 index 00000000..8ae32e3c --- /dev/null +++ b/libsrc/core/bitarray.hpp @@ -0,0 +1,210 @@ +#ifndef NETGEN_CORE_BITARRAY +#define NETGEN_CORE_BITARRAY + +/**************************************************************************/ +/* File: bitarray.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include +#include +#include + +#include "array.hpp" +#include "localheap.hpp" +#include "ngcore_api.hpp" +#include "utils.hpp" + +namespace ngcore +{ + +/** + A compressed array of bools. + + Provides bit-operations and whole array operations. +*/ +class BitArray +{ +protected: + /// number of bits + size_t size; + + /// the data + unsigned char * data; + /// + bool owns_data = true; +public: + /// empty array + BitArray () + : size(0), data(nullptr) { ; } + /// array of asize bits + NGCORE_API BitArray (size_t asize); + /// array of asize bits + NGCORE_API BitArray (size_t asize, LocalHeap & lh); + /// + NGCORE_API BitArray (const BitArray & ba2); + BitArray (BitArray && ba2) + : size(ba2.size), data(ba2.data), owns_data(ba2.owns_data) + { + ba2.owns_data = false; + ba2.data = nullptr; + } + + template + NETGEN_INLINE BitArray (std::initializer_list list) + : BitArray (list.size()) + { + Clear(); + int cnt = 0; + for (auto i = list.begin(); i < list.end(); i++, cnt++) + if (*i) SetBit(cnt); + } + + /// delete data + ~BitArray () + { + if (owns_data) + delete [] data; + } + + /// Set size, loose values + NGCORE_API void SetSize (size_t asize); + + /// the size + size_t Size () const { return size; } + + /// set all bits + NGCORE_API BitArray & Set () throw(); + + /// clear all bits + NGCORE_API BitArray & Clear () throw(); + + /// set bit i + [[deprecated("Use either SetBit() or SetBitAtomic()")]] + void Set (size_t i) { SetBitAtomic(i); } + + /// set bit i ( not thread safe ) + void SetBit (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + data[Addr(i)] |= Mask(i); + } + + /// set bit i ( thread safe ) + void SetBitAtomic (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + unsigned char * p = data+Addr(i); + unsigned char mask = Mask(i); + + AsAtomic(*p) |= mask; + } + + /// clear bit i + void Clear (size_t i) + { + NETGEN_CHECK_RANGE(i, 0, size); + data[Addr(i)] &= ~Mask(i); + } + + /// check bit i + bool Test (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, size); + return (data[Addr(i)] & Mask(i)) ? true : false; + } + + /// set all bits to b + BitArray & operator= (bool b) + { + if (b) Set(); + else Clear(); + return *this; + } + + /// check bit i + bool operator[] (size_t i) const + { + NETGEN_CHECK_RANGE(i, 0, size); + return Test(i); + } + + NGCORE_API bool operator==(const BitArray& other) const; + + /// invert all bits + NGCORE_API BitArray & Invert (); + + /// logical AND with ba2 + NGCORE_API BitArray & And (const BitArray & ba2); + + /// logical OR with ba2 + NGCORE_API BitArray & Or (const BitArray & ba2); + + /// copy from ba2 + NGCORE_API BitArray & operator= (const BitArray & ba2); + + NGCORE_API size_t NumSet () const; + + NGCORE_API void DoArchive(class 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 + { return char(1) << (i % CHAR_BIT); } + + /// + size_t Addr (size_t i) const + { return (i / CHAR_BIT); } + + MemoryTracer mt; +}; + + + inline BitArray & operator|= (BitArray & me, const BitArray & you) + { + me.Or(you); + return me; + } + + inline BitArray & operator&= (BitArray & me, const BitArray & you) + { + me.And(you); + return me; + } + + inline BitArray operator| (const BitArray & a, const BitArray & b) + { + BitArray res = a; + res |= b; + return res; + } + + inline BitArray operator& (const BitArray & a, const BitArray & b) + { + BitArray res = a; + res &= b; + return res; + } + + inline BitArray operator~ (const BitArray & a) + { + BitArray res = a; + res.Invert(); + return res; + } + + NGCORE_API std::ostream & operator<<(std::ostream & s, const BitArray & ba); + +} // namespace ngcore + +#endif // NETGEN_CORE_BITARRAY diff --git a/libsrc/core/concurrentqueue.h b/libsrc/core/concurrentqueue.h new file mode 100644 index 00000000..62689834 --- /dev/null +++ b/libsrc/core/concurrentqueue.h @@ -0,0 +1,3619 @@ +// Provides a C++11 implementation of a multi-producer, multi-consumer lock-free queue. +// An overview, including benchmark results, is provided here: +// http://moodycamel.com/blog/2014/a-fast-general-purpose-lock-free-queue-for-c++ +// The full design is also described in excruciating detail at: +// http://moodycamel.com/blog/2014/detailed-design-of-a-lock-free-queue + +// Boost Software License - Version 1.0 - August 17th, 2003 +// +// Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +#pragma once + +#if defined(__GNUC__) +// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and +// Traits::index_t are set to < 32 bits, causing integer promotion, causing warnings +// upon assigning any computed values) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" + +#ifdef MCDBGQ_USE_RELACY +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" +#endif +#endif + +#if defined(__APPLE__) +#include "TargetConditionals.h" +#endif + +#ifdef MCDBGQ_USE_RELACY +#include "relacy/relacy_std.hpp" +#include "relacy_shims.h" +// We only use malloc/free anyway, and the delete macro messes up `= delete` method declarations. +// We'll override the default trait malloc ourselves without a macro. +#undef new +#undef delete +#undef malloc +#undef free +#else +#include // Requires C++11. Sorry VS2010. +#include +#endif +#include // for max_align_t +#include +#include +#include +#include +#include +#include +#include // for CHAR_BIT +#include +#include // partly for __WINPTHREADS_VERSION if on MinGW-w64 w/ POSIX threading + +// Platform-specific definitions of a numeric thread ID type and an invalid value +namespace moodycamel { namespace details { + template struct thread_id_converter { + typedef thread_id_t thread_id_numeric_size_t; + typedef thread_id_t thread_id_hash_t; + static thread_id_hash_t prehash(thread_id_t const& x) { return x; } + }; +} } +#if defined(MCDBGQ_USE_RELACY) +namespace moodycamel { namespace details { + typedef std::uint32_t thread_id_t; + static const thread_id_t invalid_thread_id = 0xFFFFFFFFU; + static const thread_id_t invalid_thread_id2 = 0xFFFFFFFEU; + static inline thread_id_t thread_id() { return rl::thread_index(); } +} } +#elif defined(_WIN32) || defined(__WINDOWS__) || defined(__WIN32__) +// No sense pulling in windows.h in a header, we'll manually declare the function +// we use and rely on backwards-compatibility for this not to break +extern "C" __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); +namespace moodycamel { namespace details { + static_assert(sizeof(unsigned long) == sizeof(std::uint32_t), "Expected size of unsigned long to be 32 bits on Windows"); + typedef std::uint32_t thread_id_t; + static const thread_id_t invalid_thread_id = 0; // See http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx + static const thread_id_t invalid_thread_id2 = 0xFFFFFFFFU; // Not technically guaranteed to be invalid, but is never used in practice. Note that all Win32 thread IDs are presently multiples of 4. + static inline thread_id_t thread_id() { return static_cast(::GetCurrentThreadId()); } +} } +#elif defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || (defined(__APPLE__) && TARGET_OS_IPHONE) +namespace moodycamel { namespace details { + static_assert(sizeof(std::thread::id) == 4 || sizeof(std::thread::id) == 8, "std::thread::id is expected to be either 4 or 8 bytes"); + + typedef std::thread::id thread_id_t; + static const thread_id_t invalid_thread_id; // Default ctor creates invalid ID + + // Note we don't define a invalid_thread_id2 since std::thread::id doesn't have one; it's + // only used if MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is defined anyway, which it won't + // be. + static inline thread_id_t thread_id() { return std::this_thread::get_id(); } + + template struct thread_id_size { }; + template<> struct thread_id_size<4> { typedef std::uint32_t numeric_t; }; + template<> struct thread_id_size<8> { typedef std::uint64_t numeric_t; }; + + template<> struct thread_id_converter { + typedef thread_id_size::numeric_t thread_id_numeric_size_t; +#ifndef __APPLE__ + typedef std::size_t thread_id_hash_t; +#else + typedef thread_id_numeric_size_t thread_id_hash_t; +#endif + + static thread_id_hash_t prehash(thread_id_t const& x) + { +#ifndef __APPLE__ + return std::hash()(x); +#else + return *reinterpret_cast(&x); +#endif + } + }; +} } +#else +// Use a nice trick from this answer: http://stackoverflow.com/a/8438730/21475 +// In order to get a numeric thread ID in a platform-independent way, we use a thread-local +// static variable's address as a thread identifier :-) +#if defined(__GNUC__) || defined(__INTEL_COMPILER) +#define MOODYCAMEL_THREADLOCAL __thread +#elif defined(_MSC_VER) +#define MOODYCAMEL_THREADLOCAL __declspec(thread) +#else +// Assume C++11 compliant compiler +#define MOODYCAMEL_THREADLOCAL thread_local +#endif +namespace moodycamel { namespace details { + typedef std::uintptr_t thread_id_t; + static const thread_id_t invalid_thread_id = 0; // Address can't be nullptr + static const thread_id_t invalid_thread_id2 = 1; // Member accesses off a null pointer are also generally invalid. Plus it's not aligned. + static inline thread_id_t thread_id() { static MOODYCAMEL_THREADLOCAL int x; return reinterpret_cast(&x); } +} } +#endif + +// Exceptions +#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED +#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__)) +#define MOODYCAMEL_EXCEPTIONS_ENABLED +#endif +#endif +#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED +#define MOODYCAMEL_TRY try +#define MOODYCAMEL_CATCH(...) catch(__VA_ARGS__) +#define MOODYCAMEL_RETHROW throw +#define MOODYCAMEL_THROW(expr) throw (expr) +#else +#define MOODYCAMEL_TRY if (true) +#define MOODYCAMEL_CATCH(...) else if (false) +#define MOODYCAMEL_RETHROW +#define MOODYCAMEL_THROW(expr) +#endif + +#ifndef MOODYCAMEL_NOEXCEPT +#if !defined(MOODYCAMEL_EXCEPTIONS_ENABLED) +#define MOODYCAMEL_NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) true +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) true +#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1800 +// VS2012's std::is_nothrow_[move_]constructible is broken and returns true when it shouldn't :-( +// We have to assume *all* non-trivial constructors may throw on VS2012! +#define MOODYCAMEL_NOEXCEPT _NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value : std::is_trivially_copy_constructible::value) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) +#elif defined(_MSC_VER) && defined(_NOEXCEPT) && _MSC_VER < 1900 +#define MOODYCAMEL_NOEXCEPT _NOEXCEPT +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) (std::is_rvalue_reference::value && std::is_move_constructible::value ? std::is_trivially_move_constructible::value || std::is_nothrow_move_constructible::value : std::is_trivially_copy_constructible::value || std::is_nothrow_copy_constructible::value) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) ((std::is_rvalue_reference::value && std::is_move_assignable::value ? std::is_trivially_move_assignable::value || std::is_nothrow_move_assignable::value : std::is_trivially_copy_assignable::value || std::is_nothrow_copy_assignable::value) && MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr)) +#else +#define MOODYCAMEL_NOEXCEPT noexcept +#define MOODYCAMEL_NOEXCEPT_CTOR(type, valueType, expr) noexcept(expr) +#define MOODYCAMEL_NOEXCEPT_ASSIGN(type, valueType, expr) noexcept(expr) +#endif +#endif + +#ifndef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#ifdef MCDBGQ_USE_RELACY +#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#else +// VS2013 doesn't support `thread_local`, and MinGW-w64 w/ POSIX threading has a crippling bug: http://sourceforge.net/p/mingw-w64/bugs/445 +// g++ <=4.7 doesn't support thread_local either. +// Finally, iOS/ARM doesn't have support for it either, and g++/ARM allows it to compile but it's unconfirmed to actually work +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && (!defined(__MINGW32__) && !defined(__MINGW64__) || !defined(__WINPTHREADS_VERSION)) && (!defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && (!defined(__APPLE__) || !TARGET_OS_IPHONE) && !defined(__arm__) && !defined(_M_ARM) && !defined(__aarch64__) +// Assume `thread_local` is fully supported in all other C++11 compilers/platforms +//#define MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED // always disabled for now since several users report having problems with it on +#endif +#endif +#endif + +// VS2012 doesn't support deleted functions. +// In this case, we declare the function normally but don't define it. A link error will be generated if the function is called. +#ifndef MOODYCAMEL_DELETE_FUNCTION +#if defined(_MSC_VER) && _MSC_VER < 1800 +#define MOODYCAMEL_DELETE_FUNCTION +#else +#define MOODYCAMEL_DELETE_FUNCTION = delete +#endif +#endif + +// Compiler-specific likely/unlikely hints +namespace moodycamel { namespace details { +#if defined(__GNUC__) + static inline bool (likely)(bool x) { return __builtin_expect((x), true); } + static inline bool (unlikely)(bool x) { return __builtin_expect((x), false); } +#else + static inline bool (likely)(bool x) { return x; } + static inline bool (unlikely)(bool x) { return x; } +#endif +} } + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG +#include "internal/concurrentqueue_internal_debug.h" +#endif + +namespace moodycamel { +namespace details { + template + struct const_numeric_max { + static_assert(std::is_integral::value, "const_numeric_max can only be used with integers"); + static const T value = std::numeric_limits::is_signed + ? (static_cast(1) << (sizeof(T) * CHAR_BIT - 1)) - static_cast(1) + : static_cast(-1); + }; + +#if defined(__GLIBCXX__) + typedef ::max_align_t std_max_align_t; // libstdc++ forgot to add it to std:: for a while +#else + typedef std::max_align_t std_max_align_t; // Others (e.g. MSVC) insist it can *only* be accessed via std:: +#endif + + // Some platforms have incorrectly set max_align_t to a type with <8 bytes alignment even while supporting + // 8-byte aligned scalar values (*cough* 32-bit iOS). Work around this with our own union. See issue #64. + typedef union { + std_max_align_t x; + long long y; + void* z; + } max_align_t; +} + +// Default traits for the ConcurrentQueue. To change some of the +// traits without re-implementing all of them, inherit from this +// struct and shadow the declarations you wish to be different; +// since the traits are used as a template type parameter, the +// shadowed declarations will be used where defined, and the defaults +// otherwise. +struct ConcurrentQueueDefaultTraits +{ + // General-purpose size type. std::size_t is strongly recommended. + typedef std::size_t size_t; + + // The type used for the enqueue and dequeue indices. Must be at least as + // large as size_t. Should be significantly larger than the number of elements + // you expect to hold at once, especially if you have a high turnover rate; + // for example, on 32-bit x86, if you expect to have over a hundred million + // elements or pump several million elements through your queue in a very + // short space of time, using a 32-bit type *may* trigger a race condition. + // A 64-bit int type is recommended in that case, and in practice will + // prevent a race condition no matter the usage of the queue. Note that + // whether the queue is lock-free with a 64-int type depends on the whether + // std::atomic is lock-free, which is platform-specific. + typedef std::size_t index_t; + + // Internally, all elements are enqueued and dequeued from multi-element + // blocks; this is the smallest controllable unit. If you expect few elements + // but many producers, a smaller block size should be favoured. For few producers + // and/or many elements, a larger block size is preferred. A sane default + // is provided. Must be a power of 2. + static const size_t BLOCK_SIZE = 32; + + // For explicit producers (i.e. when using a producer token), the block is + // checked for being empty by iterating through a list of flags, one per element. + // For large block sizes, this is too inefficient, and switching to an atomic + // counter-based approach is faster. The switch is made for block sizes strictly + // larger than this threshold. + static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = 32; + + // How many full blocks can be expected for a single explicit producer? This should + // reflect that number's maximum for optimal performance. Must be a power of 2. + static const size_t EXPLICIT_INITIAL_INDEX_SIZE = 32; + + // How many full blocks can be expected for a single implicit producer? This should + // reflect that number's maximum for optimal performance. Must be a power of 2. + static const size_t IMPLICIT_INITIAL_INDEX_SIZE = 32; + + // The initial size of the hash table mapping thread IDs to implicit producers. + // Note that the hash is resized every time it becomes half full. + // Must be a power of two, and either 0 or at least 1. If 0, implicit production + // (using the enqueue methods without an explicit producer token) is disabled. + static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = 32; + + // Controls the number of items that an explicit consumer (i.e. one with a token) + // must consume before it causes all consumers to rotate and move on to the next + // internal queue. + static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = 256; + + // The maximum number of elements (inclusive) that can be enqueued to a sub-queue. + // Enqueue operations that would cause this limit to be surpassed will fail. Note + // that this limit is enforced at the block level (for performance reasons), i.e. + // it's rounded up to the nearest block size. + static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max::value; + + +#ifndef MCDBGQ_USE_RELACY + // Memory allocation can be customized if needed. + // malloc should return nullptr on failure, and handle alignment like std::malloc. +#if defined(malloc) || defined(free) + // Gah, this is 2015, stop defining macros that break standard code already! + // Work around malloc/free being special macros: + static inline void* WORKAROUND_malloc(size_t size) { return malloc(size); } + static inline void WORKAROUND_free(void* ptr) { return free(ptr); } + static inline void* (malloc)(size_t size) { return WORKAROUND_malloc(size); } + static inline void (free)(void* ptr) { return WORKAROUND_free(ptr); } +#else + static inline void* malloc(size_t size) { return std::malloc(size); } + static inline void free(void* ptr) { return std::free(ptr); } +#endif +#else + // Debug versions when running under the Relacy race detector (ignore + // these in user code) + static inline void* malloc(size_t size) { return rl::rl_malloc(size, $); } + static inline void free(void* ptr) { return rl::rl_free(ptr, $); } +#endif +}; + + +// When producing or consuming many elements, the most efficient way is to: +// 1) Use one of the bulk-operation methods of the queue with a token +// 2) Failing that, use the bulk-operation methods without a token +// 3) Failing that, create a token and use that with the single-item methods +// 4) Failing that, use the single-parameter methods of the queue +// Having said that, don't create tokens willy-nilly -- ideally there should be +// a maximum of one token per thread (of each kind). +struct ProducerToken; +struct ConsumerToken; + +template class ConcurrentQueue; +template class BlockingConcurrentQueue; +class ConcurrentQueueTests; + + +namespace details +{ + struct ConcurrentQueueProducerTypelessBase + { + ConcurrentQueueProducerTypelessBase* next; + std::atomic inactive; + ProducerToken* token; + + ConcurrentQueueProducerTypelessBase() + : next(nullptr), inactive(false), token(nullptr) + { + } + }; + + template struct _hash_32_or_64 { + static inline std::uint32_t hash(std::uint32_t h) + { + // MurmurHash3 finalizer -- see https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + // Since the thread ID is already unique, all we really want to do is propagate that + // uniqueness evenly across all the bits, so that we can use a subset of the bits while + // reducing collisions significantly + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + return h ^ (h >> 16); + } + }; + template<> struct _hash_32_or_64<1> { + static inline std::uint64_t hash(std::uint64_t h) + { + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + return h ^ (h >> 33); + } + }; + template struct hash_32_or_64 : public _hash_32_or_64<(size > 4)> { }; + + static inline size_t hash_thread_id(thread_id_t id) + { + static_assert(sizeof(thread_id_t) <= 8, "Expected a platform where thread IDs are at most 64-bit values"); + return static_cast(hash_32_or_64::thread_id_hash_t)>::hash( + thread_id_converter::prehash(id))); + } + + template + static inline bool circular_less_than(T a, T b) + { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4554) +#endif + static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "circular_less_than is intended to be used only with unsigned integer types"); + return static_cast(a - b) > static_cast(static_cast(1) << static_cast(sizeof(T) * CHAR_BIT - 1)); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } + + template + static inline char* align_for(char* ptr) + { + const std::size_t alignment = std::alignment_of::value; + return ptr + (alignment - (reinterpret_cast(ptr) % alignment)) % alignment; + } + + template + static inline T ceil_to_pow_2(T x) + { + static_assert(std::is_integral::value && !std::numeric_limits::is_signed, "ceil_to_pow_2 is intended to be used only with unsigned integer types"); + + // Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + for (std::size_t i = 1; i < sizeof(T); i <<= 1) { + x |= x >> (i << 3); + } + ++x; + return x; + } + + template + static inline void swap_relaxed(std::atomic& left, std::atomic& right) + { + T temp = std::move(left.load(std::memory_order_relaxed)); + left.store(std::move(right.load(std::memory_order_relaxed)), std::memory_order_relaxed); + right.store(std::move(temp), std::memory_order_relaxed); + } + + template + static inline T const& nomove(T const& x) + { + return x; + } + + template + struct nomove_if + { + template + static inline T const& eval(T const& x) + { + return x; + } + }; + + template<> + struct nomove_if + { + template + static inline auto eval(U&& x) + -> decltype(std::forward(x)) + { + return std::forward(x); + } + }; + + template + static inline auto deref_noexcept(It& it) MOODYCAMEL_NOEXCEPT -> decltype(*it) + { + return *it; + } + +#if defined(__clang__) || !defined(__GNUC__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8) + template struct is_trivially_destructible : std::is_trivially_destructible { }; +#else + template struct is_trivially_destructible : std::has_trivial_destructor { }; +#endif + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED +#ifdef MCDBGQ_USE_RELACY + typedef RelacyThreadExitListener ThreadExitListener; + typedef RelacyThreadExitNotifier ThreadExitNotifier; +#else + struct ThreadExitListener + { + typedef void (*callback_t)(void*); + callback_t callback; + void* userData; + + ThreadExitListener* next; // reserved for use by the ThreadExitNotifier + }; + + + class ThreadExitNotifier + { + public: + static void subscribe(ThreadExitListener* listener) + { + auto& tlsInst = instance(); + listener->next = tlsInst.tail; + tlsInst.tail = listener; + } + + static void unsubscribe(ThreadExitListener* listener) + { + auto& tlsInst = instance(); + ThreadExitListener** prev = &tlsInst.tail; + for (auto ptr = tlsInst.tail; ptr != nullptr; ptr = ptr->next) { + if (ptr == listener) { + *prev = ptr->next; + break; + } + prev = &ptr->next; + } + } + + private: + ThreadExitNotifier() : tail(nullptr) { } + ThreadExitNotifier(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; + ThreadExitNotifier& operator=(ThreadExitNotifier const&) MOODYCAMEL_DELETE_FUNCTION; + + ~ThreadExitNotifier() + { + // This thread is about to exit, let everyone know! + assert(this == &instance() && "If this assert fails, you likely have a buggy compiler! Change the preprocessor conditions such that MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED is no longer defined."); + for (auto ptr = tail; ptr != nullptr; ptr = ptr->next) { + ptr->callback(ptr->userData); + } + } + + // Thread-local + static inline ThreadExitNotifier& instance() + { + static thread_local ThreadExitNotifier notifier; + return notifier; + } + + private: + ThreadExitListener* tail; + }; +#endif +#endif + + template struct static_is_lock_free_num { enum { value = 0 }; }; + template<> struct static_is_lock_free_num { enum { value = ATOMIC_CHAR_LOCK_FREE }; }; + template<> struct static_is_lock_free_num { enum { value = ATOMIC_SHORT_LOCK_FREE }; }; + template<> struct static_is_lock_free_num { enum { value = ATOMIC_INT_LOCK_FREE }; }; + template<> struct static_is_lock_free_num { enum { value = ATOMIC_LONG_LOCK_FREE }; }; + template<> struct static_is_lock_free_num { enum { value = ATOMIC_LLONG_LOCK_FREE }; }; + template struct static_is_lock_free : static_is_lock_free_num::type> { }; + template<> struct static_is_lock_free { enum { value = ATOMIC_BOOL_LOCK_FREE }; }; + template struct static_is_lock_free { enum { value = ATOMIC_POINTER_LOCK_FREE }; }; +} + + +struct ProducerToken +{ + template + explicit ProducerToken(ConcurrentQueue& queue); + + template + explicit ProducerToken(BlockingConcurrentQueue& queue); + + ProducerToken(ProducerToken&& other) MOODYCAMEL_NOEXCEPT + : producer(other.producer) + { + other.producer = nullptr; + if (producer != nullptr) { + producer->token = this; + } + } + + inline ProducerToken& operator=(ProducerToken&& other) MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + void swap(ProducerToken& other) MOODYCAMEL_NOEXCEPT + { + std::swap(producer, other.producer); + if (producer != nullptr) { + producer->token = this; + } + if (other.producer != nullptr) { + other.producer->token = &other; + } + } + + // A token is always valid unless: + // 1) Memory allocation failed during construction + // 2) It was moved via the move constructor + // (Note: assignment does a swap, leaving both potentially valid) + // 3) The associated queue was destroyed + // Note that if valid() returns true, that only indicates + // that the token is valid for use with a specific queue, + // but not which one; that's up to the user to track. + inline bool valid() const { return producer != nullptr; } + + ~ProducerToken() + { + if (producer != nullptr) { + producer->token = nullptr; + producer->inactive.store(true, std::memory_order_release); + } + } + + // Disable copying and assignment + ProducerToken(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; + ProducerToken& operator=(ProducerToken const&) MOODYCAMEL_DELETE_FUNCTION; + +private: + template friend class ConcurrentQueue; + friend class ConcurrentQueueTests; + +protected: + details::ConcurrentQueueProducerTypelessBase* producer; +}; + + +struct ConsumerToken +{ + template + explicit ConsumerToken(ConcurrentQueue& q); + + template + explicit ConsumerToken(BlockingConcurrentQueue& q); + + ConsumerToken(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT + : initialOffset(other.initialOffset), lastKnownGlobalOffset(other.lastKnownGlobalOffset), itemsConsumedFromCurrent(other.itemsConsumedFromCurrent), currentProducer(other.currentProducer), desiredProducer(other.desiredProducer) + { + } + + inline ConsumerToken& operator=(ConsumerToken&& other) MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + void swap(ConsumerToken& other) MOODYCAMEL_NOEXCEPT + { + std::swap(initialOffset, other.initialOffset); + std::swap(lastKnownGlobalOffset, other.lastKnownGlobalOffset); + std::swap(itemsConsumedFromCurrent, other.itemsConsumedFromCurrent); + std::swap(currentProducer, other.currentProducer); + std::swap(desiredProducer, other.desiredProducer); + } + + // Disable copying and assignment + ConsumerToken(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; + ConsumerToken& operator=(ConsumerToken const&) MOODYCAMEL_DELETE_FUNCTION; + +private: + template friend class ConcurrentQueue; + friend class ConcurrentQueueTests; + +private: // but shared with ConcurrentQueue + std::uint32_t initialOffset; + std::uint32_t lastKnownGlobalOffset; + std::uint32_t itemsConsumedFromCurrent; + details::ConcurrentQueueProducerTypelessBase* currentProducer; + details::ConcurrentQueueProducerTypelessBase* desiredProducer; +}; + +// Need to forward-declare this swap because it's in a namespace. +// See http://stackoverflow.com/questions/4492062/why-does-a-c-friend-class-need-a-forward-declaration-only-in-other-namespaces +template +inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT; + + +template +class ConcurrentQueue +{ +public: + typedef ::moodycamel::ProducerToken producer_token_t; + typedef ::moodycamel::ConsumerToken consumer_token_t; + + typedef typename Traits::index_t index_t; + typedef typename Traits::size_t size_t; + + static const size_t BLOCK_SIZE = static_cast(Traits::BLOCK_SIZE); + static const size_t EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD = static_cast(Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD); + static const size_t EXPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::EXPLICIT_INITIAL_INDEX_SIZE); + static const size_t IMPLICIT_INITIAL_INDEX_SIZE = static_cast(Traits::IMPLICIT_INITIAL_INDEX_SIZE); + static const size_t INITIAL_IMPLICIT_PRODUCER_HASH_SIZE = static_cast(Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE); + static const std::uint32_t EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE = static_cast(Traits::EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4307) // + integral constant overflow (that's what the ternary expression is for!) +#pragma warning(disable: 4309) // static_cast: Truncation of constant value +#endif + static const size_t MAX_SUBQUEUE_SIZE = (details::const_numeric_max::value - static_cast(Traits::MAX_SUBQUEUE_SIZE) < BLOCK_SIZE) ? details::const_numeric_max::value : ((static_cast(Traits::MAX_SUBQUEUE_SIZE) + (BLOCK_SIZE - 1)) / BLOCK_SIZE * BLOCK_SIZE); +#ifdef _MSC_VER +#pragma warning(pop) +#endif + + static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::size_t must be an unsigned integral type"); + static_assert(!std::numeric_limits::is_signed && std::is_integral::value, "Traits::index_t must be an unsigned integral type"); + static_assert(sizeof(index_t) >= sizeof(size_t), "Traits::index_t must be at least as wide as Traits::size_t"); + static_assert((BLOCK_SIZE > 1) && !(BLOCK_SIZE & (BLOCK_SIZE - 1)), "Traits::BLOCK_SIZE must be a power of 2 (and at least 2)"); + static_assert((EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD > 1) && !(EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD & (EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD - 1)), "Traits::EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD must be a power of 2 (and greater than 1)"); + static_assert((EXPLICIT_INITIAL_INDEX_SIZE > 1) && !(EXPLICIT_INITIAL_INDEX_SIZE & (EXPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::EXPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); + static_assert((IMPLICIT_INITIAL_INDEX_SIZE > 1) && !(IMPLICIT_INITIAL_INDEX_SIZE & (IMPLICIT_INITIAL_INDEX_SIZE - 1)), "Traits::IMPLICIT_INITIAL_INDEX_SIZE must be a power of 2 (and greater than 1)"); + static_assert((INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) || !(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE & (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE - 1)), "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be a power of 2"); + static_assert(INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0 || INITIAL_IMPLICIT_PRODUCER_HASH_SIZE >= 1, "Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE must be at least 1 (or 0 to disable implicit enqueueing)"); + +public: + // Creates a queue with at least `capacity` element slots; note that the + // actual number of elements that can be inserted without additional memory + // allocation depends on the number of producers and the block size (e.g. if + // the block size is equal to `capacity`, only a single block will be allocated + // up-front, which means only a single producer will be able to enqueue elements + // without an extra allocation -- blocks aren't shared between producers). + // This method is not thread safe -- it is up to the user to ensure that the + // queue is fully constructed before it starts being used by other threads (this + // includes making the memory effects of construction visible, possibly with a + // memory barrier). + explicit ConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE) + : producerListTail(nullptr), + producerCount(0), + initialBlockPoolIndex(0), + nextExplicitConsumerId(0), + globalExplicitConsumerOffset(0) + { + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + populate_initial_block_list(capacity / BLOCK_SIZE + ((capacity & (BLOCK_SIZE - 1)) == 0 ? 0 : 1)); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + // Track all the producers using a fully-resolved typed list for + // each kind; this makes it possible to debug them starting from + // the root queue object (otherwise wacky casts are needed that + // don't compile in the debugger's expression evaluator). + explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + } + + // Computes the correct amount of pre-allocated blocks for you based + // on the minimum number of elements you want available at any given + // time, and the maximum concurrent number of each type of producer. + ConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers) + : producerListTail(nullptr), + producerCount(0), + initialBlockPoolIndex(0), + nextExplicitConsumerId(0), + globalExplicitConsumerOffset(0) + { + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + size_t blocks = (((minCapacity + BLOCK_SIZE - 1) / BLOCK_SIZE) - 1) * (maxExplicitProducers + 1) + 2 * (maxExplicitProducers + maxImplicitProducers); + populate_initial_block_list(blocks); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + } + + // Note: The queue should not be accessed concurrently while it's + // being deleted. It's up to the user to synchronize this. + // This method is not thread safe. + ~ConcurrentQueue() + { + // Destroy producers + auto ptr = producerListTail.load(std::memory_order_relaxed); + while (ptr != nullptr) { + auto next = ptr->next_prod(); + if (ptr->token != nullptr) { + ptr->token->producer = nullptr; + } + destroy(ptr); + ptr = next; + } + + // Destroy implicit producer hash tables + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE != 0) { + auto hash = implicitProducerHash.load(std::memory_order_relaxed); + while (hash != nullptr) { + auto prev = hash->prev; + if (prev != nullptr) { // The last hash is part of this object and was not allocated dynamically + for (size_t i = 0; i != hash->capacity; ++i) { + hash->entries[i].~ImplicitProducerKVP(); + } + hash->~ImplicitProducerHash(); + (Traits::free)(hash); + } + hash = prev; + } + } + + // Destroy global free list + auto block = freeList.head_unsafe(); + while (block != nullptr) { + auto next = block->freeListNext.load(std::memory_order_relaxed); + if (block->dynamicallyAllocated) { + destroy(block); + } + block = next; + } + + // Destroy initial free list + destroy_array(initialBlockPool, initialBlockPoolSize); + } + + // Disable copying and copy assignment + ConcurrentQueue(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; + ConcurrentQueue& operator=(ConcurrentQueue const&) MOODYCAMEL_DELETE_FUNCTION; + + // Moving is supported, but note that it is *not* a thread-safe operation. + // Nobody can use the queue while it's being moved, and the memory effects + // of that move must be propagated to other threads before they can use it. + // Note: When a queue is moved, its tokens are still valid but can only be + // used with the destination queue (i.e. semantically they are moved along + // with the queue itself). + ConcurrentQueue(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT + : producerListTail(other.producerListTail.load(std::memory_order_relaxed)), + producerCount(other.producerCount.load(std::memory_order_relaxed)), + initialBlockPoolIndex(other.initialBlockPoolIndex.load(std::memory_order_relaxed)), + initialBlockPool(other.initialBlockPool), + initialBlockPoolSize(other.initialBlockPoolSize), + freeList(std::move(other.freeList)), + nextExplicitConsumerId(other.nextExplicitConsumerId.load(std::memory_order_relaxed)), + globalExplicitConsumerOffset(other.globalExplicitConsumerOffset.load(std::memory_order_relaxed)) + { + // Move the other one into this, and leave the other one as an empty queue + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + populate_initial_implicit_producer_hash(); + swap_implicit_producer_hashes(other); + + other.producerListTail.store(nullptr, std::memory_order_relaxed); + other.producerCount.store(0, std::memory_order_relaxed); + other.nextExplicitConsumerId.store(0, std::memory_order_relaxed); + other.globalExplicitConsumerOffset.store(0, std::memory_order_relaxed); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + explicitProducers.store(other.explicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); + other.explicitProducers.store(nullptr, std::memory_order_relaxed); + implicitProducers.store(other.implicitProducers.load(std::memory_order_relaxed), std::memory_order_relaxed); + other.implicitProducers.store(nullptr, std::memory_order_relaxed); +#endif + + other.initialBlockPoolIndex.store(0, std::memory_order_relaxed); + other.initialBlockPoolSize = 0; + other.initialBlockPool = nullptr; + + reown_producers(); + } + + inline ConcurrentQueue& operator=(ConcurrentQueue&& other) MOODYCAMEL_NOEXCEPT + { + return swap_internal(other); + } + + // Swaps this queue's state with the other's. Not thread-safe. + // Swapping two queues does not invalidate their tokens, however + // the tokens that were created for one queue must be used with + // only the swapped queue (i.e. the tokens are tied to the + // queue's movable state, not the object itself). + inline void swap(ConcurrentQueue& other) MOODYCAMEL_NOEXCEPT + { + swap_internal(other); + } + +private: + ConcurrentQueue& swap_internal(ConcurrentQueue& other) + { + if (this == &other) { + return *this; + } + + details::swap_relaxed(producerListTail, other.producerListTail); + details::swap_relaxed(producerCount, other.producerCount); + details::swap_relaxed(initialBlockPoolIndex, other.initialBlockPoolIndex); + std::swap(initialBlockPool, other.initialBlockPool); + std::swap(initialBlockPoolSize, other.initialBlockPoolSize); + freeList.swap(other.freeList); + details::swap_relaxed(nextExplicitConsumerId, other.nextExplicitConsumerId); + details::swap_relaxed(globalExplicitConsumerOffset, other.globalExplicitConsumerOffset); + + swap_implicit_producer_hashes(other); + + reown_producers(); + other.reown_producers(); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + details::swap_relaxed(explicitProducers, other.explicitProducers); + details::swap_relaxed(implicitProducers, other.implicitProducers); +#endif + + return *this; + } + +public: + // Enqueues a single item (by copying it). + // Allocates memory if required. Only fails if memory allocation fails (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, + // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(T const& item) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue(item); + } + + // Enqueues a single item (by moving it, if possible). + // Allocates memory if required. Only fails if memory allocation fails (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0, + // or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(T&& item) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue(std::move(item)); + } + + // Enqueues a single item (by copying it) using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails (or + // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(producer_token_t const& token, T const& item) + { + return inner_enqueue(token, item); + } + + // Enqueues a single item (by moving it, if possible) using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails (or + // Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Thread-safe. + inline bool enqueue(producer_token_t const& token, T&& item) + { + return inner_enqueue(token, std::move(item)); + } + + // Enqueues several items. + // Allocates memory if required. Only fails if memory allocation fails (or + // implicit production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + // is 0, or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Note: Use std::make_move_iterator if the elements should be moved instead of copied. + // Thread-safe. + template + bool enqueue_bulk(It itemFirst, size_t count) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue_bulk(itemFirst, count); + } + + // Enqueues several items using an explicit producer token. + // Allocates memory if required. Only fails if memory allocation fails + // (or Traits::MAX_SUBQUEUE_SIZE has been defined and would be surpassed). + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) + { + return inner_enqueue_bulk(token, itemFirst, count); + } + + // Enqueues a single item (by copying it). + // Does not allocate memory. Fails if not enough room to enqueue (or implicit + // production is disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE + // is 0). + // Thread-safe. + inline bool try_enqueue(T const& item) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue(item); + } + + // Enqueues a single item (by moving it, if possible). + // Does not allocate memory (except for one-time implicit producer). + // Fails if not enough room to enqueue (or implicit production is + // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). + // Thread-safe. + inline bool try_enqueue(T&& item) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue(std::move(item)); + } + + // Enqueues a single item (by copying it) using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Thread-safe. + inline bool try_enqueue(producer_token_t const& token, T const& item) + { + return inner_enqueue(token, item); + } + + // Enqueues a single item (by moving it, if possible) using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Thread-safe. + inline bool try_enqueue(producer_token_t const& token, T&& item) + { + return inner_enqueue(token, std::move(item)); + } + + // Enqueues several items. + // Does not allocate memory (except for one-time implicit producer). + // Fails if not enough room to enqueue (or implicit production is + // disabled because Traits::INITIAL_IMPLICIT_PRODUCER_HASH_SIZE is 0). + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool try_enqueue_bulk(It itemFirst, size_t count) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return false; + return inner_enqueue_bulk(itemFirst, count); + } + + // Enqueues several items using an explicit producer token. + // Does not allocate memory. Fails if not enough room to enqueue. + // Note: Use std::make_move_iterator if the elements should be moved + // instead of copied. + // Thread-safe. + template + bool try_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) + { + return inner_enqueue_bulk(token, itemFirst, count); + } + + + + // Attempts to dequeue from the queue. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + bool try_dequeue(U& item) + { + // Instead of simply trying each producer in turn (which could cause needless contention on the first + // producer), we score them heuristically. + size_t nonEmptyCount = 0; + ProducerBase* best = nullptr; + size_t bestSize = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); nonEmptyCount < 3 && ptr != nullptr; ptr = ptr->next_prod()) { + auto size = ptr->size_approx(); + if (size > 0) { + if (size > bestSize) { + bestSize = size; + best = ptr; + } + ++nonEmptyCount; + } + } + + // If there was at least one non-empty queue but it appears empty at the time + // we try to dequeue from it, we need to make sure every queue's been tried + if (nonEmptyCount > 0) { + if ((details::likely)(best->dequeue(item))) { + return true; + } + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr != best && ptr->dequeue(item)) { + return true; + } + } + } + return false; + } + + // Attempts to dequeue from the queue. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // This differs from the try_dequeue(item) method in that this one does + // not attempt to reduce contention by interleaving the order that producer + // streams are dequeued from. So, using this method can reduce overall throughput + // under contention, but will give more predictable results in single-threaded + // consumer scenarios. This is mostly only useful for internal unit tests. + // Never allocates. Thread-safe. + template + bool try_dequeue_non_interleaved(U& item) + { + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr->dequeue(item)) { + return true; + } + } + return false; + } + + // Attempts to dequeue from the queue using an explicit consumer token. + // Returns false if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + bool try_dequeue(consumer_token_t& token, U& item) + { + // The idea is roughly as follows: + // Every 256 items from one producer, make everyone rotate (increase the global offset) -> this means the highest efficiency consumer dictates the rotation speed of everyone else, more or less + // If you see that the global offset has changed, you must reset your consumption counter and move to your designated place + // If there's no items where you're supposed to be, keep moving until you find a producer with some items + // If the global offset has not changed but you've run out of items to consume, move over from your current position until you find an producer with something in it + + if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { + if (!update_current_producer_after_rotation(token)) { + return false; + } + } + + // If there was at least one non-empty queue but it appears empty at the time + // we try to dequeue from it, we need to make sure every queue's been tried + if (static_cast(token.currentProducer)->dequeue(item)) { + if (++token.itemsConsumedFromCurrent == EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { + globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); + } + return true; + } + + auto tail = producerListTail.load(std::memory_order_acquire); + auto ptr = static_cast(token.currentProducer)->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + while (ptr != static_cast(token.currentProducer)) { + if (ptr->dequeue(item)) { + token.currentProducer = ptr; + token.itemsConsumedFromCurrent = 1; + return true; + } + ptr = ptr->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + } + return false; + } + + // Attempts to dequeue several elements from the queue. + // Returns the number of items actually dequeued. + // Returns 0 if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + size_t try_dequeue_bulk(It itemFirst, size_t max) + { + size_t count = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + count += ptr->dequeue_bulk(itemFirst, max - count); + if (count == max) { + break; + } + } + return count; + } + + // Attempts to dequeue several elements from the queue using an explicit consumer token. + // Returns the number of items actually dequeued. + // Returns 0 if all producer streams appeared empty at the time they + // were checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + size_t try_dequeue_bulk(consumer_token_t& token, It itemFirst, size_t max) + { + if (token.desiredProducer == nullptr || token.lastKnownGlobalOffset != globalExplicitConsumerOffset.load(std::memory_order_relaxed)) { + if (!update_current_producer_after_rotation(token)) { + return 0; + } + } + + size_t count = static_cast(token.currentProducer)->dequeue_bulk(itemFirst, max); + if (count == max) { + if ((token.itemsConsumedFromCurrent += static_cast(max)) >= EXPLICIT_CONSUMER_CONSUMPTION_QUOTA_BEFORE_ROTATE) { + globalExplicitConsumerOffset.fetch_add(1, std::memory_order_relaxed); + } + return max; + } + token.itemsConsumedFromCurrent += static_cast(count); + max -= count; + + auto tail = producerListTail.load(std::memory_order_acquire); + auto ptr = static_cast(token.currentProducer)->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + while (ptr != static_cast(token.currentProducer)) { + auto dequeued = ptr->dequeue_bulk(itemFirst, max); + count += dequeued; + if (dequeued != 0) { + token.currentProducer = ptr; + token.itemsConsumedFromCurrent = static_cast(dequeued); + } + if (dequeued == max) { + break; + } + max -= dequeued; + ptr = ptr->next_prod(); + if (ptr == nullptr) { + ptr = tail; + } + } + return count; + } + + + + // Attempts to dequeue from a specific producer's inner queue. + // If you happen to know which producer you want to dequeue from, this + // is significantly faster than using the general-case try_dequeue methods. + // Returns false if the producer's queue appeared empty at the time it + // was checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + inline bool try_dequeue_from_producer(producer_token_t const& producer, U& item) + { + return static_cast(producer.producer)->dequeue(item); + } + + // Attempts to dequeue several elements from a specific producer's inner queue. + // Returns the number of items actually dequeued. + // If you happen to know which producer you want to dequeue from, this + // is significantly faster than using the general-case try_dequeue methods. + // Returns 0 if the producer's queue appeared empty at the time it + // was checked (so, the queue is likely but not guaranteed to be empty). + // Never allocates. Thread-safe. + template + inline size_t try_dequeue_bulk_from_producer(producer_token_t const& producer, It itemFirst, size_t max) + { + return static_cast(producer.producer)->dequeue_bulk(itemFirst, max); + } + + + // Returns an estimate of the total number of elements currently in the queue. This + // estimate is only accurate if the queue has completely stabilized before it is called + // (i.e. all enqueue and dequeue operations have completed and their memory effects are + // visible on the calling thread, and no further operations start while this method is + // being called). + // Thread-safe. + size_t size_approx() const + { + size_t size = 0; + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + size += ptr->size_approx(); + } + return size; + } + + + // Returns true if the underlying atomic variables used by + // the queue are lock-free (they should be on most platforms). + // Thread-safe. + static bool is_lock_free() + { + return + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::value == 2 && + details::static_is_lock_free::thread_id_numeric_size_t>::value == 2; + } + + +private: + friend struct ProducerToken; + friend struct ConsumerToken; + struct ExplicitProducer; + friend struct ExplicitProducer; + struct ImplicitProducer; + friend struct ImplicitProducer; + friend class ConcurrentQueueTests; + + enum AllocationMode { CanAlloc, CannotAlloc }; + + + /////////////////////////////// + // Queue methods + /////////////////////////////// + + template + inline bool inner_enqueue(producer_token_t const& token, U&& element) + { + return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue(std::forward(element)); + } + + template + inline bool inner_enqueue(U&& element) + { + auto producer = get_or_add_implicit_producer(); + return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue(std::forward(element)); + } + + template + inline bool inner_enqueue_bulk(producer_token_t const& token, It itemFirst, size_t count) + { + return static_cast(token.producer)->ConcurrentQueue::ExplicitProducer::template enqueue_bulk(itemFirst, count); + } + + template + inline bool inner_enqueue_bulk(It itemFirst, size_t count) + { + auto producer = get_or_add_implicit_producer(); + return producer == nullptr ? false : producer->ConcurrentQueue::ImplicitProducer::template enqueue_bulk(itemFirst, count); + } + + inline bool update_current_producer_after_rotation(consumer_token_t& token) + { + // Ah, there's been a rotation, figure out where we should be! + auto tail = producerListTail.load(std::memory_order_acquire); + if (token.desiredProducer == nullptr && tail == nullptr) { + return false; + } + auto prodCount = producerCount.load(std::memory_order_relaxed); + auto globalOffset = globalExplicitConsumerOffset.load(std::memory_order_relaxed); + if ((details::unlikely)(token.desiredProducer == nullptr)) { + // Aha, first time we're dequeueing anything. + // Figure out our local position + // Note: offset is from start, not end, but we're traversing from end -- subtract from count first + std::uint32_t offset = prodCount - 1 - (token.initialOffset % prodCount); + token.desiredProducer = tail; + for (std::uint32_t i = 0; i != offset; ++i) { + token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); + if (token.desiredProducer == nullptr) { + token.desiredProducer = tail; + } + } + } + + std::uint32_t delta = globalOffset - token.lastKnownGlobalOffset; + if (delta >= prodCount) { + delta = delta % prodCount; + } + for (std::uint32_t i = 0; i != delta; ++i) { + token.desiredProducer = static_cast(token.desiredProducer)->next_prod(); + if (token.desiredProducer == nullptr) { + token.desiredProducer = tail; + } + } + + token.lastKnownGlobalOffset = globalOffset; + token.currentProducer = token.desiredProducer; + token.itemsConsumedFromCurrent = 0; + return true; + } + + + /////////////////////////// + // Free list + /////////////////////////// + + template + struct FreeListNode + { + FreeListNode() : freeListRefs(0), freeListNext(nullptr) { } + + std::atomic freeListRefs; + std::atomic freeListNext; + }; + + // A simple CAS-based lock-free free list. Not the fastest thing in the world under heavy contention, but + // simple and correct (assuming nodes are never freed until after the free list is destroyed), and fairly + // speedy under low contention. + template // N must inherit FreeListNode or have the same fields (and initialization of them) + struct FreeList + { + FreeList() : freeListHead(nullptr) { } + FreeList(FreeList&& other) : freeListHead(other.freeListHead.load(std::memory_order_relaxed)) { other.freeListHead.store(nullptr, std::memory_order_relaxed); } + void swap(FreeList& other) { details::swap_relaxed(freeListHead, other.freeListHead); } + + FreeList(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; + FreeList& operator=(FreeList const&) MOODYCAMEL_DELETE_FUNCTION; + + inline void add(N* node) + { +#if MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugLock lock(mutex); +#endif + // We know that the should-be-on-freelist bit is 0 at this point, so it's safe to + // set it using a fetch_add + if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST, std::memory_order_acq_rel) == 0) { + // Oh look! We were the last ones referencing this node, and we know + // we want to add it to the free list, so let's do it! + add_knowing_refcount_is_zero(node); + } + } + + inline N* try_get() + { +#if MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugLock lock(mutex); +#endif + auto head = freeListHead.load(std::memory_order_acquire); + while (head != nullptr) { + auto prevHead = head; + auto refs = head->freeListRefs.load(std::memory_order_relaxed); + if ((refs & REFS_MASK) == 0 || !head->freeListRefs.compare_exchange_strong(refs, refs + 1, std::memory_order_acquire, std::memory_order_relaxed)) { + head = freeListHead.load(std::memory_order_acquire); + continue; + } + + // Good, reference count has been incremented (it wasn't at zero), which means we can read the + // next and not worry about it changing between now and the time we do the CAS + auto next = head->freeListNext.load(std::memory_order_relaxed); + if (freeListHead.compare_exchange_strong(head, next, std::memory_order_acquire, std::memory_order_relaxed)) { + // Yay, got the node. This means it was on the list, which means shouldBeOnFreeList must be false no + // matter the refcount (because nobody else knows it's been taken off yet, it can't have been put back on). + assert((head->freeListRefs.load(std::memory_order_relaxed) & SHOULD_BE_ON_FREELIST) == 0); + + // Decrease refcount twice, once for our ref, and once for the list's ref + head->freeListRefs.fetch_sub(2, std::memory_order_release); + return head; + } + + // OK, the head must have changed on us, but we still need to decrease the refcount we increased. + // Note that we don't need to release any memory effects, but we do need to ensure that the reference + // count decrement happens-after the CAS on the head. + refs = prevHead->freeListRefs.fetch_sub(1, std::memory_order_acq_rel); + if (refs == SHOULD_BE_ON_FREELIST + 1) { + add_knowing_refcount_is_zero(prevHead); + } + } + + return nullptr; + } + + // Useful for traversing the list when there's no contention (e.g. to destroy remaining nodes) + N* head_unsafe() const { return freeListHead.load(std::memory_order_relaxed); } + + private: + inline void add_knowing_refcount_is_zero(N* node) + { + // Since the refcount is zero, and nobody can increase it once it's zero (except us, and we run + // only one copy of this method per node at a time, i.e. the single thread case), then we know + // we can safely change the next pointer of the node; however, once the refcount is back above + // zero, then other threads could increase it (happens under heavy contention, when the refcount + // goes to zero in between a load and a refcount increment of a node in try_get, then back up to + // something non-zero, then the refcount increment is done by the other thread) -- so, if the CAS + // to add the node to the actual list fails, decrease the refcount and leave the add operation to + // the next thread who puts the refcount back at zero (which could be us, hence the loop). + auto head = freeListHead.load(std::memory_order_relaxed); + while (true) { + node->freeListNext.store(head, std::memory_order_relaxed); + node->freeListRefs.store(1, std::memory_order_release); + if (!freeListHead.compare_exchange_strong(head, node, std::memory_order_release, std::memory_order_relaxed)) { + // Hmm, the add failed, but we can only try again when the refcount goes back to zero + if (node->freeListRefs.fetch_add(SHOULD_BE_ON_FREELIST - 1, std::memory_order_release) == 1) { + continue; + } + } + return; + } + } + + private: + // Implemented like a stack, but where node order doesn't matter (nodes are inserted out of order under contention) + std::atomic freeListHead; + + static const std::uint32_t REFS_MASK = 0x7FFFFFFF; + static const std::uint32_t SHOULD_BE_ON_FREELIST = 0x80000000; + +#if MCDBGQ_NOLOCKFREE_FREELIST + debug::DebugMutex mutex; +#endif + }; + + + /////////////////////////// + // Block + /////////////////////////// + + enum InnerQueueContext { implicit_context = 0, explicit_context = 1 }; + + struct Block + { + Block() + : next(nullptr), elementsCompletelyDequeued(0), freeListRefs(0), freeListNext(nullptr), shouldBeOnFreeList(false), dynamicallyAllocated(true) + { +#if MCDBGQ_TRACKMEM + owner = nullptr; +#endif + } + + template + inline bool is_empty() const + { + if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Check flags + for (size_t i = 0; i < BLOCK_SIZE; ++i) { + if (!emptyFlags[i].load(std::memory_order_relaxed)) { + return false; + } + } + + // Aha, empty; make sure we have all other memory effects that happened before the empty flags were set + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + else { + // Check counter + if (elementsCompletelyDequeued.load(std::memory_order_relaxed) == BLOCK_SIZE) { + std::atomic_thread_fence(std::memory_order_acquire); + return true; + } + assert(elementsCompletelyDequeued.load(std::memory_order_relaxed) <= BLOCK_SIZE); + return false; + } + } + + // Returns true if the block is now empty (does not apply in explicit context) + template + inline bool set_empty(index_t i) + { + if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set flag + assert(!emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].load(std::memory_order_relaxed)); + emptyFlags[BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1))].store(true, std::memory_order_release); + return false; + } + else { + // Increment counter + auto prevVal = elementsCompletelyDequeued.fetch_add(1, std::memory_order_release); + assert(prevVal < BLOCK_SIZE); + return prevVal == BLOCK_SIZE - 1; + } + } + + // Sets multiple contiguous item statuses to 'empty' (assumes no wrapping and count > 0). + // Returns true if the block is now empty (does not apply in explicit context). + template + inline bool set_many_empty(index_t i, size_t count) + { + if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set flags + std::atomic_thread_fence(std::memory_order_release); + i = BLOCK_SIZE - 1 - static_cast(i & static_cast(BLOCK_SIZE - 1)) - count + 1; + for (size_t j = 0; j != count; ++j) { + assert(!emptyFlags[i + j].load(std::memory_order_relaxed)); + emptyFlags[i + j].store(true, std::memory_order_relaxed); + } + return false; + } + else { + // Increment counter + auto prevVal = elementsCompletelyDequeued.fetch_add(count, std::memory_order_release); + assert(prevVal + count <= BLOCK_SIZE); + return prevVal + count == BLOCK_SIZE; + } + } + + template + inline void set_all_empty() + { + if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Set all flags + for (size_t i = 0; i != BLOCK_SIZE; ++i) { + emptyFlags[i].store(true, std::memory_order_relaxed); + } + } + else { + // Reset counter + elementsCompletelyDequeued.store(BLOCK_SIZE, std::memory_order_relaxed); + } + } + + template + inline void reset_empty() + { + if (context == explicit_context && BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD) { + // Reset flags + for (size_t i = 0; i != BLOCK_SIZE; ++i) { + emptyFlags[i].store(false, std::memory_order_relaxed); + } + } + else { + // Reset counter + elementsCompletelyDequeued.store(0, std::memory_order_relaxed); + } + } + + inline T* operator[](index_t idx) MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } + inline T const* operator[](index_t idx) const MOODYCAMEL_NOEXCEPT { return static_cast(static_cast(elements)) + static_cast(idx & static_cast(BLOCK_SIZE - 1)); } + + private: + // IMPORTANT: This must be the first member in Block, so that if T depends on the alignment of + // addresses returned by malloc, that alignment will be preserved. Apparently clang actually + // generates code that uses this assumption for AVX instructions in some cases. Ideally, we + // should also align Block to the alignment of T in case it's higher than malloc's 16-byte + // alignment, but this is hard to do in a cross-platform way. Assert for this case: + static_assert(std::alignment_of::value <= std::alignment_of::value, "The queue does not support super-aligned types at this time"); + // Additionally, we need the alignment of Block itself to be a multiple of max_align_t since + // otherwise the appropriate padding will not be added at the end of Block in order to make + // arrays of Blocks all be properly aligned (not just the first one). We use a union to force + // this. + union { + char elements[sizeof(T) * BLOCK_SIZE]; + details::max_align_t dummy; + }; + public: + Block* next; + std::atomic elementsCompletelyDequeued; + std::atomic emptyFlags[BLOCK_SIZE <= EXPLICIT_BLOCK_EMPTY_COUNTER_THRESHOLD ? BLOCK_SIZE : 1]; + public: + std::atomic freeListRefs; + std::atomic freeListNext; + std::atomic shouldBeOnFreeList; + bool dynamicallyAllocated; // Perhaps a better name for this would be 'isNotPartOfInitialBlockPool' + +#if MCDBGQ_TRACKMEM + void* owner; +#endif + }; + static_assert(std::alignment_of::value >= std::alignment_of::value, "Internal error: Blocks must be at least as aligned as the type they are wrapping"); + + +#if MCDBGQ_TRACKMEM +public: + struct MemStats; +private: +#endif + + /////////////////////////// + // Producer base + /////////////////////////// + + struct ProducerBase : public details::ConcurrentQueueProducerTypelessBase + { + ProducerBase(ConcurrentQueue* parent_, bool isExplicit_) : + tailIndex(0), + headIndex(0), + dequeueOptimisticCount(0), + dequeueOvercommit(0), + tailBlock(nullptr), + isExplicit(isExplicit_), + parent(parent_) + { + } + + virtual ~ProducerBase() { }; + + template + inline bool dequeue(U& element) + { + if (isExplicit) { + return static_cast(this)->dequeue(element); + } + else { + return static_cast(this)->dequeue(element); + } + } + + template + inline size_t dequeue_bulk(It& itemFirst, size_t max) + { + if (isExplicit) { + return static_cast(this)->dequeue_bulk(itemFirst, max); + } + else { + return static_cast(this)->dequeue_bulk(itemFirst, max); + } + } + + inline ProducerBase* next_prod() const { return static_cast(next); } + + inline size_t size_approx() const + { + auto tail = tailIndex.load(std::memory_order_relaxed); + auto head = headIndex.load(std::memory_order_relaxed); + return details::circular_less_than(head, tail) ? static_cast(tail - head) : 0; + } + + inline index_t getTail() const { return tailIndex.load(std::memory_order_relaxed); } + protected: + std::atomic tailIndex; // Where to enqueue to next + std::atomic headIndex; // Where to dequeue from next + + std::atomic dequeueOptimisticCount; + std::atomic dequeueOvercommit; + + Block* tailBlock; + + public: + bool isExplicit; + ConcurrentQueue* parent; + + protected: +#if MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + /////////////////////////// + // Explicit queue + /////////////////////////// + + struct ExplicitProducer : public ProducerBase + { + explicit ExplicitProducer(ConcurrentQueue* parent) : + ProducerBase(parent, true), + blockIndex(nullptr), + pr_blockIndexSlotsUsed(0), + pr_blockIndexSize(EXPLICIT_INITIAL_INDEX_SIZE >> 1), + pr_blockIndexFront(0), + pr_blockIndexEntries(nullptr), + pr_blockIndexRaw(nullptr) + { + size_t poolBasedIndexSize = details::ceil_to_pow_2(parent->initialBlockPoolSize) >> 1; + if (poolBasedIndexSize > pr_blockIndexSize) { + pr_blockIndexSize = poolBasedIndexSize; + } + + new_block_index(0); // This creates an index with double the number of current entries, i.e. EXPLICIT_INITIAL_INDEX_SIZE + } + + ~ExplicitProducer() + { + // Destruct any elements not yet dequeued. + // Since we're in the destructor, we can assume all elements + // are either completely dequeued or completely not (no halfways). + if (this->tailBlock != nullptr) { // Note this means there must be a block index too + // First find the block that's partially dequeued, if any + Block* halfDequeuedBlock = nullptr; + if ((this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) != 0) { + // The head's not on a block boundary, meaning a block somewhere is partially dequeued + // (or the head block is the tail block and was fully dequeued, but the head/tail are still not on a boundary) + size_t i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & (pr_blockIndexSize - 1); + while (details::circular_less_than(pr_blockIndexEntries[i].base + BLOCK_SIZE, this->headIndex.load(std::memory_order_relaxed))) { + i = (i + 1) & (pr_blockIndexSize - 1); + } + assert(details::circular_less_than(pr_blockIndexEntries[i].base, this->headIndex.load(std::memory_order_relaxed))); + halfDequeuedBlock = pr_blockIndexEntries[i].block; + } + + // Start at the head block (note the first line in the loop gives us the head from the tail on the first iteration) + auto block = this->tailBlock; + do { + block = block->next; + if (block->ConcurrentQueue::Block::template is_empty()) { + continue; + } + + size_t i = 0; // Offset into block + if (block == halfDequeuedBlock) { + i = static_cast(this->headIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); + } + + // Walk through all the items in the block; if this is the tail block, we need to stop when we reach the tail index + auto lastValidIndex = (this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)) == 0 ? BLOCK_SIZE : static_cast(this->tailIndex.load(std::memory_order_relaxed) & static_cast(BLOCK_SIZE - 1)); + while (i != BLOCK_SIZE && (block != this->tailBlock || i != lastValidIndex)) { + (*block)[i++]->~T(); + } + } while (block != this->tailBlock); + } + + // Destroy all blocks that we own + if (this->tailBlock != nullptr) { + auto block = this->tailBlock; + do { + auto nextBlock = block->next; + if (block->dynamicallyAllocated) { + destroy(block); + } + else { + this->parent->add_block_to_free_list(block); + } + block = nextBlock; + } while (block != this->tailBlock); + } + + // Destroy the block indices + auto header = static_cast(pr_blockIndexRaw); + while (header != nullptr) { + auto prev = static_cast(header->prev); + header->~BlockIndexHeader(); + (Traits::free)(header); + header = prev; + } + } + + template + inline bool enqueue(U&& element) + { + index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); + index_t newTailIndex = 1 + currentTailIndex; + if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + // We reached the end of a block, start a new one + auto startBlock = this->tailBlock; + auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; + if (this->tailBlock != nullptr && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { + // We can re-use the block ahead of us, it's empty! + this->tailBlock = this->tailBlock->next; + this->tailBlock->ConcurrentQueue::Block::template reset_empty(); + + // We'll put the block on the block index (guaranteed to be room since we're conceptually removing the + // last block from it first -- except instead of removing then adding, we can just overwrite). + // Note that there must be a valid block index here, since even if allocation failed in the ctor, + // it would have been re-attempted when adding the first block to the queue; since there is such + // a block, a block index must have been successfully allocated. + } + else { + // Whatever head value we see here is >= the last value we saw here (relatively), + // and <= its current value. Since we have the most recent tail, the head must be + // <= to it. + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) + || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { + // We can't enqueue in another block because there's not enough leeway -- the + // tail could surpass the head by the time the block fills up! (Or we'll exceed + // the size limit, if the second part of the condition was true.) + return false; + } + // We're going to need a new block; check that the block index has room + if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize) { + // Hmm, the circular block index is already full -- we'll need + // to allocate a new index. Note pr_blockIndexRaw can only be nullptr if + // the initial allocation failed in the constructor. + + if (allocMode == CannotAlloc || !new_block_index(pr_blockIndexSlotsUsed)) { + return false; + } + } + + // Insert a new block in the circular linked list + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + return false; + } +#if MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + if (this->tailBlock == nullptr) { + newBlock->next = newBlock; + } + else { + newBlock->next = this->tailBlock->next; + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + ++pr_blockIndexSlotsUsed; + } + + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + // The constructor may throw. We want the element not to appear in the queue in + // that case (without corrupting the queue): + MOODYCAMEL_TRY { + new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + } + MOODYCAMEL_CATCH (...) { + // Revert change to the current block, but leave the new block available + // for next time + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? this->tailBlock : startBlock; + MOODYCAMEL_RETHROW; + } + } + else { + (void)startBlock; + (void)originalBlockIndexSlotsUsed; + } + + // Add block to block index + auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + blockIndex.load(std::memory_order_relaxed)->front.store(pr_blockIndexFront, std::memory_order_release); + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + } + + // Enqueue + new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + bool dequeue(U& element) + { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { + // Might be something to dequeue, let's give it a try + + // Note that this if is purely for performance purposes in the common case when the queue is + // empty and the values are eventually consistent -- we may enter here spuriously. + + // Note that whatever the values of overcommit and tail are, they are not going to change (unless we + // change them) and must be the same value at this point (inside the if) as when the if condition was + // evaluated. + + // We insert an acquire fence here to synchronize-with the release upon incrementing dequeueOvercommit below. + // This ensures that whatever the value we got loaded into overcommit, the load of dequeueOptisticCount in + // the fetch_add below will result in a value at least as recent as that (and therefore at least as large). + // Note that I believe a compiler (signal) fence here would be sufficient due to the nature of fetch_add (all + // read-modify-write operations are guaranteed to work on the latest value in the modification order), but + // unfortunately that can't be shown to be correct using only the C++11 standard. + // See http://stackoverflow.com/questions/18223161/what-are-the-c11-memory-ordering-guarantees-in-this-corner-case + std::atomic_thread_fence(std::memory_order_acquire); + + // Increment optimistic counter, then check if it went over the boundary + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); + + // Note that since dequeueOvercommit must be <= dequeueOptimisticCount (because dequeueOvercommit is only ever + // incremented after dequeueOptimisticCount -- this is enforced in the `else` block below), and since we now + // have a version of dequeueOptimisticCount that is at least as recent as overcommit (due to the release upon + // incrementing dequeueOvercommit and the acquire above that synchronizes with it), overcommit <= myDequeueCount. + // However, we can't assert this since both dequeueOptimisticCount and dequeueOvercommit may (independently) + // overflow; in such a case, though, the logic still holds since the difference between the two is maintained. + + // Note that we reload tail here in case it changed; it will be the same value as before or greater, since + // this load is sequenced after (happens after) the earlier load above. This is supported by read-read + // coherency (as defined in the standard), explained here: http://en.cppreference.com/w/cpp/atomic/memory_order + tail = this->tailIndex.load(std::memory_order_acquire); + if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { + // Guaranteed to be at least one element to dequeue! + + // Get the index. Note that since there's guaranteed to be at least one element, this + // will never exceed tail. We need to do an acquire-release fence here since it's possible + // that whatever condition got us to this point was for an earlier enqueued element (that + // we already see the memory effects for), but that by the time we increment somebody else + // has incremented it, and we need to see the memory effects for *that* element, which is + // in such a case is necessarily visible on the thread that incremented it in the first + // place with the more current condition (they must have acquired a tail that is at least + // as recent). + auto index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); + + + // Determine which block the element is in + + auto localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); + + // We need to be careful here about subtracting and dividing because of index wrap-around. + // When an index wraps, we need to preserve the sign of the offset when dividing it by the + // block size (in order to get a correct signed block count offset in all cases): + auto headBase = localBlockIndex->entries[localBlockIndexHead].base; + auto blockBaseIndex = index & ~static_cast(BLOCK_SIZE - 1); + auto offset = static_cast(static_cast::type>(blockBaseIndex - headBase) / BLOCK_SIZE); + auto block = localBlockIndex->entries[(localBlockIndexHead + offset) & (localBlockIndex->size - 1)].block; + + // Dequeue + auto& el = *((*block)[index]); + if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { + // Make sure the element is still fully dequeued and destroyed even if the assignment + // throws + struct Guard { + Block* block; + index_t index; + + ~Guard() + { + (*block)[index]->~T(); + block->ConcurrentQueue::Block::template set_empty(index); + } + } guard = { block, index }; + + element = std::move(el); + } + else { + element = std::move(el); + el.~T(); + block->ConcurrentQueue::Block::template set_empty(index); + } + + return true; + } + else { + // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent + this->dequeueOvercommit.fetch_add(1, std::memory_order_release); // Release so that the fetch_add on dequeueOptimisticCount is guaranteed to happen before this write + } + } + + return false; + } + + template + bool enqueue_bulk(It itemFirst, size_t count) + { + // First, we need to make sure we have enough room to enqueue all of the elements; + // this means pre-allocating blocks and putting them in the block index (but only if + // all the allocations succeeded). + index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); + auto startBlock = this->tailBlock; + auto originalBlockIndexFront = pr_blockIndexFront; + auto originalBlockIndexSlotsUsed = pr_blockIndexSlotsUsed; + + Block* firstAllocatedBlock = nullptr; + + // Figure out how many blocks we'll need to allocate, and do so + size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); + index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + if (blockBaseDiff > 0) { + // Allocate as many blocks as possible from ahead + while (blockBaseDiff > 0 && this->tailBlock != nullptr && this->tailBlock->next != firstAllocatedBlock && this->tailBlock->next->ConcurrentQueue::Block::template is_empty()) { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + this->tailBlock = this->tailBlock->next; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; + + auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + } + + // Now allocate as many blocks as necessary from the block pool + while (blockBaseDiff > 0) { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); + if (pr_blockIndexRaw == nullptr || pr_blockIndexSlotsUsed == pr_blockIndexSize || full) { + if (allocMode == CannotAlloc || full || !new_block_index(originalBlockIndexSlotsUsed)) { + // Failed to allocate, undo changes (but keep injected blocks) + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + return false; + } + + // pr_blockIndexFront is updated inside new_block_index, so we need to + // update our fallback value too (since we keep the new index even if we + // later fail) + originalBlockIndexFront = originalBlockIndexSlotsUsed; + } + + // Insert a new block in the circular linked list + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + return false; + } + +#if MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template set_all_empty(); + if (this->tailBlock == nullptr) { + newBlock->next = newBlock; + } + else { + newBlock->next = this->tailBlock->next; + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? this->tailBlock : firstAllocatedBlock; + + ++pr_blockIndexSlotsUsed; + + auto& entry = blockIndex.load(std::memory_order_relaxed)->entries[pr_blockIndexFront]; + entry.base = currentTailIndex; + entry.block = this->tailBlock; + pr_blockIndexFront = (pr_blockIndexFront + 1) & (pr_blockIndexSize - 1); + } + + // Excellent, all allocations succeeded. Reset each block's emptiness before we fill them up, and + // publish the new block index front + auto block = firstAllocatedBlock; + while (true) { + block->ConcurrentQueue::Block::template reset_empty(); + if (block == this->tailBlock) { + break; + } + block = block->next; + } + + if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { + blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); + } + } + + // Enqueue, one block at a time + index_t newTailIndex = startTailIndex + static_cast(count); + currentTailIndex = startTailIndex; + auto endBlock = this->tailBlock; + this->tailBlock = startBlock; + assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { + this->tailBlock = firstAllocatedBlock; + } + while (true) { + auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + if (details::circular_less_than(newTailIndex, stopIndex)) { + stopIndex = newTailIndex; + } + if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { + while (currentTailIndex != stopIndex) { + new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); + } + } + else { + MOODYCAMEL_TRY { + while (currentTailIndex != stopIndex) { + // Must use copy constructor even if move constructor is available + // because we may have to revert if there's an exception. + // Sorry about the horrible templated next line, but it was the only way + // to disable moving *at compile time*, which is important because a type + // may only define a (noexcept) move constructor, and so calls to the + // cctor will not compile, even if they are in an if branch that will never + // be executed + new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); + ++currentTailIndex; + ++itemFirst; + } + } + MOODYCAMEL_CATCH (...) { + // Oh dear, an exception's been thrown -- destroy the elements that + // were enqueued so far and revert the entire bulk operation (we'll keep + // any allocated blocks in our linked list for later, though). + auto constructedStopIndex = currentTailIndex; + auto lastBlockEnqueued = this->tailBlock; + + pr_blockIndexFront = originalBlockIndexFront; + pr_blockIndexSlotsUsed = originalBlockIndexSlotsUsed; + this->tailBlock = startBlock == nullptr ? firstAllocatedBlock : startBlock; + + if (!details::is_trivially_destructible::value) { + auto block = startBlock; + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + block = firstAllocatedBlock; + } + currentTailIndex = startTailIndex; + while (true) { + stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + if (details::circular_less_than(constructedStopIndex, stopIndex)) { + stopIndex = constructedStopIndex; + } + while (currentTailIndex != stopIndex) { + (*block)[currentTailIndex++]->~T(); + } + if (block == lastBlockEnqueued) { + break; + } + block = block->next; + } + } + MOODYCAMEL_RETHROW; + } + } + + if (this->tailBlock == endBlock) { + assert(currentTailIndex == newTailIndex); + break; + } + this->tailBlock = this->tailBlock->next; + } + + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst))) && firstAllocatedBlock != nullptr) { + blockIndex.load(std::memory_order_relaxed)->front.store((pr_blockIndexFront - 1) & (pr_blockIndexSize - 1), std::memory_order_release); + } + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + size_t dequeue_bulk(It& itemFirst, size_t max) + { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); + if (details::circular_less_than(0, desiredCount)) { + desiredCount = desiredCount < max ? desiredCount : max; + std::atomic_thread_fence(std::memory_order_acquire); + + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed);; + + tail = this->tailIndex.load(std::memory_order_acquire); + auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); + if (details::circular_less_than(0, actualCount)) { + actualCount = desiredCount < actualCount ? desiredCount : actualCount; + if (actualCount < desiredCount) { + this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); + } + + // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this + // will never exceed tail. + auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); + + // Determine which block the first element is in + auto localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto localBlockIndexHead = localBlockIndex->front.load(std::memory_order_acquire); + + auto headBase = localBlockIndex->entries[localBlockIndexHead].base; + auto firstBlockBaseIndex = firstIndex & ~static_cast(BLOCK_SIZE - 1); + auto offset = static_cast(static_cast::type>(firstBlockBaseIndex - headBase) / BLOCK_SIZE); + auto indexIndex = (localBlockIndexHead + offset) & (localBlockIndex->size - 1); + + // Iterate the blocks and dequeue + auto index = firstIndex; + do { + auto firstIndexInBlock = index; + auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; + auto block = localBlockIndex->entries[indexIndex].block; + if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { + while (index != endIndex) { + auto& el = *((*block)[index]); + *itemFirst++ = std::move(el); + el.~T(); + ++index; + } + } + else { + MOODYCAMEL_TRY { + while (index != endIndex) { + auto& el = *((*block)[index]); + *itemFirst = std::move(el); + ++itemFirst; + el.~T(); + ++index; + } + } + MOODYCAMEL_CATCH (...) { + // It's too late to revert the dequeue, but we can make sure that all + // the dequeued objects are properly destroyed and the block index + // (and empty count) are properly updated before we propagate the exception + do { + block = localBlockIndex->entries[indexIndex].block; + while (index != endIndex) { + (*block)[index++]->~T(); + } + block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); + indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); + + firstIndexInBlock = index; + endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; + } while (index != firstIndex + actualCount); + + MOODYCAMEL_RETHROW; + } + } + block->ConcurrentQueue::Block::template set_many_empty(firstIndexInBlock, static_cast(endIndex - firstIndexInBlock)); + indexIndex = (indexIndex + 1) & (localBlockIndex->size - 1); + } while (index != firstIndex + actualCount); + + return actualCount; + } + else { + // Wasn't anything to dequeue after all; make the effective dequeue count eventually consistent + this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); + } + } + + return 0; + } + + private: + struct BlockIndexEntry + { + index_t base; + Block* block; + }; + + struct BlockIndexHeader + { + size_t size; + std::atomic front; // Current slot (not next, like pr_blockIndexFront) + BlockIndexEntry* entries; + void* prev; + }; + + + bool new_block_index(size_t numberOfFilledSlotsToExpose) + { + auto prevBlockSizeMask = pr_blockIndexSize - 1; + + // Create the new block + pr_blockIndexSize <<= 1; + auto newRawPtr = static_cast((Traits::malloc)(sizeof(BlockIndexHeader) + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * pr_blockIndexSize)); + if (newRawPtr == nullptr) { + pr_blockIndexSize >>= 1; // Reset to allow graceful retry + return false; + } + + auto newBlockIndexEntries = reinterpret_cast(details::align_for(newRawPtr + sizeof(BlockIndexHeader))); + + // Copy in all the old indices, if any + size_t j = 0; + if (pr_blockIndexSlotsUsed != 0) { + auto i = (pr_blockIndexFront - pr_blockIndexSlotsUsed) & prevBlockSizeMask; + do { + newBlockIndexEntries[j++] = pr_blockIndexEntries[i]; + i = (i + 1) & prevBlockSizeMask; + } while (i != pr_blockIndexFront); + } + + // Update everything + auto header = new (newRawPtr) BlockIndexHeader; + header->size = pr_blockIndexSize; + header->front.store(numberOfFilledSlotsToExpose - 1, std::memory_order_relaxed); + header->entries = newBlockIndexEntries; + header->prev = pr_blockIndexRaw; // we link the new block to the old one so we can free it later + + pr_blockIndexFront = j; + pr_blockIndexEntries = newBlockIndexEntries; + pr_blockIndexRaw = newRawPtr; + blockIndex.store(header, std::memory_order_release); + + return true; + } + + private: + std::atomic blockIndex; + + // To be used by producer only -- consumer must use the ones in referenced by blockIndex + size_t pr_blockIndexSlotsUsed; + size_t pr_blockIndexSize; + size_t pr_blockIndexFront; // Next slot (not current) + BlockIndexEntry* pr_blockIndexEntries; + void* pr_blockIndexRaw; + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + public: + ExplicitProducer* nextExplicitProducer; + private: +#endif + +#if MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + ////////////////////////////////// + // Implicit queue + ////////////////////////////////// + + struct ImplicitProducer : public ProducerBase + { + ImplicitProducer(ConcurrentQueue* parent) : + ProducerBase(parent, false), + nextBlockIndexCapacity(IMPLICIT_INITIAL_INDEX_SIZE), + blockIndex(nullptr) + { + new_block_index(); + } + + ~ImplicitProducer() + { + // Note that since we're in the destructor we can assume that all enqueue/dequeue operations + // completed already; this means that all undequeued elements are placed contiguously across + // contiguous blocks, and that only the first and last remaining blocks can be only partially + // empty (all other remaining blocks must be completely full). + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + // Unregister ourselves for thread termination notification + if (!this->inactive.load(std::memory_order_relaxed)) { + details::ThreadExitNotifier::unsubscribe(&threadExitListener); + } +#endif + + // Destroy all remaining elements! + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto index = this->headIndex.load(std::memory_order_relaxed); + Block* block = nullptr; + assert(index == tail || details::circular_less_than(index, tail)); + bool forceFreeLastBlock = index != tail; // If we enter the loop, then the last (tail) block will not be freed + while (index != tail) { + if ((index & static_cast(BLOCK_SIZE - 1)) == 0 || block == nullptr) { + if (block != nullptr) { + // Free the old block + this->parent->add_block_to_free_list(block); + } + + block = get_block_index_entry_for_index(index)->value.load(std::memory_order_relaxed); + } + + ((*block)[index])->~T(); + ++index; + } + // Even if the queue is empty, there's still one block that's not on the free list + // (unless the head index reached the end of it, in which case the tail will be poised + // to create a new block). + if (this->tailBlock != nullptr && (forceFreeLastBlock || (tail & static_cast(BLOCK_SIZE - 1)) != 0)) { + this->parent->add_block_to_free_list(this->tailBlock); + } + + // Destroy block index + auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); + if (localBlockIndex != nullptr) { + for (size_t i = 0; i != localBlockIndex->capacity; ++i) { + localBlockIndex->index[i]->~BlockIndexEntry(); + } + do { + auto prev = localBlockIndex->prev; + localBlockIndex->~BlockIndexHeader(); + (Traits::free)(localBlockIndex); + localBlockIndex = prev; + } while (localBlockIndex != nullptr); + } + } + + template + inline bool enqueue(U&& element) + { + index_t currentTailIndex = this->tailIndex.load(std::memory_order_relaxed); + index_t newTailIndex = 1 + currentTailIndex; + if ((currentTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + // We reached the end of a block, start a new one + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + if (!details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head))) { + return false; + } +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Find out where we'll be inserting this block in the block index + BlockIndexEntry* idxEntry; + if (!insert_block_index_entry(idxEntry, currentTailIndex)) { + return false; + } + + // Get ahold of a new block + auto newBlock = this->parent->ConcurrentQueue::template requisition_block(); + if (newBlock == nullptr) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + return false; + } +#if MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + // May throw, try to insert now before we publish the fact that we have this new block + MOODYCAMEL_TRY { + new ((*newBlock)[currentTailIndex]) T(std::forward(element)); + } + MOODYCAMEL_CATCH (...) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + this->parent->add_block_to_free_list(newBlock); + MOODYCAMEL_RETHROW; + } + } + + // Insert the new block into the index + idxEntry->value.store(newBlock, std::memory_order_relaxed); + + this->tailBlock = newBlock; + + if (!MOODYCAMEL_NOEXCEPT_CTOR(T, U, new (nullptr) T(std::forward(element)))) { + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + } + + // Enqueue + new ((*this->tailBlock)[currentTailIndex]) T(std::forward(element)); + + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + bool dequeue(U& element) + { + // See ExplicitProducer::dequeue for rationale and explanation + index_t tail = this->tailIndex.load(std::memory_order_relaxed); + index_t overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + if (details::circular_less_than(this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit, tail)) { + std::atomic_thread_fence(std::memory_order_acquire); + + index_t myDequeueCount = this->dequeueOptimisticCount.fetch_add(1, std::memory_order_relaxed); + tail = this->tailIndex.load(std::memory_order_acquire); + if ((details::likely)(details::circular_less_than(myDequeueCount - overcommit, tail))) { + index_t index = this->headIndex.fetch_add(1, std::memory_order_acq_rel); + + // Determine which block the element is in + auto entry = get_block_index_entry_for_index(index); + + // Dequeue + auto block = entry->value.load(std::memory_order_relaxed); + auto& el = *((*block)[index]); + + if (!MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, element = std::move(el))) { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + // Note: Acquiring the mutex with every dequeue instead of only when a block + // is released is very sub-optimal, but it is, after all, purely debug code. + debug::DebugLock lock(producer->mutex); +#endif + struct Guard { + Block* block; + index_t index; + BlockIndexEntry* entry; + ConcurrentQueue* parent; + + ~Guard() + { + (*block)[index]->~T(); + if (block->ConcurrentQueue::Block::template set_empty(index)) { + entry->value.store(nullptr, std::memory_order_relaxed); + parent->add_block_to_free_list(block); + } + } + } guard = { block, index, entry, this->parent }; + + element = std::move(el); + } + else { + element = std::move(el); + el.~T(); + + if (block->ConcurrentQueue::Block::template set_empty(index)) { + { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Add the block back into the global free pool (and remove from block index) + entry->value.store(nullptr, std::memory_order_relaxed); + } + this->parent->add_block_to_free_list(block); // releases the above store + } + } + + return true; + } + else { + this->dequeueOvercommit.fetch_add(1, std::memory_order_release); + } + } + + return false; + } + + template + bool enqueue_bulk(It itemFirst, size_t count) + { + // First, we need to make sure we have enough room to enqueue all of the elements; + // this means pre-allocating blocks and putting them in the block index (but only if + // all the allocations succeeded). + + // Note that the tailBlock we start off with may not be owned by us any more; + // this happens if it was filled up exactly to the top (setting tailIndex to + // the first index of the next block which is not yet allocated), then dequeued + // completely (putting it on the free list) before we enqueue again. + + index_t startTailIndex = this->tailIndex.load(std::memory_order_relaxed); + auto startBlock = this->tailBlock; + Block* firstAllocatedBlock = nullptr; + auto endBlock = this->tailBlock; + + // Figure out how many blocks we'll need to allocate, and do so + size_t blockBaseDiff = ((startTailIndex + count - 1) & ~static_cast(BLOCK_SIZE - 1)) - ((startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1)); + index_t currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + if (blockBaseDiff > 0) { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + do { + blockBaseDiff -= static_cast(BLOCK_SIZE); + currentTailIndex += static_cast(BLOCK_SIZE); + + // Find out where we'll be inserting this block in the block index + BlockIndexEntry* idxEntry = nullptr; // initialization here unnecessary but compiler can't always tell + Block* newBlock; + bool indexInserted = false; + auto head = this->headIndex.load(std::memory_order_relaxed); + assert(!details::circular_less_than(currentTailIndex, head)); + bool full = !details::circular_less_than(head, currentTailIndex + BLOCK_SIZE) || (MAX_SUBQUEUE_SIZE != details::const_numeric_max::value && (MAX_SUBQUEUE_SIZE == 0 || MAX_SUBQUEUE_SIZE - BLOCK_SIZE < currentTailIndex - head)); + if (full || !(indexInserted = insert_block_index_entry(idxEntry, currentTailIndex)) || (newBlock = this->parent->ConcurrentQueue::template requisition_block()) == nullptr) { + // Index allocation or block allocation failed; revert any other allocations + // and index insertions done so far for this operation + if (indexInserted) { + rewind_block_index_tail(); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + } + currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { + currentTailIndex += static_cast(BLOCK_SIZE); + idxEntry = get_block_index_entry_for_index(currentTailIndex); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + rewind_block_index_tail(); + } + this->parent->add_blocks_to_free_list(firstAllocatedBlock); + this->tailBlock = startBlock; + + return false; + } + +#if MCDBGQ_TRACKMEM + newBlock->owner = this; +#endif + newBlock->ConcurrentQueue::Block::template reset_empty(); + newBlock->next = nullptr; + + // Insert the new block into the index + idxEntry->value.store(newBlock, std::memory_order_relaxed); + + // Store the chain of blocks so that we can undo if later allocations fail, + // and so that we can find the blocks when we do the actual enqueueing + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr) { + assert(this->tailBlock != nullptr); + this->tailBlock->next = newBlock; + } + this->tailBlock = newBlock; + endBlock = newBlock; + firstAllocatedBlock = firstAllocatedBlock == nullptr ? newBlock : firstAllocatedBlock; + } while (blockBaseDiff > 0); + } + + // Enqueue, one block at a time + index_t newTailIndex = startTailIndex + static_cast(count); + currentTailIndex = startTailIndex; + this->tailBlock = startBlock; + assert((startTailIndex & static_cast(BLOCK_SIZE - 1)) != 0 || firstAllocatedBlock != nullptr || count == 0); + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0 && firstAllocatedBlock != nullptr) { + this->tailBlock = firstAllocatedBlock; + } + while (true) { + auto stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + if (details::circular_less_than(newTailIndex, stopIndex)) { + stopIndex = newTailIndex; + } + if (MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))) { + while (currentTailIndex != stopIndex) { + new ((*this->tailBlock)[currentTailIndex++]) T(*itemFirst++); + } + } + else { + MOODYCAMEL_TRY { + while (currentTailIndex != stopIndex) { + new ((*this->tailBlock)[currentTailIndex]) T(details::nomove_if<(bool)!MOODYCAMEL_NOEXCEPT_CTOR(T, decltype(*itemFirst), new (nullptr) T(details::deref_noexcept(itemFirst)))>::eval(*itemFirst)); + ++currentTailIndex; + ++itemFirst; + } + } + MOODYCAMEL_CATCH (...) { + auto constructedStopIndex = currentTailIndex; + auto lastBlockEnqueued = this->tailBlock; + + if (!details::is_trivially_destructible::value) { + auto block = startBlock; + if ((startTailIndex & static_cast(BLOCK_SIZE - 1)) == 0) { + block = firstAllocatedBlock; + } + currentTailIndex = startTailIndex; + while (true) { + stopIndex = (currentTailIndex & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + if (details::circular_less_than(constructedStopIndex, stopIndex)) { + stopIndex = constructedStopIndex; + } + while (currentTailIndex != stopIndex) { + (*block)[currentTailIndex++]->~T(); + } + if (block == lastBlockEnqueued) { + break; + } + block = block->next; + } + } + + currentTailIndex = (startTailIndex - 1) & ~static_cast(BLOCK_SIZE - 1); + for (auto block = firstAllocatedBlock; block != nullptr; block = block->next) { + currentTailIndex += static_cast(BLOCK_SIZE); + auto idxEntry = get_block_index_entry_for_index(currentTailIndex); + idxEntry->value.store(nullptr, std::memory_order_relaxed); + rewind_block_index_tail(); + } + this->parent->add_blocks_to_free_list(firstAllocatedBlock); + this->tailBlock = startBlock; + MOODYCAMEL_RETHROW; + } + } + + if (this->tailBlock == endBlock) { + assert(currentTailIndex == newTailIndex); + break; + } + this->tailBlock = this->tailBlock->next; + } + this->tailIndex.store(newTailIndex, std::memory_order_release); + return true; + } + + template + size_t dequeue_bulk(It& itemFirst, size_t max) + { + auto tail = this->tailIndex.load(std::memory_order_relaxed); + auto overcommit = this->dequeueOvercommit.load(std::memory_order_relaxed); + auto desiredCount = static_cast(tail - (this->dequeueOptimisticCount.load(std::memory_order_relaxed) - overcommit)); + if (details::circular_less_than(0, desiredCount)) { + desiredCount = desiredCount < max ? desiredCount : max; + std::atomic_thread_fence(std::memory_order_acquire); + + auto myDequeueCount = this->dequeueOptimisticCount.fetch_add(desiredCount, std::memory_order_relaxed); + + tail = this->tailIndex.load(std::memory_order_acquire); + auto actualCount = static_cast(tail - (myDequeueCount - overcommit)); + if (details::circular_less_than(0, actualCount)) { + actualCount = desiredCount < actualCount ? desiredCount : actualCount; + if (actualCount < desiredCount) { + this->dequeueOvercommit.fetch_add(desiredCount - actualCount, std::memory_order_release); + } + + // Get the first index. Note that since there's guaranteed to be at least actualCount elements, this + // will never exceed tail. + auto firstIndex = this->headIndex.fetch_add(actualCount, std::memory_order_acq_rel); + + // Iterate the blocks and dequeue + auto index = firstIndex; + BlockIndexHeader* localBlockIndex; + auto indexIndex = get_block_index_index_for_index(index, localBlockIndex); + do { + auto blockStartIndex = index; + auto endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; + + auto entry = localBlockIndex->index[indexIndex]; + auto block = entry->value.load(std::memory_order_relaxed); + if (MOODYCAMEL_NOEXCEPT_ASSIGN(T, T&&, details::deref_noexcept(itemFirst) = std::move((*(*block)[index])))) { + while (index != endIndex) { + auto& el = *((*block)[index]); + *itemFirst++ = std::move(el); + el.~T(); + ++index; + } + } + else { + MOODYCAMEL_TRY { + while (index != endIndex) { + auto& el = *((*block)[index]); + *itemFirst = std::move(el); + ++itemFirst; + el.~T(); + ++index; + } + } + MOODYCAMEL_CATCH (...) { + do { + entry = localBlockIndex->index[indexIndex]; + block = entry->value.load(std::memory_order_relaxed); + while (index != endIndex) { + (*block)[index++]->~T(); + } + + if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + entry->value.store(nullptr, std::memory_order_relaxed); + this->parent->add_block_to_free_list(block); + } + indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); + + blockStartIndex = index; + endIndex = (index & ~static_cast(BLOCK_SIZE - 1)) + static_cast(BLOCK_SIZE); + endIndex = details::circular_less_than(firstIndex + static_cast(actualCount), endIndex) ? firstIndex + static_cast(actualCount) : endIndex; + } while (index != firstIndex + actualCount); + + MOODYCAMEL_RETHROW; + } + } + if (block->ConcurrentQueue::Block::template set_many_empty(blockStartIndex, static_cast(endIndex - blockStartIndex))) { + { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + // Note that the set_many_empty above did a release, meaning that anybody who acquires the block + // we're about to free can use it safely since our writes (and reads!) will have happened-before then. + entry->value.store(nullptr, std::memory_order_relaxed); + } + this->parent->add_block_to_free_list(block); // releases the above store + } + indexIndex = (indexIndex + 1) & (localBlockIndex->capacity - 1); + } while (index != firstIndex + actualCount); + + return actualCount; + } + else { + this->dequeueOvercommit.fetch_add(desiredCount, std::memory_order_release); + } + } + + return 0; + } + + private: + // The block size must be > 1, so any number with the low bit set is an invalid block base index + static const index_t INVALID_BLOCK_BASE = 1; + + struct BlockIndexEntry + { + std::atomic key; + std::atomic value; + }; + + struct BlockIndexHeader + { + size_t capacity; + std::atomic tail; + BlockIndexEntry* entries; + BlockIndexEntry** index; + BlockIndexHeader* prev; + }; + + template + inline bool insert_block_index_entry(BlockIndexEntry*& idxEntry, index_t blockStartIndex) + { + auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); // We're the only writer thread, relaxed is OK + if (localBlockIndex == nullptr) { + return false; // this can happen if new_block_index failed in the constructor + } + auto newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); + idxEntry = localBlockIndex->index[newTail]; + if (idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE || + idxEntry->value.load(std::memory_order_relaxed) == nullptr) { + + idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); + localBlockIndex->tail.store(newTail, std::memory_order_release); + return true; + } + + // No room in the old block index, try to allocate another one! + if (allocMode == CannotAlloc || !new_block_index()) { + return false; + } + localBlockIndex = blockIndex.load(std::memory_order_relaxed); + newTail = (localBlockIndex->tail.load(std::memory_order_relaxed) + 1) & (localBlockIndex->capacity - 1); + idxEntry = localBlockIndex->index[newTail]; + assert(idxEntry->key.load(std::memory_order_relaxed) == INVALID_BLOCK_BASE); + idxEntry->key.store(blockStartIndex, std::memory_order_relaxed); + localBlockIndex->tail.store(newTail, std::memory_order_release); + return true; + } + + inline void rewind_block_index_tail() + { + auto localBlockIndex = blockIndex.load(std::memory_order_relaxed); + localBlockIndex->tail.store((localBlockIndex->tail.load(std::memory_order_relaxed) - 1) & (localBlockIndex->capacity - 1), std::memory_order_relaxed); + } + + inline BlockIndexEntry* get_block_index_entry_for_index(index_t index) const + { + BlockIndexHeader* localBlockIndex; + auto idx = get_block_index_index_for_index(index, localBlockIndex); + return localBlockIndex->index[idx]; + } + + inline size_t get_block_index_index_for_index(index_t index, BlockIndexHeader*& localBlockIndex) const + { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + debug::DebugLock lock(mutex); +#endif + index &= ~static_cast(BLOCK_SIZE - 1); + localBlockIndex = blockIndex.load(std::memory_order_acquire); + auto tail = localBlockIndex->tail.load(std::memory_order_acquire); + auto tailBase = localBlockIndex->index[tail]->key.load(std::memory_order_relaxed); + assert(tailBase != INVALID_BLOCK_BASE); + // Note: Must use division instead of shift because the index may wrap around, causing a negative + // offset, whose negativity we want to preserve + auto offset = static_cast(static_cast::type>(index - tailBase) / BLOCK_SIZE); + size_t idx = (tail + offset) & (localBlockIndex->capacity - 1); + assert(localBlockIndex->index[idx]->key.load(std::memory_order_relaxed) == index && localBlockIndex->index[idx]->value.load(std::memory_order_relaxed) != nullptr); + return idx; + } + + bool new_block_index() + { + auto prev = blockIndex.load(std::memory_order_relaxed); + size_t prevCapacity = prev == nullptr ? 0 : prev->capacity; + auto entryCount = prev == nullptr ? nextBlockIndexCapacity : prevCapacity; + auto raw = static_cast((Traits::malloc)( + sizeof(BlockIndexHeader) + + std::alignment_of::value - 1 + sizeof(BlockIndexEntry) * entryCount + + std::alignment_of::value - 1 + sizeof(BlockIndexEntry*) * nextBlockIndexCapacity)); + if (raw == nullptr) { + return false; + } + + auto header = new (raw) BlockIndexHeader; + auto entries = reinterpret_cast(details::align_for(raw + sizeof(BlockIndexHeader))); + auto index = reinterpret_cast(details::align_for(reinterpret_cast(entries) + sizeof(BlockIndexEntry) * entryCount)); + if (prev != nullptr) { + auto prevTail = prev->tail.load(std::memory_order_relaxed); + auto prevPos = prevTail; + size_t i = 0; + do { + prevPos = (prevPos + 1) & (prev->capacity - 1); + index[i++] = prev->index[prevPos]; + } while (prevPos != prevTail); + assert(i == prevCapacity); + } + for (size_t i = 0; i != entryCount; ++i) { + new (entries + i) BlockIndexEntry; + entries[i].key.store(INVALID_BLOCK_BASE, std::memory_order_relaxed); + index[prevCapacity + i] = entries + i; + } + header->prev = prev; + header->entries = entries; + header->index = index; + header->capacity = nextBlockIndexCapacity; + header->tail.store((prevCapacity - 1) & (nextBlockIndexCapacity - 1), std::memory_order_relaxed); + + blockIndex.store(header, std::memory_order_release); + + nextBlockIndexCapacity <<= 1; + + return true; + } + + private: + size_t nextBlockIndexCapacity; + std::atomic blockIndex; + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + public: + details::ThreadExitListener threadExitListener; + private: +#endif + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + public: + ImplicitProducer* nextImplicitProducer; + private: +#endif + +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODBLOCKINDEX + mutable debug::DebugMutex mutex; +#endif +#if MCDBGQ_TRACKMEM + friend struct MemStats; +#endif + }; + + + ////////////////////////////////// + // Block pool manipulation + ////////////////////////////////// + + void populate_initial_block_list(size_t blockCount) + { + initialBlockPoolSize = blockCount; + if (initialBlockPoolSize == 0) { + initialBlockPool = nullptr; + return; + } + + initialBlockPool = create_array(blockCount); + if (initialBlockPool == nullptr) { + initialBlockPoolSize = 0; + } + for (size_t i = 0; i < initialBlockPoolSize; ++i) { + initialBlockPool[i].dynamicallyAllocated = false; + } + } + + inline Block* try_get_block_from_initial_pool() + { + if (initialBlockPoolIndex.load(std::memory_order_relaxed) >= initialBlockPoolSize) { + return nullptr; + } + + auto index = initialBlockPoolIndex.fetch_add(1, std::memory_order_relaxed); + + return index < initialBlockPoolSize ? (initialBlockPool + index) : nullptr; + } + + inline void add_block_to_free_list(Block* block) + { +#if MCDBGQ_TRACKMEM + block->owner = nullptr; +#endif + freeList.add(block); + } + + inline void add_blocks_to_free_list(Block* block) + { + while (block != nullptr) { + auto next = block->next; + add_block_to_free_list(block); + block = next; + } + } + + inline Block* try_get_block_from_free_list() + { + return freeList.try_get(); + } + + // Gets a free block from one of the memory pools, or allocates a new one (if applicable) + template + Block* requisition_block() + { + auto block = try_get_block_from_initial_pool(); + if (block != nullptr) { + return block; + } + + block = try_get_block_from_free_list(); + if (block != nullptr) { + return block; + } + + if (canAlloc == CanAlloc) { + return create(); + } + + return nullptr; + } + + +#if MCDBGQ_TRACKMEM + public: + struct MemStats { + size_t allocatedBlocks; + size_t usedBlocks; + size_t freeBlocks; + size_t ownedBlocksExplicit; + size_t ownedBlocksImplicit; + size_t implicitProducers; + size_t explicitProducers; + size_t elementsEnqueued; + size_t blockClassBytes; + size_t queueClassBytes; + size_t implicitBlockIndexBytes; + size_t explicitBlockIndexBytes; + + friend class ConcurrentQueue; + + private: + static MemStats getFor(ConcurrentQueue* q) + { + MemStats stats = { 0 }; + + stats.elementsEnqueued = q->size_approx(); + + auto block = q->freeList.head_unsafe(); + while (block != nullptr) { + ++stats.allocatedBlocks; + ++stats.freeBlocks; + block = block->freeListNext.load(std::memory_order_relaxed); + } + + for (auto ptr = q->producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + bool implicit = dynamic_cast(ptr) != nullptr; + stats.implicitProducers += implicit ? 1 : 0; + stats.explicitProducers += implicit ? 0 : 1; + + if (implicit) { + auto prod = static_cast(ptr); + stats.queueClassBytes += sizeof(ImplicitProducer); + auto head = prod->headIndex.load(std::memory_order_relaxed); + auto tail = prod->tailIndex.load(std::memory_order_relaxed); + auto hash = prod->blockIndex.load(std::memory_order_relaxed); + if (hash != nullptr) { + for (size_t i = 0; i != hash->capacity; ++i) { + if (hash->index[i]->key.load(std::memory_order_relaxed) != ImplicitProducer::INVALID_BLOCK_BASE && hash->index[i]->value.load(std::memory_order_relaxed) != nullptr) { + ++stats.allocatedBlocks; + ++stats.ownedBlocksImplicit; + } + } + stats.implicitBlockIndexBytes += hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry); + for (; hash != nullptr; hash = hash->prev) { + stats.implicitBlockIndexBytes += sizeof(typename ImplicitProducer::BlockIndexHeader) + hash->capacity * sizeof(typename ImplicitProducer::BlockIndexEntry*); + } + } + for (; details::circular_less_than(head, tail); head += BLOCK_SIZE) { + //auto block = prod->get_block_index_entry_for_index(head); + ++stats.usedBlocks; + } + } + else { + auto prod = static_cast(ptr); + stats.queueClassBytes += sizeof(ExplicitProducer); + auto tailBlock = prod->tailBlock; + bool wasNonEmpty = false; + if (tailBlock != nullptr) { + auto block = tailBlock; + do { + ++stats.allocatedBlocks; + if (!block->ConcurrentQueue::Block::template is_empty() || wasNonEmpty) { + ++stats.usedBlocks; + wasNonEmpty = wasNonEmpty || block != tailBlock; + } + ++stats.ownedBlocksExplicit; + block = block->next; + } while (block != tailBlock); + } + auto index = prod->blockIndex.load(std::memory_order_relaxed); + while (index != nullptr) { + stats.explicitBlockIndexBytes += sizeof(typename ExplicitProducer::BlockIndexHeader) + index->size * sizeof(typename ExplicitProducer::BlockIndexEntry); + index = static_cast(index->prev); + } + } + } + + auto freeOnInitialPool = q->initialBlockPoolIndex.load(std::memory_order_relaxed) >= q->initialBlockPoolSize ? 0 : q->initialBlockPoolSize - q->initialBlockPoolIndex.load(std::memory_order_relaxed); + stats.allocatedBlocks += freeOnInitialPool; + stats.freeBlocks += freeOnInitialPool; + + stats.blockClassBytes = sizeof(Block) * stats.allocatedBlocks; + stats.queueClassBytes += sizeof(ConcurrentQueue); + + return stats; + } + }; + + // For debugging only. Not thread-safe. + MemStats getMemStats() + { + return MemStats::getFor(this); + } + private: + friend struct MemStats; +#endif + + + ////////////////////////////////// + // Producer list manipulation + ////////////////////////////////// + + ProducerBase* recycle_or_create_producer(bool isExplicit) + { + bool recycled; + return recycle_or_create_producer(isExplicit, recycled); + } + + ProducerBase* recycle_or_create_producer(bool isExplicit, bool& recycled) + { +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + // Try to re-use one first + for (auto ptr = producerListTail.load(std::memory_order_acquire); ptr != nullptr; ptr = ptr->next_prod()) { + if (ptr->inactive.load(std::memory_order_relaxed) && ptr->isExplicit == isExplicit) { + bool expected = true; + if (ptr->inactive.compare_exchange_strong(expected, /* desired */ false, std::memory_order_acquire, std::memory_order_relaxed)) { + // We caught one! It's been marked as activated, the caller can have it + recycled = true; + return ptr; + } + } + } + + recycled = false; + return add_producer(isExplicit ? static_cast(create(this)) : create(this)); + } + + ProducerBase* add_producer(ProducerBase* producer) + { + // Handle failed memory allocation + if (producer == nullptr) { + return nullptr; + } + + producerCount.fetch_add(1, std::memory_order_relaxed); + + // Add it to the lock-free list + auto prevTail = producerListTail.load(std::memory_order_relaxed); + do { + producer->next = prevTail; + } while (!producerListTail.compare_exchange_weak(prevTail, producer, std::memory_order_release, std::memory_order_relaxed)); + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + if (producer->isExplicit) { + auto prevTailExplicit = explicitProducers.load(std::memory_order_relaxed); + do { + static_cast(producer)->nextExplicitProducer = prevTailExplicit; + } while (!explicitProducers.compare_exchange_weak(prevTailExplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); + } + else { + auto prevTailImplicit = implicitProducers.load(std::memory_order_relaxed); + do { + static_cast(producer)->nextImplicitProducer = prevTailImplicit; + } while (!implicitProducers.compare_exchange_weak(prevTailImplicit, static_cast(producer), std::memory_order_release, std::memory_order_relaxed)); + } +#endif + + return producer; + } + + void reown_producers() + { + // After another instance is moved-into/swapped-with this one, all the + // producers we stole still think their parents are the other queue. + // So fix them up! + for (auto ptr = producerListTail.load(std::memory_order_relaxed); ptr != nullptr; ptr = ptr->next_prod()) { + ptr->parent = this; + } + } + + + ////////////////////////////////// + // Implicit producer hash + ////////////////////////////////// + + struct ImplicitProducerKVP + { + std::atomic key; + ImplicitProducer* value; // No need for atomicity since it's only read by the thread that sets it in the first place + + ImplicitProducerKVP() : value(nullptr) { } + + ImplicitProducerKVP(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT + { + key.store(other.key.load(std::memory_order_relaxed), std::memory_order_relaxed); + value = other.value; + } + + inline ImplicitProducerKVP& operator=(ImplicitProducerKVP&& other) MOODYCAMEL_NOEXCEPT + { + swap(other); + return *this; + } + + inline void swap(ImplicitProducerKVP& other) MOODYCAMEL_NOEXCEPT + { + if (this != &other) { + details::swap_relaxed(key, other.key); + std::swap(value, other.value); + } + } + }; + + template + friend void moodycamel::swap(typename ConcurrentQueue::ImplicitProducerKVP&, typename ConcurrentQueue::ImplicitProducerKVP&) MOODYCAMEL_NOEXCEPT; + + struct ImplicitProducerHash + { + size_t capacity; + ImplicitProducerKVP* entries; + ImplicitProducerHash* prev; + }; + + inline void populate_initial_implicit_producer_hash() + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; + + implicitProducerHashCount.store(0, std::memory_order_relaxed); + auto hash = &initialImplicitProducerHash; + hash->capacity = INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; + hash->entries = &initialImplicitProducerHashEntries[0]; + for (size_t i = 0; i != INITIAL_IMPLICIT_PRODUCER_HASH_SIZE; ++i) { + initialImplicitProducerHashEntries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); + } + hash->prev = nullptr; + implicitProducerHash.store(hash, std::memory_order_relaxed); + } + + void swap_implicit_producer_hashes(ConcurrentQueue& other) + { + if (INITIAL_IMPLICIT_PRODUCER_HASH_SIZE == 0) return; + + // Swap (assumes our implicit producer hash is initialized) + initialImplicitProducerHashEntries.swap(other.initialImplicitProducerHashEntries); + initialImplicitProducerHash.entries = &initialImplicitProducerHashEntries[0]; + other.initialImplicitProducerHash.entries = &other.initialImplicitProducerHashEntries[0]; + + details::swap_relaxed(implicitProducerHashCount, other.implicitProducerHashCount); + + details::swap_relaxed(implicitProducerHash, other.implicitProducerHash); + if (implicitProducerHash.load(std::memory_order_relaxed) == &other.initialImplicitProducerHash) { + implicitProducerHash.store(&initialImplicitProducerHash, std::memory_order_relaxed); + } + else { + ImplicitProducerHash* hash; + for (hash = implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &other.initialImplicitProducerHash; hash = hash->prev) { + continue; + } + hash->prev = &initialImplicitProducerHash; + } + if (other.implicitProducerHash.load(std::memory_order_relaxed) == &initialImplicitProducerHash) { + other.implicitProducerHash.store(&other.initialImplicitProducerHash, std::memory_order_relaxed); + } + else { + ImplicitProducerHash* hash; + for (hash = other.implicitProducerHash.load(std::memory_order_relaxed); hash->prev != &initialImplicitProducerHash; hash = hash->prev) { + continue; + } + hash->prev = &other.initialImplicitProducerHash; + } + } + + // Only fails (returns nullptr) if memory allocation fails + ImplicitProducer* get_or_add_implicit_producer() + { + // Note that since the data is essentially thread-local (key is thread ID), + // there's a reduced need for fences (memory ordering is already consistent + // for any individual thread), except for the current table itself. + + // Start by looking for the thread ID in the current and all previous hash tables. + // If it's not found, it must not be in there yet, since this same thread would + // have added it previously to one of the tables that we traversed. + + // Code and algorithm adapted from http://preshing.com/20130605/the-worlds-simplest-lock-free-hash-table + +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + + auto id = details::thread_id(); + auto hashedId = details::hash_thread_id(id); + + auto mainHash = implicitProducerHash.load(std::memory_order_acquire); + for (auto hash = mainHash; hash != nullptr; hash = hash->prev) { + // Look for the id in this hash + auto index = hashedId; + while (true) { // Not an infinite loop because at least one slot is free in the hash table + index &= hash->capacity - 1; + + auto probedKey = hash->entries[index].key.load(std::memory_order_relaxed); + if (probedKey == id) { + // Found it! If we had to search several hashes deep, though, we should lazily add it + // to the current main hash table to avoid the extended search next time. + // Note there's guaranteed to be room in the current hash table since every subsequent + // table implicitly reserves space for all previous tables (there's only one + // implicitProducerHashCount). + auto value = hash->entries[index].value; + if (hash != mainHash) { + index = hashedId; + while (true) { + index &= mainHash->capacity - 1; + probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); + auto empty = details::invalid_thread_id; +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + auto reusable = details::invalid_thread_id2; + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || + (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { +#else + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed))) { +#endif + mainHash->entries[index].value = value; + break; + } + ++index; + } + } + + return value; + } + if (probedKey == details::invalid_thread_id) { + break; // Not in this hash table + } + ++index; + } + } + + // Insert! + auto newCount = 1 + implicitProducerHashCount.fetch_add(1, std::memory_order_relaxed); + while (true) { + if (newCount >= (mainHash->capacity >> 1) && !implicitProducerHashResizeInProgress.test_and_set(std::memory_order_acquire)) { + // We've acquired the resize lock, try to allocate a bigger hash table. + // Note the acquire fence synchronizes with the release fence at the end of this block, and hence when + // we reload implicitProducerHash it must be the most recent version (it only gets changed within this + // locked block). + mainHash = implicitProducerHash.load(std::memory_order_acquire); + if (newCount >= (mainHash->capacity >> 1)) { + auto newCapacity = mainHash->capacity << 1; + while (newCount >= (newCapacity >> 1)) { + newCapacity <<= 1; + } + auto raw = static_cast((Traits::malloc)(sizeof(ImplicitProducerHash) + std::alignment_of::value - 1 + sizeof(ImplicitProducerKVP) * newCapacity)); + if (raw == nullptr) { + // Allocation failed + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + implicitProducerHashResizeInProgress.clear(std::memory_order_relaxed); + return nullptr; + } + + auto newHash = new (raw) ImplicitProducerHash; + newHash->capacity = newCapacity; + newHash->entries = reinterpret_cast(details::align_for(raw + sizeof(ImplicitProducerHash))); + for (size_t i = 0; i != newCapacity; ++i) { + new (newHash->entries + i) ImplicitProducerKVP; + newHash->entries[i].key.store(details::invalid_thread_id, std::memory_order_relaxed); + } + newHash->prev = mainHash; + implicitProducerHash.store(newHash, std::memory_order_release); + implicitProducerHashResizeInProgress.clear(std::memory_order_release); + mainHash = newHash; + } + else { + implicitProducerHashResizeInProgress.clear(std::memory_order_release); + } + } + + // If it's < three-quarters full, add to the old one anyway so that we don't have to wait for the next table + // to finish being allocated by another thread (and if we just finished allocating above, the condition will + // always be true) + if (newCount < (mainHash->capacity >> 1) + (mainHash->capacity >> 2)) { + bool recycled; + auto producer = static_cast(recycle_or_create_producer(false, recycled)); + if (producer == nullptr) { + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + return nullptr; + } + if (recycled) { + implicitProducerHashCount.fetch_sub(1, std::memory_order_relaxed); + } + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + producer->threadExitListener.callback = &ConcurrentQueue::implicit_producer_thread_exited_callback; + producer->threadExitListener.userData = producer; + details::ThreadExitNotifier::subscribe(&producer->threadExitListener); +#endif + + auto index = hashedId; + while (true) { + index &= mainHash->capacity - 1; + auto probedKey = mainHash->entries[index].key.load(std::memory_order_relaxed); + + auto empty = details::invalid_thread_id; +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + auto reusable = details::invalid_thread_id2; + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed)) || + (probedKey == reusable && mainHash->entries[index].key.compare_exchange_strong(reusable, id, std::memory_order_acquire, std::memory_order_acquire))) { +#else + if ((probedKey == empty && mainHash->entries[index].key.compare_exchange_strong(empty, id, std::memory_order_relaxed, std::memory_order_relaxed))) { +#endif + mainHash->entries[index].value = producer; + break; + } + ++index; + } + return producer; + } + + // Hmm, the old hash is quite full and somebody else is busy allocating a new one. + // We need to wait for the allocating thread to finish (if it succeeds, we add, if not, + // we try to allocate ourselves). + mainHash = implicitProducerHash.load(std::memory_order_acquire); + } + } + +#ifdef MOODYCAMEL_CPP11_THREAD_LOCAL_SUPPORTED + void implicit_producer_thread_exited(ImplicitProducer* producer) + { + // Remove from thread exit listeners + details::ThreadExitNotifier::unsubscribe(&producer->threadExitListener); + + // Remove from hash +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugLock lock(implicitProdMutex); +#endif + auto hash = implicitProducerHash.load(std::memory_order_acquire); + assert(hash != nullptr); // The thread exit listener is only registered if we were added to a hash in the first place + auto id = details::thread_id(); + auto hashedId = details::hash_thread_id(id); + details::thread_id_t probedKey; + + // We need to traverse all the hashes just in case other threads aren't on the current one yet and are + // trying to add an entry thinking there's a free slot (because they reused a producer) + for (; hash != nullptr; hash = hash->prev) { + auto index = hashedId; + do { + index &= hash->capacity - 1; + probedKey = hash->entries[index].key.load(std::memory_order_relaxed); + if (probedKey == id) { + hash->entries[index].key.store(details::invalid_thread_id2, std::memory_order_release); + break; + } + ++index; + } while (probedKey != details::invalid_thread_id); // Can happen if the hash has changed but we weren't put back in it yet, or if we weren't added to this hash in the first place + } + + // Mark the queue as being recyclable + producer->inactive.store(true, std::memory_order_release); + } + + static void implicit_producer_thread_exited_callback(void* userData) + { + auto producer = static_cast(userData); + auto queue = producer->parent; + queue->implicit_producer_thread_exited(producer); + } +#endif + + ////////////////////////////////// + // Utility functions + ////////////////////////////////// + + template + static inline U* create_array(size_t count) + { + assert(count > 0); + auto p = static_cast((Traits::malloc)(sizeof(U) * count)); + if (p == nullptr) { + return nullptr; + } + + for (size_t i = 0; i != count; ++i) { + new (p + i) U(); + } + return p; + } + + template + static inline void destroy_array(U* p, size_t count) + { + if (p != nullptr) { + assert(count > 0); + for (size_t i = count; i != 0; ) { + (p + --i)->~U(); + } + (Traits::free)(p); + } + } + + template + static inline U* create() + { + auto p = (Traits::malloc)(sizeof(U)); + return p != nullptr ? new (p) U : nullptr; + } + + template + static inline U* create(A1&& a1) + { + auto p = (Traits::malloc)(sizeof(U)); + return p != nullptr ? new (p) U(std::forward(a1)) : nullptr; + } + + template + static inline void destroy(U* p) + { + if (p != nullptr) { + p->~U(); + } + (Traits::free)(p); + } + +private: + std::atomic producerListTail; + std::atomic producerCount; + + std::atomic initialBlockPoolIndex; + Block* initialBlockPool; + size_t initialBlockPoolSize; + +#if !MCDBGQ_USEDEBUGFREELIST + FreeList freeList; +#else + debug::DebugFreeList freeList; +#endif + + std::atomic implicitProducerHash; + std::atomic implicitProducerHashCount; // Number of slots logically used + ImplicitProducerHash initialImplicitProducerHash; + std::array initialImplicitProducerHashEntries; + std::atomic_flag implicitProducerHashResizeInProgress; + + std::atomic nextExplicitConsumerId; + std::atomic globalExplicitConsumerOffset; + +#if MCDBGQ_NOLOCKFREE_IMPLICITPRODHASH + debug::DebugMutex implicitProdMutex; +#endif + +#ifdef MOODYCAMEL_QUEUE_INTERNAL_DEBUG + std::atomic explicitProducers; + std::atomic implicitProducers; +#endif +}; + + +template +ProducerToken::ProducerToken(ConcurrentQueue& queue) + : producer(queue.recycle_or_create_producer(true)) +{ + if (producer != nullptr) { + producer->token = this; + } +} + +template +ProducerToken::ProducerToken(BlockingConcurrentQueue& queue) + : producer(reinterpret_cast*>(&queue)->recycle_or_create_producer(true)) +{ + if (producer != nullptr) { + producer->token = this; + } +} + +template +ConsumerToken::ConsumerToken(ConcurrentQueue& queue) + : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) +{ + initialOffset = queue.nextExplicitConsumerId.fetch_add(1, std::memory_order_release); + lastKnownGlobalOffset = -1; +} + +template +ConsumerToken::ConsumerToken(BlockingConcurrentQueue& queue) + : itemsConsumedFromCurrent(0), currentProducer(nullptr), desiredProducer(nullptr) +{ + initialOffset = reinterpret_cast*>(&queue)->nextExplicitConsumerId.fetch_add(1, std::memory_order_release); + lastKnownGlobalOffset = -1; +} + +template +inline void swap(ConcurrentQueue& a, ConcurrentQueue& b) MOODYCAMEL_NOEXCEPT +{ + a.swap(b); +} + +inline void swap(ProducerToken& a, ProducerToken& b) MOODYCAMEL_NOEXCEPT +{ + a.swap(b); +} + +inline void swap(ConsumerToken& a, ConsumerToken& b) MOODYCAMEL_NOEXCEPT +{ + a.swap(b); +} + +template +inline void swap(typename ConcurrentQueue::ImplicitProducerKVP& a, typename ConcurrentQueue::ImplicitProducerKVP& b) MOODYCAMEL_NOEXCEPT +{ + a.swap(b); +} + +} + +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif diff --git a/libsrc/core/exception.cpp b/libsrc/core/exception.cpp new file mode 100644 index 00000000..b89d721e --- /dev/null +++ b/libsrc/core/exception.cpp @@ -0,0 +1,239 @@ +#include "exception.hpp" +#include "utils.hpp" + +namespace ngcore +{ + Exception :: Exception(const std::string& s) + : m_what(s) {} + + Exception :: Exception(const char* s) + : m_what(s) {} + + + void ThrowException(const std::string & s) + { + throw Exception (s); + } + + void ThrowException(const char * s) + { + throw Exception (s); + } +} // namespace ngcore + + +// ********* STUFF FOR GETBACKTRACE *************************** +#ifdef __GNUC__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ngcore +{ + namespace detail + { + static int exec(std::string cmd, std::string & out) { + std::array buffer; + FILE *pipe = popen(cmd.c_str(), "r"); + + if (!pipe) + throw std::runtime_error("popen() failed!"); + + out = ""; + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) + out += buffer.data(); + + int error_code = pclose(pipe); + return error_code; + } + +#ifdef __APPLE__ + // Split output line from backtrace_symbols to recover function name and offset + // then use `nm` command line tool to get the address of the function + // then use `add42line` command line tool to map function address + offset to line in source code + static std::string TranslateBacktrace( std::string s, std::string libname ) + { + // example line + // 1 libngcore.dylib 0x000000010ddb298c _ZL21ngcore_signal_handleri + 316 + constexpr char reset_shell[] = "\033[0m"; + constexpr char green[] = "\033[32m"; + constexpr char yellow[] = "\033[33m"; + + std::istringstream in(s); + + std::string libname1, funcname, addr, plus_sign; + size_t i,offset; + + in >> i >> libname1 >> addr >> funcname >> plus_sign >> std::hex >> offset; + + std::stringstream out; + + if(!funcname.empty() && !libname.empty()) + { + std::string nm_command = "nm " + libname + " | grep \"" + funcname + "$\" | cut -f 1 -d ' '"; + std::string output; + auto exit_code = exec(nm_command, output); + auto fptr = std::strtoul(output.c_str(), 0, 16); + if(fptr == 0) + return out.str()+'\n'; + std::stringstream offset_s; + offset_s << "0x" << std::hex << fptr+offset - 5; + std::string addr2line_command = std::string("atos -o ") + libname + " --fullPath " + offset_s.str(); + exit_code = exec(addr2line_command, output); + if(exit_code==0) + out << " at " << green << output << reset_shell; + else + out << '\n'; + } + else + out << s << '\n'; + + return out.str(); + } +#else // __APPLE__ + + // Split output line from backtrace_symbols to recover function name and offset + // then use `nm` command line tool to get the address of the function + // then use `addr2line` command line tool to map function address + offset to line in source code + static std::string TranslateBacktrace( std::string s, std::string /*dummy*/ ) + { + // example line: + // /home/mhochsteger/install/ngs_clang/bin/../lib/libngcore.so(_ZN6ngcore11TaskManager4LoopEi+0x1e0) [0x7f2991fe1030] + constexpr char reset_shell[] = "\033[0m"; + constexpr char green[] = "\033[32m"; + constexpr char yellow[] = "\033[33m"; + + auto brace_open_pos = s.find('('); + auto brace_close_pos = s.find(')', brace_open_pos); + auto plus_pos = s.find('+', brace_open_pos); + auto bracket_open_pos = s.find('['); + auto bracket_close_pos = s.find(']'); + + auto libname = s.substr(0, brace_open_pos); + auto funcname = s.substr(brace_open_pos+1, plus_pos - brace_open_pos - 1); + auto offset = std::strtoul(s.substr(plus_pos+1, brace_close_pos - plus_pos - 1).c_str(), 0, 16); + auto position = std::strtoul(s.substr(bracket_open_pos+1, bracket_close_pos - bracket_open_pos - 1).c_str(), 0, 16); + std::stringstream out; + + if(!funcname.empty()) + { + std::vector buffer(10240); + int status; + size_t size = buffer.size(); + abi::__cxa_demangle(funcname.c_str(), &buffer[0], &size, &status); + out << "in " << yellow << &buffer[0] << reset_shell << '\n'; + + std::string nm_command = "nm " + libname + " | grep " + funcname + " | cut -f 1 -d ' '"; + std::string output; + auto exit_code = exec(nm_command, output); + auto fptr = std::strtoul(output.c_str(), 0, 16); + + std::stringstream offset_s; + offset_s << "0x" << std::hex << fptr+offset - 5; + std::string addr2line_command = std::string("addr2line -i -p -e ") + libname + " " + offset_s.str(); + exit_code = exec(addr2line_command, output); + if(exit_code==0) + { + std::stringstream soutput(output); + std::string s; + while(soutput) + { + if(getline(soutput, s)) + out << "\t at " << green << s << reset_shell << '\n'; + } + } + else + out << '\n'; + } + else + out << s << '\n'; + + return out.str(); + } +#endif // __APPLE__ + + } // namespace detail + + std::string GetBackTrace() + { + if(!getenv("NG_BACKTRACE")) + return ""; + std::cerr << "Collecting backtrace..." << std::endl; + std::stringstream result; + void *bt[100]; + int bt_size; + char **bt_syms; + int i; + + bt_size = backtrace(bt, 100); + bt_syms = backtrace_symbols(bt, bt_size); + Dl_info info; + for (i = 1; i < bt_size-1; i++) + { + dladdr(bt[i], &info); + size_t len = strlen(bt_syms[i]); + result << '#'<< i << '\t' << detail::TranslateBacktrace( bt_syms[i], info.dli_fname ); + } + free(bt_syms); + return result.str(); + } + +} // namespace ngcore + +static void ngcore_signal_handler(int sig) +{ + static bool first_call = true; + if(!first_call) + exit(1); // avoid endless recursions if signals are caused by this handler + first_call = false; + + switch(sig) + { + case SIGABRT: + std::cerr << "Caught SIGABRT: usually caused by abort() or assert()" << std::endl; + break; + case SIGILL: + std::cerr << "Caught SIGILL: illegal instruction" << std::endl; + break; + case SIGSEGV: + std::cerr << "Caught SIGSEGV: segmentation fault" << std::endl; + break; + } + + std::cerr << ngcore::GetBackTrace() << std::endl; + exit(1); +} + +// register signal handler when library is loaded +static bool dummy = []() +{ + if(getenv("NG_BACKTRACE")) + { + signal(SIGABRT, ngcore_signal_handler); + signal(SIGILL, ngcore_signal_handler); + signal(SIGSEGV, ngcore_signal_handler); + } + return true; +}(); + +#else // __GNUC__ + +namespace ngcore +{ + std::string GetBackTrace() + { + return std::string(); + } +} // namespace ngcore + +#endif // __GNUC__ diff --git a/libsrc/core/exception.hpp b/libsrc/core/exception.hpp index 35359e65..32b02cb2 100644 --- a/libsrc/core/exception.hpp +++ b/libsrc/core/exception.hpp @@ -7,8 +7,12 @@ #include "ngcore_api.hpp" // for NGCORE_API + namespace ngcore { + + NGCORE_API std::string GetBackTrace(); + // Exception for code that shouldn't be executed class NGCORE_API UnreachableCodeException : public std::exception { @@ -27,8 +31,8 @@ namespace ngcore Exception() = default; Exception(const Exception&) = default; Exception(Exception&&) = default; - Exception(const std::string& s) : m_what(s) {} - Exception(const char* s) : m_what(s) {} + Exception(const std::string& s); // : m_what(s) {} + Exception(const char* s); // : m_what(s) {} ~Exception() override = default; Exception& operator =(const Exception&) = default; @@ -45,7 +49,10 @@ namespace ngcore /// implement virtual function of std::exception const char* what() const noexcept override { return m_what.c_str(); } }; - + + NGCORE_API void ThrowException(const std::string & s); + NGCORE_API void ThrowException(const char * s); + // Out of Range exception class NGCORE_API RangeException : public Exception { @@ -55,8 +62,9 @@ namespace ngcore int ind, int imin, int imax) : Exception("") { std::stringstream str; - str << where << ": index " << ind << " out of range [" << imin << "," << imax << "]\n"; + str << where << ": index " << ind << " out of range [" << imin << "," << imax << ")\n"; Append (str.str()); + Append (GetBackTrace()); } template @@ -79,12 +87,19 @@ namespace ngcore // Convenience macro to append file name and line of exception origin to the string #define NG_EXCEPTION(s) ngcore::Exception(__FILE__ ":" NETGEN_CORE_NGEXEPTION_STR(__LINE__) "\t"+std::string(s)) -#ifdef NETGEN_ENABLE_CHECK_RANGE -#define NETGEN_CHECK_RANGE(value, min, max) \ - { if ((value)<(min) || (value)>=(max)) \ - throw ngcore::RangeException(__FILE__ ":" NETGEN_CORE_NGEXEPTION_STR(__LINE__) "\t", (value), (min), (max)); } -#else // NETGEN_ENABLE_CHECK_RANGE +#if defined(NETGEN_ENABLE_CHECK_RANGE) && !defined(__CUDA_ARCH__) +#define NETGEN_CHECK_RANGE(value, min, max_plus_one) \ + { if ((value)<(min) || (value)>=(max_plus_one)) \ + throw ngcore::RangeException(__FILE__ ":" NETGEN_CORE_NGEXEPTION_STR(__LINE__) "\t", (value), (min), (max_plus_one)); } +#define NETGEN_CHECK_SHAPE(a,b) \ + { if(a.Shape() != b.Shape()) \ + throw ngcore::Exception(__FILE__": shape don't match"); } +#else // defined(NETGEN_ENABLE_CHECK_RANGE) && !defined(__CUDA_ARCH__) #define NETGEN_CHECK_RANGE(value, min, max) -#endif // NETGEN_ENABLE_CHECK_RANGE +#define NETGEN_CHECK_SHAPE(a,b) +#endif // defined(NETGEN_ENABLE_CHECK_RANGE) && !defined(__CUDA_ARCH__) + + + #endif // NETGEN_CORE_EXCEPTION_HPP diff --git a/libsrc/core/flags.cpp b/libsrc/core/flags.cpp new file mode 100644 index 00000000..e968ce9c --- /dev/null +++ b/libsrc/core/flags.cpp @@ -0,0 +1,642 @@ +/**************************************************************************/ +/* File: flags.cpp */ +/* Author: Joachim Schoeberl */ +/* Date: 10. Oct. 96 */ +/**************************************************************************/ + +#include "archive.hpp" +#include "flags.hpp" + +#ifdef WIN32 +#include +#endif + +#include + +namespace ngcore +{ + using std::string; + using std::endl; + Flags :: Flags () { ; } + + Flags :: Flags (const Flags & flags) + { + string name; + for (int i = 0; i < flags.GetNStringFlags(); i++) + { + string str = flags.GetStringFlag (i, name); + SetFlag (name, str); + } + for (int i = 0; i < flags.GetNNumFlags(); i++) + { + double val = flags.GetNumFlag (i, name); + SetFlag (name, val); + } + for (int i = 0; i < flags.GetNDefineFlags(); i++) + { + bool val = flags.GetDefineFlag (i, name); + SetFlag (name, val); + } + for (int i = 0; i < flags.GetNNumListFlags(); i++) + { + auto numa = flags.GetNumListFlag (i, name); + SetFlag (name, *numa); + } + for (int i = 0; i < flags.GetNStringListFlags(); i++) + { + auto stra = flags.GetStringListFlag (i, name); + SetFlag (name, *stra); + } + for (int i = 0; i < flags.GetNFlagsFlags(); i++) + { + 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) + : strflags(flags.strflags), numflags(flags.numflags), + defflags(flags.defflags), strlistflags(flags.strlistflags), + numlistflags(flags.numlistflags) { ; } + + Flags :: Flags (std::initializer_list list) + { + for (auto i = list.begin(); i < list.end(); i++) + SetCommandLineFlag ((string("-")+*i).c_str()); + } + + + Flags :: Flags (string f1, string f2, string f3, string f4, string f5) + { + SetCommandLineFlag ((string("-")+f1).c_str()); + if (f2.length()) SetCommandLineFlag ( (string("-")+f2).c_str() ); + if (f3.length()) SetCommandLineFlag ( (string("-")+f3).c_str() ); + if (f4.length()) SetCommandLineFlag ( (string("-")+f4).c_str() ); + if (f5.length()) SetCommandLineFlag ( (string("-")+f5).c_str() ); + } + + Flags :: ~Flags () + { + DeleteFlags (); + } + + void Flags :: DeleteFlags () + { + strflags.DeleteAll(); + numflags.DeleteAll(); + defflags.DeleteAll(); + strlistflags.DeleteAll(); + numlistflags.DeleteAll(); + } + + + Flags Flags :: SetFlag (const char * name, bool b) && + { + this -> SetFlag (name, b); + return std::move(*this); + } + + Flags Flags :: SetFlag (const char * name, double val) && + { + this -> SetFlag (name, val); + return std::move(*this); + } + + + + Flags & Flags :: SetFlag (const char * name, const string & val) + { + strflags.Set (name, val); + return *this; + } + + Flags & Flags :: SetFlag (const char * name, double val) & + { + numflags.Set (name, val); + return *this; + } + + Flags & Flags :: SetFlag (const char * name, bool b) & + { + defflags.Set (name, b); + return *this; + } + + Flags & Flags :: SetFlag (const char * name, Flags & val) & + { + flaglistflags.Set (name, val); + return *this; + } + + + + Flags & Flags :: SetFlag (const string & name, const string & val) + { + // char * hval = new char[strlen (val) + 1]; + // strcpy (hval, val); + strflags.Set (name, val); + return *this; + } + + Flags & Flags :: SetFlag (const string & name, double val) + { + numflags.Set (name, val); + return *this; + } + + Flags & Flags :: SetFlag (const string & name, bool b) + { + defflags.Set (name, b); + return *this; + } + + Flags & Flags :: SetFlag (const string & name, Flags & val) + { + flaglistflags.Set (name, val); + return *this; + } + + Flags & Flags :: SetFlag (const string & name, const Array & val) + { + auto strarray = std::make_shared>(val); + /* + for (int i = 0; i < val.Size(); i++) + { + strarray->Append (new char[strlen(val[i])+1]); + strcpy (strarray->Last(), val[i]); + } + */ + strlistflags.Set (name, strarray); + return *this; + } + + Flags & Flags :: SetFlag (const string & name, const Array & val) + { + // Array * numarray = new Array(val); + auto numarray = std::make_shared> (val); + + numlistflags.Set (name, numarray); + 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 + { + if (strflags.Used (name)) + return strflags[name]; + else + { + if (!def) return string(""); + return def; + } + } + + string Flags :: GetStringFlag (const string & name, string def) const + { + if (strflags.Used (name)) + return strflags[name]; + else + return def; + } + + + double Flags :: GetNumFlag (const string & name, double def) const + { + if (numflags.Used (name)) + return numflags[name]; + else + return def; + } + + const double * Flags :: GetNumFlagPtr (const string & name) const + { + if (numflags.Used (name)) + return & ((SymbolTable&)numflags)[name]; + else + return NULL; + } + + double * Flags :: GetNumFlagPtr (const string & name) + { + if (numflags.Used (name)) + return & ((SymbolTable&)numflags)[name]; + else + return NULL; + } + + /* + int Flags :: GetDefineFlag (const char * name) const + { + return defflags.Used (name); + } + */ + bool Flags :: GetDefineFlag (const string & name) const throw() + { + if (!defflags.Used (name)) return false; + return defflags[name]; + } + + xbool Flags :: GetDefineFlagX (const string & name) const throw() + { + if (!defflags.Used (name)) return maybe; + return bool(defflags[name]); + } + + + const Array & + Flags :: GetStringListFlag (const string & name) const + { + if (strlistflags.Used (name)) + return *strlistflags[name]; + else + { + static Array hstra(0); + return hstra; + } + } + + const Array & + Flags ::GetNumListFlag (const string & name) const + { + if (numlistflags.Used (name)) + return *numlistflags[name]; + else + { + static Array hnuma(0); + return hnuma; + } + } + + const Flags & + Flags ::GetFlagsFlag (const string & name) const + { + if (flaglistflags.Used (name)) + return flaglistflags[name]; + else + { + static Flags empty; + return empty; + } + } + + 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); + } + + bool Flags :: NumFlagDefined (const string &name) const + { + return numflags.Used (name); + } + + bool Flags :: FlagsFlagDefined (const string &name) const + { + return flaglistflags.Used (name); + } + + bool Flags :: StringListFlagDefined (const string & name) const + { + return strlistflags.Used (name); + } + + bool Flags :: NumListFlagDefined (const string & name) const + { + 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++) + str << strflags.GetName(i) << " = " << strflags[i] << endl; + for (int i = 0; i < numflags.Size(); i++) + str << numflags.GetName(i) << " = " << numflags[i] << endl; + for (int i = 0; i < defflags.Size(); i++) + str << defflags.GetName(i) << " = " << (defflags[i] ? "_TRUE" : "_FALSE") << endl; + for (int i = 0; i < flaglistflags.Size(); i++) + str << flaglistflags.GetName(i) << " =*" << flaglistflags[i] << endl; + for (int i = 0; i < numlistflags.Size(); i++) + { + str << numlistflags.GetName(i) << " = ["; + int j = 0; + for (j = 0; j + 1 < numlistflags[i]->Size(); ++j) + str << (*numlistflags[i])[j] << ", "; + if (numlistflags[i]->Size()) + str << (*numlistflags[i])[j]; + str << "]" << endl; + } + } + + void Flags :: SaveFlags (const char * filename) const + { + std::ofstream outfile (filename); + SaveFlags(outfile); + } + + + + void Flags :: PrintFlags (ostream & ost) const + { + for (int i = 0; i < strflags.Size(); i++) + ost << strflags.GetName(i) << " = " << strflags[i] << endl; + for (int i = 0; i < numflags.Size(); i++) + ost << numflags.GetName(i) << " = " << numflags[i] << endl; + for (int i = 0; i < defflags.Size(); i++) + ost << defflags.GetName(i) << endl; + for (int i = 0; i < strlistflags.Size(); i++) + ost << strlistflags.GetName(i) << " = " << *strlistflags[i] << endl; + for (int i = 0; i < numlistflags.Size(); i++) + ost << numlistflags.GetName(i) << " = " << *numlistflags[i] << endl; + for (int i = 0; i < flaglistflags.Size(); i++) + ost << flaglistflags.GetName(i) << " = " << flaglistflags[i] << endl; + } + + void Flags :: LoadFlags (const char * filename, SymbolTable * sf) + { + std::ifstream str(filename); + LoadFlags(str,sf); + } + + void Flags :: LoadFlags (std::istream & istr, SymbolTable * sf ) + { + char str[100]; + char ch; + // double val; + + while (istr.good()) + { + string name; + string content; + string line; + getline(istr, line); + std::istringstream line_stream(line); + + getline(line_stream, name, '='); + name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); + + getline(line_stream, content); + content.erase(std::remove(content.begin(), content.end(), ' '), content.end()); + + // if (name[0] == '/' && name[1] == '/') + // { + // ch = 0; + // while (ch != '\n' && istr.good()) + // { + // ch = istr.get(); + // } + // continue; + // } + + if (strlen(content.c_str())==0) + { + SetFlag (name); + continue; + } + else + { + std::istringstream content_stream(content); + + content_stream >> ch; + if (ch != '*') + { + if (ch == '[') + { + // content_stream.putback (ch); + // content_stream >> ch; + string inner_string; + getline(content_stream, inner_string, ']'); + std::istringstream inner_string_stream(inner_string); + + Array values; + Array strings; + + string cur; + while (getline(inner_string_stream, cur, ',')) + { + char* endptr; + double vald = strtod (cur.c_str(), &endptr); + + if (endptr != cur.c_str() && strings.Size() == 0) + values.Append(vald); + else + strings.Append(cur); + } + if (strings.Size() > 0) + SetFlag(name, strings); + else + SetFlag(name, values); + } + else + { + if(content == "_TRUE" || content == "_FALSE") + { + SetFlag(name, (content =="_TRUE") ? true : false); + continue; + } + char* endptr; + double vald = strtod (content.c_str(), &endptr); + if (endptr != content.c_str()) + SetFlag (name, vald); + else + SetFlag (name, content); + } + } + else + { + content_stream.clear(); + content_stream >> str; + if (sf) + SetFlag (name, (*sf)[str]); + else + throw Exception (" no symboltable of flags "); + } + } + } + } + + void Flags :: DoArchive(Archive & archive) + { + archive & strflags & numflags & defflags & numlistflags & strlistflags & flaglistflags; + } + + void Flags :: Update(const Flags& other) + { + strflags.Update(other.strflags); + numflags.Update(other.numflags); + defflags.Update(other.defflags); + numlistflags.Update(other.numlistflags); + strlistflags.Update(other.strlistflags); + flaglistflags.Update(other.flaglistflags); + } + + void Flags :: SetCommandLineFlag (const char * st, SymbolTable * sf ) + { + //cout << "SetCommandLineFlag: flag = " << st << endl; + std::istringstream inst( (char *)st); + + char name[100]; + double val; + + + if (st[0] != '-') + { + std::cerr << "flag must start with '-'" << endl; + return; + } + + // flag with double -- + if (st[1] == '-') st++; + + const char * pos = strchr (st, '='); + const char * posstar = strchr (st, '*'); + const char * posbrack = strchr (st, '['); + + if (!pos) + { + // (cout) << "Add def flag: " << st+1 << endl; + SetFlag (st+1); + } + else + { + //cout << "pos = " << pos << endl; + + strncpy (name, st+1, (pos-st)-1); + name[pos-st-1] = 0; + + //cout << "name = " << name << endl; + + pos++; + char * endptr = NULL; + val = strtod (pos, &endptr); + + /* + cout << "val = " << val << endl; + cout << "isfinite = " << std::isfinite (val) << endl; + cout << "isinf = " << std::isinf (val) << endl; + cout << "pos = " << pos << ", endpos = " << endptr << endl; + */ + if (endptr != pos && !std::isfinite (val)) + endptr = const_cast(pos); + + /* +#ifdef WIN32 + if(endptr != pos && !_finite(val)) + endptr = const_cast(pos); +#else +#ifdef MACOS + if(endptr != pos && (__isnand(val) || __isinfd(val))) + endptr = const_cast(pos); +#else +#ifdef SUN +#else + if(endptr != pos && (std::isnan(val) || std::isinf(val))) + endptr = const_cast(pos); +#endif +#endif +#endif + */ + + //cout << "val = " << val << endl; + + if (!posbrack) + { + if (posstar) + { + pos++; + if (sf) + SetFlag (name, (*sf)[pos]); + else + throw Exception (" no symboltable of flags "); + } + else if (endptr == pos) + { + // string-flag + //(cout) << "Add String Flag: " << name << " = " << pos << endl; + SetFlag (name, pos); + } + else + { + // num-flag + //(cout) << "Add Num Flag: " << name << " = " << val << endl; + SetFlag (name, val); + } + } + else + { + // list-flag + char hc; + double val; + + val = strtod (posbrack+1, &endptr); + if (endptr != posbrack+1) + { + Array values; + + std::istringstream ist(posbrack); + ist >> hc; // '[' + ist >> val; + while (ist.good()) + { + values.Append (val); + ist >> hc; // ',' + ist >> val; + } + SetFlag (name, values); + } + else + { + // to be cleaned up ... + Array strs; + + posbrack++; + char * hstr = new char[strlen(posbrack)+1]; + strcpy (hstr, posbrack); + + char * chp = hstr; + + bool start = 1; + while (*chp && *chp != ']') + { + if (start) + strs.Append (chp); + start = 0; + if (*chp == ',') + { + *chp = 0; + start = 1; + } + chp++; + } + *chp = 0; + + Array strings; + for (int i = 0; i < strs.Size(); i++) + strings.Append (string (strs[i])); + SetFlag (name, strings); + delete [] hstr; + } + } + } + } +} // namespace ngcore diff --git a/libsrc/core/flags.hpp b/libsrc/core/flags.hpp new file mode 100644 index 00000000..938fb789 --- /dev/null +++ b/libsrc/core/flags.hpp @@ -0,0 +1,199 @@ +#ifndef NETGEN_CORE_FLAGS_HPP +#define NETGEN_CORE_FLAGS_HPP + + +/**************************************************************************/ +/* File: flags.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 10. Oct. 96 */ +/**************************************************************************/ + +#include +#include +#include +#include + +#include "array.hpp" +#include "symboltable.hpp" +#include "xbool.hpp" + +namespace ngcore +{ + + /** + A storage for command-line flags. + The flag structure maintains string flags, numerical flags, + define flags, string list flags, num list flags. + */ + class NGCORE_API Flags + { + /// string flags + SymbolTable strflags; + /// numerical flags + SymbolTable numflags; + /// define flags + SymbolTable defflags; + /// string list flags + SymbolTable>> strlistflags; + /// numerical list flags + SymbolTable>> numlistflags; + /// flags list flags + SymbolTable flaglistflags; + /// any object can be stored as a flag + SymbolTable anyflags; + public: + /// no flags + Flags (); + /// copy flags + Flags (const Flags & flags); + /// steal flags + Flags (Flags && flags); + /// + Flags (std::initializer_list list); + /// + Flags (std::string f1, std::string f2 = "", std::string f3 = "", std::string f4 = "", std::string f5 = ""); + /// delete mem + ~Flags (); + + Flags & operator= (const Flags & f2) = default; + Flags & operator= (Flags && f2) = default; + + void DoArchive(class Archive& ar); + + void Update(const Flags& other); + + /// Deletes all flags + void DeleteFlags (); + + /// Sets string flag, overwrite if exists + Flags & SetFlag (const char * name, const std::string & val); + /// Sets string flag, overwrite if exists + Flags & SetFlag (const char * name, const char * str) + { return SetFlag (name, std::string(str)); } + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const char * name, double val) &; + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const char * name, int val) + { return SetFlag (name, double(val)); } + /// Sets boolean flag + Flags & SetFlag (const char * name, bool b = true) &; + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const char * name, Flags & val) &; + + /// Sets string flag, overwrite if exists + Flags & SetFlag (const std::string & name, const std::string & val); + Flags & SetFlag (const std::string & name, const char * str) + { return SetFlag (name, std::string(str)); } + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const std::string & name, double val); + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const std::string & name, int val) + { return SetFlag (name, double(val)); } + /// Sets boolean flag + Flags & SetFlag (const std::string & name, bool b = true); + /// Sets numerical flag, overwrite if exists + Flags & SetFlag (const std::string & name, Flags & val); + /// Sets string array flag + 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) &&; + Flags SetFlag (const char * name, double val) &&; + + + + /// Save flags to file + void SaveFlags (const char * filename) const; + void SaveFlags (ostream & str) const; + /// write flags to stream + void PrintFlags (ostream & ost) const; + /// Load flags from file + void LoadFlags (const char * filename, SymbolTable * sf = nullptr); + void LoadFlags (std::istream & str, SymbolTable * sf = nullptr); + /** + Set command line flag. + Flag must be in form: -name=hello -val=0.5 -defflag + -names=[Joe,Jim] -values=[1,3,4] -solverflags=*abc + */ + void SetCommandLineFlag (const char * st, SymbolTable * sf = nullptr); + /// Returns string flag, default value if not exists + std::string GetStringFlag (const std::string & name, const char * def) const; + /// Returns std::string flag, default value if not exists + std::string GetStringFlag (const std::string & name, std::string def = "") const; + /// Returns numerical flag, default value if not exists + double GetNumFlag (const std::string & name, double def) const; + /// Returns address of numerical flag, null if not exists + const double * GetNumFlagPtr (const std::string & name) const; + /// Returns address of numerical flag, null if not exists + double * GetNumFlagPtr (const std::string & name); + /// Returns boolean flag + // int GetDefineFlag (const char * name) const; + bool GetDefineFlag (const std::string & name) const throw(); + xbool GetDefineFlagX (const std::string & name) const throw(); + /// Returns string list flag, empty array if not exist + const Array & GetStringListFlag (const std::string & name) const; + /// Returns num list flag, empty array if not exist + 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 + bool StringFlagDefined (const std::string & name) const; + /// Test, if num flag is defined + bool NumFlagDefined (const std::string & name) const; + /// Test, if num flag is defined + bool FlagsFlagDefined (const std::string & name) const; + /// Test, if string list flag is defined + 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(); } + /// number of num flags + int GetNNumFlags () const { return numflags.Size(); } + /// number of num flags + int GetNFlagsFlags () const { return flaglistflags.Size(); } + /// number of define flags + int GetNDefineFlags () const { return defflags.Size(); } + /// number of string-list flags + int GetNStringListFlags () const { return strlistflags.Size(); } + /// number of num-list flags + int GetNNumListFlags () const { return numlistflags.Size(); } + int GetNAnyFlags() const { return anyflags.Size(); } + + /// + const std::string & GetStringFlag (int i, std::string & name) const + { name = strflags.GetName(i); return strflags[i]; } + double GetNumFlag (int i, std::string & name) const + { name = numflags.GetName(i); return numflags[i]; } + bool GetDefineFlag (int i, std::string & name) const + { name = defflags.GetName(i); return defflags[i]; } + const std::shared_ptr> GetNumListFlag (int i, std::string & name) const + { name = numlistflags.GetName(i).c_str(); return numlistflags[i]; } + const std::shared_ptr> GetStringListFlag (int i, std::string & name) const + { name = strlistflags.GetName(i); return strlistflags[i]; } + const Flags & GetFlagsFlag (int i, std::string & name) const + { name = flaglistflags.GetName(i); return flaglistflags[i]; } + const std::any& GetAnyFlag(int i, std::string& name) const + { name = anyflags.GetName(i); return anyflags[i]; } + }; + + /// Print flags + inline std::ostream & operator<< (std::ostream & s, const Flags & flags) + { + flags.PrintFlags (s); + return s; + } +} // namespace ngcore + + +#endif // NETGEN_CORE_FLAGS_HPP + diff --git a/libsrc/core/hashtable.hpp b/libsrc/core/hashtable.hpp new file mode 100644 index 00000000..a1c603cf --- /dev/null +++ b/libsrc/core/hashtable.hpp @@ -0,0 +1,1106 @@ +#ifndef FILE_NGSTD_HASHTABLE +#define FILE_NGSTD_HASHTABLE + +/**************************************************************************/ +/* File: hashtable.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include +#include + +#include "mpi_wrapper.hpp" +#include "ngcore_api.hpp" +#include "table.hpp" +#include "utils.hpp" + +namespace ngcore +{ + + + template + class MakeTupleFromInt + { + public: + template + auto operator()(I & i) + { return tuple_cat(MakeTupleFromInt ()(i), std::tie(i[K-1])); } + }; + + template <> + class MakeTupleFromInt<1> + { + public: + template + auto operator()(I & i) { return std::tie(i[0]); } + }; + + + + + /// N integers + template + class INT + { + /// data + T i[(N>0)?N:1]; + + public: + /// + NETGEN_INLINE INT () { } + + /// init all + NETGEN_INLINE INT (T ai1) + { + for (int j = 0; j < N; j++) { i[j] = ai1; } + } + + /// init i[0], i[1] + constexpr NETGEN_INLINE INT (T ai1, T ai2) + : i{ai1, ai2} { ; } + + /// init i[0], i[1], i[2] + constexpr NETGEN_INLINE INT (T ai1, T ai2, T ai3) + : i{ai1, ai2, ai3} { ; } + + /// init i[0], i[1], i[2] + constexpr NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4) + : i{ai1, ai2, ai3, ai4} { ; } + + /// init i[0], i[1], i[2] + constexpr NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4, T ai5) + : i{ai1, ai2, ai3, ai4, ai5} { ; } + + /// init i[0], i[1], i[2] + NETGEN_INLINE INT (T ai1, T ai2, T ai3, T ai4, T ai5, T ai6, T ai7, T ai8, T ai9) + : i{ai1, ai2, ai3, ai4, ai5, ai6, ai7, ai8, ai9 } { ; } + + template + void DoArchive(ARCHIVE& ar) + { + ar.Do(i, N); + } + + template + NETGEN_INLINE INT (const INT & in2) + { + if (N2 <= N) + { + for (int j = 0; j < N2; j++) + i[j] = in2[j]; + for (int j = N2; j < N; j++) + i[j] = 0; + } + else + { + for (int j = 0; j < N; j++) + i[j] = in2[j]; + } + } + + template + NETGEN_INLINE INT (const BaseArrayObject & ao) + { + for (int j = 0; j < N; j++) + i[j] = ao.Spec()[j]; + } + + NETGEN_INLINE size_t Size() const { return N; } + /// all ints equal ? + NETGEN_INLINE bool operator== (const INT & in2) const + { + for (int j = 0; j < N; j++) + if (i[j] != in2.i[j]) return 0; + return 1; + } + + /// any ints unequal ? + NETGEN_INLINE bool operator!= (const INT & in2) const + { + for (int j = 0; j < N; j++) + if (i[j] != in2.i[j]) return 1; + return 0; + } + + /// sort integers + NETGEN_INLINE INT & Sort () & + { + for (int k = 0; k < N; k++) + for (int l = k+1; l < N; l++) + if (i[k] > i[l]) + Swap (i[k], i[l]); + return *this; + } + + NETGEN_INLINE INT Sort () && + { + for (int k = 0; k < N; k++) + for (int l = k+1; l < N; l++) + if (i[k] > i[l]) + Swap (i[k], i[l]); + return *this; + } + + /// access + NETGEN_INLINE T & operator[] (int j) + { return i[j]; } + + /// access + NETGEN_INLINE constexpr const T & operator[] (int j) const + { return i[j]; } + + template + constexpr T get() const { return i[J]; } + + operator FlatArray () { return FlatArray (N, &i[0]); } + + NETGEN_INLINE INT & operator= (T value) + { + for (int j = 0; j < N; j++) + i[j] = value; + return *this; + } + + template + NETGEN_INLINE INT & operator= (INT v2) + { + for (int j = 0; j < N; j++) + i[j] = v2[j]; + return *this; + } + + template + operator std::tuple () + { + 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 + template <> + NETGEN_INLINE INT<2> & INT<2>::Sort () & + { + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + template <> + NETGEN_INLINE INT<2> INT<2>::Sort () && + { + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + /// sort 3 integers + template <> + NETGEN_INLINE INT<3> INT<3>::Sort () && + { + if (i[0] > i[1]) Swap (i[0], i[1]); + if (i[1] > i[2]) Swap (i[1], i[2]); + if (i[0] > i[1]) Swap (i[0], i[1]); + return *this; + } + + /// Print integers + template + inline ostream & operator<<(ostream & s, const INT & i2) + { + for (int j = 0; j < N; j++) + s << (int) i2[j] << " "; + return s; + } + + template + auto begin(const INT & ind) + { + return AOWrapperIterator> (ind, 0); + } + + template + auto end(const INT & ind) + { + return AOWrapperIterator> (ind, N); + } + + + + + + + template + NETGEN_INLINE size_t HashValue (const INT & ind, size_t size) + { + INT lind = ind; + size_t sum = 0; + for (int i = 0; i < N; i++) + sum += lind[i]; + return sum % size; + } + + /// hash value of 1 int + template + NETGEN_INLINE size_t HashValue (const INT<1,TI> & ind, size_t size) + { + return ind[0] % size; + } + + /// hash value of 2 int + template + NETGEN_INLINE size_t HashValue (const INT<2,TI> & ind, size_t size) + { + INT<2,size_t> lind = ind; + return (113*lind[0]+lind[1]) % size; + } + + /// hash value of 3 int + template + NETGEN_INLINE size_t HashValue (const INT<3,TI> & ind, size_t size) + { + INT<3,size_t> lind = ind; + return (113*lind[0]+59*lind[1]+lind[2]) % size; + } + + NETGEN_INLINE size_t HashValue (size_t ind, size_t size) + { + return ind%size; + } + NETGEN_INLINE size_t HashValue (int ind, size_t size) + { + return size_t(ind)%size; + } + + + + + + + + template + NETGEN_INLINE size_t HashValue2 (const INT & ind, size_t mask) + { + INT lind = ind; + size_t sum = 0; + for (int i = 0; i < N; i++) + sum += lind[i]; + return sum & mask; + } + + /// hash value of 1 int + template + NETGEN_INLINE size_t HashValue2 (const INT<1,TI> & ind, size_t mask) + { + return ind[0] & mask; + } + + /// hash value of 2 int + template + NETGEN_INLINE size_t HashValue2 (const INT<2,TI> & ind, size_t mask) + { + INT<2,size_t> lind = ind; + return (113*lind[0]+lind[1]) & mask; + } + + /// hash value of 3 int + template + NETGEN_INLINE size_t HashValue2 (const INT<3,TI> & ind, size_t mask) + { + INT<3,size_t> lind = ind; + return (113*lind[0]+59*lind[1]+lind[2]) & mask; + } + + NETGEN_INLINE size_t HashValue2 (size_t ind, size_t mask) + { + return ind & mask; + } + NETGEN_INLINE size_t HashValue2 (int ind, size_t mask) + { + return size_t(ind) & mask; + } + + + + + + // using ngstd::max; + + template + NETGEN_INLINE T Max (const INT & i) + { + if (D == 0) return 0; + T m = i[0]; + for (int j = 1; j < D; j++) + if (i[j] > m) m = i[j]; + return m; + } + + template + NETGEN_INLINE T Min (const INT & i) + { + if (D == 0) return 0; + T m = i[0]; + for (int j = 1; j < D; j++) + if (i[j] < m) m = i[j]; + return m; + } + + template + NETGEN_INLINE INT Max (INT i1, INT i2) + { + INT tmp; + for (int i = 0; i < D; i++) + tmp[i] = std::max(i1[i], i2[i]); + return tmp; + } + + template + NETGEN_INLINE INT operator+ (INT i1, INT i2) + { + INT tmp; + for (int i = 0; i < D; i++) + tmp[i] = i1[i]+i2[i]; + return tmp; + } + + + + + + + + + + + + /** + A hash-table. + Generic identifiers are mapped to the generic type T. + An open hashtable. The table is implemented by a DynamicTable. + Identifiers must provide a HashValue method. + */ + template + class HashTable + { + /* + DynamicTable hash; + DynamicTable cont; + */ + DynamicTable> table; + public: + /// Constructs a hashtable of size bags. + NETGEN_INLINE HashTable (int size) + // : hash(size), cont(size) + : table(size) + { ; } + NETGEN_INLINE ~HashTable () { ; } + + /// Sets identifier ahash to value acont + void Set (const T_HASH & ahash, const T & acont) + { + int bnr = HashValue (ahash, Size()); + int pos = CheckPosition (bnr, ahash); + if (pos != -1) + // cont.Set (bnr, pos, acont); + table[bnr][pos].second = acont; + else + { + // hash.Add (bnr, ahash); + // cont.Add (bnr, acont); + table.Add (bnr, std::make_pair(ahash, acont)); + } + } + + /// get value of identifier ahash, exception if unused + const T & Get (const T_HASH & ahash) const + { + int bnr = HashValue (ahash, Size()); + int pos = Position (bnr, ahash); + // return cont.Get (bnr, pos); + return table.Get (bnr, pos).second; + } + + /// get value of identifier ahash, exception if unused + const T & Get (int bnr, int pos) const + { + // return cont.Get (bnr, pos); + return table.Get (bnr, pos).second; + } + + /// is identifier used ? + bool Used (const T_HASH & ahash) const + { + // return (CheckPosition (HashValue (ahash, hash.Size()), ahash) != -1); + return (CheckPosition (HashValue (ahash, table.Size()), ahash) != -1); + } + + /// is identifier used ? + bool Used (const T_HASH & ahash, int & bnr, int & pos) const + { + // bnr = HashValue (ahash, hash.Size()); + bnr = HashValue (ahash, Size()); + pos = CheckPosition (bnr, ahash); + return (pos != -1); + } + + + /// number of hash entries + size_t Size () const + { + // return hash.Size(); + return table.Size(); + } + + /// size of hash entry + size_t EntrySize (int bnr) const + { + // return hash[bnr].Size(); + return table[bnr].Size(); + } + + /// get identifier and value of entry bnr, position colnr + void GetData (int bnr, int colnr, T_HASH & ahash, T & acont) const + { + // ahash = hash[bnr][colnr]; + // acont = cont[bnr][colnr]; + ahash = table[bnr][colnr].first; + acont = table[bnr][colnr].second; + } + + /// set identifier and value of entry bnr, position colnr + void SetData (int bnr, int colnr, const T_HASH & ahash, const T & acont) + { + // hash[bnr][colnr] = ahash; + // cont[bnr][colnr] = acont; + table[bnr][colnr] = std::make_pair(ahash, acont); + } + + /// returns position of index. returns -1 on unused + int CheckPosition (int bnr, const T_HASH & ind) const + { + /* + for (int i = 0; i < hash[bnr].Size(); i++) + if (hash[bnr][i] == ind) + return i; + */ + for (int i = 0; i < table[bnr].Size(); i++) + if (table[bnr][i].first == ind) + return i; + return -1; + } + + /// returns position of index. exception on unused + int Position (int bnr, const T_HASH & ind) const + { + for (int i = 0; i < table[bnr].Size(); i++) + if (table[bnr][i].first == ind) + return i; + throw Exception ("Ask for unused hash-value"); + } + + T & operator[] (T_HASH ahash) + { + int bnr, pos; + if (Used (ahash, bnr, pos)) + return table[bnr][pos].second; + else + { + // hash.Add (bnr, ahash); + // cont.Add (bnr, T(0)); + table.Add (bnr, std::make_pair(ahash, T(0))); + // return cont[bnr][cont[bnr].Size()-1]; + return table[bnr][table[bnr].Size()-1].second; + } + } + + const T & operator[] (T_HASH ahash) const + { + return Get(ahash); + } + + class Iterator + { + const HashTable & ht; + int bnr; + int pos; + public: + Iterator (const HashTable & aht, int abnr, int apos) + : ht(aht), bnr(abnr), pos(apos) { ; } + std::pair operator* () const + { + T_HASH hash; + T data; + ht.GetData (bnr, pos, hash, data); + return std::pair (hash, data); + } + + Iterator & operator++() + { + pos++; + if (pos == ht.EntrySize(bnr)) + { + pos = 0; + bnr++; + for ( ; bnr < ht.Size(); bnr++) + if (ht.EntrySize(bnr) != 0) break; + } + return *this; + } + + bool operator!= (const Iterator & it2) { return bnr != it2.bnr || pos != it2.pos; } + }; + + Iterator begin () const + { + int i = 0; + for ( ; i < Size(); i++) + if (EntrySize(i) != 0) break; + return Iterator(*this, i,0); + } + Iterator end () const { return Iterator(*this, Size(),0); } + }; + + + + inline size_t RoundUp2 (size_t i) + { + size_t res = 1; + while (res < i) res *= 2; // hope it will never be too large + return res; + } + + + + /** + A closed hash-table. + All information is stored in one fixed array. + The array should be allocated with the double size of the expected number of entries. + */ + template + class ClosedHashTable + { + protected: + /// + size_t size; + size_t mask; + /// + size_t used = 0; + /// + Array hash; + /// + Array cont; + /// + T_HASH invalid = -1; + public: + /// + ClosedHashTable (size_t asize = 128) + : size(RoundUp2(asize)), hash(size), cont(size) + { + mask = size-1; + hash = T_HASH(invalid); + } + + ClosedHashTable (ClosedHashTable && ht2) = default; + + /// allocate on local heap + ClosedHashTable (size_t asize, LocalHeap & lh) + : size(RoundUp2(asize)), mask(size-1), hash(size, lh), cont(size, lh) + { + hash = T_HASH(invalid); + } + + ClosedHashTable & operator= (ClosedHashTable && ht2) = default; + + /// + size_t Size() const + { + return size; + } + + /// is position used + bool UsedPos (size_t pos) const + { + return ! (hash[pos] == invalid); + } + + /// number of used elements + size_t UsedElements () const + { + return used; + } + + size_t Position (const T_HASH ind) const + { + size_t i = HashValue2(ind, mask); + while (true) + { + if (hash[i] == ind) return i; + if (hash[i] == invalid) return size_t(-1); + i = (i+1) & mask; + } + } + + void DoubleSize() + { + ClosedHashTable tmp(2*Size()); + for (auto both : *this) + tmp[both.first] = both.second; + *this = std::move(tmp); + } + + // returns true if new position is created + bool PositionCreate (const T_HASH ind, size_t & apos) + { + if (UsedElements()*2 > Size()) DoubleSize(); + + size_t i = HashValue2 (ind, mask); + + while (true) + { + if (hash[i] == invalid) + { + hash[i] = ind; + apos = i; + used++; + return true; + } + if (hash[i] == ind) + { + apos = i; + return false; + } + i = (i+1) & mask; + } + } + + + /// + void Set (const T_HASH & ahash, const T & acont) + { + size_t pos; + PositionCreate (ahash, pos); + hash[pos] = ahash; + cont[pos] = acont; + } + + /// + const T & Get (const T_HASH & ahash) const + { + size_t pos = Position (ahash); + if (pos == size_t(-1)) + throw Exception (std::string("illegal key: ") + ToString(ahash) ); + return cont[pos]; + } + + /// + bool Used (const T_HASH & ahash) const + { + return (Position (ahash) != size_t(-1)); + } + + void SetData (size_t pos, const T_HASH & ahash, const T & acont) + { + hash[pos] = ahash; + cont[pos] = acont; + } + + void GetData (size_t pos, T_HASH & ahash, T & acont) const + { + ahash = hash[pos]; + acont = cont[pos]; + } + + void SetData (size_t pos, const T & acont) + { + cont[pos] = acont; + } + + void GetData (size_t pos, T & acont) const + { + 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]); + } + + const T & operator[] (T_HASH key) const { return Get(key); } + T & operator[] (T_HASH key) + { + size_t pos; + PositionCreate(key, pos); + return cont[pos]; + } + + void SetSize (size_t asize) + { + size = asize; + hash.Alloc(size); + cont.Alloc(size); + + // for (size_t i = 0; i < size; i++) + // hash[i] = invalid; + hash = T_HASH(invalid); + } + + void Delete (T_HASH key) + { + size_t pos = Position(key); + if (pos == size_t(-1)) return; + hash[pos] = invalid; used--; + + while (1) + { + size_t nextpos = pos+1; + if (nextpos == size) nextpos = 0; + if (hash[nextpos] == invalid) break; + + auto key = hash[nextpos]; + auto val = cont[nextpos]; + hash[pos] = invalid; used--; + + Set (key, val); + pos = nextpos; + } + } + + void DeleteData() + { + hash = T_HASH(invalid); + used = 0; + } + + class Iterator + { + const ClosedHashTable & tab; + size_t nr; + public: + Iterator (const ClosedHashTable & _tab, size_t _nr) + : tab(_tab), nr(_nr) + { + while (nr < tab.Size() && !tab.UsedPos(nr)) nr++; + } + Iterator & operator++() + { + nr++; + while (nr < tab.Size() && !tab.UsedPos(nr)) nr++; + return *this; + } + bool operator!= (const Iterator & it2) { return nr != it2.nr; } + auto operator* () const + { + T_HASH hash; + T val; + tab.GetData(nr, hash,val); + return std::make_pair(hash,val); + } + }; + + Iterator begin() const { return Iterator(*this, 0); } + Iterator end() const { return Iterator(*this, Size()); } + }; + + template + ostream & operator<< (ostream & ost, + const ClosedHashTable & tab) + { + for (size_t i = 0; i < tab.Size(); i++) + if (tab.UsedPos(i)) + { + T_HASH key; + T val; + tab.GetData (i, key, val); + ost << key << ": " << val << ", "; + } + return ost; + } + + template + NETGEN_INLINE size_t HashValue (const INT<3,TI> ind) + { + INT<3,size_t> lind = ind; + return 113*lind[0]+59*lind[1]+lind[2]; + } + + template + NETGEN_INLINE size_t HashValue (const INT<2,TI> ind) + { + INT<2,size_t> lind = ind; + return 113*lind[0]+lind[1]; + } + + template + NETGEN_INLINE size_t HashValue (const INT<1,TI> ind) + { + return ind[0]; + } + + + template + class ParallelHashTable + { + class ClosedHT + { + Array keys; + Array values; + size_t used; + + public: + ClosedHT(size_t asize = 256) : keys(asize), values(asize), used(0) + { + keys = TKEY(-1); + } + + size_t Size () const { return keys.Size(); } + size_t Used () const { return used; } + + ClosedHT & operator= (ClosedHT&&) = default; + + void Resize() + { + ClosedHT tmp(keys.Size()*2); + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + { + TKEY hkey = keys[i]; + T hval = values[i]; + size_t hhash = HashValue(hkey); + size_t hhash2 = hhash / 256; + tmp.DoSave(hkey, [hval] (T & v) { v = hval; }, hhash2); + } + (*this) = std::move(tmp); + } + + template + auto Do (TKEY key, TFUNC func, size_t hash) + { + if (used > keys.Size()/2) + Resize(); + return DoSave (key, func, hash); + } + + template + auto DoSave (TKEY key, TFUNC func, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + while (1) + { + if (keys[pos] == key) + break; + if (keys[pos] == TKEY(-1)) + { + keys[pos] = key; + values[pos] = T(0); + used++; + break; + } + pos++; + if (pos == keys.Size()) pos = 0; + } + return func(values[pos]); + } + + T Get (TKEY key, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + while (1) + { + if (keys[pos] == key) + return values[pos]; + if (keys[pos] == TKEY(-1)) + throw Exception ("ParallelHashTable::Get of unused key"); + pos++; + if (pos == keys.Size()) pos = 0; + } + } + + size_t GetCosts (TKEY key, size_t hash) + { + size_t pos = hash & (keys.Size()-1); + size_t costs = 1; + while (1) + { + if (keys[pos] == key) + return costs; + if (keys[pos] == TKEY(-1)) + throw Exception ("ParallelHashTable::Get of unused key"); + costs++; + pos++; + if (pos == keys.Size()) pos = 0; + } + } + + + template + void Iterate (TFUNC func) const + { + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + func(keys[i], values[i]); + } + + void Print (ostream & ost) const + { + for (size_t i = 0; i < keys.Size(); i++) + if (keys[i] != TKEY(-1)) + ost << keys[i] << ": " << values[i] << ", "; + } + }; + + Array hts; + class alignas(64) MyMutex64 : public MyMutex { }; + + Array locks; + + public: + ParallelHashTable() : hts(256), locks(256) { ; } + size_t NumBuckets() const { return hts.Size(); } + auto & Bucket(size_t nr) { return hts[nr]; } + size_t BucketSize(size_t nr) const { return hts[nr].Size(); } + size_t Used (size_t nr) const { return hts[nr].Used(); } + size_t Used() const + { + size_t used = 0; + for (auto & ht : hts) + used += ht.Used(); + return used; + } + template + auto Do (TKEY key, TFUNC func) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + // locks[hash1].lock(); + // hts[hash1].Do (key, func, hash2); + // locks[hash1].unlock(); + MyLock lock(locks[hash1]); + return hts[hash1].Do (key, func, hash2); + } + + T Get (TKEY key) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + return hts[hash1].Get (key, hash2); + } + + auto GetCosts (TKEY key) + { + size_t hash = HashValue(key); + size_t hash1 = hash % 256; + size_t hash2 = hash / 256; + + return hts[hash1].GetCosts (key, hash2); + } + + + template + void Iterate(TFUNC func) const + { + for (auto & bucket : hts) + bucket.Iterate(func); + } + + template + void Iterate(size_t nr, TFUNC func) const + { + hts[nr].Iterate(func); + } + + + template + void IterateParallel (FUNC func) + { + Array base(NumBuckets()); + size_t sum = 0; + for (size_t i = 0; i < NumBuckets(); i++) + { + base[i] = sum; + sum += Used(i); + } + ParallelFor(NumBuckets(), + [&] (size_t nr) + { + size_t cnt = base[nr]; + Iterate(nr, + [&cnt, func] (TKEY key, T val) + { + func(cnt, key, val); + cnt++; + }); + }); + } + + + + + void Print (ostream & ost) const + { + for (size_t i : Range(hts)) + if (hts[i].Used() > 0) + { + ost << i << ": "; + hts[i].Print(ost); + } + } + }; + + template + inline ostream & operator<< (ostream & ost, const ParallelHashTable & ht) + { + ht.Print(ost); + return ost; + } + +} // namespace ngcore + + + +#ifdef PARALLEL +namespace ngcore { + template + class MPI_typetrait > + { + public: + /// gets the MPI datatype + static MPI_Datatype MPIType () + { + static MPI_Datatype MPI_T = 0; + if (!MPI_T) + { + MPI_Type_contiguous ( S, MPI_typetrait::MPIType(), &MPI_T); + MPI_Type_commit ( &MPI_T ); + } + return MPI_T; + } + }; +} +#endif + + + +namespace std +{ + // structured binding support + template + struct tuple_size> : std::integral_constant {}; + template struct tuple_element> { using type = T; }; +} + +#endif diff --git a/libsrc/core/localheap.cpp b/libsrc/core/localheap.cpp new file mode 100644 index 00000000..5706a07e --- /dev/null +++ b/libsrc/core/localheap.cpp @@ -0,0 +1,72 @@ +/**************************************************************************/ +/* File: localheap.cpp */ +/* Author: Joachim Schoeberl */ +/* Date: 19. Apr. 2002 */ +/**************************************************************************/ + +#include +#include + +#include "localheap.hpp" +#include "taskmanager.hpp" + +namespace ngcore +{ + + LocalHeap :: LocalHeap (size_t asize, const char * aname, bool mult_by_threads) + { + if (mult_by_threads) + asize *= TaskManager::GetMaxThreads(); + totsize = asize; + try + { + data = new char[asize]; + } + catch (std::exception & e) + { + throw Exception (ToString ("Could not allocate localheap, heapsize = ") + ToString(asize)); + } + + next = data + totsize; + p = data; + owner = true; + name = aname; + CleanUp(); // align pointer + } + + LocalHeap LocalHeap :: Split() const + { + int pieces = TaskManager::GetNumThreads(); + int i = TaskManager::GetThreadId(); + size_t freemem = totsize - (p - data); + size_t size_of_piece = freemem / pieces; + return LocalHeap (p + i * size_of_piece, size_of_piece, name); + } + + void LocalHeap :: ThrowException() // throw (LocalHeapOverflow) + { + /* + cout << "allocated: " << (p-data) << endl; + cout << "throw LocalHeapOverflow, totsize = "<< totsize << endl; + cout << "heap name = " << name << endl; + */ + throw LocalHeapOverflow(totsize); + } + + + LocalHeapOverflow :: LocalHeapOverflow (size_t size) + : Exception("Local Heap overflow\n") + { + std::stringstream str; + str << "Current heapsize is " << size << '\n'; + Append (str.str()); + // Append ("please use 'define constant heapsize = xxx' with larger value\n"); + } + + LocalHeapOverflow :: ~LocalHeapOverflow () + { + ; + } + +} + diff --git a/libsrc/core/localheap.hpp b/libsrc/core/localheap.hpp new file mode 100644 index 00000000..74e80a5e --- /dev/null +++ b/libsrc/core/localheap.hpp @@ -0,0 +1,318 @@ +#ifndef NETGEN_CORE_LOCALHEAP_HPP +#define NETGEN_CORE_LOCALHEAP_HPP + +/**************************************************************************/ +/* File: localheap.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 19. Apr. 2000 */ +/**************************************************************************/ + +#include + +#include "exception.hpp" +#include "ngcore_api.hpp" +#include "utils.hpp" + +namespace ngcore +{ + +class Allocator +{ +public: + virtual ~Allocator() {} + virtual void * Alloc (size_t size) + { + return new char[size]; + } + virtual void Delete(void* p) + { + delete (char*) p; + } + virtual void ArrayDelete(void* p) + { + delete [] (char*) p; + } +}; +static Allocator global_alloc; + +/** + Exception on heap overflow. + Thrown by allocation on LocalHeap. +*/ +class NGCORE_API LocalHeapOverflow : public Exception +{ +public: + LocalHeapOverflow (size_t size); + virtual ~LocalHeapOverflow (); +}; + + + +/** + Optimized memory handler. + One block of data is organized as stack memory. + One can allocate memory out of it. This increases the stack pointer. + With \Ref{CleanUp}, the pointer is reset to the beginning or to a + specific position. +*/ +class LocalHeap : public Allocator +{ + char * data; + char * next; + char * p; + size_t totsize; +public: + bool owner; + const char * name; + +#if defined(__MIC__) || defined (__AVX512F__) + enum { ALIGN = 64 }; +#else + enum { ALIGN = 32 }; +#endif + +public: + /// Allocate one block of size asize. + NGCORE_API LocalHeap (size_t asize, + const char * aname = "noname", + bool mult_by_threads = false); + + /// Use provided memory for the LocalHeap + NETGEN_INLINE LocalHeap (char * adata, size_t asize, + const char * aname = "noname") throw () + { + totsize = asize; + data = adata; + next = data + totsize; + owner = 0; + // p = data; + name = aname; + CleanUp(); + } + + /* + /// Use provided memory for the LocalHeap + NETGEN_INLINE LocalHeap (const LocalHeap & lh2) + : data(lh2.data), p(lh2.p), totsize(lh2.totsize), owner(false), + name(lh2.name) + { + next = data + totsize; + } + */ + NETGEN_INLINE LocalHeap (const LocalHeap & lh2) = delete; + + NETGEN_INLINE LocalHeap (LocalHeap && lh2) + : data(lh2.data), p(lh2.p), totsize(lh2.totsize), owner(lh2.owner), + name(lh2.name) + { + next = data + totsize; + lh2.owner = false; + } + + NETGEN_INLINE LocalHeap Borrow() + { + return LocalHeap (p, Available()); + } + + + NETGEN_INLINE LocalHeap & operator= (LocalHeap && lh2) + { + if (owner) + delete [] data; + + data = lh2.data; + p = lh2.p; + totsize = lh2.totsize; + owner = lh2.owner; + name = lh2.name; + + next = data + totsize; + lh2.owner = false; + return *this; + } + + NETGEN_INLINE LocalHeap () + : data(nullptr), next(nullptr), p(nullptr), totsize(0), owner(false) { ; } + + + /// free memory + virtual ~LocalHeap () + { + if (owner) + delete [] data; + } + + /// delete all memory on local heap + NETGEN_INLINE void CleanUp() throw () + { + p = data; + // p += (16 - (long(p) & 15) ); + p += (ALIGN - (size_t(p) & (ALIGN-1) ) ); + } + + /// returns heap-pointer + NETGEN_INLINE void * GetPointer () throw () + { + return p; + } + + /// deletes memory back to heap-pointer + NETGEN_INLINE void CleanUp (void * addr) throw () + { + p = (char*)addr; + } + + /// allocates size bytes of memory from local heap + void * Alloc (size_t size) final // throw (LocalHeapOverflow) + { + char * oldp = p; + + // 16 byte alignment + size += (ALIGN - size % ALIGN); + p += size; + + // if ( size_t(p - data) >= totsize ) +#ifndef FULLSPEED + if (likely(p >= next)) + ThrowException(); +#endif + return oldp; + } + + /// allocates size objects of type T on local heap + template + T * Alloc (size_t size) // throw (LocalHeapOverflow) + { + char * oldp = p; + size *= sizeof (T); + + // 16 byte alignment + size += (ALIGN - size % ALIGN); + p += size; + +#ifndef FULLSPEED + if (likely(p >= next)) + ThrowException(); +#endif + + return reinterpret_cast (oldp); + } + + virtual void Delete(void* /* p */) {} + + virtual void ArrayDelete(void* /* p */) {} + private: + /// +#ifndef __CUDA_ARCH__ + [[noreturn]] NGCORE_API void ThrowException(); +#else + NETGEN_INLINE void ThrowException() { ; } +#endif + + public: + /// free memory (dummy function) + NETGEN_INLINE void Free (void * /* data */) throw () + { + ; + } + + /// available memory on LocalHeap + NETGEN_INLINE size_t Available () const throw () { return (totsize - (p-data)); } + + /// Split free memory on heap into pieces for each thread + NGCORE_API LocalHeap Split () const; + + /// Split free memory on heap into pieces + NETGEN_INLINE LocalHeap Split (int partnr, int nparts) const + { + int pieces = nparts; + int i = partnr; + + size_t freemem = totsize - (p - data); + size_t size_of_piece = freemem / pieces; + return LocalHeap (p + i * size_of_piece, size_of_piece, name); + } + + + NETGEN_INLINE void ClearValues () + { + for (size_t i = 0; i < totsize; i++) data[i] = 47; + } + + NETGEN_INLINE size_t UsedSize () + { + for (size_t i = totsize-1; i != 0; i--) + if (data[i] != 47) return i; + return 0; + } + }; + + + + /** + Optimized memory handler. + Provides static memory for the local heap. The template argument specifies the size in number of chars. + */ + template + class LocalHeapMem : public LocalHeap + { + char mem[S]; + public: + NETGEN_INLINE LocalHeapMem (const char * aname) throw () : LocalHeap (mem, S, aname) { ; } + }; + + + + + + + + + /** + A reset for the heap-pointer of a LocalHeap.. + The constructor stores the heap-pointer, the constructor at the end of the regions resets the heap-pointer. + */ + class HeapReset + { + LocalHeap & lh; + void * pointer; + public: + /// + NETGEN_INLINE HeapReset (LocalHeap & alh) + : lh(alh), pointer (alh.GetPointer()) { ; } + + /// + NETGEN_INLINE ~HeapReset () + { + lh.CleanUp (pointer); + } + }; + +} + + + +NETGEN_INLINE void * operator new (size_t size, ngcore::Allocator & alloc) +{ + return alloc.Alloc(size); +} + +NETGEN_INLINE void * operator new [] (size_t size, ngcore::Allocator & alloc) +{ + return alloc.Alloc(size); +} + + +NETGEN_INLINE void operator delete (void * p, ngcore::Allocator & lh) +{ + lh.Delete(p); +} + +NETGEN_INLINE void operator delete [] (void * p, ngcore::Allocator & lh) +{ + lh.ArrayDelete(p); +} + + + +#endif // NETGEN_CORE_LOCALHEAP_HPP diff --git a/libsrc/core/logging.cpp b/libsrc/core/logging.cpp index eaaedf02..b5056406 100644 --- a/libsrc/core/logging.cpp +++ b/libsrc/core/logging.cpp @@ -13,13 +13,16 @@ namespace ngcore { + std::ostream* testout = new std::ostream(nullptr); // NOLINT + + level::level_enum Logger::global_level = level::warn; void Logger::log(level::level_enum level, std::string && s) { #ifdef NETGEN_USE_SPDLOG logger->log(spdlog::level::level_enum(level), s); #else // NETGEN_USE_SPDLOG - if(level>level::debug) + if(level>=global_level) std::clog << s << '\n'; #endif // NETGEN_USE_SPDLOG } @@ -125,7 +128,11 @@ namespace ngcore return std::make_shared(std::make_shared()); } - void SetLoggingLevel(level::level_enum /*unused*/, const std::string& /*unused*/) {} + void SetLoggingLevel(level::level_enum level, const std::string& /*unused*/) + { + Logger::SetGlobalLoggingLevel(level); + } + void AddFileSink(const std::string& /*unused*/, level::level_enum /*unused*/, const std::string& /*unused*/) {} diff --git a/libsrc/core/logging.hpp b/libsrc/core/logging.hpp index 1553ad54..adfed7ed 100644 --- a/libsrc/core/logging.hpp +++ b/libsrc/core/logging.hpp @@ -31,6 +31,8 @@ namespace spdlog namespace ngcore { + NGCORE_API extern std::ostream* testout; // NOLINT + namespace level { enum level_enum @@ -47,7 +49,11 @@ namespace ngcore class Logger { + static NGCORE_API level::level_enum global_level; + public: + static void SetGlobalLoggingLevel( level::level_enum level ) { global_level = level; } + std::shared_ptr logger; Logger(std::shared_ptr l) : logger(std::move(l)) {} diff --git a/libsrc/core/memtracer.hpp b/libsrc/core/memtracer.hpp new file mode 100644 index 00000000..15b05ebf --- /dev/null +++ b/libsrc/core/memtracer.hpp @@ -0,0 +1,173 @@ +#ifndef NETGEN_CORE_MEMTRACER_HPP +#define NETGEN_CORE_MEMTRACER_HPP + +#include +#include +#include + +#include "array.hpp" +#include "logging.hpp" +#include "paje_trace.hpp" +#include "utils.hpp" + +namespace ngcore +{ + + 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*8*1024) + 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 + +#endif // NETGEN_CORE_MEMTRACER_HPP diff --git a/libsrc/core/mpi_wrapper.hpp b/libsrc/core/mpi_wrapper.hpp index 4928dfda..9a007a80 100644 --- a/libsrc/core/mpi_wrapper.hpp +++ b/libsrc/core/mpi_wrapper.hpp @@ -6,6 +6,11 @@ #include #endif +#include "array.hpp" +#include "table.hpp" +#include "exception.hpp" +#include "profiler.hpp" +#include "ngstream.hpp" namespace ngcore { @@ -23,6 +28,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; } }; @@ -40,6 +48,27 @@ namespace ngcore inline MPI_Datatype GetMPIType () { return MPI_typetrait::MPIType(); } + + template + inline MPI_Datatype GetMPIType (T &) { + return GetMPIType(); + } + + + 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); + } + + inline int MyMPI_WaitAny (FlatArray requests) + { + int nr; + MPI_Waitany (requests.Size(), requests.Data(), &nr, MPI_STATUS_IGNORE); + return nr; + } + class NgMPI_Comm @@ -57,6 +86,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 @@ -87,6 +127,11 @@ namespace ngcore MPI_Comm_free(&comm); } + bool ValidCommunicator() const + { + return valid_comm; + } + NgMPI_Comm & operator= (const NgMPI_Comm & c) { if (refcount) @@ -115,6 +160,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); } @@ -125,12 +171,48 @@ 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 { + MPI_Send (s.Data(), s.Size(), GetMPIType(), dest, tag, comm); + } template())> void Recv (T & val, int src, int tag) const { MPI_Recv (&val, 1, GetMPIType(), src, tag, comm, MPI_STATUS_IGNORE); } + 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 + { + MPI_Status status; + int len; + const MPI_Datatype MPI_T = GetMPIType (); + MPI_Probe (src, tag, comm, &status); + MPI_Get_count (&status, MPI_T, &len); + s.SetSize (len); + MPI_Recv (s.Data(), len, MPI_T, src, tag, comm, MPI_STATUS_IGNORE); + } /** --- non-blocking P2P --- **/ @@ -141,7 +223,15 @@ namespace ngcore MPI_Isend (&val, 1, GetMPIType(), dest, tag, comm, &request); return request; } - + + template())> + MPI_Request ISend (FlatArray s, int dest, int tag) const + { + MPI_Request request; + MPI_Isend (s.Data(), s.Size(), GetMPIType(), dest, tag, comm, &request); + return request; + } + template())> MPI_Request IRecv (T & val, int dest, int tag) const { @@ -150,11 +240,21 @@ namespace ngcore return request; } + template())> + MPI_Request IRecv (FlatArray s, int src, int tag) const + { + MPI_Request request; + MPI_Irecv (s.Data(), s.Size(), GetMPIType(), src, tag, comm, &request); + return request; + } + + /** --- collectives --- **/ template ())> - T Reduce (T d, const MPI_Op & op, int root = 0) + 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; @@ -165,6 +265,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; @@ -172,11 +273,35 @@ namespace ngcore return global_d; } + template ())> + void AllReduce (FlatArray d, const MPI_Op & op) const + { + static Timer t("MPI - AllReduce Array"); RegionTimer reg(t); + if (size == 1) return; + + MPI_Allreduce (MPI_IN_PLACE, d.Data(), d.Size(), GetMPIType(), op, comm); + } + template ())> void Bcast (T & s, int root = 0) const { - if (size == 1) return ; + if (size == 1) return; + static Timer t("MPI - Bcast"); RegionTimer reg(t); MPI_Bcast (&s, 1, GetMPIType(), root, comm); } + + + template + void Bcast (Array & d, int root = 0) + { + if (size == 1) return; + + int ds = d.Size(); + Bcast (ds, root); + if (Rank() != root) d.SetSize (ds); + if (ds != 0) + MPI_Bcast (d.Data(), ds, GetMPIType(), root, comm); + } + void Bcast (std::string & s, int root = 0) const { @@ -187,11 +312,145 @@ 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 GatherRoot (FlatArray recv) const + { + recv[0] = T(0); + if (size == 1) return; + MPI_Gather (MPI_IN_PLACE, 1, GetMPIType(), + recv.Data(), 1, GetMPIType(), 0, comm); + } + + template + void Gather (T send) const + { + if (size == 1) return; + MPI_Gather (&send, 1, GetMPIType(), + NULL, 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); + } + + + + template + void ExchangeTable (DynamicTable & send_data, + DynamicTable & recv_data, int tag) + { + Array send_sizes(size); + Array recv_sizes(size); + + for (int i = 0; i < size; i++) + send_sizes[i] = send_data[i].Size(); + + AllToAll (send_sizes, recv_sizes); + + recv_data = DynamicTable (recv_sizes, true); + + Array requests; + for (int dest = 0; dest < size; dest++) + if (dest != rank && send_data[dest].Size()) + requests.Append (ISend (FlatArray(send_data[dest]), dest, tag)); + + for (int dest = 0; dest < size; dest++) + if (dest != rank && recv_data[dest].Size()) + requests.Append (IRecv (FlatArray(recv_data[dest]), dest, tag)); + + MyMPI_WaitAll (requests); + } + + + + + + NgMPI_Comm SubCommunicator (FlatArray procs) const + { + MPI_Comm subcomm; + MPI_Group gcomm, gsubcomm; + MPI_Comm_group(comm, &gcomm); + MPI_Group_incl(gcomm, procs.Size(), procs.Data(), &gsubcomm); + MPI_Comm_create_group(comm, gsubcomm, 4242, &subcomm); + return NgMPI_Comm(subcomm, true); + } + + }; // class NgMPI_Comm + + + + -#else + class MyMPI + { + bool initialized_by_me; + public: + MyMPI(int argc, char ** argv) + { + int is_init = -1; + MPI_Initialized(&is_init); + if (!is_init) + { + MPI_Init (&argc, &argv); + initialized_by_me = true; + } + else + initialized_by_me = false; + + NgMPI_Comm comm(MPI_COMM_WORLD); + NGSOStream::SetGlobalActive (comm.Rank() == 0); + + if (comm.Size() > 1) + TaskManager::SetNumThreads (1); + } + + ~MyMPI() + { + if (initialized_by_me) + MPI_Finalize (); + } + }; + + + + + +#else // PARALLEL class MPI_Comm { int nr; public: @@ -202,9 +461,13 @@ 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 }; + + inline void MPI_Type_contiguous ( int, MPI_Datatype, MPI_Datatype*) { ; } + inline void MPI_Type_commit ( MPI_Datatype * ) { ; } class NgMPI_Comm { @@ -215,6 +478,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(); } @@ -222,35 +486,72 @@ namespace ngcore void Send( T & val, int dest, int tag) const { ; } template - void MyMPI_Recv (T & val, int src, int tag) const { ; } + void Send(FlatArray s, int dest, int tag) const { ; } + + template + void Recv (T & val, int src, int tag) const { ; } + + template + void Recv (FlatArray s, int src, int tag) const { ; } + + template + void Recv (Array & s, int src, int tag) const { ; } template MPI_Request ISend (T & val, int dest, int tag) const { return 0; } + template + 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 (FlatArray s, int src, int tag) const { return 0; } + template - T Reduce (T d, const MPI_Op & op, int root = 0) { return d; } + T Reduce (T d, const MPI_Op & op, int root = 0) const { return d; } template T AllReduce (T d, const MPI_Op & op) const { return d; } + template + void AllReduce (FlatArray d, const MPI_Op & op) const { ; } + template void Bcast (T & s, int root = 0) const { ; } + + template + void Bcast (Array & d, int root = 0) { ; } + + template + void AllGather (T val, FlatArray recv) const + { + recv[0] = val; + } + + template + void ExchangeTable (DynamicTable & send_data, + DynamicTable & recv_data, int tag) { ; } + + + NgMPI_Comm SubCommunicator (FlatArray procs) const + { return *this; } }; - -#endif - - - + inline void MyMPI_WaitAll (FlatArray requests) { ; } + inline int MyMPI_WaitAny (FlatArray requests) { return 0; } + class MyMPI + { + public: + MyMPI(int argc, char ** argv) { ; } + }; +#endif // PARALLEL - -} +} // namespace ngcore -#endif +#endif // NGCORE_MPIWRAPPER_HPP diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 8cc937c0..84c7f0c1 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -2,11 +2,23 @@ #define NETGEN_CORE_NGCORE_HPP #include "archive.hpp" +#include "array.hpp" +#include "bitarray.hpp" #include "exception.hpp" +#include "flags.hpp" +#include "table.hpp" +#include "hashtable.hpp" +#include "localheap.hpp" #include "logging.hpp" -#include "profiler.hpp" -#include "symboltable.hpp" -#include "version.hpp" #include "mpi_wrapper.hpp" +#include "profiler.hpp" +#include "signal.hpp" +#include "simd.hpp" +#include "symboltable.hpp" +#include "taskmanager.hpp" +#include "version.hpp" +#include "xbool.hpp" +#include "ngstream.hpp" +#include "utils.hpp" #endif // NETGEN_CORE_NGCORE_HPP diff --git a/libsrc/core/ngcore_api.hpp b/libsrc/core/ngcore_api.hpp index a84a1381..e66e9b87 100644 --- a/libsrc/core/ngcore_api.hpp +++ b/libsrc/core/ngcore_api.hpp @@ -1,6 +1,39 @@ #ifndef NETGEN_CORE_NGCORE_API_HPP #define NETGEN_CORE_NGCORE_API_HPP +#ifdef WIN32 + +// This function or variable may be unsafe. Consider using _ftime64_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. +#pragma warning(disable:4244) +#pragma warning(disable:4996) + +// multiple inheritance via dominance +#pragma warning(disable:4250) + +// needs to have dll-interface to be used by clients of class +#pragma warning(disable:4251) + +// size_t to int conversion: +#pragma warning(disable:4267) + +// non dll-interface class 'std::exception' used as base for dll-interface class +#pragma warning(disable:4275) + +// C++ exception specification ignored except to indicate a function is not __declspec(nothrow) +#pragma warning(disable:4290) + +// no suitable definition provided for explicit template instantiation request +#pragma warning(disable:4661) + +// bool-int conversion +#pragma warning(disable:4800) + +// '__declspec(dllexport)' and 'extern' are incompatible on an explicit instantiation +#pragma warning(disable:4910) + +#endif // WIN32 + + #ifdef WIN32 #define NGCORE_API_EXPORT __declspec(dllexport) #define NGCORE_API_IMPORT __declspec(dllimport) @@ -15,23 +48,103 @@ #define NGCORE_API NGCORE_API_IMPORT #endif +// Set __host__ __device__ for all inline functions +#ifdef __CUDACC__ + #define NETGEN_HD __host__ __device__ +#else // __CUDACC__ + #define NETGEN_HD +#endif // __CUDACC__ + #ifdef __INTEL_COMPILER + #define NETGEN_ALWAYS_INLINE __forceinline + #define NETGEN_INLINE __forceinline inline #ifdef WIN32 - #define NETGEN_INLINE __forceinline inline #define NETGEN_LAMBDA_INLINE #else - #define NETGEN_INLINE __forceinline inline #define NETGEN_LAMBDA_INLINE __attribute__ ((__always_inline__)) #endif #else #ifdef __GNUC__ - #define NETGEN_INLINE __attribute__ ((__always_inline__)) inline - #define NETGEN_LAMBDA_INLINE __attribute__ ((__always_inline__)) + #define NETGEN_ALWAYS_INLINE __attribute__ ((__always_inline__)) + #define NETGEN_INLINE __attribute__ ((__always_inline__)) inline NETGEN_HD + #define NETGEN_LAMBDA_INLINE __attribute__ ((__always_inline__)) NETGEN_HD #define NETGEN_VLA #else + #define NETGEN_ALWAYS_INLINE #define NETGEN_INLINE inline #define NETGEN_LAMBDA_INLINE #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, +// thus implement it here globally +#include +#ifdef __clang__ +#pragma clang diagnostic ignored "-Winline-new-delete" +#endif +inline void * operator new (size_t s, std::align_val_t al) +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + return _mm_malloc(s, int(al)); + else + return new char[s]; +} + +inline void * operator new[] (size_t s, std::align_val_t al) +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + return _mm_malloc(s, int(al)); + else + return new char[s]; +} + +inline void operator delete ( void* ptr, std::align_val_t al ) noexcept +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + _mm_free(ptr); + else + delete (char*)ptr; +} + +inline void operator delete[]( void* ptr, std::align_val_t al ) noexcept +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + _mm_free(ptr); + else + delete[] (char*)ptr; +} + +inline void operator delete ( void* ptr, std::size_t sz, std::align_val_t al ) noexcept +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + _mm_free(ptr); + else + delete (char*)ptr; +} + +inline void operator delete[]( void* ptr, std::size_t sz, std::align_val_t al ) noexcept +{ + if (int(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) + _mm_free(ptr); + else + delete[] (char*)ptr; +} + +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED +#endif // __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + #endif // NETGEN_CORE_NGCORE_API_HPP diff --git a/libsrc/core/ngstream.hpp b/libsrc/core/ngstream.hpp new file mode 100644 index 00000000..a423677a --- /dev/null +++ b/libsrc/core/ngstream.hpp @@ -0,0 +1,115 @@ +#ifndef FILE_NGSTREAM +#define FILE_NGSTREAM + +/**************************************************************************/ +/* File: ng(s)stream.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 20. Jul. 2011 */ +/**************************************************************************/ + +// #include +// #include +namespace ngcore +{ + + NGCORE_API extern int printmessage_importance; + + // important message + class IM + { + int value; + public: + IM (int val) : value(val) { ; } + int Value () const { return value; } + }; + + class trunc + { + double eps; + public: + trunc (double aeps) : eps(aeps) { ; } + double Eps() const { return eps; } + }; + + class NGSOStream + { + std::ostream & ost; + bool active; + NGCORE_API static bool glob_active; + double trunc; + public: + NGSOStream (std::ostream & aost, bool aactive) + : ost(aost), active(aactive), trunc(-1) { ; } + NGSOStream & SetTrunc (double atrunc) { trunc = atrunc; return *this; } + double GetTrunc () const { return trunc; } + bool Active () const { return active && glob_active; } + std::ostream & GetStream () { return ost; } + static void SetGlobalActive (bool b) { glob_active = b; } + }; + + inline NGSOStream operator<< (std::ostream & ost, const IM & im) + { + return NGSOStream (ost, + (im.Value() <= printmessage_importance)); + } + + /* + // doesn't work for matrices + inline NGSOStream operator<< (ostream & ost, trunc tr) + { + cout << "set trunc modifier" << endl; + return NGSOStream (ost, true).SetTrunc (tr.Eps()); + } + */ + + + template + inline NGSOStream operator<< (NGSOStream ngsost, const T & data) + { + if (ngsost.Active()) + ngsost.GetStream() << data; + return ngsost; + } + + /* + inline NGSOStream operator<< (NGSOStream ngsost, const double & data) + { + cout << "double out" << endl; + if (ngsost.Active()) + { + double hdata = data; + if (fabs (hdata) < ngsost.GetTrunc()) hdata = 0.0; + ngsost.GetStream() << hdata; + } + return ngsost; + } + */ + + inline NGSOStream operator<< (NGSOStream ngsost, std::ostream& ( *pf )(std::ostream&)) + { + if ( ngsost.Active() ) + ngsost.GetStream() << (*pf); + + return ngsost; + } + + inline NGSOStream operator<< (NGSOStream ngsost, std::ios& ( *pf )(std::ios&)) + { + if ( ngsost.Active() ) + ngsost.GetStream() << (*pf); + + return ngsost; + } + + inline NGSOStream operator<< (NGSOStream ngsost, std::ios_base& ( *pf )(std::ios_base&)) + { + if ( ngsost.Active() ) + ngsost.GetStream() << (*pf); + + return ngsost; + } + + +} + +#endif diff --git a/libsrc/core/paje_trace.cpp b/libsrc/core/paje_trace.cpp index 844710d5..818a92c8 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,73 @@ namespace ngcore jobs.reserve(reserve_size); timer_events.reserve(reserve_size); + gpu_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.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 & event : user_events) + { + event.t_start -= start_time; + event.t_end -= start_time; + } + + for(auto & event : gpu_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(); + + for(auto & event : gpu_events) + event.timer_id += NgProfiler::SIZE*comm.Rank(); + + if(comm.Rank() == MPI_PAJE_WRITER) + Write(tracefile_name); + else + SendData(); + } } @@ -84,13 +161,12 @@ namespace ngcore else if (x<5*d) r=6*(x-4*d), g=0,b=1; else - r=1, g=0,b=1-5*(x-d); + r=1, g=0,b=1-6*(x-5*d); }; int alias_counter; FILE * ctrace_stream; - TTimePoint start_time; std::shared_ptr logger = GetLogger("PajeTrace"); @@ -98,7 +174,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 (t-start_time) / 2.7e6; + return 1000.0*static_cast(t) * seconds_per_tick; } enum PType @@ -122,6 +198,10 @@ namespace ngcore : time(atime), event_type(aevent_type), type(atype), container(acontainer), value(avalue), id(aid), value_is_alias(avalue_is_alias) { } + PajeEvent( int aevent_type, double atime, int atype, int acontainer, std::string as_value, int aid = 0 ) + : time(atime), event_type(aevent_type), type(atype), container(acontainer), id(aid), s_value(as_value), value_is_alias(false), value_is_int(false) + { } + PajeEvent( int aevent_type, double atime, int atype, int acontainer, int avalue, int astart_container, int akey ) : time(atime), event_type(aevent_type), type(atype), container(acontainer), value(avalue), start_container(astart_container), id(akey) { } @@ -131,10 +211,12 @@ namespace ngcore int event_type; int type; int container; + std::string s_value = ""; int value = 0; int start_container = 0; int id = 0; bool value_is_alias = true; + bool value_is_int = true; bool operator < (const PajeEvent & other) const { // Same start and stop times can occur for very small tasks -> take "starting" events first (eg. PajePushState before PajePopState) @@ -158,8 +240,10 @@ namespace ngcore case PajePushState: if(value_is_alias) return fprintf( stream, "%d\t%.15g\ta%d\ta%d\ta%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT - else + else if(value_is_int) return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t%d\t%d\n", PajePushState, time, type, container, value, id); // NOLINT + else + return fprintf( stream, "%d\t%.15g\ta%d\ta%d\t\"%s\"\t%d\n", PajePushState, time, type, container, s_value.c_str(), id); // NOLINT case PajePopState: return fprintf( stream, "%d\t%.15g\ta%d\ta%d\n", PajePopState, time, type, container ); // NOLINT case PajeStartLink: @@ -180,10 +264,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; } @@ -241,7 +325,9 @@ namespace ngcore } int alias = ++alias_counter; - double r,g,b; + double r; + double g; + double b; Hue2RGB( hue, r, g, b ); fprintf( ctrace_stream, "%d\ta%d\ta%d\t\"%s\"\t\"%.15g %.15g %.15g\"\n", PajeDefineEntityValue, alias, type, name.c_str(), r,g,b ); // NOLINT return alias; @@ -282,6 +368,11 @@ namespace ngcore events.emplace_back( PajeEvent( PajePushState, ConvertTime(time), type, container, value, id, value_is_alias) ); } + void PushState ( TTimePoint time, int type, int container, std::string value, int id = 0) + { + events.emplace_back( PajeEvent( PajePushState, ConvertTime(time), type, container, value, id) ); + } + void PopState ( TTimePoint time, int type, int container ) { events.emplace_back( PajeEvent( PajePopState, ConvertTime(time), type, container ) ); @@ -346,7 +437,7 @@ namespace ngcore void PajeTrace::Write( const std::string & filename ) { - int n_events = jobs.size() + timer_events.size(); + auto n_events = jobs.size() + timer_events.size(); for(auto & vtasks : tasks) n_events += vtasks.size(); @@ -363,39 +454,86 @@ 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; @@ -414,20 +552,75 @@ 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); + for(auto & event : gpu_events) + 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; @@ -459,6 +652,54 @@ namespace ngcore paje.PopState( event.time, state_type_timer, timer_container_aliases[--timerdepth] ); } + if(gpu_events.size()) + { + auto gpu_container = paje.CreateContainer( container_type_timer, container_task_manager, "GPU" ); + for(auto & event : gpu_events) + { + if(event.is_start) + paje.PushState( event.time, state_type_timer, gpu_container, timer_aliases[event.timer_id] ); + else + paje.PopState( event.time, state_type_timer, gpu_container); + } + } + + if(user_events.size()) + { + std::sort (user_events.begin(), user_events.end()); + + std::map containers; + + for(auto i : Range(user_containers.size())) + { + auto & [name, parent] = user_containers[i]; + int a_parent = parent == -1 ? container_task_manager : containers[parent]; + containers[i] = paje.CreateContainer( container_type_timer, a_parent, name ); + } + + for(auto ev : user_events) + { + if(containers[ev.container]==0) + { + std::string name = "User " + ToString(ev.container); + containers[ev.container] = paje.CreateContainer( container_type_timer, container_task_manager, name ); + } + } + + int i_start = 0; + for(auto i : Range(user_events.size())) + { + auto & event = user_events[i]; + while(i_start < user_events.size() && user_events[i_start].t_start < event.t_end) + { + auto & ev = user_events[i_start]; + paje.PushState( ev.t_start, state_type_timer, containers[ev.container], ev.data, ev.id ); + i_start++; + } + paje.PopState( event.t_end, state_type_timer, containers[event.container]); + } + } + for(auto & vtasks : tasks) { for (Task & t : vtasks) { @@ -470,28 +711,80 @@ namespace ngcore value_id = job_task_map[jobs[t.id-1].type]; if(trace_thread_counter) { - paje.AddVariable( t.start_time, variable_type_active_threads, container_jobs, 1.0 ); - paje.SubVariable( t.stop_time, variable_type_active_threads, container_jobs, 1.0 ); + if(t.is_start) + paje.AddVariable( t.time, variable_type_active_threads, container_jobs, 1.0 ); + else + paje.SubVariable( t.time, variable_type_active_threads, container_jobs, 1.0 ); } if(trace_threads) { - paje.PushState( t.start_time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, true ); - paje.PopState( t.stop_time, state_type_task, thread_aliases[t.thread_id] ); + if(t.is_start) + paje.PushState( t.time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, true ); + else + paje.PopState( t.time, state_type_task, thread_aliases[t.thread_id] ); } break; case Task::ID_TIMER: value_id = timer_aliases[t.id]; - paje.PushState( t.start_time, state_type_timer, thread_aliases[t.thread_id], value_id, t.additional_value, true ); - paje.PopState( t.stop_time, state_type_timer, thread_aliases[t.thread_id] ); + if(t.is_start) + paje.PushState( t.time, state_type_timer, thread_aliases[t.thread_id], value_id, t.additional_value, true ); + else + paje.PopState( t.time, state_type_timer, thread_aliases[t.thread_id] ); break; default: - paje.PushState( t.start_time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, false ); - paje.PopState( t.stop_time, state_type_task, thread_aliases[t.thread_id] ); + if(t.is_start) + paje.PushState( t.time, state_type_task, thread_aliases[t.thread_id], value_id, t.additional_value, false ); + else + paje.PopState( t.time, state_type_task, thread_aliases[t.thread_id] ); break; } } } +#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) @@ -550,8 +843,416 @@ namespace ngcore } } } + 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 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, std::ofstream & f) + { + 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) + { + int i = 0; + f << ", children: ["; + for(auto & c : n.children) + { + 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.name="all"; + TreeNode *current = &root; + + std::vector node_stack; + + node_stack.push_back(&root); + + TTimePoint stop_time = 0; + + for(auto & event : timer_events) + { + events.push_back(event); + stop_time = std::max(event.time, stop_time); + } + + std::map jobs_map; + std::vector job_names; + for(auto & job : jobs) + { + auto name = Demangle(job.type->name()); + int id = job_names.size(); + if(jobs_map.count(name)==0) + { + jobs_map[name] = id; + job_names.push_back(name); + } + else + id = jobs_map[name]; + + events.push_back(TimerEvent{-1, job.start_time, true, id}); + events.push_back(TimerEvent{-1, job.stop_time, false, id}); + stop_time = std::max(job.stop_time, stop_time); + } + + std::sort (events.begin(), events.end()); + + 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) + { + bool is_timer_event = event.timer_id != -1; + int id = is_timer_event ? event.timer_id : event.thread_id; + + if(event.is_start) + { + bool need_init = !current->children.count(id); + + node_stack.push_back(current); + current = ¤t->children[id]; + + if(need_init) + { + current->name = is_timer_event ? GetTimerName(id) : job_names[id]; + current->size = 0.0; + current->id = id; + } + + current->start_time = event.time; + } + else + { + if(node_stack.size()==0) { + std::cout << "node stack empty!" << std::endl; + break; + } + 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->chart_size -= size; + node_stack.pop_back(); + } + } + + root.chart_size = 0.0; + + ngcore::WriteSunburstHTML( root, tracefile_name, true ); + } + } // namespace ngcore const char *header = diff --git a/libsrc/core/paje_trace.hpp b/libsrc/core/paje_trace.hpp index 96684285..7adc6c58 100644 --- a/libsrc/core/paje_trace.hpp +++ b/libsrc/core/paje_trace.hpp @@ -1,6 +1,7 @@ #ifndef NETGEN_CORE_PAJE_TRACE_HPP #define NETGEN_CORE_PAJE_TRACE_HPP +#include #include #include @@ -23,17 +24,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: + 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; @@ -68,8 +80,8 @@ namespace ngcore int additional_value; - TTimePoint start_time; - TTimePoint stop_time; + TTimePoint time; + bool is_start; static constexpr int ID_NONE = -1; static constexpr int ID_JOB = 1; @@ -86,6 +98,16 @@ namespace ngcore bool operator < (const TimerEvent & other) const { return time < other.time; } }; + struct UserEvent + { + TTimePoint t_start = 0, t_end = 0; + std::string data = ""; + int container = 0; + int id = 0; + + bool operator < (const UserEvent & other) const { return t_start < other.t_start; } + }; + struct ThreadLink { int thread_id; @@ -95,10 +117,24 @@ 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 user_events; + std::vector> user_containers; + std::vector gpu_events; std::vector > links; + NGCORE_API static std::vector memory_events; public: NGCORE_API void StopTracing(); @@ -112,6 +148,36 @@ namespace ngcore void operator=(const PajeTrace &) = delete; void operator=(PajeTrace &&) = delete; + int AddUserContainer(std::string name, int parent=-1) + { + if(auto pos = std::find(user_containers.begin(), user_containers.end(), std::tuple{name,parent}); pos != user_containers.end()) + return pos - user_containers.begin(); + int id = user_containers.size(); + user_containers.push_back({name, parent}); + return id; + } + + void AddUserEvent(UserEvent ue) + { + if(!tracing_enabled) return; + user_events.push_back(ue); + } + void StartGPU(int timer_id = 0) + { + if(!tracing_enabled) return; + if(unlikely(gpu_events.size() == max_num_events_per_thread)) + StopTracing(); + gpu_events.push_back(TimerEvent{timer_id, GetTimeCounter(), true}); + } + + void StopGPU(int timer_id) + { + if(!tracing_enabled) return; + if(unlikely(gpu_events.size() == max_num_events_per_thread)) + StopTracing(); + gpu_events.push_back(TimerEvent{timer_id, GetTimeCounter(), false}); + } + void StartTimer(int timer_id) { if(!tracing_enabled) return; @@ -128,30 +194,44 @@ namespace ngcore timer_events.push_back(TimerEvent{timer_id, GetTimeCounter(), false}); } - NETGEN_INLINE int StartTask(int thread_id, int id, int id_type = Task::ID_NONE, int additional_value = -1) + 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); + } + + + int StartTask(int thread_id, int id, int id_type = Task::ID_NONE, int additional_value = -1) { if(!tracing_enabled) return -1; if(!trace_threads && !trace_thread_counter) return -1; if(unlikely(tasks[thread_id].size() == max_num_events_per_thread)) StopTracing(); int task_num = tasks[thread_id].size(); - tasks[thread_id].push_back( Task{thread_id, id, id_type, additional_value, GetTimeCounter()} ); + tasks[thread_id].push_back( Task{thread_id, id, id_type, additional_value, GetTimeCounter(), true} ); return task_num; } - void StopTask(int thread_id, int task_num) + void StopTask(int thread_id, int id, int id_type = Task::ID_NONE) { if(!trace_threads && !trace_thread_counter) return; - if(task_num>=0) - tasks[thread_id][task_num].stop_time = GetTimeCounter(); + tasks[thread_id].push_back( Task{thread_id, id, id_type, 0, GetTimeCounter(), false} ); } - void SetTask(int thread_id, int task_num, int additional_value) { - if(!trace_threads && !trace_thread_counter) return; - if(task_num>=0) - tasks[thread_id][task_num].additional_value = additional_value; - } - void StartJob(int job_id, const std::type_info & type) { if(!tracing_enabled) return; @@ -184,6 +264,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 3302342d..cf49654b 100644 --- a/libsrc/core/profiler.cpp +++ b/libsrc/core/profiler.cpp @@ -8,10 +8,10 @@ namespace ngcore std::string NgProfiler::filename; - size_t NgProfiler::dummy_thread_times[NgProfiler::SIZE]; - size_t * NgProfiler::thread_times = NgProfiler::dummy_thread_times; // NOLINT - size_t NgProfiler::dummy_thread_flops[NgProfiler::SIZE]; - size_t * NgProfiler::thread_flops = NgProfiler::dummy_thread_flops; // NOLINT + std::array NgProfiler::dummy_thread_times; + size_t * NgProfiler::thread_times = NgProfiler::dummy_thread_times.data(); // NOLINT + std::array NgProfiler::dummy_thread_flops; + size_t * NgProfiler::thread_flops = NgProfiler::dummy_thread_flops.data(); // NOLINT std::shared_ptr NgProfiler::logger = GetLogger("Profiler"); // NOLINT @@ -94,7 +94,7 @@ namespace ngcore if (first_overflow) { first_overflow = false; - NgProfiler::logger->warn("no more timer available, reusing last one"); + NgProfiler::logger->warn( ("no more timer available ("+name+"), reusing last one").c_str()); } return 0; } @@ -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 5a9b16e1..aca524b8 100644 --- a/libsrc/core/profiler.hpp +++ b/libsrc/core/profiler.hpp @@ -1,11 +1,15 @@ #ifndef NETGEN_CORE_PROFILER_HPP #define NETGEN_CORE_PROFILER_HPP +#include #include +#include #include +#include "array.hpp" #include "logging.hpp" #include "paje_trace.hpp" +#include "taskmanager.hpp" #include "utils.hpp" namespace ngcore @@ -21,7 +25,7 @@ namespace ngcore TimerVal() = default; double tottime = 0.0; - double starttime = 0.0; + TTimePoint starttime=0; double flops = 0.0; double loads = 0.0; double stores = 0.0; @@ -35,8 +39,8 @@ namespace ngcore NGCORE_API static TTimePoint * thread_times; NGCORE_API static TTimePoint * thread_flops; NGCORE_API static std::shared_ptr logger; - NGCORE_API static size_t dummy_thread_times[NgProfiler::SIZE]; - NGCORE_API static size_t dummy_thread_flops[NgProfiler::SIZE]; + NGCORE_API static std::array dummy_thread_times; + NGCORE_API static std::array dummy_thread_flops; private: NGCORE_API static std::string filename; @@ -60,13 +64,13 @@ namespace ngcore /// start timer of index nr static void StartTimer (int nr) { - timers[nr].starttime = WallTime(); timers[nr].count++; + timers[nr].starttime = GetTimeCounter(); timers[nr].count++; } /// stop timer of index nr static void StopTimer (int nr) { - timers[nr].tottime += WallTime()-timers[nr].starttime; + timers[nr].tottime += (GetTimeCounter()-timers[nr].starttime)*seconds_per_tick; } static void StartThreadTimer (size_t nr, size_t tid) @@ -143,39 +147,98 @@ namespace ngcore }; }; + + struct TNoTracing{ static constexpr bool do_tracing=false; }; + struct TTracing{ static constexpr bool do_tracing=true; }; + struct TNoTiming{ static constexpr bool do_timing=false; }; + struct TTiming{ static constexpr bool do_timing=true; }; - class NGCORE_API Timer + namespace detail { + + template + constexpr bool is_tracing_type_v = std::is_same_v || std::is_same_v; + + template + constexpr bool is_timing_type_v = std::is_same_v || std::is_same_v; + } + + [[maybe_unused]] static TNoTracing NoTracing; + [[maybe_unused]] static TNoTiming NoTiming; + + template + class Timer { int timernr; - int priority; - public: - Timer (const std::string & name, int apriority = 1) - : priority(apriority) + int Init( const std::string & name ) { - timernr = NgProfiler::CreateTimer (name); + return NgProfiler::CreateTimer (name); } + public: + static constexpr bool do_tracing = TTracing::do_tracing; + static constexpr bool do_timing = TTiming::do_timing; + + Timer (const std::string & name) : timernr(Init(name)) { } + + template, bool> = false> + Timer( const std::string & name, TTracing ) : timernr(Init(name)) { } + + template, bool> = false> + Timer( const std::string & name, TTiming ) : timernr(Init(name)) { } + + Timer( const std::string & name, TTracing, TTiming ) : timernr(Init(name)) { } + + [[deprecated ("Use Timer(name, NoTracing/NoTiming) instead")]] Timer( const std::string & name, int ) : timernr(Init(name)) {} + void SetName (const std::string & name) { NgProfiler::SetName (timernr, name); } - void Start () + void Start () const { - if (priority <= 2) - NgProfiler::StartTimer (timernr); - if (priority <= 1) - if(trace) trace->StartTimer(timernr); + Start(TaskManager::GetThreadId()); } - void Stop () + void Stop () const { - if (priority <= 2) - NgProfiler::StopTimer (timernr); - if (priority <= 1) - if(trace) trace->StopTimer(timernr); + Stop(TaskManager::GetThreadId()); + } + void Start (int tid) const + { + if(tid==0) + { + if constexpr(do_timing) + NgProfiler::StartTimer (timernr); + if constexpr(do_tracing) + if(trace) trace->StartTimer(timernr); + } + else + { + if constexpr(do_timing) + NgProfiler::StartThreadTimer(timernr, tid); + if constexpr(do_tracing) + if(trace) trace->StartTask (tid, timernr, PajeTrace::Task::ID_TIMER); + } + } + void Stop (int tid) const + { + if(tid==0) + { + if constexpr(do_timing) + NgProfiler::StopTimer (timernr); + if constexpr(do_tracing) + if(trace) trace->StopTimer(timernr); + } + else + { + if constexpr(do_timing) + NgProfiler::StopThreadTimer(timernr, tid); + if constexpr(do_tracing) + if(trace) trace->StopTask (tid, timernr, PajeTrace::Task::ID_TIMER); + } } void AddFlops (double aflops) { - if (priority <= 2) + if constexpr(do_timing) NgProfiler::AddFlops (timernr, aflops); } @@ -184,7 +247,7 @@ namespace ngcore double GetMFlops () { return NgProfiler::GetFlops(timernr) / NgProfiler::GetTime(timernr) * 1e-6; } - operator int () { return timernr; } + operator int () const { return timernr; } }; @@ -192,14 +255,21 @@ namespace ngcore Timer object. Start / stop timer at constructor / destructor. */ + template class RegionTimer { - Timer & timer; + const TTimer & timer; + int tid; public: /// start timer - RegionTimer (Timer & atimer) : timer(atimer) { timer.Start(); } + RegionTimer (const TTimer & atimer) : timer(atimer) + { + tid = TaskManager::GetThreadId(); + timer.Start(tid); + } + /// stop timer - ~RegionTimer () { timer.Stop(); } + ~RegionTimer () { timer.Stop(tid); } RegionTimer() = delete; RegionTimer(const RegionTimer &) = delete; @@ -208,7 +278,7 @@ namespace ngcore void operator=(RegionTimer &&) = delete; }; - class ThreadRegionTimer + class [[deprecated("Use RegionTimer instead (now thread safe)")]] ThreadRegionTimer { size_t nr; size_t tid; @@ -231,6 +301,7 @@ namespace ngcore { int nr; int thread_id; + int type; public: static constexpr int ID_JOB = PajeTrace::Task::ID_JOB; static constexpr int ID_NONE = PajeTrace::Task::ID_NONE; @@ -247,28 +318,26 @@ namespace ngcore : thread_id(athread_id) { if (trace) - nr = trace->StartTask (athread_id, region_id, id_type, additional_value); + trace->StartTask (athread_id, region_id, id_type, additional_value); + type = id_type; + nr = region_id; } /// start trace with timer - RegionTracer (int athread_id, Timer & timer, int additional_value = -1 ) + template + RegionTracer (int athread_id, TTimer & timer, int additional_value = -1 ) : thread_id(athread_id) { + nr = timer; + type = ID_TIMER; if (trace) - nr = trace->StartTask (athread_id, static_cast(timer), ID_TIMER, additional_value); + trace->StartTask (athread_id, nr, type, additional_value); } - /// set user defined value - void SetValue( int additional_value ) - { - if (trace) - trace->SetTask( thread_id, nr, additional_value ); - } - /// stop trace ~RegionTracer () { if (trace) - trace->StopTask (thread_id, nr); + trace->StopTask (thread_id, nr, type); } }; @@ -300,5 +369,14 @@ namespace ngcore } // 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 47c16740..651db906 100644 --- a/libsrc/core/python_ngcore.cpp +++ b/libsrc/core/python_ngcore.cpp @@ -1,30 +1,168 @@ -#include - #include "logging.hpp" +#include "python_ngcore.hpp" namespace py = pybind11; -using namespace ngcore; +using std::string; -PYBIND11_MODULE(pyngcore, m) // NOLINT +namespace ngcore { - py::enum_(m, "LOG_LEVEL", "Logging level") - .value("Trace", level::trace) - .value("Debug", level::debug) - .value("Info", level::info) - .value("Warn", level::warn) - .value("Error", level::err) - .value("Critical", level::critical) - .value("Off", level::off); + bool ngcore_have_numpy = false; + bool parallel_pickling = true; + + void SetFlag(Flags &flags, string s, py::object value) + { + if (py::isinstance(value)) + { + py::dict vdd(value); + // call recursively to set dictionary + for (auto item : vdd) { + string name = item.first.cast(); + py::object val = py::reinterpret_borrow(item.second); + SetFlag(flags, name, val); + } + return; + } - m.def("SetLoggingLevel", &SetLoggingLevel, py::arg("level"), py::arg("logger")="", - "Set logging level, if name is given only to the specific logger, else set the global logging level"); - m.def("AddFileSink", &AddFileSink, py::arg("filename"), py::arg("level"), py::arg("logger")="", - "Add File sink, either only to logger specified or globally to all loggers"); - m.def("AddConsoleSink", &AddConsoleSink, py::arg("level"), py::arg("logger")="", - "Add console output for specific logger or all if none given"); - m.def("ClearLoggingSinks", &ClearLoggingSinks, py::arg("logger")="", - "Clear sinks of specific logger, or all if none given"); - m.def("FlushOnLoggingLevel", &FlushOnLoggingLevel, py::arg("level"), py::arg("logger")="", - "Flush every message with level at least `level` for specific logger or all loggers if none given."); -} + if (py::isinstance(value)) + flags.SetFlag(s, value.cast()); + + if (py::isinstance(value)) + flags.SetFlag(s, value.cast()); + + if (py::isinstance(value)) + flags.SetFlag(s, double(value.cast())); + + if (py::isinstance(value)) + flags.SetFlag(s, value.cast()); + + if (py::isinstance(value)) + { + auto vdl = py::cast(value); + if (py::len(vdl) > 0) + { + if(py::isinstance(vdl[0]) || py::isinstance(vdl[0])) + flags.SetFlag(s, makeCArray(vdl)); + if(py::isinstance(vdl[0])) + flags.SetFlag(s, makeCArray(vdl)); + } + else + { + Array dummystr; + Array dummydbl; + flags.SetFlag(s,dummystr); + flags.SetFlag(s,dummydbl); + } + } + + if (py::isinstance(value)) + { + auto vdt = py::cast(value); + if (py::isinstance(value)) + flags.SetFlag(s, makeCArray(vdt)); + if (py::isinstance(value)) + flags.SetFlag(s, makeCArray(vdt)); + if (py::isinstance(value)) + flags.SetFlag(s, makeCArray(vdt)); + } + } + + Flags CreateFlagsFromKwArgs(const py::kwargs& kwargs, py::object pyclass, py::list info) + { + static std::shared_ptr logger = GetLogger("Flags"); + py::dict flags_dict; + + if (kwargs.contains("flags")) + { + logger->warn("WARNING: using flags as kwarg is deprecated{}, use the flag arguments as kwargs instead!", + pyclass.is_none() ? "" : std::string(" in ") + std::string(py::str(pyclass))); + auto addflags = py::cast(kwargs["flags"]); + for (auto item : addflags) + flags_dict[item.first.cast().c_str()] = item.second; + } + py::dict special; + if(!pyclass.is_none()) + { + auto flags_doc = pyclass.attr("__flags_doc__")(); + for (auto item : kwargs) + if (!flags_doc.contains(item.first.cast().c_str()) && + !(item.first.cast() == "flags")) + logger->warn("WARNING: kwarg '{}' is an undocumented flags option for class {}, maybe there is a typo?", + item.first.cast(), std::string(py::str(pyclass))); + + if(py::hasattr(pyclass,"__special_treated_flags__")) + special = pyclass.attr("__special_treated_flags__")(); + } + for (auto item : kwargs) + { + auto name = item.first.cast(); + if (name != "flags") + { + if(!special.contains(name.c_str())) + flags_dict[name.c_str()] = item.second; + } + } + + auto flags = py::cast(flags_dict); + + for (auto item : kwargs) + { + auto name = item.first.cast(); + if (name != "flags") + { + if(special.contains(name.c_str())) + special[name.c_str()](item.second, &flags, info); + } + } + return flags; + } + + py::dict CreateDictFromFlags(const Flags& flags) + { + py::dict d; + std::string key; + for(auto i : Range(flags.GetNFlagsFlags())) + { + auto& f = flags.GetFlagsFlag(i, key); + d[key.c_str()] = CreateDictFromFlags(f); + } + for(auto i : Range(flags.GetNStringListFlags())) + { + auto strlistflag = flags.GetStringListFlag(i, key); + py::list lst; + for(auto& val : *strlistflag) + lst.append(val); + d[key.c_str()] = lst; + } + for(auto i : Range(flags.GetNNumListFlags())) + { + auto numlistflag = flags.GetNumListFlag(i, key); + py::list lst; + for(auto& val : *numlistflag) + lst.append(val); + d[key.c_str()] = lst; + } + for(auto i : Range(flags.GetNStringFlags())) + { + auto val = flags.GetStringFlag(i, key); + d[key.c_str()] = val; + } + for(auto i : Range(flags.GetNNumFlags())) + { + auto val = flags.GetNumFlag(i, key); + d[key.c_str()] = val; + } + for(auto i : Range(flags.GetNDefineFlags())) + { + auto val = flags.GetDefineFlag(i, key); + d[key.c_str()] = val; + } + for(auto i : Range(flags.GetNAnyFlags())) + { + auto& a = flags.GetAnyFlag(i, key); + d[key.c_str()] = CastAnyToPy(a); + } + return d; + } + +} // namespace ngcore diff --git a/libsrc/core/python_ngcore.hpp b/libsrc/core/python_ngcore.hpp new file mode 100644 index 00000000..79c91885 --- /dev/null +++ b/libsrc/core/python_ngcore.hpp @@ -0,0 +1,436 @@ +#ifndef NETGEN_CORE_PYTHON_NGCORE_HPP +#define NETGEN_CORE_PYTHON_NGCORE_HPP + +#include "ngcore_api.hpp" // for operator new +#include +#include +#include +#include +#include + +#include "array.hpp" +#include "table.hpp" +#include "archive.hpp" +#include "flags.hpp" +#include "ngcore_api.hpp" +#include "profiler.hpp" +namespace py = pybind11; + +namespace ngcore +{ + namespace detail + { + template + struct HasPyFormat + { + private: + template + static auto check(T2*) -> std::enable_if_t>().format()), std::string>, std::true_type>; + static auto check(...) -> std::false_type; + public: + static constexpr bool value = decltype(check((T*) nullptr))::value; + }; + } // namespace detail +} // namespace ngcore + + +//////////////////////////////////////////////////////////////////////////////// +// 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, enable_if_t::value>> + : ngcore_list_caster, Type> { }; + + + /* + template struct type_caster>> + { + template + static handle cast(T &&src, return_value_policy policy, handle parent) + { + std::cout << "handle called with type src = " << typeid(src).name() << std::endl; + + return handle(); // what so ever + } + + PYBIND11_TYPE_CASTER(Type, _("Table[") + make_caster::name + _("]")); + }; + */ + + + +} // 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 { + static const std::string & GetName() + { + static const std::string name = typeid(T).name(); + return name; + } + }; + + template + std::string GetPyName(const char *prefix = 0) { + std::string s; + if(prefix) s = std::string(prefix); + s+= PyNameTraits::GetName(); + return s; + } + + template<> + struct PyNameTraits { + static std::string GetName() { return "I"; } + }; + + template<> + struct PyNameTraits { + static std::string GetName() { return "U"; } + }; + + template<> + struct PyNameTraits { + static std::string GetName() { return "F"; } + }; + + template<> + struct PyNameTraits { + static std::string GetName() { return "D"; } + }; + + template<> + struct PyNameTraits { + static std::string GetName() { return "S"; } + }; + + template + struct PyNameTraits> { + static std::string GetName() + { return std::string("sp_")+GetPyName(); } + }; + + // *************** Archiving functionality ************** + + template + Archive& Archive :: Shallow(T& val) + { + static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); +#ifdef NETGEN_PYTHON + if(shallow_to_python) + { + if(is_output) + ShallowOutPython(pybind11::cast(val)); + else + { + pybind11::object obj; + ShallowInPython(obj); + val = pybind11::cast(obj); + } + } + else +#endif // NETGEN_PYTHON + *this & val; + return *this; + } + + template + class NGCORE_API_EXPORT PyArchive : public ARCHIVE + { + private: + pybind11::list lst; + size_t index = 0; + std::map version_needed; + protected: + using ARCHIVE::stream; + using ARCHIVE::version_map; + using ARCHIVE::logger; + public: + PyArchive(const pybind11::object& alst = pybind11::none()) : + ARCHIVE(std::make_shared()), + lst(alst.is_none() ? pybind11::list() : pybind11::cast(alst)) + { + ARCHIVE::shallow_to_python = true; + if(Input()) + { + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-1])); + *this & version_needed; + logger->debug("versions needed for unpickling = {}", version_needed); + for(auto& libversion : version_needed) + if(libversion.second > GetLibraryVersion(libversion.first)) + throw Exception("Error in unpickling data:\nLibrary " + libversion.first + + " must be at least " + libversion.second.to_string()); + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-2])); + *this & version_map; + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-3])); + } + } + + void NeedsVersion(const std::string& library, const std::string& version) override + { + if(Output()) + { + logger->debug("Need version {} of library {}.", version, library); + version_needed[library] = version_needed[library] > version ? version_needed[library] : version; + } + } + + using ARCHIVE::Output; + using ARCHIVE::Input; + using ARCHIVE::FlushBuffer; + using ARCHIVE::operator&; + using ARCHIVE::operator<<; + using ARCHIVE::GetVersion; + void ShallowOutPython(const pybind11::object& val) override { lst.append(val); } + void ShallowInPython(pybind11::object& val) override { val = lst[index++]; } + + pybind11::list WriteOut() + { + auto version_runtime = GetLibraryVersions(); + FlushBuffer(); + lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); + stream = std::make_shared(); + *this & version_runtime; + FlushBuffer(); + lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); + stream = std::make_shared(); + logger->debug("Writeout version needed = {}", version_needed); + *this & version_needed; + FlushBuffer(); + lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); + return lst; + } + }; + + template + auto NGSPickle() + { + return pybind11::pickle([](T* self) + { + PyArchive ar; + ar.SetParallel(parallel_pickling); + ar & self; + auto output = pybind11::make_tuple(ar.WriteOut()); + return output; + }, + [](const pybind11::tuple & state) + { + T* val = nullptr; + PyArchive ar(state[0]); + ar & val; + return val; + }); + } + + template + Array makeCArray(const py::object& obj) + { + Array arr; + if(py::isinstance(obj)) + for(auto& val : py::cast(obj)) + arr.Append(py::cast(val)); + else if(py::isinstance(obj)) + for(auto& val : py::cast(obj)) + arr.Append(py::cast(val)); + else + throw py::type_error("Cannot convert Python object to C Array"); + return arr; + } + + template ::index_type> + void ExportArray (py::module &m) + { + using TFlat = FlatArray; + using TArray = Array; + std::string suffix = GetPyName() + "_" + + GetPyName(); + std::string fname = std::string("FlatArray_") + suffix; + auto flatarray_class = py::class_(m, fname.c_str(), + py::buffer_protocol()) + .def ("__len__", [] ( TFlat &self ) { return self.Size(); } ) + .def ("__getitem__", + [](TFlat & self, TIND i) -> T& + { + static constexpr int base = IndexBASE(); + if (i < base || i >= self.Size()+base) + throw py::index_error(); + return self[i]; + }, + py::return_value_policy::reference) + .def ("__setitem__", + [](TFlat & self, TIND i, T val) -> T& + { + static constexpr int base = IndexBASE(); + if (i < base || i >= self.Size()+base) + throw py::index_error(); + self[i] = val; + return self[i]; + }, + py::return_value_policy::reference) + + .def ("__setitem__", + [](TFlat & self, py::slice slice, T val) + { + size_t start, stop, step, slicelength; + if (!slice.compute(self.Size(), &start, &stop, &step, &slicelength)) + throw py::error_already_set(); + static constexpr int base = IndexBASE(); + if (start < base || start+(slicelength-1)*step >= self.Size()+base) + throw py::index_error(); + for (size_t i = 0; i < slicelength; i++, start+=step) + self[start] = val; + }) + + .def("__iter__", [] ( TFlat & self) { + 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) + { + if(ngcore_have_numpy && !py::detail::npy_format_descriptor::dtype().is_none()) + { + flatarray_class + .def_buffer([](TFlat& self) + { + return py::buffer_info( + self.Addr(0), + sizeof(T), + py::format_descriptor::format(), + 1, + { self.Size() }, + { sizeof(T) * (self.Addr(1) - self.Addr(0)) }); + }) + .def("NumPy", [](py::object self) + { + return py::module::import("numpy") + .attr("frombuffer")(self, py::detail::npy_format_descriptor::dtype()); + }) + ; + } + } + + std::string aname = std::string("Array_") + suffix; + auto arr = py::class_ (m, aname.c_str()) + .def(py::init([] (size_t n) { return new TArray(n); }),py::arg("n"), "Makes array of given length") + .def(py::init([] (std::vector const & x) + { + size_t s = x.size(); + TArray tmp(s); + for (size_t i : Range(tmp)) + tmp[TIND(i)] = x[i]; + return tmp; + }), py::arg("vec"), "Makes array with given list of elements") + ; + if constexpr(is_archivable) + arr.def(NGSPickle()); + py::implicitly_convertible, TArray>(); + } + + template + void ExportTable (py::module &m) + { + py::class_, std::shared_ptr>> (m, ("Table_"+GetPyName()).c_str()) + .def(py::init([] (py::list blocks) + { + size_t size = py::len(blocks); + Array cnt(size); + size_t i = 0; + for (auto block : blocks) + cnt[i++] = py::len(block); + + i = 0; + Table blocktable(cnt); + for (auto block : blocks) + { + auto row = blocktable[i++]; + size_t j = 0; + for (auto val : block) + row[j++] = val.cast(); + } + // cout << "blocktable = " << *blocktable << endl; + return blocktable; + + }), py::arg("blocks"), "a list of lists") + + .def ("__len__", [] (Table &self ) { return self.Size(); } ) + .def ("__getitem__", + [](Table & self, size_t i) -> FlatArray + { + if (i >= self.Size()) + throw py::index_error(); + return self[i]; + }) + .def("__str__", [](Table & self) + { + return ToString(self); + }) + ; + } + + + void NGCORE_API SetFlag(Flags &flags, std::string s, py::object value); + // Parse python kwargs to flags + Flags NGCORE_API CreateFlagsFromKwArgs(const py::kwargs& kwargs, py::object pyclass = py::none(), + py::list info = py::list()); + // Create python dict from kwargs + py::dict NGCORE_API CreateDictFromFlags(const Flags& flags); + + +} // namespace ngcore + +#endif // NETGEN_CORE_PYTHON_NGCORE_HPP diff --git a/libsrc/core/python_ngcore_export.cpp b/libsrc/core/python_ngcore_export.cpp new file mode 100644 index 00000000..facbcda7 --- /dev/null +++ b/libsrc/core/python_ngcore_export.cpp @@ -0,0 +1,290 @@ +#include "python_ngcore.hpp" +#include "bitarray.hpp" +#include "taskmanager.hpp" + +using namespace ngcore; +using namespace std; +using namespace pybind11::literals; + +PYBIND11_MODULE(pyngcore, m) // NOLINT +{ + try + { + auto numpy = py::module::import("numpy"); + ngcore_have_numpy = !numpy.is_none(); + } + catch(...) {} + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); + + ExportTable(m); + + py::class_> (m, "BitArray") + .def(py::init([] (size_t n) { return make_shared(n); }),py::arg("n")) + .def(py::init([] (const BitArray& a) { return make_shared(a); } ), py::arg("ba")) + .def(py::init([] (const vector & a) + { + auto ba = make_shared(a.size()); + ba->Clear(); + for (size_t i = 0; i < a.size(); i++) + 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) + { + if (i < 0) i+=self.Size(); + if (i < 0 || i >= self.Size()) + throw py::index_error(); + return self.Test(i); + }, py::arg("pos"), "Returns bit from given position") + .def("__setitem__", [] (BitArray & self, int i, bool b) + { + if (i < 0) i+=self.Size(); + if (i < 0 || i >= self.Size()) + throw py::index_error(); + if (b) self.SetBit(i); else self.Clear(i); + }, py::arg("pos"), py::arg("value"), "Clear/Set bit at given position") + + .def("__setitem__", [] (BitArray & self, py::slice inds, bool b) + { + 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) + { // base branch + if (b) + self.Set(); + else + self.Clear(); + } + else + { + if (b) + for (size_t i=0; i(m, "Flags") + .def(py::init<>()) + .def("__str__", &ToString) + .def(py::init([](py::object & obj) { + Flags flags; + py::dict d(obj); + SetFlag (flags, "", d); + return flags; + }), py::arg("obj"), "Create Flags by given object") + .def(py::pickle([] (const Flags& self) + { + std::stringstream str; + self.SaveFlags(str); + return py::make_tuple(py::cast(str.str())); + }, + [] (py::tuple state) + { + string s = state[0].cast(); + std::stringstream str(s); + Flags flags; + flags.LoadFlags(str); + return flags; + } + )) + .def("Set",[](Flags & self,const py::dict & aflags)->Flags& + { + SetFlag(self, "", aflags); + return self; + }, py::arg("aflag"), "Set the flags by given dict") + + .def("Set",[](Flags & self, const char * akey, const py::object & value)->Flags& + { + SetFlag(self, akey, value); + return self; + }, py::arg("akey"), py::arg("value"), "Set flag by given value.") + + .def("__getitem__", [](Flags & self, const string& name) -> py::object { + + if(self.NumListFlagDefined(name)) + return py::cast(self.GetNumListFlag(name)); + + if(self.StringListFlagDefined(name)) + return py::cast(self.GetStringListFlag(name)); + + if(self.NumFlagDefined(name)) + return py::cast(*self.GetNumFlagPtr(name)); + + if(self.StringFlagDefined(name)) + return py::cast(self.GetStringFlag(name)); + + if(self.FlagsFlagDefined(name)) + return py::cast(self.GetFlagsFlag(name)); + + return py::cast(self.GetDefineFlag(name)); + }, py::arg("name"), "Return flag by given name") + .def("ToDict", [](const Flags& flags) + { + return CreateDictFromFlags(flags); + }) + ; + py::implicitly_convertible(); + + + py::enum_(m, "LOG_LEVEL", "Logging level") + .value("Trace", level::trace) + .value("Debug", level::debug) + .value("Info", level::info) + .value("Warn", level::warn) + .value("Error", level::err) + .value("Critical", level::critical) + .value("Off", level::off); + + m.def("SetLoggingLevel", &SetLoggingLevel, py::arg("level"), py::arg("logger")="", + "Set logging level, if name is given only to the specific logger, else set the global logging level"); + m.def("AddFileSink", &AddFileSink, py::arg("filename"), py::arg("level"), py::arg("logger")="", + "Add File sink, either only to logger specified or globally to all loggers"); + m.def("AddConsoleSink", &AddConsoleSink, py::arg("level"), py::arg("logger")="", + "Add console output for specific logger or all if none given"); + m.def("ClearLoggingSinks", &ClearLoggingSinks, py::arg("logger")="", + "Clear sinks of specific logger, or all if none given"); + m.def("FlushOnLoggingLevel", &FlushOnLoggingLevel, py::arg("level"), py::arg("logger")="", + "Flush every message with level at least `level` for specific logger or all loggers if none given."); + + m.def("RunWithTaskManager", + [](py::object lam) + { + GetLogger("TaskManager")->info("running Python function with task-manager"); + RunWithTaskManager ([&] () { lam(); }); + }, py::arg("lam"), R"raw_string( +Parameters: + +lam : object + input function + +)raw_string") + ; + + m.def("SetNumThreads", &TaskManager::SetNumThreads, py::arg("threads"), R"raw_string( +Set number of threads + +Parameters: + +threads : int + input number of threads + +)raw_string"); + + // local TaskManager class to be used as context manager in Python + class ParallelContextManager { + int num_threads; + public: + ParallelContextManager() : num_threads(0) { + TaskManager::SetPajeTrace(0); + PajeTrace::SetMaxTracefileSize(0); + }; + ParallelContextManager(size_t pajesize) : num_threads(0) { + TaskManager::SetPajeTrace(pajesize > 0); + PajeTrace::SetMaxTracefileSize(pajesize); + } + void Enter() {num_threads = EnterTaskManager(); } + void Exit(py::object exc_type, py::object exc_value, py::object traceback) { + ExitTaskManager(num_threads); + } + }; + + py::class_(m, "TaskManager") + .def(py::init<>()) + .def(py::init(), "pajetrace"_a, "Run paje-tracer, specify buffersize in bytes") + .def("__enter__", &ParallelContextManager::Enter) + .def("__exit__", &ParallelContextManager::Exit) + .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) { 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..e752eb4b --- /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) + : iter(aiter), end(aend), f(af) + { + 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/register_archive.hpp b/libsrc/core/register_archive.hpp new file mode 100644 index 00000000..93221cd6 --- /dev/null +++ b/libsrc/core/register_archive.hpp @@ -0,0 +1,39 @@ +#ifndef NETGEN_REGISTER_ARCHIVE_HPP +#define NETGEN_REGISTER_ARCHIVE_HPP + +#ifdef NETGEN_PYTHON +#include +#include +#endif // NETGEN_PYTHON + +#include "archive.hpp" + +namespace ngcore { + + template + class RegisterClassForArchive + { + public: + RegisterClassForArchive() + { + static_assert(detail::all_of_tmpl::value...>, + "Variadic template arguments must be base classes of T"); + detail::ClassArchiveInfo info {}; + info.creator = [](const std::type_info& ti) -> void* + { return typeid(T) == ti ? detail::constructIfPossible() + : Archive::Caster::tryUpcast(ti, detail::constructIfPossible()); }; + info.upcaster = [/*this*/](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryUpcast(ti, static_cast(p)); }; + info.downcaster = [/*this*/](const std::type_info& ti, void* p) -> void* + { return typeid(T) == ti ? p : Archive::Caster::tryDowncast(ti, p); }; +#ifdef NETGEN_PYTHON + info.anyToPyCaster = [](const std::any& a) + { + const T* val = std::any_cast(&a); + return pybind11::cast(val); }; +#endif // NETGEN_PYTHON + Archive::SetArchiveRegister(std::string(Demangle(typeid(T).name())),info); + } + }; +} // namespace ngcore +#endif // NETGEN_REGISTER_ARCHIVE_HPP diff --git a/libsrc/core/signal.hpp b/libsrc/core/signal.hpp new file mode 100644 index 00000000..082dd32b --- /dev/null +++ b/libsrc/core/signal.hpp @@ -0,0 +1,48 @@ +#ifndef NGCORE_SIGNALS_HPP +#define NGCORE_SIGNALS_HPP + +#include +#include + +namespace ngcore +{ + template + class Signal + { + private: + std::list> funcs; + bool is_emitting; + public: + Signal() : is_emitting(true) {} + + template + void Connect(Cls* self, FUNC f) + { + auto ptr = self->weak_from_this(); + auto func = [ptr, f](ParameterTypes... args) + { + if (ptr.expired()) + return false; + f(args...); + return true; + }; + funcs.push_back(func); + } + + inline void Emit(ParameterTypes ...args) + { + if(is_emitting) + funcs.remove_if([&](auto& f){ return !f(args...); }); + } + + inline bool SetEmitting(bool emitting) + { + bool was_emitting = is_emitting; + is_emitting = emitting; + return was_emitting; + } + inline bool GetEmitting() const { return is_emitting; } + }; +} // namespace ngcore + +#endif // NGCORE_SIGNALS_HPP diff --git a/libsrc/core/simd.hpp b/libsrc/core/simd.hpp new file mode 100644 index 00000000..d5a6341f --- /dev/null +++ b/libsrc/core/simd.hpp @@ -0,0 +1,92 @@ +#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" + +#ifndef __CUDA_ARCH__ + +#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 + +#endif // __CUDA_ARCH__ + +namespace ngcore +{ +#ifndef __CUDA_ARCH__ +#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 +#endif // __CUDA_ARCH__ + + NETGEN_INLINE void SIMDTranspose (SIMD a1, SIMD a2, SIMD a3, SIMD a4, + SIMD & b1, SIMD & b2, SIMD & b3, SIMD & b4) + { + if constexpr (sizeof(a1.Lo()) == 16) + { + auto [h1,h2] = Unpack(a1,a2); + auto [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()); + } + else + { + b1 = SIMD (a1[0], a2[0], a3[0], a4[0]); + b2 = SIMD (a1[1], a2[1], a3[1], a4[1]); + b3 = SIMD (a1[2], a2[2], a3[2], a4[2]); + b4 = SIMD (a1[3], a2[3], a3[3], a4[3]); + } + } + + 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)); + return SIMD(HSum(s1, s2), HSum(s3,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..9b22c36a --- /dev/null +++ b/libsrc/core/simd_arm64.hpp @@ -0,0 +1,187 @@ +#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} { } + SIMD (int64x2_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 (SIMD v0, SIMD v1) : data{vcombine_f64(float64x1_t{v0.Data()}, float64x1_t{v1.Data()})} { } + 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()); + } + + NETGEN_INLINE SIMD HSum(SIMD a, SIMD b, SIMD c, SIMD d) + { + return SIMD (HSum(a,b), HSum(c,d)); + } + + + + // 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] }; + uint64x2_t mask = vreinterpretq_u64_s64(a.Data()); + return vbslq_f64(mask, 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) + { + uint64x2_t m1 = vreinterpretq_u64_s64(a.Data()); + uint64x2_t m2 = vreinterpretq_u64_s64(b.Data()); + uint64x2_t res = vandq_u64 (m1, m2); + return vreinterpretq_s64_u64(res); + } + +} + diff --git a/libsrc/core/simd_avx.hpp b/libsrc/core/simd_avx.hpp new file mode 100644 index 00000000..bf27b82c --- /dev/null +++ b/libsrc/core/simd_avx.hpp @@ -0,0 +1,326 @@ +#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 elements of SIMD")]] + // NETGEN_INLINE double & operator[] (int i) { 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]); } + + template + double Get() const + { + static_assert(I>=0 && I<4, "Index out of range"); + return (*this)[I]; + } + }; + + 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 + +#if defined(__FMA__) + NETGEN_INLINE SIMD FMAddSub (SIMD a, SIMD b, SIMD c) + { + return _mm256_fmaddsub_pd(a.Data(), b.Data(), c.Data()); + } +#endif + + NETGEN_INLINE SIMD SwapPairs (SIMD a) + { + return _mm256_shuffle_pd (a.Data(), a.Data(), 0b0101); + } + + + 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..b1f74a21 --- /dev/null +++ b/libsrc/core/simd_avx512.hpp @@ -0,0 +1,274 @@ +#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; } + SIMD (SIMD v0, SIMD v1) + : data(_mm512_set_pd(v1[3], v1[2], v1[1], v1[0], v0[3], v0[2], v0[1], v0[0])) + {} + SIMD (SIMD v0, SIMD v1) + : data(_mm512_set_pd(v1[1], v1[0], v0[5], v0[4], v0[3], v0[2], v0[1], v0[0])) + {} + + 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; } + + SIMD Lo() const { return _mm512_extractf64x4_pd(data, 0); } + SIMD Hi() const { return _mm512_extractf64x4_pd(data, 1); } + + template + double Get() const + { + static_assert(I>=0 && I<8, "Index out of range"); + return (*this)[I]; + } + }; + + NETGEN_INLINE SIMD operator- (SIMD a) { return _mm512_xor_pd(a.Data(), _mm512_set1_pd(-0.0)); } //{ 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()); + SIMD ab = _mm512_extractf64x4_pd(sum01.Data(),0) + _mm512_extractf64x4_pd(sum01.Data(),1); + SIMD cd = _mm512_extractf64x4_pd(sum23.Data(),0) + _mm512_extractf64x4_pd(sum23.Data(),1); + return _mm256_add_pd (_mm256_permute2f128_pd (ab.Data(), cd.Data(), 1 + 2 * 16), _mm256_blend_pd(ab.Data(), cd.Data(), 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()); + } + + NETGEN_INLINE SIMD FMAddSub (SIMD a, SIMD b, SIMD c) + { + return _mm512_fmaddsub_pd(a.Data(), b.Data(), c.Data()); + } + + NETGEN_INLINE SIMD SwapPairs (SIMD a) + { + return _mm512_shuffle_pd (a.Data(), a.Data(), 0b01010101); + } + +} + +#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..a54aa4e9 --- /dev/null +++ b/libsrc/core/simd_generic.hpp @@ -0,0 +1,790 @@ +#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 + +#include "array.hpp" + +namespace ngcore +{ +#if defined __AVX512F__ + #define NETGEN_DEFAULT_SIMD_SIZE 8 + #define NETGEN_NATIVE_SIMD_SIZE 8 +#elif defined __AVX__ + #define NETGEN_DEFAULT_SIMD_SIZE 4 + #define NETGEN_NATIVE_SIMD_SIZE 4 +#elif defined NETGEN_ARCH_AMD64 + #define NETGEN_DEFAULT_SIMD_SIZE 2 + #define NETGEN_NATIVE_SIMD_SIZE 2 +#else + #define NETGEN_DEFAULT_SIMD_SIZE 2 + #define NETGEN_NATIVE_SIMD_SIZE 1 +#endif + + constexpr int GetDefaultSIMDSize() { + return NETGEN_DEFAULT_SIMD_SIZE; + } + + constexpr bool IsNativeSIMDSize(int n) { + if(n==1) return true; +#if defined NETGEN_ARCH_AMD64 || defined __SSE__ || defined __aarch64__ + if(n==2) return true; +#endif +#if defined __AVX__ + if(n==4) return true; +#endif +#if defined __AVX512F__ + if(n==8) return true; +#endif + return false; + } + + // split n = k+l such that k is the largest natively supported simd size < n + constexpr int GetLargestNativeSIMDPart(int n) { + int k = n-1; + while(!IsNativeSIMDSize(k)) + k--; + return k; + } + + + 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(GetLargestNativeSIMDPart(N)*sizeof(int64_t)) SIMD + { + static constexpr int N1 = GetLargestNativeSIMDPart(N); + 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(GetLargestNativeSIMDPart(N)*sizeof(int64_t)) SIMD + { + static constexpr int N1 = GetLargestNativeSIMDPart(N); + 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(GetLargestNativeSIMDPart(N)*sizeof(double)) SIMD + { + static constexpr int N1 = GetLargestNativeSIMDPart(N); + 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()); + } + + + 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.template Get(); } + + 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::erf; + template + NETGEN_INLINE ngcore::SIMD erf (ngcore::SIMD a) { + return ngcore::SIMD([a](int i)->double { return erf(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 }); + } + } + + // TODO: specialize for AVX, ... + template + NETGEN_INLINE auto SwapPairs (SIMD a) + { + if constexpr(N==1) { + // static_assert(false); + return a; + } + else if constexpr(N==2) { + return SIMD (a.Hi(), a.Lo()); + } + else { + return SIMD (SwapPairs(a.Lo()), SwapPairs(a.Hi())); + } + } + + + template + NETGEN_INLINE auto HSum128 (SIMD a) + { + if constexpr(N==1) { + // static_assert(false); + return a; + } + else if constexpr(N==2) { + return a; + } + else { + return HSum128(a.Lo()) + HSum128(a.Hi()); + } + } + + + // TODO: specialize for AVX, ... + // a*b+-c (even: -, odd: +) + template + NETGEN_INLINE auto FMAddSub (SIMD a, SIMD b, SIMD c) + { + if constexpr(N==1) { + // static_assert(false); + return a*b-c; + } + else if constexpr(N==2) { + return SIMD (a.Lo()*b.Lo()-c.Lo(), + a.Hi()*b.Hi()+c.Hi()); + } + else { + return SIMD (FMAddSub(a.Lo(), b.Lo(), c.Lo()), + FMAddSub(a.Hi(), b.Hi(), c.Hi())); + } + } +} + + +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..a836ca49 --- /dev/null +++ b/libsrc/core/simd_sse.hpp @@ -0,0 +1,279 @@ +#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 (SIMD v0, SIMD v1) + : data{_mm_set_pd(v0.Data(), v1.Data())} + { } + 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; } + + template + double Get() const + { + static_assert(I>=0 && I<2, "Index out of range"); + return (*this)[I]; + } + + double Lo() const { return Get<0>(); } + double Hi() const { return Get<1>(); } + + 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/symboltable.hpp b/libsrc/core/symboltable.hpp index 10f34389..62a943e9 100644 --- a/libsrc/core/symboltable.hpp +++ b/libsrc/core/symboltable.hpp @@ -5,7 +5,6 @@ #include #include -#include "archive.hpp" #include "exception.hpp" #include "ngcore_api.hpp" @@ -38,8 +37,9 @@ namespace ngcore SymbolTable& operator=(const SymbolTable&) = default; SymbolTable& operator=(SymbolTable&&) = default; - template - auto DoArchive(Archive& ar) -> typename std::enable_if_t> + template + auto DoArchive(ARCHIVE& ar) + -> typename std::enable_if_t, void> { ar & names & data; } diff --git a/libsrc/core/table.cpp b/libsrc/core/table.cpp new file mode 100644 index 00000000..1fbcda75 --- /dev/null +++ b/libsrc/core/table.cpp @@ -0,0 +1,190 @@ +/**************************************************************************/ +/* File: table.cpp */ +/* Author: Joachim Schoeberl */ +/* Date: 25. Mar. 2000 */ +/**************************************************************************/ + +/* + Abstract data type Table +*/ + +#include "table.hpp" + +namespace ngcore +{ + template + size_t * TablePrefixSum2 (FlatArray entrysize) + { + 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 + ([&] (TaskInfo ti) + { + IntRange r = IntRange(size).Split(ti.task_nr, ti.ntasks); + size_t mysum = 0; + for (size_t i : r) + mysum += entrysize[i]; + partial_sums[ti.task_nr+1] = mysum; + }); + + for (size_t i = 1; i < partial_sums.Size(); i++) + partial_sums[i] += partial_sums[i-1]; + + ParallelJob + ([&] (TaskInfo ti) + { + IntRange r = IntRange(size).Split(ti.task_nr, ti.ntasks); + size_t mysum = partial_sums[ti.task_nr]; + for (size_t i : r) + { + index[i] = mysum; + mysum += entrysize[i]; + } + }); + index[size] = partial_sums.Last(); + + return index; + } + + NGCORE_API size_t * TablePrefixSum32 (FlatArray entrysize) + { return TablePrefixSum2 (entrysize); } + NGCORE_API size_t * TablePrefixSum64 (FlatArray entrysize) + { return TablePrefixSum2 (entrysize); } + + /* + BaseDynamicTable :: BaseDynamicTable (int size) + : data(size) + { + for (int i = 0; i < size; i++) + { + data[i].maxsize = 0; + data[i].size = 0; + data[i].col = NULL; + } + oneblock = NULL; + } + + BaseDynamicTable :: BaseDynamicTable (const Array & entrysizes, int elemsize) + : data(entrysizes.Size()) + { + int cnt = 0; + int n = entrysizes.Size(); + + for (int i = 0; i < n; i++) + cnt += entrysizes[i]; + oneblock = new char[elemsize * cnt]; + + cnt = 0; + for (int i = 0; i < n; i++) + { + data[i].maxsize = entrysizes[i]; + data[i].size = 0; + + data[i].col = &oneblock[elemsize * cnt]; + cnt += entrysizes[i]; + } + } + + BaseDynamicTable :: ~BaseDynamicTable () + { + if (oneblock) + delete [] oneblock; + else + for (int i = 0; i < data.Size(); i++) + delete [] static_cast (data[i].col); + } + + void BaseDynamicTable :: SetSize (int size) + { + for (int i = 0; i < data.Size(); i++) + delete [] static_cast (data[i].col); + + data.SetSize(size); + for (int i = 0; i < size; i++) + { + data[i].maxsize = 0; + data[i].size = 0; + data[i].col = NULL; + } + } + + void BaseDynamicTable :: IncSize (IndexType i, int elsize) + { + if (i < 0 || i >= data.Size()) + { + std::cerr << "BaseDynamicTable::Inc: Out of range, i = " << i << ", size = " << data.Size() << std::endl; + return; + } + + linestruct & line = data[i]; + + if (line.size == line.maxsize) + { + void * p = new char [(2*line.maxsize+5) * elsize]; + + memcpy (p, line.col, line.maxsize * elsize); + delete [] static_cast (line.col); + line.col = p; + line.maxsize = 2*line.maxsize+5; + } + + line.size++; + } + + void BaseDynamicTable :: DecSize (IndexType i) + { + if (i < 0 || i >= data.Size()) + { + std::cerr << "BaseDynamicTable::Dec: Out of range" << std::endl; + return; + } + + linestruct & line = data[i]; + + if (line.size == 0) + { + std::cerr << "BaseDynamicTable::Dec: EntrySize < 0" << std::endl; + return; + } + + line.size--; + } + */ + + void FilteredTableCreator::Add (size_t blocknr, int data) + { + if (!takedofs||takedofs->Test(data)) + TableCreator::Add(blocknr,data); + } + + void FilteredTableCreator::Add (size_t blocknr, IntRange range) + { + for (size_t i=range.First(); iTest(i)) + TableCreator::Add(blocknr,i); + } + + void FilteredTableCreator::Add (size_t blocknr, FlatArray dofs) + { + for (size_t i = 0; i < dofs.Size(); i++) + if (!takedofs||takedofs->Test(dofs[i])) + TableCreator::Add(blocknr,dofs[i]); + } + +} // namespace ngcore diff --git a/libsrc/core/table.hpp b/libsrc/core/table.hpp new file mode 100644 index 00000000..47893ed5 --- /dev/null +++ b/libsrc/core/table.hpp @@ -0,0 +1,787 @@ +#ifndef NETGEN_CORE_TABLE_HPP +#define NETGEN_CORE_TABLE_HPP + +/**************************************************************************/ +/* File: table.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 25. Mar. 2000 */ +/**************************************************************************/ + +#include +#include +#include + +#include "array.hpp" +#include "bitarray.hpp" +#include "memtracer.hpp" +#include "ngcore_api.hpp" +#include "profiler.hpp" + +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; + FlatTable (const FlatTable &) = default; + + 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 + { + return FlatArray (index[i-BASE+1]-index[i-BASE], data+index[i-BASE]); + } + + 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); + + + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum32 (entrysize); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum32 (FlatArray (entrysize.Size(), (unsigned int*)(int*)(entrysize.Addr(0)))); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray> entrysize) + { return TablePrefixSum32 (FlatArray (entrysize.Size(), (unsigned int*)(std::atomic*)entrysize.Addr(0))); } + NETGEN_INLINE size_t * TablePrefixSum (FlatArray entrysize) + { return TablePrefixSum64 (entrysize); } + + + /** + 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; + } + + /// 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]; + } + + explicit NETGEN_INLINE Table (const FlatTable & tab2) + : FlatTable(0, nullptr, nullptr) + { + size = tab2.Size(); + if (size == 0) return; + + index = new size_t[size+1]; + this->IndexArray() = tab2.IndexArray(); + // for (size_t i = 0; i <= size; i++) + // index[i] = tab2.index[i]; + + size_t cnt = index[size]; + data = new T[cnt]; + this->AsArray() = tab2.AsArray(); + /* + 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(); + if (size == 0) return; + + index = new size_t[size+1]; + for (size_t i = 0; i <= size; i++) + index[i] = tab2.index[i]; + + 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); + } + + template + auto DoArchive(ARCHIVE& ar) + { + ar & size; + if(size == 0) + return; + if(ar.Input()) + { + index = new IndexType[size+1]; + mt.Alloc(sizeof(IndexType) * (size+1)); + } + ar.Do(index, size+1); + if(ar.Input()) + { + data = new T[index[size]]; + mt.Alloc(sizeof(T) * index[size]); + } + ar.Do(data, index[size]); + } + + 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 () + { + mt.Free(GetMemUsage()); + delete [] data; + delete [] index; + } + + /// Size of table + using FlatTable::Size; + + /// number of elements in all rows + NETGEN_INLINE size_t NElements() const { return index[size]; } + + 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 + 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; + } + + + + + template + class TableCreator + { + protected: + int mode; // 1 .. cnt, 2 .. cnt entries, 3 .. fill table + std::atomic nd; + Array,IndexType> cnt; + Table table; + public: + TableCreator() + { nd = 0; mode = 1; } + TableCreator (size_t acnt) + { nd = acnt; SetMode(2); } + + Table MoveTable() + { + return std::move(table); + } + + bool Done () { return mode > 3; } + void operator++(int) { SetMode (mode+1); } + + int GetMode () const { return mode; } + void SetMode (int amode) + { + mode = amode; + if (mode == 2) + { + // cnt.SetSize(nd); // atomic has no copy + cnt = Array,IndexType> (nd); + for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed); + } + if (mode == 3) + { + table = Table (cnt); + // for (auto & ci : cnt) ci = 0; + for (auto & ci : cnt) ci.store (0, std::memory_order_relaxed); + // cnt = 0; + } + } + + void SetSize (size_t _nd) + { + if (mode == 1) + nd = _nd; + else + { + if (nd != _nd) + throw Exception ("cannot change size of table-creator"); + } + } + + void Add (IndexType blocknr, const T & data) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr]++; + break; + case 3: + int ci = cnt[blocknr]++; + table[blocknr][ci] = data; + break; + } + } + + + void Add (IndexType blocknr, IntRange range) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr] += range.Size(); + break; + case 3: + size_t ci = ( cnt[blocknr] += range.Size() ) - range.Size(); + for (size_t j = 0; j < range.Size(); j++) + table[blocknr][ci+j] = range.First()+j; + break; + } + } + + void Add (IndexType blocknr, const FlatArray & dofs) + { + switch (mode) + { + case 1: + { + size_t oldval = nd; + while (blocknr+1>nd) { + nd.compare_exchange_weak (oldval, blocknr+1); + oldval = nd; + } + break; + } + case 2: + cnt[blocknr] += dofs.Size(); + break; + case 3: + size_t ci = ( cnt[blocknr] += dofs.Size() ) - dofs.Size(); + for (size_t j = 0; j < dofs.Size(); j++) + table[blocknr][ci+j] = dofs[j]; + break; + } + } + }; + + template + Table CreateTable( const TRange & range, const TFunc & func, std::optional< size_t > cnt ) + { + static Timer timer("CreateTable"); + RegionTimer rt(timer); + std::unique_ptr> pcreator; + + if(cnt) + pcreator = std::make_unique>(*cnt); + else + pcreator = std::make_unique>(); + + auto & creator = *pcreator; + + for ( ; !creator.Done(); creator++) + ParallelForRange + (range, [&] (auto myrange) + { + for (auto i : myrange) + func(creator, i); + }, TasksPerThread(4) + ); + + return creator.MoveTable(); + } + + template + Table CreateSortedTable( const TRange & range, const TFunc & func, std::optional< size_t > cnt ) + { + static Timer timer("CreateSortedTable"); + RegionTimer rt(timer); + Table table = CreateTable(range, func, cnt); + ParallelForRange + (table.Range(), [&] (auto myrange) + { + for (auto i : myrange) + QuickSort(table[i]); + }, TasksPerThread(4) + ); + + return table; + } + + class NGCORE_API FilteredTableCreator : public TableCreator + { + protected: + const BitArray* takedofs; + public: + FilteredTableCreator(const BitArray* atakedofs) + : TableCreator(), takedofs(atakedofs) { }; + FilteredTableCreator(int acnt, const BitArray* atakedofs) + : TableCreator(acnt),takedofs(atakedofs) { }; + void Add (size_t blocknr, int data); + void Add (size_t blocknr, IntRange range); + void Add (size_t blocknr, FlatArray dofs); + }; + + + + /** + A dynamic table class. + + A DynamicTable contains entries of variable size. Entry sizes can + be increased dynamically. + */ + 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) + : data(size) + { + 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, bool setentrysize=false) + : 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]; + if (setentrysize) + data[i].size = entrysizes[i]; + else + 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 (IndexType i, const T & cont) + { + int es = EntrySize (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 (IndexType i) + { + IncSize (i); + } + + /** Set the nr-th element in the i-th row to acont. + Does not check for overflow. */ + 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 (IndexType i, int nr) const + { + return data[i].col[nr]; + } + + + /** Returns pointer to the first element in row i. */ + const T * GetLine (IndexType i) const + { + return data[i].col; + } + + /// Returns size of the table. + size_t Size () const + { + return data.Size(); + } + + auto Range () const + { + return data.Range(); + } + + /// Returns size of the i-th row. + int EntrySize (IndexType i) const + { + return data[i].size; + } + + /// + void DecEntrySize (IndexType i) + { + DecSize(i); + } + + /// Access entry i + 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)); } + */ + 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 (auto i : Range(table)) + { + s << i << ":"; + for (int j = 0; j < table[i].Size(); j++) + s << " " << table[i][j]; + s << "\n"; + } + s << std::flush; + return s; + } + + + // Helper function to calculate coloring of a set of indices for parallel processing of independent elements/points/etc. + // Assigns a color to each of colors.Size() elements, such that two elements with the same color don't share a common 'dof', + // the mapping from element to dofs is provided by the function getDofs(int) -> iterable + // + // Returns the number of used colors + template + int ComputeColoring( FlatArray colors, size_t ndofs, Tmask const & getDofs) + { + static Timer timer("ComputeColoring - "+Demangle(typeid(Tmask).name())); RegionTimer rt(timer); + static_assert(sizeof(unsigned int)==4, "Adapt type of mask array"); + size_t n = colors.Size(); + + Array mask(ndofs); + + size_t colored_blocks = 0; + + // We are coloring with 32 colors at once and use each bit to mask conflicts + unsigned int check = 0; + unsigned int checkbit = 0; + + int current_color = 0; + colors = -1; + int maxcolor = 0; + + while(colored_blocks-1) continue; + check = 0; + const auto & dofs = getDofs(i); + + // Check if adjacent dofs are already marked by current color + for (auto dof : dofs) + check|=mask[dof]; + + // Did we find a free color? + if(check != 0xFFFFFFFF) + { + checkbit = 1; + int color = current_color; + // find the actual color, which is free (out of 32) + while (check & checkbit) + { + color++; + checkbit *= 2; + } + colors[i] = color; + maxcolor = color > maxcolor ? color : maxcolor; + colored_blocks++; + // mask all adjacent dofs with the found color + for (auto dof : dofs) + mask[dof] |= checkbit; + } + } + current_color+=32; + } + return maxcolor+1; + } + + + typedef DynamicTable IntTable; + +} // namespace ngcore + +#endif // NETGEN_CORE_TABLE_HPP diff --git a/libsrc/core/taskmanager.cpp b/libsrc/core/taskmanager.cpp new file mode 100644 index 00000000..f57be4db --- /dev/null +++ b/libsrc/core/taskmanager.cpp @@ -0,0 +1,831 @@ +/********************************************************************/ +/* File: taskmanager.cpp */ +/* Author: M. Hochsterger, J. Schoeberl */ +/* Date: 10. Mar. 2015 */ +/********************************************************************/ + +#include +#include +#include +#include + +#include "concurrentqueue.h" +#include "mpi_wrapper.hpp" +#include "paje_trace.hpp" +#include "profiler.hpp" +#include "taskmanager.hpp" + +#ifdef USE_MKL +#include +#endif + + + +namespace ngcore +{ + using std::mutex; + using std::lock_guard; + using std::memory_order_release; + using std::memory_order_relaxed; + using std::make_tuple; + + TaskManager * task_manager = nullptr; + bool TaskManager :: use_paje_trace = false; + int TaskManager :: max_threads = getenv("NGS_NUM_THREADS") ? atoi(getenv("NGS_NUM_THREADS")) : std::thread::hardware_concurrency(); + int TaskManager :: num_threads = 1; + + + thread_local int TaskManager :: thread_id = 0; + + const function * TaskManager::func; + const function * TaskManager::startup_function = nullptr; + const function * TaskManager::cleanup_function = nullptr; + + atomic TaskManager::ntasks; + Exception * TaskManager::ex; + + atomic TaskManager::jobnr; + + atomic TaskManager::complete[8]; // max nodes + atomic TaskManager::done; + atomic TaskManager::active_workers; + atomic TaskManager::workers_on_node[8]; // max nodes + + + int TaskManager::sleep_usecs = 1000; + bool TaskManager::sleep = false; + + TaskManager::NodeData *TaskManager::nodedata[8]; + int TaskManager::num_nodes; + + static mutex copyex_mutex; + + int EnterTaskManager () + { + if (task_manager) + { + // no task manager started + return 0; + } + + task_manager = new TaskManager(); + + GetLogger("TaskManager")->info("task-based parallelization (C++11 threads) using {} threads", task_manager->GetNumThreads()); + +#ifdef USE_NUMA + numa_run_on_node (0); +#endif + +#ifndef WIN32 + // master has maximal priority ! + int policy; + struct sched_param param; + pthread_getschedparam(pthread_self(), &policy, ¶m); + param.sched_priority = sched_get_priority_max(policy); + pthread_setschedparam(pthread_self(), policy, ¶m); +#endif // WIN32 + + + task_manager->StartWorkers(); + + ParallelFor (Range(100), [&] (int i) { ; }); // startup + return task_manager->GetNumThreads(); + } + + + void ExitTaskManager (int num_threads) + { + if(num_threads > 0) + { + task_manager->StopWorkers(); + delete task_manager; + task_manager = nullptr; + } + } + + void RunWithTaskManager (function alg) + { + int num_threads = EnterTaskManager(); + alg(); + ExitTaskManager(num_threads); + } + + + + + void TaskManager :: SetNumThreads(int amax_threads) + { + if(task_manager && task_manager->active_workers>0) + { + std::cerr << "Warning: can't change number of threads while TaskManager active!" << std::endl; + return; + } + max_threads = amax_threads; + } + + + TaskManager :: TaskManager() + { + num_threads = GetMaxThreads(); + // if (MyMPI_GetNTasks() > 1) num_threads = 1; + +#ifdef USE_NUMA + numa_available(); + num_nodes = numa_max_node() + 1; + if (num_nodes > num_threads) num_nodes = num_threads; + + for (int j = 0; j < num_nodes; j++) + { + void * mem = numa_alloc_onnode (sizeof(NodeData), j); + nodedata[j] = new (mem) NodeData; + complete[j] = -1; + workers_on_node[j] = 0; + } +#else + num_nodes = 1; + nodedata[0] = new NodeData; + complete[0] = -1; + workers_on_node[0] = 0; +#endif + + jobnr = 0; + done = 0; + sleep = false; + sleep_usecs = 1000; + active_workers = 0; + + static int cnt = 0; + if (use_paje_trace) + trace = new PajeTrace(num_threads, "ng" + ToString(cnt++)); + } + + + TaskManager :: ~TaskManager () + { + if (use_paje_trace) + { + delete trace; + trace = nullptr; + } + num_threads = 1; + } + +#ifdef WIN32 + int TaskManager :: GetThreadId() + { + return thread_id; + } +#endif + + void TaskManager :: StartWorkers() + { + done = false; + + for (int i = 1; i < num_threads; i++) + { + std::thread([this,i]() { this->Loop(i); }).detach(); + } + thread_id = 0; + + size_t alloc_size = num_threads*NgProfiler::SIZE; + NgProfiler::thread_times = new size_t[alloc_size]; + for (size_t i = 0; i < alloc_size; i++) + NgProfiler::thread_times[i] = 0; + NgProfiler::thread_flops = new size_t[alloc_size]; + for (size_t i = 0; i < alloc_size; i++) + NgProfiler::thread_flops[i] = 0; + + while (active_workers < num_threads-1) + ; + } + + 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 = 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; + + // cout << "cpu frequ = " << frequ << endl; + // collect timings + for (size_t i = 0; i < num_threads; i++) + for (size_t j = NgProfiler::SIZE; j-- > 0; ) + { + if (!NgProfiler::timers[j].usedcounter) break; + NgProfiler::timers[j].tottime += 1.0/frequ * NgProfiler::thread_times[i*NgProfiler::SIZE+j]; + NgProfiler::timers[j].flops += NgProfiler::thread_flops[i*NgProfiler::SIZE+j]; + } + delete [] NgProfiler::thread_times; + NgProfiler::thread_times = NgProfiler::dummy_thread_times.data(); + delete [] NgProfiler::thread_flops; + NgProfiler::thread_flops = NgProfiler::dummy_thread_flops.data(); + + while (active_workers) + ; + } + + /////////////////////// NEW: nested tasks using concurrent queue + + struct TNestedTask + { + const function * func; + int mynr; + int total; + int producing_thread; + atomic * endcnt; + + TNestedTask () { ; } + TNestedTask (const function & _func, + int _mynr, int _total, + atomic & _endcnt, int prod_tid) + : func(&_func), mynr(_mynr), total(_total), producing_thread(prod_tid), endcnt(&_endcnt) + { + ; + } + }; + + typedef moodycamel::ConcurrentQueue TQueue; + typedef moodycamel::ProducerToken TPToken; + typedef moodycamel::ConsumerToken TCToken; + + static TQueue taskqueue; + + void AddTask (const function & afunc, + atomic & endcnt) + + { + TPToken ptoken(taskqueue); + + int num = endcnt; + auto tid = TaskManager::GetThreadId(); + for (int i = 0; i < num; i++) + taskqueue.enqueue (ptoken, { afunc, i, num, endcnt, tid }); + } + + bool TaskManager :: ProcessTask() + { + // static Timer t("process task"); + TNestedTask task; + TCToken ctoken(taskqueue); + + if (taskqueue.try_dequeue(ctoken, task)) + { + TaskInfo ti; + ti.task_nr = task.mynr; + ti.ntasks = task.total; + ti.thread_nr = TaskManager::GetThreadId(); + ti.nthreads = TaskManager::GetNumThreads(); + /* + { + lock_guard guard(m); + cout << "process nested, nr = " << ti.task_nr << "/" << ti.ntasks << endl; + } + */ + // if(trace && task.producing_thread != ti.thread_nr) + // trace->StartTask (ti.thread_nr, t, PajeTrace::Task::ID_TIMER, task.producing_thread); + + (*task.func)(ti); + --*task.endcnt; + + // if(trace && task.producing_thread != ti.thread_nr) + // trace->StopTask (ti.thread_nr, t); + return true; + } + return false; + } + + + void TaskManager :: CreateJob (const function & afunc, + int antasks) + { + if (num_threads == 1 || !task_manager) // || func) + { + if (startup_function) (*startup_function)(); + + TaskInfo ti; + ti.ntasks = antasks; + ti.thread_nr = 0; ti.nthreads = 1; + // ti.node_nr = 0; ti.nnodes = 1; + for (ti.task_nr = 0; ti.task_nr < antasks; ti.task_nr++) + afunc(ti); + + if (cleanup_function) (*cleanup_function)(); + return; + } + + + if (func) + { // we are already parallel, use nested tasks + // startup for inner function not supported ... + // if (startup_function) (*startup_function)(); + + if (antasks == 1) + { + TaskInfo ti; + ti.task_nr = 0; + ti.ntasks = 1; + ti.thread_nr = 0; ti.nthreads = 1; + afunc(ti); + return; + } + + atomic endcnt(antasks); + AddTask (afunc, endcnt); + while (endcnt > 0) + { + ProcessTask(); + } + + // if (cleanup_function) (*cleanup_function)(); + 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; + } + + if (trace) + trace->StartJob(jobnr, afunc.target_type()); + + func = &afunc; + + ntasks.store (antasks); // , memory_order_relaxed); + ex = nullptr; + + + nodedata[0]->start_cnt.store (0, memory_order_relaxed); + + jobnr++; + + for (int j = 0; j < num_nodes; j++) + nodedata[j]->participate |= 1; + + if (startup_function) (*startup_function)(); + + int thd = 0; + int thds = GetNumThreads(); + int mynode = num_nodes * thd/thds; + + IntRange mytasks = Range(int(ntasks)).Split (mynode, num_nodes); + NodeData & mynode_data = *(nodedata[mynode]); + + TaskInfo ti; + ti.nthreads = thds; + ti.thread_nr = thd; + // ti.nnodes = num_nodes; + // ti.node_nr = mynode; + + try + { + while (1) + { + int mytask = mynode_data.start_cnt++; + if (mytask >= mytasks.Size()) break; + + ti.task_nr = mytasks.First()+mytask; + ti.ntasks = ntasks; + + { + RegionTracer t(ti.thread_nr, jobnr, RegionTracer::ID_JOB, ti.task_nr); + (*func)(ti); + } + } + + } + catch (Exception e) + { + { + lock_guard guard(copyex_mutex); + delete ex; + ex = new Exception (e); + mynode_data.start_cnt = mytasks.Size(); + } + } + + if (cleanup_function) (*cleanup_function)(); + + for (int j = 0; j < num_nodes; j++) + 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); + + if (trace) + trace->StopJob(); + } + + void TaskManager :: Loop(int thd) + { + /* + static Timer tADD("add entry counter"); + static Timer tCASready1("spin-CAS ready tick1"); + static Timer tCASready2("spin-CAS ready tick2"); + static Timer tCASyield("spin-CAS yield"); + static Timer tCAS1("spin-CAS wait"); + static Timer texit("exit zone"); + static Timer tdec("decrement"); + */ + thread_id = thd; + + int thds = GetNumThreads(); + + int mynode = num_nodes * thd/thds; + + NodeData & mynode_data = *(nodedata[mynode]); + + + + TaskInfo ti; + ti.nthreads = thds; + ti.thread_nr = thd; + // ti.nnodes = num_nodes; + // ti.node_nr = mynode; + + +#ifdef USE_NUMA + numa_run_on_node (mynode); +#endif + active_workers++; + workers_on_node[mynode]++; + int jobdone = 0; + + +#ifdef USE_MKL + auto mkl_max = mkl_get_max_threads(); + mkl_set_num_threads_local(1); +#endif + + + size_t no_job_counter = 0; + while (!done) + { + if (complete[mynode] > jobdone) + jobdone = complete[mynode]; + + if (jobnr == jobdone) + { + no_job_counter++; + // RegionTracer t(ti.thread_nr, tCASyield, ti.task_nr); + while (ProcessTask()) no_job_counter = 0; // do the nested tasks + + if(sleep || no_job_counter > 10000) + std::this_thread::sleep_for(std::chrono::microseconds(10)); + else + { +#ifdef WIN32 + std::this_thread::yield(); +#else // WIN32 + sched_yield(); +#endif // WIN32 + } + continue; + } + + { + // RegionTracer t(ti.thread_nr, tADD, ti.task_nr); + + // non-atomic fast check ... + if ( (mynode_data.participate & 1) == 0) continue; + + int oldval = mynode_data.participate += 2; + if ( (oldval & 1) == 0) + { // job not active, going out again + mynode_data.participate -= 2; + continue; + } + } + + if (startup_function) (*startup_function)(); + + IntRange mytasks = Range(int(ntasks)).Split (mynode, num_nodes); + + try + { + + while (1) + { + if (mynode_data.start_cnt >= mytasks.Size()) break; + int mytask = mynode_data.start_cnt.fetch_add(1, memory_order_relaxed); + if (mytask >= mytasks.Size()) break; + + ti.task_nr = mytasks.First()+mytask; + ti.ntasks = ntasks; + no_job_counter = 0; + + { + RegionTracer t(ti.thread_nr, jobnr, RegionTracer::ID_JOB, ti.task_nr); + (*func)(ti); + } + } + + } + catch (Exception e) + { + { + // cout << "got exception in TM" << endl; + lock_guard guard(copyex_mutex); + delete ex; + ex = new Exception (e); + mynode_data.start_cnt = mytasks.Size(); + } + } + +#ifndef __MIC__ + atomic_thread_fence (memory_order_release); +#endif // __MIC__ + + if (cleanup_function) (*cleanup_function)(); + + jobdone = jobnr; + + mynode_data.participate-=2; + + { + int oldpart = 1; + if (mynode_data.participate.compare_exchange_strong (oldpart, 0)) + { + if (jobdone < jobnr.load()) + { // reopen gate + mynode_data.participate |= 1; + } + else + { + if (mynode != 0) + mynode_data.start_cnt = 0; + complete[mynode] = jobnr.load(); + } + } + } + } + + +#ifdef USE_MKL + mkl_set_num_threads_local(mkl_max); +#endif + + workers_on_node[mynode]--; + active_workers--; + } + + + std::list> TaskManager :: Timing () + { + /* + list>timings; + double time = + RunTiming + ( [&] () + { + ParallelJob ( [] (TaskInfo ti) { ; } , + TasksPerThread(1) ); + }); + timings.push_back (make_tuple("parallel job with 1 task per thread", time*1e9)); + + time = + RunTiming + ( [&] () + { + ParallelJob ( [] (TaskInfo ti) { ; } , + TasksPerThread(10) ); + }); + timings.push_back (make_tuple("parallel job with 10 tasks per thread", time*1e9)); + + time = + RunTiming + ( [&] () + { + ParallelJob ( [] (TaskInfo ti) { ; } , + TasksPerThread(100) ); + }); + timings.push_back (make_tuple("parallel job with 100 tasks per thread", time*1e9)); + + return timings; + */ + + + + // this is the old function moved from the py-interface: + std::list>timings; + double starttime, time; + double maxtime = 0.5; + size_t steps; + + starttime = WallTime(); + steps = 0; + do + { + for (size_t i = 0; i < 1000; i++) + ParallelJob ( [] (TaskInfo ti) { ; }, + TasksPerThread(1)); + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("ParallelJob 1 task/thread", time/steps*1e9)); + + + starttime = WallTime(); + steps = 0; + do + { + for (size_t i = 0; i < 1000; i++) + ParallelJob ( [] (TaskInfo ti) { ; }, + TasksPerThread(100)); + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("ParallelJob 100 task/thread", time/steps*1e9)); + + + starttime = WallTime(); + steps = 0; + do + { + for (int k = 0; k < 10000; k++) + { + SharedLoop2 sl(1000); + steps += 1; + } + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("SharedLoop init", time/steps*1e9)); + + starttime = WallTime(); + steps = 0; + do + { + for (int k = 0; k < 1000; k++) + { + SharedLoop sl(5); + ParallelJob ( [&sl] (TaskInfo ti) + { + for (auto i : sl) + (void)i; // silence warning + } ); + } + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("short SharedLoop", time/steps*1e9)); + + + starttime = WallTime(); + steps = 0; + do + { + for (int k = 0; k < 1000; k++) + { + SharedLoop sl1(5), sl2(5), sl3(5), sl4(5), sl5(5); + ParallelJob ( [&sl1, &sl2, &sl3, &sl4, &sl5] (TaskInfo ti) + { + for (auto i : sl1) + (void)i; // silence warning + for (auto i : sl2) + (void)i; // silence warning + for (auto i : sl3) + (void)i; // silence warning + for (auto i : sl4) + (void)i; // silence warning + for (auto i : sl5) + (void)i; // silence warning + } ); + } + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("5 short SharedLoops", time/steps*1e9)); + + + starttime = WallTime(); + steps = 0; + SharedLoop2 sl2(5); + do + { + for (int k = 0; k < 1000; k++) + { + sl2.Reset(5); + ParallelJob ( [&sl2] (TaskInfo ti) + { + for (auto i : sl2) + (void)i; // silence warning + } ); + } + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("short SharedLoop2", time/steps*1e9)); + + { + starttime = WallTime(); + steps = 0; + SharedLoop2 sl1(5), sl2(5), sl3(5), sl4(5), sl5(5); + do + { + for (int k = 0; k < 1000; k++) + { + sl1.Reset(5); + sl2.Reset(5); + sl3.Reset(5); + sl4.Reset(5); + sl5.Reset(5); + ParallelJob ( [&sl1,&sl2,&sl3,&sl4,&sl5] (TaskInfo ti) + { + for (auto i : sl1) + (void)i; // silence warning + for (auto i : sl2) + (void)i; // silence warning + for (auto i : sl3) + (void)i; // silence warning + for (auto i : sl4) + (void)i; // silence warning + for (auto i : sl5) + (void)i; // silence warning + } ); + } + steps += 1000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("5 short SharedLoop2", time/steps*1e9)); + } + + + starttime = WallTime(); + steps = 0; + { + SharedLoop2 sl(1000); + do + { + for (int k = 0; k < 1000; k++) + { + sl.Reset(1000); + ParallelJob ( [&sl] (TaskInfo ti) + { + for (auto i : sl) + (void)i; // silence warning + } ); + steps += 1000; + } + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("SharedLoop2 1000, time per iteration", time/steps*1e9)); + } + + { + starttime = WallTime(); + steps = 0; + SharedLoop2 sl(1000000); + do + { + sl.Reset(1000000); + ParallelJob ( [&sl] (TaskInfo ti) + { + for (auto i : sl) + (void)i; // silence warning + } ); + steps += 1000000; + time = WallTime()-starttime; + } + while (time < maxtime); + timings.push_back(make_tuple("SharedLoop2 1000000, time per iteration", time/steps*1e9)); + } + + return timings; + } + +} diff --git a/libsrc/core/taskmanager.hpp b/libsrc/core/taskmanager.hpp new file mode 100644 index 00000000..53cf21b4 --- /dev/null +++ b/libsrc/core/taskmanager.hpp @@ -0,0 +1,1121 @@ +#ifndef NETGEN_CORE_TASKMANAGER_HPP +#define NETGEN_CORE_TASKMANAGER_HPP + +/*********************************************************************/ +/* File: taskmanager.hpp */ +/* Author: M. Hochsterger, J. Schoeberl */ +/* Date: 10. Mar. 2015 */ +/*********************************************************************/ + +#include +#include +#include +#include +#include + +#include "array.hpp" +#include "paje_trace.hpp" +#include "taskmanager.hpp" + +#ifdef USE_NUMA +#include +#include +#endif + + +namespace ngcore +{ + using std::atomic; + using std::function; + + class TaskInfo + { + public: + int task_nr; + int ntasks; + + int thread_nr; + int nthreads; + + // int node_nr; + // int nnodes; + }; + + NGCORE_API extern class TaskManager * task_manager; + + class TaskManager + { +// PajeTrace *trace; + + class alignas(64) NodeData + { + public: + atomic start_cnt{0}; + atomic participate{0}; + }; + + NGCORE_API static const function * func; + NGCORE_API static const function * startup_function; + NGCORE_API static const function * cleanup_function; + NGCORE_API static atomic ntasks; + NGCORE_API static Exception * ex; + + NGCORE_API static atomic jobnr; + + static atomic complete[8]; // max nodes + static atomic done; + static atomic active_workers; + static atomic workers_on_node[8]; // max nodes + // Array*> sync; + NGCORE_API static int sleep_usecs; + NGCORE_API static bool sleep; + + static NodeData *nodedata[8]; + + static int num_nodes; + NGCORE_API static int num_threads; + NGCORE_API static int max_threads; + + + +#ifdef WIN32 // no exported thread_local in dlls on Windows + static thread_local int thread_id; +#else + NGCORE_API static thread_local int thread_id; +#endif + NGCORE_API static bool use_paje_trace; + public: + + NGCORE_API TaskManager(); + NGCORE_API ~TaskManager(); + + + NGCORE_API void StartWorkers(); + NGCORE_API void StopWorkers(); + + void SuspendWorkers(int asleep_usecs = 1000 ) + { + sleep_usecs = asleep_usecs; + sleep = true; + } + void ResumeWorkers() { sleep = false; } + + NGCORE_API static void SetNumThreads(int amax_threads); + static int GetMaxThreads() { return max_threads; } + // static int GetNumThreads() { return task_manager ? task_manager->num_threads : 1; } + static int GetNumThreads() { return num_threads; } +#ifdef WIN32 + NGCORE_API static int GetThreadId(); +#else + static int GetThreadId() { return thread_id; } +#endif + int GetNumNodes() const { return num_nodes; } + + static void SetPajeTrace (bool use) { use_paje_trace = use; } + + NGCORE_API static bool ProcessTask(); + + NGCORE_API static void CreateJob (const function & afunc, + int antasks = task_manager->GetNumThreads()); + + static void SetStartupFunction (const function & func) { startup_function = &func; } + static void SetStartupFunction () { startup_function = nullptr; } + static void SetCleanupFunction (const function & func) { cleanup_function = &func; } + static void SetCleanupFunction () { cleanup_function = nullptr; } + + void Done() { done = true; } + NGCORE_API void Loop(int thread_num); + + NGCORE_API static std::list> Timing (); + }; + + + + + + + + + + NGCORE_API void RunWithTaskManager (function alg); + + // For Python context manager + NGCORE_API int EnterTaskManager (); + NGCORE_API void ExitTaskManager (int num_threads); + + class RegionTaskManager + { + int nthreads_before; + int nthreads; + bool started_taskmanager; + + public: + RegionTaskManager(int anthreads=TaskManager::GetMaxThreads()) + : nthreads(anthreads) + { + if(task_manager || nthreads==0) + { + // already running, no need to do anything + started_taskmanager = false; + return; + } + else + { + nthreads_before = TaskManager::GetMaxThreads(); + TaskManager::SetNumThreads(nthreads); + nthreads = EnterTaskManager(); + started_taskmanager = true; + } + } + + ~RegionTaskManager() + { + if(started_taskmanager) + { + ExitTaskManager(nthreads); + TaskManager::SetNumThreads(nthreads_before); + } + } + }; + + NETGEN_INLINE int TasksPerThread (int tpt) + { + // return task_manager ? tpt*task_manager->GetNumThreads() : 1; + return tpt*TaskManager::GetNumThreads(); + } + + + class TotalCosts + { + size_t cost; + public: + TotalCosts (size_t _cost) : cost(_cost) { ; } + size_t operator ()() { return cost; } + }; + + template + NETGEN_INLINE void ParallelFor (T_Range r, TFUNC f, + int antasks = TaskManager::GetNumThreads(), + TotalCosts costs = 1000) + { + // if (task_manager && costs() >= 1000) + + TaskManager::CreateJob + ([r, f] (TaskInfo & ti) + { + auto myrange = r.Split (ti.task_nr, ti.ntasks); + for (auto i : myrange) f(i); + }, + antasks); + + /* + else + for (auto i : r) f(i); + */ + } + + /* + template + NETGEN_INLINE void ParallelFor (size_t n, TFUNC f, + int antasks = task_manager ? task_manager->GetNumThreads() : 0) + { + ParallelFor (IntRange (n), f, antasks); + } + */ + template + NETGEN_INLINE void ParallelFor (size_t n, Args...args) + { + ParallelFor (IntRange (n), args...); + } + + template + NETGEN_INLINE void ParallelForRange (T_Range r, TFUNC f, + int antasks = TaskManager::GetNumThreads(), + TotalCosts costs = 1000) + { + // if (task_manager && costs() >= 1000) + + TaskManager::CreateJob + ([r, f] (TaskInfo & ti) + { + auto myrange = r.Split (ti.task_nr, ti.ntasks); + f(myrange); + }, + antasks); + /* + else + f(r); + */ + } + + /* + template + NETGEN_INLINE void ParallelForRange (size_t n, TFUNC f, + int antasks = task_manager ? task_manager->GetNumThreads() : 0) + { + ParallelForRange (IntRange(n), f, antasks); + } + */ + template + NETGEN_INLINE void ParallelForRange (size_t n, Args...args) + { + ParallelForRange (IntRange(n), args...); + } + + template + NETGEN_INLINE void ParallelJob (TFUNC f, + int antasks = TaskManager::GetNumThreads()) + { + TaskManager::CreateJob (f, antasks); + } + + + /* + Usage example: + + ShareLoop myloop(100); + task_manager->CreateJob ([]() + { + for (int i : myloop) + cout << "i = " << i << endl; + }); + + */ + + class SharedLoop + { + atomic cnt; + IntRange r; + + + class SharedIterator + { + atomic & cnt; + int myval; + int endval; + public: + SharedIterator (atomic & acnt, int aendval, bool begin_iterator) + : cnt (acnt) + { + endval = aendval; + myval = begin_iterator ? cnt++ : endval; + if (myval > endval) myval = endval; + } + + SharedIterator & operator++ () + { + myval = cnt++; + if (myval > endval) myval = endval; + return *this; + } + + int operator* () const { return myval; } + bool operator!= (const SharedIterator & it2) const { return myval != it2.myval; } + }; + + + public: + SharedLoop (IntRange ar) : r(ar) { cnt = r.begin(); } + SharedIterator begin() { return SharedIterator (cnt, r.end(), true); } + SharedIterator end() { return SharedIterator (cnt, r.end(), false); } + }; + + + /* +class alignas(4096) AtomicRange +{ + mutex lock; + int begin; + int end; +public: + + void Set (IntRange r) + { + lock_guard guard(lock); + begin = r.begin(); + end = r.end(); + } + + IntRange Get() + { + lock_guard guard(lock); + return IntRange(begin, end); + } + + bool PopFirst (int & first) + { + lock_guard guard(lock); + bool non_empty = end > begin; + first = begin; + if (non_empty) begin++; + return non_empty; + } + + bool PopHalf (IntRange & r) + { + lock_guard guard(lock); + bool non_empty = end > begin; + if (non_empty) + { + int mid = (begin+end+1)/2; + r = IntRange(begin, mid); + begin = mid; + } + return non_empty; + } +}; +*/ + + + + // lock free popfirst + // faster for large loops, bug slower for small loops (~1000) ???? + /* + class alignas(4096) AtomicRange +{ + mutex lock; + atomic begin; + int end; +public: + + void Set (IntRange r) + { + lock_guard guard(lock); + // begin = r.begin(); + begin.store(r.begin(), std::memory_order_relaxed); + end = r.end(); + } + + void SetNoLock (IntRange r) + { + begin.store(r.begin(), std::memory_order_relaxed); + end = r.end(); + } + + // IntRange Get() + // { + // lock_guard guard(lock); + // return IntRange(begin, end); + // } + + bool PopFirst (int & first) + { + // int oldbegin = begin; + int oldbegin = begin.load(std::memory_order_relaxed); + if (oldbegin >= end) return false; + while (!begin.compare_exchange_weak (oldbegin, oldbegin+1, + std::memory_order_relaxed, std::memory_order_relaxed)) + if (oldbegin >= end) return false; + + first = oldbegin; + return true; + } + + bool PopHalf (IntRange & r) + { + // int oldbegin = begin; + int oldbegin = begin.load(std::memory_order_relaxed); + if (oldbegin >= end) return false; + + lock_guard guard(lock); + while (!begin.compare_exchange_weak (oldbegin, (oldbegin+end+1)/2, + std::memory_order_relaxed, std::memory_order_relaxed)) + if (oldbegin >= end) return false; + + r = IntRange(oldbegin, (oldbegin+end+1)/2); + return true; + } +}; + + + // inline ostream & operator<< (ostream & ost, AtomicRange & r) + // { + // ost << r.Get(); + // return ost; + // } + */ + + + + class alignas(4096) AtomicRange + { + atomic begin; + atomic end; + public: + + void Set (IntRange r) + { + begin.store(std::numeric_limits::max(), std::memory_order_release); + end.store(r.end(), std::memory_order_release); + begin.store(r.begin(), std::memory_order_release); + } + + void SetNoLock (IntRange r) + { + end.store(r.end(), std::memory_order_release); + begin.store(r.begin(), std::memory_order_release); + } + + // IntRange Get() + // { + // lock_guard guard(lock); + // return IntRange(begin, end); + // } + + bool PopFirst (size_t & hfirst) + { + // first = begin++; + // return first < end; + + size_t first = begin.load(std::memory_order_relaxed); + + size_t nextfirst = first+1; + if (first >= end) nextfirst = std::numeric_limits::max()-1; + + // while (!begin.compare_exchange_weak (first, nextfirst)) + while (!begin.compare_exchange_weak (first, nextfirst, + std::memory_order_relaxed, + std::memory_order_relaxed)) + { + first = begin; + nextfirst = first+1; + if (nextfirst >= end) nextfirst = std::numeric_limits::max()-1; + } + hfirst = first; + return first < end; + } + + bool PopHalf (IntRange & r) + { + /* + // int oldbegin = begin; + size_t oldbegin = begin.load(std::memory_order_acquire); + size_t oldend = end.load(std::memory_order_acquire); + if (oldbegin >= oldend) return false; + + // lock_guard guard(lock); + while (!begin.compare_exchange_weak (oldbegin, (oldbegin+oldend+1)/2, + std::memory_order_relaxed, std::memory_order_relaxed)) + { + oldend = end.load(std::memory_order_acquire); + if (oldbegin >= oldend) return false; + } + + r = IntRange(oldbegin, (oldbegin+oldend+1)/2); + return true; + */ + + + size_t oldbegin = begin; // .load(std::memory_order_acquire); + size_t oldend = end; // .load(std::memory_order_acquire); + if (oldbegin >= oldend) return false; + + size_t nextbegin = (oldbegin+oldend+1)/2; + if (nextbegin >= oldend) nextbegin = std::numeric_limits::max()-1; + + while (!begin.compare_exchange_weak (oldbegin, nextbegin)) + // std::memory_order_relaxed, std::memory_order_relaxed)) + { + oldend = end; // .load(std::memory_order_acquire); + if (oldbegin >= oldend) return false; + + nextbegin = (oldbegin+oldend+1)/2; + if (nextbegin >= oldend) nextbegin = std::numeric_limits::max()-1; + } + + r = IntRange(oldbegin, (oldbegin+oldend+1)/2); + return true; + } + }; + + + + + class SharedLoop2 + { + Array ranges; + atomic processed; + atomic total; + atomic participants; + + class SharedIterator + { + FlatArray ranges; + atomic & processed; + size_t total; + size_t myval; + size_t processed_by_me = 0; + int me; + int steal_from; + public: + SharedIterator (FlatArray _ranges, atomic & _processed, size_t _total, + int _me, bool begin_it) + : ranges(_ranges), processed(_processed), total(_total) + { + if (begin_it) + { + // me = TaskManager::GetThreadId(); + me = _me; + steal_from = me; + GetNext(); + } + } + ~SharedIterator() + { + if (processed_by_me) + processed += processed_by_me; + } + + SharedIterator & operator++ () { GetNext(); return *this;} + + void GetNext() + { + size_t nr; + if (ranges[me].PopFirst(nr)) + { + processed_by_me++; + myval = nr; + return; + } + GetNext2(); + } + + void GetNext2() + { + processed += processed_by_me; + processed_by_me = 0; + + // done with my work, going to steal ... + while (1) + { + if (processed >= total) return; + + steal_from++; + if (steal_from == ranges.Size()) steal_from = 0; + + // steal half of the work reserved for 'from': + IntRange steal; + if (ranges[steal_from].PopHalf(steal)) + { + myval = steal.First(); + processed_by_me++; + if (myval+1 < steal.Next()) + ranges[me].Set (IntRange(myval+1, steal.Next())); + return; + } + } + } + + size_t operator* () const { return myval; } + bool operator!= (const SharedIterator & it2) const { return processed < total; } + }; + + + public: + SharedLoop2 () + : ranges(TaskManager::GetNumThreads()) + { ; } + + SharedLoop2 (IntRange r) + : ranges(TaskManager::GetNumThreads()) + { + Reset (r); + } + + void Reset (IntRange r) + { + for (size_t i = 0; i < ranges.Size(); i++) + ranges[i].SetNoLock (r.Split(i,ranges.Size())); + + total.store(r.Size(), std::memory_order_relaxed); + participants.store(0, std::memory_order_relaxed); + processed.store(0, std::memory_order_release); + } + + SharedIterator begin() + { + /* + int me = participants++; + if (me < ranges.Size()) + return SharedIterator (ranges, processed, total, me, true); + else + // more participants than buckets. set processed to total, and the loop is terminated immediately + return SharedIterator (ranges, total, total, me, true); + */ + return SharedIterator (ranges, processed, total, TaskManager::GetThreadId(), true); + } + + SharedIterator end() { return SharedIterator (ranges, processed, total, -1, false); } + }; + + + + + + class Partitioning + { + Array part; + size_t total_costs; + public: + Partitioning () { ; } + + template + Partitioning (const Array & apart) { part = apart; } + + template + Partitioning & operator= (const Array & apart) { part = apart; return *this; } + + size_t GetTotalCosts() const { return total_costs; } + + template + void Calc (size_t n, TFUNC costs, int size = task_manager ? task_manager->GetNumThreads() : 1) + { + Array prefix (n); + + /* + size_t sum = 0; + for (auto i : ngstd::Range(n)) + { + sum += costs(i); + prefix[i] = sum; + } + total_costs = sum; + */ + + Array partial_sums(TaskManager::GetNumThreads()+1); + partial_sums[0] = 0; + ParallelJob + ([&] (TaskInfo ti) + { + IntRange r = IntRange(n).Split(ti.task_nr, ti.ntasks); + size_t mysum = 0; + for (size_t i : r) + { + size_t c = costs(i); + mysum += c; + prefix[i] = c; + } + partial_sums[ti.task_nr+1] = mysum; + }); + + for (size_t i = 1; i < partial_sums.Size(); i++) + partial_sums[i] += partial_sums[i-1]; + total_costs = partial_sums.Last(); + + ParallelJob + ([&] (TaskInfo ti) + { + IntRange r = IntRange(n).Split(ti.task_nr, ti.ntasks); + size_t mysum = partial_sums[ti.task_nr]; + for (size_t i : r) + { + mysum += prefix[i]; + prefix[i] = mysum; + } + }); + + + part.SetSize (size+1); + part[0] = 0; + + for (int i = 1; i <= size; i++) + part[i] = BinSearch (prefix, total_costs*i/size); + } + + size_t Size() const { return part.Size()-1; } + IntRange operator[] (size_t i) const { return ngcore::Range(part[i], part[i+1]); } + IntRange Range() const { return ngcore::Range(part[0], part[Size()]); } + + + + + private: + template + int BinSearch(const Tarray & v, size_t i) { + int n = v.Size(); + if (n == 0) return 0; + + int first = 0; + int last = n-1; + if(v[0]>i) return 0; + if(v[n-1] <= i) return n; + while(last-first>1) { + int m = (first+last)/2; + if(v[m] + NETGEN_INLINE void ParallelFor (const Partitioning & part, TFUNC f, int tasks_per_thread = 1) + { + if (task_manager) + { + int ntasks = tasks_per_thread * task_manager->GetNumThreads(); + if (ntasks % part.Size() != 0) + throw Exception ("tasks must be a multiple of part.size"); + + task_manager -> CreateJob + ([&] (TaskInfo & ti) + { + int tasks_per_part = ti.ntasks / part.Size(); + int mypart = ti.task_nr / tasks_per_part; + int num_in_part = ti.task_nr % tasks_per_part; + + auto myrange = part[mypart].Split (num_in_part, tasks_per_part); + for (auto i : myrange) f(i); + }, ntasks); + } + else + { + for (auto i : part.Range()) + f(i); + } + } + + + + + + template + NETGEN_INLINE void ParallelForRange (const Partitioning & part, TFUNC f, + int tasks_per_thread = 1, TotalCosts costs = 1000) + { + if (task_manager && costs() >= 1000) + { + int ntasks = tasks_per_thread * task_manager->GetNumThreads(); + if (ntasks % part.Size() != 0) + throw Exception ("tasks must be a multiple of part.size"); + + task_manager -> CreateJob + ([&] (TaskInfo & ti) + { + int tasks_per_part = ti.ntasks / part.Size(); + int mypart = ti.task_nr / tasks_per_part; + int num_in_part = ti.task_nr % tasks_per_part; + + auto myrange = part[mypart].Split (num_in_part, tasks_per_part); + f(myrange); + }, ntasks); + } + else + { + f(part.Range()); + } + } + + + + + + template + auto ParallelReduce (size_t n, FUNC f, OP op, T initial1) + { + typedef decltype (op(initial1,initial1)) TRES; + TRES initial(initial1); + /* + for (size_t i = 0; i < n; i++) + initial = op(initial, f(i)); + */ + Array part_reduce(TaskManager::GetNumThreads()); + ParallelJob ([&] (TaskInfo ti) + { + auto r = Range(n).Split(ti.task_nr, ti.ntasks); + auto var = initial; + for (auto i : r) + var = op(var, f(i)); + part_reduce[ti.task_nr] = var; + }); + for (auto v : part_reduce) + initial = op(initial, v); + return initial; + } + + + + + + + +// // some suggar for working with arrays +// +// template template +// const FlatArray FlatArray::operator= (ParallelValue val) +// { +// ParallelForRange (Size(), +// [this, val] (IntRange r) +// { +// for (auto i : r) +// (*this)[i] = val; +// }); +// return *this; +// } +// +// template template +// const FlatArray FlatArray::operator= (ParallelFunction func) +// { +// ParallelForRange (Size(), +// [this, func] (IntRange r) +// { +// for (auto i : r) +// (*this)[i] = func(i); +// }); +// return *this; +// } + +class Tasks +{ + size_t num; +public: + explicit Tasks (size_t _num = TaskManager::GetNumThreads()) : num(_num) { ; } + auto GetNum() const { return num; } +}; + + +/* + // some idea, not yet supported + + using namespace std; + template + class ParallelValue + { + T val; + public: + ParallelValue (const T & _val) : val(_val) { ; } + operator T () const { return val; } + }; + + template class ParallelFunction + { + FUNC f; + public: + ParallelFunction (const FUNC & _f) : f(_f) { ; } + operator FUNC () const { return f; } + auto operator() (size_t i) const { return f(i); } + }; +*/ + +/* currently not used, plus causing problems on MSVC 2017 +template ::value, int>::type = 0> +inline ParallelFunction operator| (const T & func, Tasks tasks) +{ + return func; +} + +template ::value, int>::type = 0> +inline ParallelValue operator| (const T & obj, Tasks tasks) +{ + return obj; +} + +inline Tasks operator "" _tasks_per_thread (unsigned long long n) +{ + return Tasks(n * TaskManager::GetNumThreads()); +} +*/ + +/* + thought to be used as: array = 1 | tasks +class DefaultTasks +{ +public: + operator Tasks () const { return TaskManager::GetNumThreads(); } +}; +static DefaultTasks tasks; +*/ + + + + + + + +#ifdef USE_NUMA + +template +class NumaInterleavedArray : public Array +{ + T * numa_ptr; + size_t numa_size; +public: + NumaInterleavedArray () { numa_size = 0; numa_ptr = nullptr; } + NumaInterleavedArray (size_t s) + : Array (s, (T*)numa_alloc_interleaved(s*sizeof(T))) + { + numa_ptr = this->data; + numa_size = s; + } + + ~NumaInterleavedArray () + { + numa_free (numa_ptr, numa_size*sizeof(T)); + } + + NumaInterleavedArray & operator= (T val) + { + Array::operator= (val); + return *this; + } + + NumaInterleavedArray & operator= (NumaInterleavedArray && a2) + { + Array::operator= ((Array&&)a2); + ngcore::Swap (numa_ptr, a2.numa_ptr); + ngcore::Swap (numa_size, a2.numa_size); + return *this; + } + + void Swap (NumaInterleavedArray & b) + { + Array::Swap(b); + ngcore::Swap (numa_ptr, b.numa_ptr); + ngcore::Swap (numa_size, b.numa_size); + } + + void SetSize (size_t size) + { + std::cerr << "************************* NumaDistArray::SetSize not overloaded" << std::endl; + Array::SetSize(size); + } +}; + +template +class NumaDistributedArray : public Array +{ + T * numa_ptr; + size_t numa_size; +public: + NumaDistributedArray () { numa_size = 0; numa_ptr = nullptr; } + NumaDistributedArray (size_t s) + : Array (s, (T*)numa_alloc_local(s*sizeof(T))) + { + numa_ptr = this->data; + numa_size = s; + + /* int avail = */ numa_available(); // initialize libnuma + int num_nodes = numa_num_configured_nodes(); + size_t pagesize = numa_pagesize(); + + int npages = ceil ( double(s)*sizeof(T) / pagesize ); + + // cout << "size = " << numa_size << endl; + // cout << "npages = " << npages << endl; + + for (int i = 0; i < num_nodes; i++) + { + int beg = (i * npages) / num_nodes; + int end = ( (i+1) * npages) / num_nodes; + // cout << "node " << i << " : [" << beg << "-" << end << ")" << endl; + numa_tonode_memory(numa_ptr+beg*pagesize/sizeof(T), (end-beg)*pagesize, i); + } + } + + ~NumaDistributedArray () + { + numa_free (numa_ptr, numa_size*sizeof(T)); + } + + NumaDistributedArray & operator= (NumaDistributedArray && a2) + { + Array::operator= ((Array&&)a2); + ngcore::Swap (numa_ptr, a2.numa_ptr); + ngcore::Swap (numa_size, a2.numa_size); + return *this; + } + + void Swap (NumaDistributedArray & b) + { + Array::Swap(b); + ngcore::Swap (numa_ptr, b.numa_ptr); + ngcore::Swap (numa_size, b.numa_size); + } + + void SetSize (size_t size) + { + std::cerr << "************************* NumaDistArray::SetSize not overloaded" << std::endl; + Array::SetSize(size); + } +}; + + + +template +class NumaLocalArray : public Array +{ + T * numa_ptr; + size_t numa_size; +public: + NumaLocalArray () { numa_size = 0; numa_ptr = nullptr; } + NumaLocalArray (size_t s) + : Array (s, (T*)numa_alloc_local(s*sizeof(T))) + { + numa_ptr = this->data; + numa_size = s; + } + + ~NumaLocalArray () + { + numa_free (numa_ptr, numa_size*sizeof(T)); + } + + NumaLocalArray & operator= (T val) + { + Array::operator= (val); + return *this; + } + + NumaLocalArray & operator= (NumaLocalArray && a2) + { + Array::operator= ((Array&&)a2); + ngcore::Swap (numa_ptr, a2.numa_ptr); + ngcore::Swap (numa_size, a2.numa_size); + return *this; + } + + void Swap (NumaLocalArray & b) + { + Array::Swap(b); + ngcore::Swap (numa_ptr, b.numa_ptr); + ngcore::Swap (numa_size, b.numa_size); + } + + void SetSize (size_t size) + { + std::cerr << "************************* NumaDistArray::SetSize not overloaded" << std::endl; + Array::SetSize(size); + } +}; + + +#else // USE_NUMA + + template + using NumaDistributedArray = Array; + + template + using NumaInterleavedArray = Array; + + template + using NumaLocalArray = Array; + +#endif // USE_NUMA + +} + + + +#endif // NETGEN_CORE_TASKMANAGER_HPP diff --git a/libsrc/core/type_traits.hpp b/libsrc/core/type_traits.hpp index 3863940b..17da6253 100644 --- a/libsrc/core/type_traits.hpp +++ b/libsrc/core/type_traits.hpp @@ -28,6 +28,29 @@ namespace ngcore template constexpr bool is_any_pointer = is_any_pointer_impl::value; } // namespace detail + + + // Type trait to check if a class implements a 'range_type Range()' function + namespace detail + { + template + struct has_Range + { + private: + template + static constexpr auto check(T2*) -> + std::enable_if_t().Range()), void>, std::true_type> + { std::true_type(); } + template + static constexpr std::false_type check(...); + using type = decltype(check(nullptr)); // NOLINT + public: + NGCORE_API static constexpr bool value = type::value; + }; + } + template + constexpr bool has_range = detail::has_Range::value; + } // namespace ngcore #endif // NETGEN_CORE_TYPE_TRAITS_HPP diff --git a/libsrc/core/utils.cpp b/libsrc/core/utils.cpp index 0347d244..062114e6 100644 --- a/libsrc/core/utils.cpp +++ b/libsrc/core/utils.cpp @@ -1,27 +1,90 @@ +#include "ngcore_api.hpp" #include "utils.hpp" #include "logging.hpp" +#include "simd_generic.hpp" #ifndef WIN32 #include #endif +#include +#include #include +#include + +#include "ngstream.hpp" namespace ngcore { + namespace detail + { + // see https://github.com/RobotLocomotion/drake/blob/master/common/nice_type_name.cc + static const auto demangle_regexes = + std::array, 8>{ + // Remove unwanted keywords and following space. (\b is word boundary.) + std::make_pair(std::regex("\\b(class|struct|enum|union) "), ""), + // Tidy up anonymous namespace. + {std::regex("[`(]anonymous namespace[')]"), "(anonymous)"}, + // Replace Microsoft __int64 with long long. + {std::regex("\\b__int64\\b"), "long long"}, + // Temporarily replace spaces we want to keep with "!". (\w is + // alphanumeric or underscore.) + {std::regex("(\\w) (\\w)"), "$1!$2"}, + {std::regex(" "), ""}, // Delete unwanted spaces. + // Some compilers throw in extra namespaces like "__1" or "__cxx11". + // Delete them. + {std::regex("\\b__[[:alnum:]_]+::"), ""}, + {std::regex("!"), " "}, // Restore wanted spaces. + + // Recognize std::string's full name and abbreviate. + {std::regex("\\bstd::basic_string," + "std::allocator>"), "std::string"} + }; + std::string CleanupDemangledName( std::string s ) + { + for(const auto & [r, sub] : demangle_regexes) + s = std::regex_replace (s,r,sub); + + return s; + } + } // namespace detail + // parallel netgen int id = 0, ntasks = 1; #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; + return detail::CleanupDemangledName(name); + } #else - NGCORE_API std::string Demangle(const char* typeinfo) { int status; return abi::__cxa_demangle(typeinfo, - nullptr, - nullptr, - &status); } + NGCORE_API std::string Demangle(const char* typeinfo) + { + int status=0; + try + { + char *s = abi::__cxa_demangle(typeinfo, nullptr, nullptr, &status); + std::string result; + if (s == nullptr) + result = typeinfo; + else + { + result = s; + free(s); + } + result = detail::CleanupDemangledName(result); + return result; + } + catch( const std::exception & e ) + { + GetLogger("utils")->warn("{}:{} cannot demangle {}, status: {}, error:{}", __FILE__, __LINE__, typeinfo, status, e.what()); + } + std::string name = typeinfo; + return detail::CleanupDemangledName(name); + } #endif - double ticks_per_second = [] () noexcept + double seconds_per_tick = [] () noexcept { auto tick_start = GetTimeCounter(); double tstart = WallTime(); @@ -33,10 +96,35 @@ namespace ngcore auto tick_end = GetTimeCounter(); tend = WallTime(); - return (tick_end-tick_start)/(tend-tstart); + return (tend-tstart)/static_cast(tick_end-tick_start); }(); const std::chrono::time_point wall_time_start = TClock::now(); + int printmessage_importance = 0; + bool NGSOStream :: glob_active = true; + + NGCORE_API int GetCompiledSIMDSize() + { + return GetDefaultSIMDSize(); + } + + NGCORE_API bool IsRangeCheckEnabled() + { +#ifdef NETGEN_ENABLE_CHECK_RANGE + return true; +#else + return false; +#endif + } + + NGCORE_API std::filesystem::path GetTempFilename() + { + static int counter = 0; + auto path = std::filesystem::temp_directory_path(); + path += ".temp_netgen_file_"+ToString(counter++)+"_"+ToString(GetTimeCounter()); + return path; + } + } // namespace ngcore diff --git a/libsrc/core/utils.hpp b/libsrc/core/utils.hpp index f2a73578..4f37795c 100644 --- a/libsrc/core/utils.hpp +++ b/libsrc/core/utils.hpp @@ -1,27 +1,42 @@ #ifndef NETGEN_CORE_UTILS_HPP #define NETGEN_CORE_UTILS_HPP +#include #include +#include #include #include #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 { // MPI rank, nranks TODO: Rename - extern NGCORE_API int id, ntasks; + // [[deprecated("don't use global id/ntasks")]] + extern NGCORE_API int id; + // [[deprecated("don't use global id/ntasks")]] + extern NGCORE_API int ntasks; 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)); } @@ -43,11 +58,23 @@ namespace ngcore // High precision clock counter register using TTimePoint = size_t; - extern NGCORE_API double ticks_per_second; + extern NGCORE_API double seconds_per_tick; 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 @@ -58,6 +85,42 @@ namespace ngcore return ss.str(); } + inline std::string ToLower( const std::string & s ) + { + std::string res; + res.reserve(s.size()); + + for(auto & c : s) + res.push_back(tolower(c)); + + return res; + } + + inline std::string ToLower( const std::filesystem::path & p ) + { + return ToLower(p.string()); + } + + + + template + void SaveBin (std::ostream & ost, const T & val) + { + const char * cp = reinterpret_cast (&val); + for (unsigned j = 0; j < sizeof(T); j++) + ost.put(cp[j]); + } + + + template + void LoadBin (std::istream & ist, T & val) + { + char * cp = reinterpret_cast (&val); + for (unsigned j = 0; j < sizeof(T); j++) + ist.get(cp[j]); + } + + template std::ostream& operator << (std::ostream& ost, const std::map& map) { @@ -65,6 +128,202 @@ namespace ngcore ost << "\n" << val.first << ": " << val.second; return ost; } + + template + NETGEN_INLINE void Swap (T & a, T & b) + { + T temp = std::move(a); + a = std::move(b); + b = std::move(temp); + } + + + /// min of 2 values + template + NETGEN_INLINE T min2 (T a, T b) + { + return (a < b) ? a : b; + } + + /// max of 2 values + template + NETGEN_INLINE T max2 (T a, T b) + { + return (a > b) ? a : b; +} + + /// min of 3 values + template + NETGEN_INLINE T min3 (T a, T b, T c) + { + return (a < b) ? (a < c) ? a : c + : (b < c) ? b : c; + } + + /// max of 3 values + template + NETGEN_INLINE T max3 (T a, T b, T c) + { + /// + return (a > b) ? ((a > c) ? a : c) + : ((b > c) ? b : c); + } + + + /// sign of value (+1, 0, -1) + template + NETGEN_INLINE int sgn (T a) + { + return (a > 0) ? 1 : ( ( a < 0) ? -1 : 0 ); + } + + /// square element + template + NETGEN_INLINE T sqr (const T a) + { + return a * a; + } + + /// element to the third power + template + NETGEN_INLINE T pow3 (const T a) + { + return a * a * a; + } + + + + 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; } + + // checks if string starts with sequence + inline bool StartsWith(const std::string& str, const std::string& start) + { + if(start.size() > str.size()) + return false; + return std::equal(start.begin(), start.end(), str.begin()); + } + + // checks if string ends with sequence + inline bool EndsWith(const std::string& str, const std::string& end) + { + if(end.size() > str.size()) + return false; + return std::equal(end.rbegin(), end.rend(), str.rbegin()); + } + + template + NETGEN_INLINE std::atomic & AsAtomic (T & d) + { + return reinterpret_cast&> (d); + } + + NETGEN_INLINE double AtomicAdd( double & sum, double val ) + { + std::atomic & asum = AsAtomic(sum); + double current = asum.load(); + while (!asum.compare_exchange_weak(current, current + val)) + ; + return current; + } + + template + NETGEN_INLINE T AtomicMin( T & minval, T val ) + { + std::atomic & aminval = AsAtomic(minval); + T current = aminval.load(); + while (!aminval.compare_exchange_weak(current, std::min(current, val))) + ; + return current; + } + + template + NETGEN_INLINE T AtomicMax( T & maxval, T val ) + { + std::atomic & amaxval = AsAtomic(maxval); + T current = amaxval.load(); + while (!amaxval.compare_exchange_weak(current, std::max(current, val))) + ; + return current; + } + + + + + template using IC = std::integral_constant; // needed for Iterate + + template + NETGEN_INLINE void Iterate (FUNC f) + { + if constexpr (NUM > 1) Iterate (f); + if constexpr (NUM >= 1) f(IC()); + } + + + template + NETGEN_INLINE void Switch (size_t nr, FUNC f) + { + if (NUM-1 == nr) f(IC()); + if constexpr (NUM > 1) Switch (nr, f); + } + + + + namespace detail + { + template + struct IndexTypeHelper + { + private: + template + static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } + static constexpr size_t check(...); + + public: + using type = decltype(check((T*) nullptr)); // NOLINT + }; + + } // namespace detail + + // Get index type of object. If object has a typedef index_type it is this type, else size_t + template + using index_type = typename detail::IndexTypeHelper::type; + + class MyMutex + { + std::atomic m; + public: + MyMutex() { m.store(false, std::memory_order_relaxed); } + void lock() + { + bool should = false; + while (!m.compare_exchange_weak(should, true)) + { + should = false; +#ifdef NETGEN_ARCH_AMD64 + _mm_pause(); +#endif // NETGEN_ARCH_AMD64 + } + } + void unlock() + { + m = false; + } + }; + + class MyLock + { + MyMutex & mutex; + public: + MyLock (MyMutex & amutex) : mutex(amutex) { mutex.lock(); } + ~MyLock () { mutex.unlock(); } + }; + + NGCORE_API int GetCompiledSIMDSize(); + NGCORE_API bool IsRangeCheckEnabled(); + + NGCORE_API std::filesystem::path GetTempFilename(); + } // namespace ngcore #endif // NETGEN_CORE_UTILS_HPP 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 new file mode 100644 index 00000000..fd0da95e --- /dev/null +++ b/libsrc/core/xbool.hpp @@ -0,0 +1,47 @@ +#ifndef NETGEN_CORE_XBOOL_HPP +#define NETGEN_CORE_XBOOL_HPP + +/**************************************************************************/ +/* File: xbool.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 14. Nov. 07 */ +/**************************************************************************/ + + +namespace ngcore +{ + // an extended bool with values false/maybe/true + + enum TMAYBE { maybe }; + + class xbool + { + uint8_t state; + + public: + xbool (bool b) : state(b ? 2 : 0) { ; } + 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; } + + 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 + +#endif // NETGEN_CORE_XBOOL_HPP diff --git a/libsrc/csg/CMakeLists.txt b/libsrc/csg/CMakeLists.txt index eaf7dffc..d20c12d5 100644 --- a/libsrc/csg/CMakeLists.txt +++ b/libsrc/csg/CMakeLists.txt @@ -1,30 +1,14 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(csg ${NG_LIB_TYPE} +target_sources(nglib PRIVATE algprim.cpp brick.cpp bspline2d.cpp csgeom.cpp csgparser.cpp curve2d.cpp edgeflw.cpp explicitcurve2d.cpp extrusion.cpp gencyl.cpp genmesh.cpp identify.cpp manifold.cpp meshsurf.cpp polyhedra.cpp revolution.cpp singularref.cpp solid.cpp specpoin.cpp spline3d.cpp surface.cpp triapprox.cpp zrefine.cpp python_csg.cpp splinesurface.cpp - ) -if(APPLE) - set_target_properties( csg PROPERTIES SUFFIX ".so") -endif(APPLE) - -target_link_libraries(csg PUBLIC mesh ${PYTHON_LIBRARIES}) -install( TARGETS csg ${NG_INSTALL_DIR}) - -target_link_libraries(csg PUBLIC ngcore) +) if(USE_GUI) - add_library(csgvis ${NG_LIB_TYPE} vscsg.cpp ) - if(NOT WIN32) - target_link_libraries(csgvis csg visual) - if(APPLE) - set_target_properties( csgvis PROPERTIES SUFFIX ".so") - endif(APPLE) - install( TARGETS csgvis ${NG_INSTALL_DIR}) - endif(NOT WIN32) + target_sources(nggui PRIVATE vscsg.cpp csgpkg.cpp) endif(USE_GUI) install(FILES diff --git a/libsrc/csg/algprim.cpp b/libsrc/csg/algprim.cpp index 0f870d98..45a16f9c 100644 --- a/libsrc/csg/algprim.cpp +++ b/libsrc/csg/algprim.cpp @@ -1,4 +1,5 @@ #include +#include #include @@ -116,7 +117,7 @@ namespace netgen void Plane :: GetPrimitiveData (const char *& classname, - Array & coeffs) const + NgArray & coeffs) const { classname = "plane"; coeffs.SetSize (6); @@ -128,7 +129,7 @@ namespace netgen coeffs.Elem(6) = n(2); } - void Plane :: SetPrimitiveData (Array & coeffs) + void Plane :: SetPrimitiveData (NgArray & coeffs) { p(0) = coeffs.Elem(1); p(1) = coeffs.Elem(2); @@ -367,7 +368,7 @@ namespace netgen c1 = (c(0) * c(0) + c(1) * c(1) + c(2) * c(2)) / (2 * r) - r / 2; } - void Sphere :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void Sphere :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "sphere"; coeffs.SetSize (4); @@ -377,7 +378,7 @@ namespace netgen coeffs.Elem(4) = r; } - void Sphere :: SetPrimitiveData (Array & coeffs) + void Sphere :: SetPrimitiveData (NgArray & coeffs) { c(0) = coeffs.Elem(1); c(1) = coeffs.Elem(2); @@ -642,6 +643,32 @@ namespace netgen cz = v(2); } + void Ellipsoid :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const + { + classname = "ellipsoid"; + coeffs.SetSize (12); + for(auto i : Range(3)) + { + coeffs[i] = a(i); + coeffs[3+i] = v1(i); + coeffs[6+i] = v2(i); + coeffs[9+i] = v3(i); + } + } + + void Ellipsoid :: SetPrimitiveData (NgArray & coeffs) + { + for(auto i : Range(3)) + { + a(i) = coeffs[i]; + v1(i) = coeffs[3+i]; + v2(i) = coeffs[6+i]; + v3(i) = coeffs[9+i]; + } + + CalcData(); + } + INSOLID_TYPE Ellipsoid :: BoxInSolid (const BoxSphere<3> & box) const { @@ -731,7 +758,7 @@ namespace netgen - Cylinder :: Cylinder (Array & coeffs) + Cylinder :: Cylinder (NgArray & coeffs) { SetPrimitiveData(coeffs); } @@ -773,7 +800,7 @@ namespace netgen - void Cylinder :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void Cylinder :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "cylinder"; coeffs.SetSize (7); @@ -786,7 +813,7 @@ namespace netgen coeffs.Elem(7) = r; } - void Cylinder :: SetPrimitiveData (Array & coeffs) + void Cylinder :: SetPrimitiveData (NgArray & coeffs) { a(0) = coeffs.Elem(1); a(1) = coeffs.Elem(2); @@ -851,9 +878,11 @@ namespace netgen Vec<3> v1 = b - a; Vec<3> v2 = cyl2->a - a; - if ( fabs (v1 * v2) < (1-eps) * v1.Length() * v2.Length()) return 0; + // if ( fabs (v1 * v2) < (1-1e-12) * v1.Length() * v2.Length()) return 0; + if ( Cross(v1,v2).Length2() > 1e-20 * v1.Length2() * v2.Length2()) return 0; v2 = cyl2->b - a; - if ( fabs (v1 * v2) < (1-eps) * v1.Length() * v2.Length()) return 0; + // if ( fabs (v1 * v2) < (1-eps) * v1.Length() * v2.Length()) return 0; + if ( Cross(v1,v2).Length2() > 1e-20 * v1.Length2() * v2.Length2()) return 0; inv = 0; return 1; @@ -1127,14 +1156,14 @@ namespace netgen CalcData(); } - EllipticCylinder :: EllipticCylinder (Array & coeffs) + EllipticCylinder :: EllipticCylinder (NgArray & coeffs) { SetPrimitiveData(coeffs); } - void EllipticCylinder :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void EllipticCylinder :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "ellipticcylinder"; coeffs.SetSize (9); @@ -1149,7 +1178,7 @@ namespace netgen coeffs[8] = vs(2); } - void EllipticCylinder :: SetPrimitiveData (Array & coeffs) + void EllipticCylinder :: SetPrimitiveData (NgArray & coeffs) { a(0) = coeffs[0]; a(1) = coeffs[1]; @@ -1312,7 +1341,7 @@ namespace netgen - void Cone :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void Cone :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "cone"; coeffs.SetSize (8); @@ -1326,7 +1355,7 @@ namespace netgen coeffs.Elem(8) = rb; } - void Cone :: SetPrimitiveData (Array & coeffs) + void Cone :: SetPrimitiveData (NgArray & coeffs) { a(0) = coeffs.Elem(1); a(1) = coeffs.Elem(2); @@ -1537,10 +1566,10 @@ Primitive * EllipticCone :: CreateDefault () } - void EllipticCone :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void EllipticCone :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "ellipticcone"; - coeffs.SetSize (15); + coeffs.SetSize (11); coeffs.Elem(1) = a(0); coeffs.Elem(2) = a(1); coeffs.Elem(3) = a(2); @@ -1556,7 +1585,7 @@ Primitive * EllipticCone :: CreateDefault () } - void EllipticCone :: SetPrimitiveData (Array & coeffs) + void EllipticCone :: SetPrimitiveData (NgArray & coeffs) { a(0) = coeffs.Elem(1); @@ -1727,7 +1756,7 @@ void EllipticCone :: GetTriangleApproximation r = ar; } - void Torus :: GetPrimitiveData (const char *& classname, Array & coeffs) const + void Torus :: GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "torus"; coeffs.SetSize (8); @@ -1741,7 +1770,7 @@ void EllipticCone :: GetTriangleApproximation coeffs.Elem(8) = r; } - void Torus :: SetPrimitiveData (Array & coeffs) + void Torus :: SetPrimitiveData (NgArray & coeffs) { c(0) = coeffs.Elem(1); c(1) = coeffs.Elem(2); diff --git a/libsrc/csg/algprim.hpp b/libsrc/csg/algprim.hpp index 6daf643b..e32b0eee 100644 --- a/libsrc/csg/algprim.hpp +++ b/libsrc/csg/algprim.hpp @@ -80,8 +80,8 @@ namespace netgen Point<3> P() const { return p; } Vec<3> N() const { return n; } virtual void GetPrimitiveData (const char *& classname, - Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); static Primitive * CreateDefault (); virtual Primitive * Copy () const; @@ -153,8 +153,8 @@ namespace netgen } virtual void GetPrimitiveData (const char *& classname, - Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); static Primitive * CreateDefault (); virtual Primitive * Copy () const; @@ -208,7 +208,7 @@ namespace netgen public: Cylinder (const Point<3> & aa, const Point<3> & ab, double ar); - Cylinder (Array & coeffs); + Cylinder (NgArray & coeffs); // default constructor for archive Cylinder() {} @@ -220,8 +220,8 @@ namespace netgen Point<3> A() const { return a; } Point<3> B() const { return b; } double R() const { return r; } - virtual void GetPrimitiveData (const char *& classname, Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); static Primitive * CreateDefault (); virtual Primitive * Copy () const; @@ -278,7 +278,7 @@ namespace netgen /// EllipticCylinder (const Point<3> & aa, const Vec<3> & avl, const Vec<3> & avs); - EllipticCylinder (Array & coeffs); + EllipticCylinder (NgArray & coeffs); // default constructor for archive EllipticCylinder() {} @@ -289,8 +289,8 @@ namespace netgen } // static Primitive * CreateDefault (); - virtual void GetPrimitiveData (const char *& classname, Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); /// virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; @@ -339,23 +339,26 @@ namespace netgen // default constructor for archive Ellipsoid() {} - virtual void DoArchive(Archive& ar) + void DoArchive(Archive& ar) override { QuadraticSurface::DoArchive(ar); ar & a & v1 & v2 & v3 & rmin; } /// - virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; + INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const override; /// - virtual double HesseNorm () const; + double HesseNorm () const override; /// - virtual double MaxCurvature () const; + double MaxCurvature () const override; /// - virtual Point<3> GetSurfacePoint () const; + Point<3> GetSurfacePoint () const override; - virtual void GetTriangleApproximation (TriangleApproximation & tas, + void GetTriangleApproximation (TriangleApproximation & tas, const Box<3> & bbox, - double facets) const; + double facets) const override; + + void GetPrimitiveData (const char *& classname, NgArray & coeffs) const override; + void SetPrimitiveData (NgArray & coeffs) override; private: void CalcData(); @@ -393,8 +396,8 @@ namespace netgen ar & a & b & ra & rb & minr & vab & t0vec & t1vec & vabl & t0 & t1 & cosphi; } static Primitive * CreateDefault (); - virtual void GetPrimitiveData (const char *& classname, Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); /// virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; @@ -445,8 +448,8 @@ namespace netgen ar & a & vl & vs & h & vlr; } static Primitive * CreateDefault (); - virtual void GetPrimitiveData (const char *& classname, Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); /// virtual INSOLID_TYPE BoxInSolid (const BoxSphere<3> & box) const; @@ -513,9 +516,9 @@ namespace netgen virtual Point<3> GetSurfacePoint () const; /// OK virtual void GetPrimitiveData (const char *& classname, - Array & coeffs) const; + NgArray & coeffs) const; /// OK - virtual void SetPrimitiveData (Array & coeffs); + virtual void SetPrimitiveData (NgArray & coeffs); /// OK static Primitive * CreateDefault (); /// OK diff --git a/libsrc/csg/brick.cpp b/libsrc/csg/brick.cpp index b9508fba..0c1c6546 100644 --- a/libsrc/csg/brick.cpp +++ b/libsrc/csg/brick.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -343,7 +344,7 @@ INSOLID_TYPE Brick :: VecInSolid4 (const Point<3> & p, void Brick :: -GetPrimitiveData (const char *& classname, Array & coeffs) const +GetPrimitiveData (const char *& classname, NgArray & coeffs) const { classname = "brick"; coeffs.SetSize(12); @@ -364,7 +365,7 @@ GetPrimitiveData (const char *& classname, Array & coeffs) const coeffs.Elem(12) = p4(2); } -void Brick :: SetPrimitiveData (Array & coeffs) +void Brick :: SetPrimitiveData (NgArray & coeffs) { p1(0) = coeffs.Elem(1); p1(1) = coeffs.Elem(2); @@ -414,7 +415,7 @@ void Brick :: CalcData() { 1, 5, 3, 7 }, { 2, 4, 6, 8 } }; - Array data(6); + NgArray data(6); for (i = 0; i < 6; i++) { const Point<3> lp1 = pi[lface[i][0]-1]; diff --git a/libsrc/csg/brick.hpp b/libsrc/csg/brick.hpp index 7db7b02d..68bf13be 100644 --- a/libsrc/csg/brick.hpp +++ b/libsrc/csg/brick.hpp @@ -63,8 +63,8 @@ namespace netgen { Point<3> p1, p2, p3, p4; Vec<3> v12, v13, v14; - // Array faces; - Array faces; + // NgArray faces; + NgArray faces; public: Brick (Point<3> ap1, Point<3> ap2, Point<3> ap3, Point<3> ap4); @@ -115,8 +115,8 @@ namespace netgen { return *faces[i]; } - virtual void GetPrimitiveData (const char *& classname, Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + virtual void GetPrimitiveData (const char *& classname, NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); virtual void Reduce (const BoxSphere<3> & box); virtual void UnReduce (); diff --git a/libsrc/csg/csgeom.cpp b/libsrc/csg/csgeom.cpp index 4aebe271..2d5dcfef 100644 --- a/libsrc/csg/csgeom.cpp +++ b/libsrc/csg/csgeom.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -72,10 +73,97 @@ namespace netgen Clean(); } + PointGeomInfo CSGeometry :: ProjectPoint(int surfind, Point<3> & p) const + { + Point<3> hp = p; + GetSurface(surfind)->Project (hp); + p = hp; + return PointGeomInfo(); + } + + bool CSGeometry :: ProjectPointGI(int surfind, Point<3> & p, PointGeomInfo & gi) const + { + GetSurface(surfind)->Project (p); + return true; + } + + void CSGeometry :: ProjectPointEdge(int surfind, INDEX surfind2, + Point<3> & p, EdgePointGeomInfo* /*unused*/) const + { + Point<3> hp = p; + ProjectToEdge (GetSurface(surfind), + GetSurface(surfind2), hp); + p = hp; + } + + + Vec<3> CSGeometry :: GetNormal(int surfind, const Point<3> & p, + const PointGeomInfo* /*unused*/) const + { + Vec<3> hn; + GetSurface(surfind)->CalcGradient(p, hn); + hn.Normalize(); + return hn; + } + + void CSGeometry :: + 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 + { + Point<3> hnewp; + hnewp = p1+secpoint*(p2-p1); + if (surfi != -1) + { + GetSurface (surfi) -> Project (hnewp); + newgi.trignum = 1; + } + + newp = hnewp; + } + + void CSGeometry :: 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 + { + Point<3> hnewp = p1+secpoint*(p2-p1); + + //(*testout) << "hnewp " << hnewp << " s1 " << surfi1 << " s2 " << surfi2 << endl; + if (surfi1 != -1 && surfi2 != -1 && surfi1 != surfi2) + { + netgen::ProjectToEdge (GetSurface(surfi1), + GetSurface(surfi2), + hnewp); + // (*testout) << "Pointbetween, newp = " << hnewp << endl + // << ", err = " << sqrt (sqr (hnewp(0))+ sqr(hnewp(1)) + sqr (hnewp(2))) - 1 << endl; + newgi.edgenr = 1; + //(*testout) << "hnewp (a1) " << hnewp << endl; + } + else if (surfi1 != -1) + { + GetSurface (surfi1) -> Project (hnewp); + //(*testout) << "hnewp (a2) " << hnewp << endl; + } + + newp = hnewp; + }; + + Vec<3> CSGeometry :: GetTangent(const Point<3> & p, int surfi1, int surfi2, + const EdgePointGeomInfo & ap1) const + { + Vec<3> n1 = GetSurface (surfi1)->GetNormalVector (p); + Vec<3> n2 = GetSurface (surfi2)->GetNormalVector (p); + Vec<3> tau = Cross (n1, n2).Normalize(); + return tau; + } void CSGeometry :: Clean () { - Array< Solid* > to_delete; + NgArray< Solid* > to_delete; for (int i = 0; i < solids.Size(); i++) if(!to_delete.Contains(solids[i]->S1())) @@ -89,9 +177,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++) @@ -134,18 +221,18 @@ namespace netgen int CSGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) { - return CSGGenerateMesh (*this, mesh, mparam); + if(restricted_h.Size()) + { + // copy so that we don't change mparam outside + MeshingParameters mp = mparam; + for(const auto& [pnt, maxh] : restricted_h) + mp.meshsize_points.Append({pnt, maxh}); + return CSGGenerateMesh (*this, mesh, mp); + } + else + return CSGGenerateMesh (*this, mesh, mparam); } - const Refinement & CSGeometry :: GetRefinement () const - { - // cout << "get CSGeometry - Refinement" << endl; - // should become class variables - RefinementSurfaces * ref = new RefinementSurfaces(*this); - ref -> Set2dOptimizer(new MeshOptimize2dSurfaces(*this)); - return *ref; - } - class WritePrimitivesIt : public SolidIterator { ostream & ost; @@ -162,7 +249,7 @@ namespace netgen if (prim) { const char * classname; - Array coeffs; + NgArray coeffs; prim -> GetPrimitiveData (classname, coeffs); @@ -177,7 +264,7 @@ namespace netgen } - void CSGeometry :: Save (string filename) const + void CSGeometry :: Save (const filesystem::path & filename) const { ofstream ost (filename.c_str()); Save (ost); @@ -235,9 +322,9 @@ namespace netgen { // CSGeometry * geo = new CSGeometry; - char key[100], name[100], classname[100], sname[100]; + char key[100], name[100], classname[100], sname[150]; int ncoeff, i, j; - Array coeff; + NgArray coeff; while (ist.good()) { @@ -262,7 +349,7 @@ namespace netgen for (j = 0; j < nprim->GetNSurfaces(); j++) { - sprintf (sname, "%s,%d", name, j); + snprintf (sname, size(sname), "%s,%d", name, j); AddSurface (sname, &nprim->GetSurface(j)); nprim -> SetSurfaceId (j, GetNSurf()); } @@ -330,7 +417,7 @@ namespace netgen & identpoints & boundingbox & isidenticto & ideps & filename & spline_surfaces & splinecurves2d & splinecurves3d & surf2prim; if(archive.Input()) - FindIdenticSurfaces(1e-6); + FindIdenticSurfaces(1e-8 * MaxSize()); } void CSGeometry :: SaveSurfaces (ostream & out) const @@ -343,7 +430,7 @@ namespace netgen - Array coeffs; + NgArray coeffs; const char * classname; out << "csgsurfaces " << GetNSurf() << "\n"; @@ -408,7 +495,7 @@ namespace netgen void CSGeometry :: LoadSurfaces (istream & in) { - Array coeffs; + NgArray coeffs; string classname; int nsurfaces,size; @@ -485,6 +572,15 @@ namespace netgen delete_them.Append(cone); } + else if(classname == "ellipsoid") + { + Ellipsoid * ellipsoid = new Ellipsoid(dummypoint,dummyvec,dummyvec,dummyvec); + ellipsoid->SetPrimitiveData(coeffs); + + AddSurface(ellipsoid); + delete_them.Append(ellipsoid); + } + else if(classname == "ellipticcone") { EllipticCone * ellipticcone = new EllipticCone(dummypoint,dummyvec,dummyvec,dummydouble,dummydouble); @@ -536,8 +632,8 @@ namespace netgen { static int cntsurfs = 0; cntsurfs++; - char name[15]; - sprintf (name, "nnsurf%d", cntsurfs); + char name[20]; + snprintf (name, size(name), "nnsurf%d", cntsurfs); AddSurface (name, surf); } @@ -625,24 +721,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]; @@ -722,7 +818,7 @@ namespace netgen void CSGeometry :: SetFlags (const char * solidname, const Flags & flags) { Solid * solid = solids[solidname]; - Array surfind; + NgArray surfind; int i; double maxh = flags.GetNumFlag ("maxh", -1); @@ -752,7 +848,7 @@ namespace netgen if (flags.StringListFlagDefined ("bcname")) { - const Array & bcname = flags.GetStringListFlag("bcname"); + auto& bcname = flags.GetStringListFlag("bcname"); Polyhedra * polyh; if(solid->S1()) @@ -762,7 +858,7 @@ namespace netgen if(polyh) { - Array < Array * > polysurfs; + NgArray < NgArray * > polysurfs; polyh->GetPolySurfs(polysurfs); if(bcname.Size() != polysurfs.Size()) cerr << "WARNING: solid \"" << solidname << "\" has " << polysurfs.Size() @@ -806,7 +902,7 @@ namespace netgen if (flags.NumListFlagDefined ("bc")) { - const Array & bcnum = flags.GetNumListFlag("bc"); + const auto& bcnum = flags.GetNumListFlag("bc"); Polyhedra * polyh; if(solid->S1()) @@ -816,7 +912,7 @@ namespace netgen if(polyh) { - Array < Array * > polysurfs; + NgArray < NgArray * > polysurfs; polyh->GetPolySurfs(polysurfs); if(bcnum.Size() != polysurfs.Size()) cerr << "WARNING: solid \"" << solidname << "\" has " << polysurfs.Size() @@ -853,6 +949,7 @@ namespace netgen { int inv; int nsurf = GetNSurf(); + identicsurfaces.DeleteData(); isidenticto.SetSize(nsurf); @@ -880,7 +977,7 @@ namespace netgen void CSGeometry :: GetSurfaceIndices (const Solid * sol, const BoxSphere<3> & box, - Array & locsurf) const + NgArray & locsurf) const { ReducePrimitiveIterator rpi(box); UnReducePrimitiveIterator urpi; @@ -909,7 +1006,7 @@ namespace netgen void CSGeometry :: GetIndependentSurfaceIndices (const Solid * sol, const BoxSphere<3> & box, - Array & locsurf) const + NgArray & locsurf) const { ReducePrimitiveIterator rpi(box); UnReducePrimitiveIterator urpi; @@ -968,7 +1065,7 @@ namespace netgen void CSGeometry :: GetIndependentSurfaceIndices (const Solid * sol, const Point<3> & p, Vec<3> & v, - Array & locsurf) const + NgArray & locsurf) const { cout << "very dangerous" << endl; Point<3> p2 = p + 1e-2 * v; @@ -980,7 +1077,7 @@ namespace netgen */ void CSGeometry :: - GetIndependentSurfaceIndices (Array & locsurf) const + GetIndependentSurfaceIndices (NgArray & locsurf) const { for (int i = 0; i < locsurf.Size(); i++) locsurf[i] = isidenticto[locsurf[i]]; @@ -1022,7 +1119,7 @@ namespace netgen delete triapprox[i]; triapprox.SetSize (ntlo); - Array surfind; + NgArray surfind; IndexSet iset(GetNSurf()); for (int i = 0; i < ntlo; i++) @@ -1148,7 +1245,7 @@ namespace netgen //return; int pinds[6]; - ArrayMem surfused(GetNSurf()); + NgArrayMem surfused(GetNSurf()); ReducePrimitiveIterator rpi(box); UnReducePrimitiveIterator urpi; @@ -1159,7 +1256,7 @@ namespace netgen // IndexSet iset(GetNSurf()); locsol -> GetSurfaceIndices (iset); - const Array & lsurfi = iset.GetArray(); + const NgArray & lsurfi = iset.GetArray(); locsol -> IterateSolid (urpi); @@ -1523,21 +1620,21 @@ namespace netgen class CSGeometryRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const; - virtual NetgenGeometry * LoadFromMeshFile (istream & ist) const; + virtual NetgenGeometry * Load (const filesystem::path & filename) const; + virtual NetgenGeometry * LoadFromMeshFile (istream & ist, string token) const; // virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; }; extern CSGeometry * ParseCSG (istream & istr, CSGeometry *instance=nullptr); - NetgenGeometry * CSGeometryRegister :: Load (string filename) const + NetgenGeometry * CSGeometryRegister :: Load (const filesystem::path & filename) const { - const char * cfilename = filename.c_str(); - if (strcmp (&cfilename[strlen(cfilename)-3], "geo") == 0) + string extension = filename.extension().string(); + if (extension == ".geo") { - PrintMessage (1, "Load CSG geometry file ", cfilename); + PrintMessage (1, "Load CSG geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); CSGeometry * hgeom = ParseCSG (infile); if (!hgeom) @@ -1547,38 +1644,28 @@ namespace netgen return hgeom; } - if (strcmp (&cfilename[strlen(cfilename)-3], "ngg") == 0) + if (extension == ".ngg") { - PrintMessage (1, "Load new CSG geometry file ", cfilename); + PrintMessage (1, "Load new CSG geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); CSGeometry * hgeom = new CSGeometry(""); hgeom -> Load (infile); return hgeom; } - - return NULL; } - NetgenGeometry * CSGeometryRegister :: LoadFromMeshFile (istream & ist) const + NetgenGeometry * CSGeometryRegister :: LoadFromMeshFile (istream & ist, string token) const { - string auxstring; - if (ist.good()) - { - ist >> auxstring; - if (auxstring == "csgsurfaces") - { - CSGeometry * geometry = new CSGeometry (""); - geometry -> LoadSurfaces(ist); - return geometry; - } - // else - // ist.putback (auxstring); - } - return NULL; + if (token != "csgsurfaces") + return nullptr; + + CSGeometry * geometry = new CSGeometry (""); + geometry -> LoadSurfaces(ist); + return geometry; } diff --git a/libsrc/csg/csgeom.hpp b/libsrc/csg/csgeom.hpp index 40d90d51..63de9443 100644 --- a/libsrc/csg/csgeom.hpp +++ b/libsrc/csg/csgeom.hpp @@ -106,47 +106,50 @@ namespace netgen public: /// primitive of surface - Array surf2prim; + NgArray surf2prim; private: - Array delete_them; + NgArray delete_them; /// all named solids 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 - Array toplevelobjects; + NgArray toplevelobjects; public: /// additional points specified by user class UserPoint : public Point<3> { int index; + string name; public: UserPoint() = default; UserPoint (Point<3> p, int _index) : Point<3>(p), index(_index) { ; } + UserPoint (Point<3> p, const string & _name) : Point<3>(p), index(-1), name(_name) { ; } int GetIndex() const { return index; } + const string & GetName() const { return name; } void DoArchive(Archive& archive) { - archive & index; + archive & index & name; Point<3>::DoArchive(archive); } }; private: - // Array > userpoints; - Array userpoints; - Array userpoints_ref_factor; + // NgArray > userpoints; + NgArray userpoints; + NgArray userpoints_ref_factor; - mutable Array > identpoints; + mutable NgArray > identpoints; /// triangular approximation of top level objects - Array triapprox; + NgArray triapprox; /// increment, if geometry is changed static int changeval; @@ -159,7 +162,7 @@ namespace netgen /// identic surfaces are stored by pair of indizes, val = inverse INDEX_2_HASHTABLE identicsurfaces; - Array isidenticto; + NgArray isidenticto; /// identification of boundaries (periodic, thin domains, ...) double ideps; @@ -168,8 +171,10 @@ namespace netgen string filename; /// store splinesurfaces, such that added ones do not get deleted before geometry does - Array> spline_surfaces; + NgArray> spline_surfaces; + shared_ptr solid_ball = Solid::ball; + public: CSGeometry (); CSGeometry (const string & afilename); @@ -177,7 +182,7 @@ namespace netgen void Clean (); - virtual void Save (string filename) const override; + virtual void Save (const filesystem::path & filename) const override; void Save (ostream & ost) const; void Load (istream & ist); @@ -186,6 +191,27 @@ namespace netgen virtual void SaveToMeshFile (ostream & ost) const override; + PointGeomInfo ProjectPoint(INDEX surfind, Point<3> & p) const override; + bool ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const override; + void ProjectPointEdge(INDEX surfind, INDEX surfind2, Point<3> & p, + EdgePointGeomInfo* gi = nullptr) const override; + Vec<3> GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi = nullptr) const override; + + 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; + + 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; + + Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2, + const EdgePointGeomInfo & ap1) const override; + int GetChangeVal() { return changeval; } void Change() { changeval++; } @@ -206,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; @@ -261,10 +287,10 @@ namespace netgen // quick implementations: - Array singfaces; - Array singedges; - Array singpoints; - Array identifications; + NgArray singfaces; + NgArray singedges; + NgArray singpoints; + NgArray identifications; int GetNIdentifications (void) const { return identifications.Size(); } void AddIdentification (Identification * ident); @@ -278,19 +304,19 @@ namespace netgen /// void GetSurfaceIndices (const Solid * sol, const BoxSphere<3> & box, - Array & locsurf) const; + NgArray & locsurf) const; /// void GetIndependentSurfaceIndices (const Solid * sol, const BoxSphere<3> & box, - Array & locsurf) const; + NgArray & locsurf) const; /// /* void GetIndependentSurfaceIndices (const Solid * sol, const Point<3> & p, Vec<3> & v, - Array & locsurf) const; + NgArray & locsurf) const; */ /// - void GetIndependentSurfaceIndices (Array & locsurf) const; + void GetIndependentSurfaceIndices (NgArray & locsurf) const; /// int GetSurfaceClassRepresentant (int si) const @@ -342,12 +368,15 @@ namespace netgen string * bcname; }; - Array bcmodifications; + NgArray bcmodifications; + + map, string> named_edges; + + + virtual int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) override; - virtual const Refinement & GetRefinement () const override; - void AddSplineSurface (shared_ptr ss) { spline_surfaces.Append(ss); } }; diff --git a/libsrc/csg/csgparser.cpp b/libsrc/csg/csgparser.cpp index c202c9fd..1666a2ab 100644 --- a/libsrc/csg/csgparser.cpp +++ b/libsrc/csg/csgparser.cpp @@ -399,7 +399,7 @@ namespace netgen int inputface = 0; while (1) { - Array pnums,cleaned_pnums; + NgArray pnums,cleaned_pnums; for(int i=0; i<3; i++) { pnums.Append((int) (ParseNumber (scan))); @@ -479,7 +479,7 @@ namespace netgen } Primitive * nprim = new Revolution(p0,p1, - *(geom->GetSplineCurve2d(spline))); + geom->GetSplineCurve2d(spline)); geom->AddSurfaces (nprim); return new Solid(nprim); @@ -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); @@ -794,28 +794,22 @@ namespace netgen vals.Append (ParseNumber(scan)); } ParseChar (scan, ']'); - flags.SetFlag (name.c_str(), vals); + flags.SetFlag (name, vals); } else { // string list - Array vals; - string val = scan.GetStringValue(); - vals.Append(new char[val.size()+1]); - strcpy(vals.Last(),val.c_str()); + Array vals; + vals.Append(scan.GetStringValue()); scan.ReadNext(); while (scan.GetToken() == ',') { scan.ReadNext(); - val = scan.GetStringValue(); - vals.Append(new char[val.size()+1]); - strcpy(vals.Last(),val.c_str()); + vals.Append(scan.GetStringValue()); scan.ReadNext(); } ParseChar (scan, ']'); - flags.SetFlag (name.c_str(), vals); - for(int i=0; i si; + NgArray si; geom->GetSolid(surfname)->GetSurfaceIndices(si); int tlonr = geom->SetTopLevelObject ((Solid*)geom->GetSolid(name), @@ -942,8 +936,8 @@ namespace netgen TopLevelObject * tlo = geom->GetTopLevelObject (tlonr); if (flags.NumListFlagDefined ("col")) { - const Array & col = flags.GetNumListFlag ("col"); - tlo->SetRGB (col.Get(1), col.Get(2), col.Get(3)); + const auto& col = flags.GetNumListFlag ("col"); + tlo->SetRGB (col[0], col[1], col[2]); } if (flags.GetDefineFlag ("transparent")) tlo->SetTransparent (1); @@ -980,7 +974,7 @@ namespace netgen ParseChar (scan, ';'); - Array si1, si2; + NgArray si1, si2; geom->GetSolid(name1)->GetSurfaceIndices(si1); geom->GetSolid(name2)->GetSurfaceIndices(si2); @@ -1016,7 +1010,7 @@ namespace netgen ParseChar (scan, ';'); - Array si1, si2; + NgArray si1, si2; geom->GetSolid(name1)->GetSurfaceIndices(si1); geom->GetSolid(name2)->GetSurfaceIndices(si2); @@ -1192,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); @@ -1218,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); @@ -1246,7 +1240,7 @@ namespace netgen CSGeometry::BCModification bcm; bcm.bcname = NULL; - Array si; + NgArray si; geom->GetSolid(name1)->GetSurfaceIndices(si); if(si.Size() == 0) @@ -1298,7 +1292,7 @@ namespace netgen bcm.bcname = NULL; - Array si; + NgArray si; geom->GetSolid(name1)->GetSurfaceIndices(si); if(si.Size() == 0) diff --git a/libsrc/csg/csgpkg.cpp b/libsrc/csg/csgpkg.cpp index dcea1258..33922bcd 100644 --- a/libsrc/csg/csgpkg.cpp +++ b/libsrc/csg/csgpkg.cpp @@ -17,7 +17,6 @@ extern "C" int Ng_CSG_Init (Tcl_Interp * interp); namespace netgen { - // extern DLL_HEADER NetgenGeometry * ng_geometry; extern DLL_HEADER shared_ptr ng_geometry; extern DLL_HEADER shared_ptr mesh; @@ -66,18 +65,18 @@ namespace netgen Point3d pmin = geometry->BoundingBox ().PMin(); Point3d pmax = geometry->BoundingBox ().PMax(); - sprintf (buf, "%5.1lf", pmin.X()); + snprintf (buf, size(buf), "%5.1lf", pmin.X()); Tcl_SetVar (interp, "::geooptions.minx", buf, 0); - sprintf (buf, "%5.1lf", pmin.Y()); + snprintf (buf, size(buf), "%5.1lf", pmin.Y()); Tcl_SetVar (interp, "::geooptions.miny", buf, 0); - sprintf (buf, "%5.1lf", pmin.Z()); + snprintf (buf, size(buf), "%5.1lf", pmin.Z()); Tcl_SetVar (interp, "::geooptions.minz", buf, 0); - sprintf (buf, "%5.1lf", pmax.X()); + snprintf (buf, size(buf), "%5.1lf", pmax.X()); Tcl_SetVar (interp, "::geooptions.maxx", buf, 0); - sprintf (buf, "%5.1lf", pmax.Y()); + snprintf (buf, size(buf), "%5.1lf", pmax.Y()); Tcl_SetVar (interp, "::geooptions.maxy", buf, 0); - sprintf (buf, "%5.1lf", pmax.Z()); + snprintf (buf, size(buf), "%5.1lf", pmax.Z()); Tcl_SetVar (interp, "::geooptions.maxz", buf, 0); } } @@ -154,7 +153,7 @@ namespace netgen tcl_const char * name = argv[1]; tcl_const char * value = argv[2]; - Array coeffs; + NgArray coeffs; cout << "Set primitive data, name = " << name @@ -222,7 +221,7 @@ namespace netgen const char * classname; - Array coeffs; + NgArray coeffs; geometry->GetSolid (name)->GetPrimitive()->GetPrimitiveData (classname, coeffs); @@ -433,17 +432,17 @@ namespace netgen if (!tlo) return TCL_OK; char varname[50]; - sprintf (varname, "%s(red)", propvar); + snprintf (varname, size(varname), "%s(red)", propvar); double red = atof (Tcl_GetVar (interp, varname, 0)); - sprintf (varname, "%s(blue)", propvar); + snprintf (varname, size(varname), "%s(blue)", propvar); double blue = atof (Tcl_GetVar (interp, varname, 0)); - sprintf (varname, "%s(green)", propvar); + snprintf (varname, size(varname), "%s(green)", propvar); double green = atof (Tcl_GetVar (interp, varname, 0)); tlo -> SetRGB (red, green, blue); - sprintf (varname, "%s(visible)", propvar); + snprintf (varname, size(varname), "%s(visible)", propvar); tlo -> SetVisible (bool(atoi (Tcl_GetVar (interp, varname, 0)))); - sprintf (varname, "%s(transp)", propvar); + snprintf (varname, size(varname), "%s(transp)", propvar); tlo -> SetTransparent (bool(atoi (Tcl_GetVar (interp, varname, 0)))); } @@ -461,24 +460,24 @@ namespace netgen char varname[50], varval[10]; - sprintf (varname, "%s(red)", propvar); - sprintf (varval, "%lf", tlo->GetRed()); + snprintf (varname, size(varname), "%s(red)", propvar); + snprintf (varval, size(varval), "%lf", tlo->GetRed()); Tcl_SetVar (interp, varname, varval, 0); - sprintf (varname, "%s(green)", propvar); - sprintf (varval, "%lf", tlo->GetGreen()); + snprintf (varname, size(varname), "%s(green)", propvar); + snprintf (varval, size(varval), "%lf", tlo->GetGreen()); Tcl_SetVar (interp, varname, varval, 0); - sprintf (varname, "%s(blue)", propvar); - sprintf (varval, "%lf", tlo->GetBlue()); + snprintf (varname, size(varname), "%s(blue)", propvar); + snprintf (varval, size(varval), "%lf", tlo->GetBlue()); Tcl_SetVar (interp, varname, varval, 0); - sprintf (varname, "%s(visible)", propvar); - sprintf (varval, "%d", tlo->GetVisible()); + snprintf (varname, size(varname), "%s(visible)", propvar); + snprintf (varval, size(varval), "%d", tlo->GetVisible()); Tcl_SetVar (interp, varname, varval, 0); - sprintf (varname, "%s(transp)", propvar); - sprintf (varval, "%d", tlo->GetTransparent()); + snprintf (varname, size(varname), "%s(transp)", propvar); + snprintf (varval, size(varval), "%d", tlo->GetTransparent()); Tcl_SetVar (interp, varname, varval, 0); } @@ -547,86 +546,10 @@ namespace netgen } - /* - class CSGeometryRegister : public GeometryRegister - { - public: - virtual NetgenGeometry * Load (string filename) const; - virtual NetgenGeometry * LoadFromMeshFile (istream & ist) const; - virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; - }; - - extern CSGeometry * ParseCSG (istream & istr); - - NetgenGeometry * CSGeometryRegister :: Load (string filename) const - { - const char * cfilename = filename.c_str(); - if (strcmp (&cfilename[strlen(cfilename)-3], "geo") == 0) - { - PrintMessage (1, "Load CSG geometry file ", cfilename); - - ifstream infile(cfilename); - - CSGeometry * hgeom = ParseCSG (infile); - if (!hgeom) - throw NgException ("geo-file should start with 'algebraic3d'"); - - hgeom -> FindIdenticSurfaces(1e-8 * hgeom->MaxSize()); - return hgeom; - } - - if (strcmp (&cfilename[strlen(cfilename)-3], "ngg") == 0) - { - PrintMessage (1, "Load new CSG geometry file ", cfilename); - - ifstream infile(cfilename); - CSGeometry * hgeom = new CSGeometry(""); - hgeom -> Load (infile); - - return hgeom; - } - - - - return NULL; - } - - NetgenGeometry * CSGeometryRegister :: LoadFromMeshFile (istream & ist) const - { - string auxstring; - if (ist.good()) - { - ist >> auxstring; - if (auxstring == "csgsurfaces") - { - CSGeometry * geometry = new CSGeometry (""); - geometry -> LoadSurfaces(ist); - return geometry; - } - // else - // ist.putback (auxstring); - } - return NULL; - } - - VisualScene * CSGeometryRegister :: GetVisualScene (const NetgenGeometry * geom) const - { - CSGeometry * geometry = dynamic_cast (ng_geometry.get()); - if (geometry) - { - vsgeom.SetGeometry (geometry); - return &vsgeom; - } - return NULL; - } - */ - - - class CSGeometryVisRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const { return NULL; } + virtual NetgenGeometry * Load (const filesystem::path & filename) const { return NULL; } virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; }; diff --git a/libsrc/csg/curve2d.hpp b/libsrc/csg/curve2d.hpp index 066a40ad..e9a3690c 100644 --- a/libsrc/csg/curve2d.hpp +++ b/libsrc/csg/curve2d.hpp @@ -13,7 +13,7 @@ namespace netgen /* - 2D Curve repesentation + 2D Curve representation */ diff --git a/libsrc/csg/edgeflw.cpp b/libsrc/csg/edgeflw.cpp index 61ca2530..26d5172d 100644 --- a/libsrc/csg/edgeflw.cpp +++ b/libsrc/csg/edgeflw.cpp @@ -10,7 +10,7 @@ namespace netgen EdgeCalculation :: EdgeCalculation (const CSGeometry & ageometry, - Array & aspecpoints, + NgArray & aspecpoints, MeshingParameters & amparam) : geometry(ageometry), specpoints(aspecpoints), mparam(amparam) { @@ -48,7 +48,7 @@ namespace netgen // add all special points before edge points (important for periodic identification) // JS, Jan 2007 const double di=1e-7*geometry.MaxSize(); - Array locsearch; + NgArray locsearch; for (int i = 0; i < specpoints.Size(); i++) if (specpoints[i].unconditional) @@ -96,9 +96,9 @@ namespace netgen void EdgeCalculation :: CalcEdges1 (double h, Mesh & mesh) { - Array hsp(specpoints.Size()); - Array glob2hsp(specpoints.Size()); - Array startpoints, endpoints; + NgArray hsp(specpoints.Size()); + NgArray glob2hsp(specpoints.Size()); + NgArray startpoints, endpoints; int pos, ep; @@ -107,11 +107,11 @@ namespace netgen Point<3> p, np; int pi1, s1, s2, s1_orig, s2_orig; - Array > edgepoints; - Array curvelength; + NgArray > edgepoints; + NgArray curvelength; int copyedge = 0, copyfromedge = -1, copyedgeidentification = -1; - Array locsurfind, locind; + NgArray locsurfind, locind; int checkedcopy = 0; @@ -180,7 +180,7 @@ namespace netgen pi1 = 0; copyedge = 0; - // identifyable point available ? + // identifiable point available ? for (int i = 0; i < geometry.identifications.Size() && !pi1; i++) @@ -191,8 +191,8 @@ namespace netgen << ", v = " << specpoints[startpoints[j]].v << " for copying (i,j = " << i << ", " << j << ")" << endl; #endif - if (geometry.identifications[i]->IdentifyableCandidate (specpoints[startpoints[j]]) && - geometry.identifications[i]->IdentifyableCandidate (specpoints[endpoints[j]])) + if (geometry.identifications[i]->IdentifiableCandidate (specpoints[startpoints[j]]) && + geometry.identifications[i]->IdentifiableCandidate (specpoints[endpoints[j]])) { @@ -201,7 +201,7 @@ namespace netgen for (int k = 0; k < hsp.Size() && !pi1; k++) { - //(*testout) << " ? identifyable with " << specpoints[hsp[k]].p + //(*testout) << " ? identifiable with " << specpoints[hsp[k]].p //<< ", v = " << specpoints[hsp[k]].v // << endl; if (identification_used.Used (INDEX_2(i, startpoints[j])) || @@ -212,12 +212,12 @@ namespace netgen } if (geometry.identifications[i] - ->Identifyable(specpoints[startpoints[j]], specpoints[hsp[k]], specpoint2tlo, specpoint2surface) || + ->Identifiable(specpoints[startpoints[j]], specpoints[hsp[k]], specpoint2tlo, specpoint2surface) || geometry.identifications[i] - ->Identifyable(specpoints[hsp[k]], specpoints[startpoints[j]], specpoint2tlo, specpoint2surface)) + ->Identifiable(specpoints[hsp[k]], specpoints[startpoints[j]], specpoint2tlo, specpoint2surface)) { #ifdef DEVELOP - (*testout) << "identifyable: " << specpoints[hsp[k]].p << ", v = " << specpoints[hsp[k]].v + (*testout) << "identifiable: " << specpoints[hsp[k]].p << ", v = " << specpoints[hsp[k]].v << " and " << specpoints[startpoints[j]].p << ", v = " << specpoints[startpoints[j]].v << " (identification " << i+1 << ")" << endl; #endif @@ -245,7 +245,7 @@ namespace netgen } - // cannot copy from other ege ? + // cannot copy from other edge ? if (!pi1) checkedcopy = startpoints.Size(); @@ -406,8 +406,8 @@ namespace netgen } - Array refedges; - Array refedgesinv; + NgArray refedges; + NgArray refedgesinv; AnalyzeEdge (s1_orig, s2_orig, s1, s2, pos, layer, @@ -485,6 +485,29 @@ namespace netgen layer, mesh); } + + + { + // named edge ? + // cout << "check edge name, size = " << geometry.named_edges.size() << endl; + // for (auto pair : geometry.named_edges) + // cout << "key = " << get<0> (pair.first) << "-" << get<1> (pair.first) << ", val = " << pair.second << endl; + + Surface * sp1 = const_cast (geometry.GetSurface(s1)); + Surface * sp2 = const_cast (geometry.GetSurface(s2)); + // cout << "sp1 = " << sp1 << ", sp2 = " << sp2 << endl; + + auto ptr = geometry.named_edges.find(tuple(sp1, sp2)); + if (ptr != geometry.named_edges.end()) + for (int i = 0; i < refedges.Size(); i++) + mesh.SetCD2Name(refedges[i].edgenr, ptr->second); + + ptr = geometry.named_edges.find(tuple(sp2, sp1)); + if (ptr != geometry.named_edges.end()) + for (int i = 0; i < refedges.Size(); i++) + mesh.SetCD2Name(refedges[i].edgenr, ptr->second); + } + for(int i=0; i(geometry.GetSurface(refedges[i].surfnr1)); @@ -546,7 +569,7 @@ namespace netgen SegmentIndex si; PointIndex pi; - Array osedges(cntedge); + NgArray osedges(cntedge); INDEX_2_HASHTABLE osedgesht (cntedge+1); osedges = 2; @@ -678,17 +701,17 @@ namespace netgen void EdgeCalculation :: FollowEdge (int pi1, int & ep, int & pos, - const Array & hsp, + const NgArray & hsp, double h, const Mesh & mesh, - Array > & edgepoints, - Array & curvelength) + NgArray > & edgepoints, + NgArray & curvelength) { int s1, s2, s1_rep, s2_rep; double len, steplen, cursteplen, loch; Point<3> p, np, pnp; Vec<3> a1, a2, t; - Array locind; + NgArray locind; double size = geometry.MaxSize(); double epspointdist2 = size * 1e-6; @@ -904,14 +927,14 @@ namespace netgen void EdgeCalculation :: AnalyzeEdge (int s1, int s2, int s1_rep, int s2_rep, int pos, int layer, - const Array > & edgepoints, - Array & refedges, - Array & refedgesinv) + const NgArray > & edgepoints, + NgArray & refedges, + NgArray & refedgesinv) { Segment seg; - Array locsurfind, locsurfind2; + NgArray locsurfind, locsurfind2; - Array edges_priority; + NgArray edges_priority; double size = geometry.MaxSize(); bool debug = 0; @@ -947,7 +970,7 @@ namespace netgen for (int i = 0; i < geometry.GetNTopLevelObjects(); i++) { - Solid * locsol; + // Solid * locsol; if (geometry.GetTopLevelObject(i)->GetLayer() != layer) continue; @@ -955,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; @@ -972,7 +996,8 @@ namespace netgen ReducePrimitiveIterator rpi(boxp); UnReducePrimitiveIterator urpi; - ((Solid*)locsol) -> IterateSolid (rpi); + // ((Solid*)locsol) -> IterateSolid (rpi); + locsol -> IterateSolid (rpi); locsol -> CalcSurfaceInverse (); @@ -997,7 +1022,8 @@ namespace netgen } } - ((Solid*)locsol) -> IterateSolid (urpi); + // ((Solid*)locsol) -> IterateSolid (urpi); + locsol -> IterateSolid (urpi); if (debug) @@ -1062,23 +1088,33 @@ namespace netgen //int k; double eps = 1e-8*size; - Array 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); @@ -1127,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) @@ -1174,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; @@ -1223,7 +1262,7 @@ namespace netgen m *= -1; } } - delete locsol; + // delete locsol; } @@ -1232,8 +1271,11 @@ 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."); - BitArray todelete(refedges.Size()); + NgBitArray todelete(refedges.Size()); todelete.Clear(); @@ -1291,17 +1333,17 @@ namespace netgen void EdgeCalculation :: - StoreEdge (const Array & refedges, - const Array & refedgesinv, - const Array > & edgepoints, - const Array & curvelength, + StoreEdge (const NgArray & refedges, + const NgArray & refedgesinv, + const NgArray > & edgepoints, + const NgArray & curvelength, int layer, Mesh & mesh) { // Calculate optimal element-length int i, j, k; - PointIndex pi; + // PointIndex pi; int ne; double len, corr, lam; @@ -1326,7 +1368,7 @@ namespace netgen // generate initial point p = edgepoints.Get(1); - lastpi = -1; + lastpi = PointIndex::INVALID; /* for (pi = PointIndex::BASE; @@ -1340,7 +1382,7 @@ namespace netgen const double di=1e-7*geometry.MaxSize(); - Array locsearch; + NgArray locsearch; meshpoint_tree -> GetIntersecting (p-Vec<3> (di,di,di), p+Vec<3> (di,di,di), locsearch); if (locsearch.Size()) @@ -1348,7 +1390,7 @@ namespace netgen - if (lastpi == -1) + if (!lastpi.IsValid()) { lastpi = mesh.AddPoint (p, layer, FIXEDPOINT); meshpoint_tree -> Insert (p, lastpi); @@ -1368,7 +1410,7 @@ namespace netgen np(1) = (1-lam) * edgepoints.Get(j-1)(1) + lam * edgepoints.Get(j)(1); np(2) = (1-lam) * edgepoints.Get(j-1)(2) + lam * edgepoints.Get(j)(2); - thispi = -1; + thispi = PointIndex::INVALID; if (i == ne) { /* @@ -1384,7 +1426,7 @@ namespace netgen thispi = locsearch[0]; } - if (thispi == -1) + if (!thispi.IsValid()) { ProjectToEdge (surf1, surf2, np); thispi = mesh.AddPoint (np, layer, (i==ne) ? FIXEDPOINT : EDGEPOINT); @@ -1463,10 +1505,10 @@ namespace netgen void EdgeCalculation :: - StoreShortEdge (const Array & refedges, - const Array & refedgesinv, - const Array > & edgepoints, - const Array & curvelength, + StoreShortEdge (const NgArray & refedges, + const NgArray & refedgesinv, + const NgArray > & edgepoints, + const NgArray & curvelength, int layer, Mesh & mesh) { @@ -1496,7 +1538,7 @@ namespace netgen // generate initial point Point<3> p = edgepoints[0]; - PointIndex pi1 = -1; + PointIndex pi1 = PointIndex::INVALID; for (pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++) @@ -1506,7 +1548,7 @@ namespace netgen break; } - if (pi1 == -1) + if (!pi1.IsValid()) { pi1 = mesh.AddPoint (p, layer, FIXEDPOINT); meshpoint_tree -> Insert (p, pi1); @@ -1514,7 +1556,7 @@ namespace netgen } p = edgepoints.Last(); - PointIndex pi2 = -1; + PointIndex pi2 = PointIndex::INVALID; for (pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++) @@ -1523,7 +1565,7 @@ namespace netgen pi2 = pi; break; } - if (pi2==-1) + if (!pi2.IsValid()) { pi2 = mesh.AddPoint (p, layer, FIXEDPOINT); meshpoint_tree -> Insert (p, pi2); @@ -1594,8 +1636,8 @@ namespace netgen void EdgeCalculation :: - CopyEdge (const Array & refedges, - const Array & refedgesinv, + CopyEdge (const NgArray & refedges, + const NgArray & refedgesinv, int copyfromedge, const Point<3> & fromstart, const Point<3> & fromend, const Point<3> & tostart, const Point<3> & toend, @@ -1616,8 +1658,8 @@ namespace netgen Point<3> top = (i == 1) ? tostart : toend; - PointIndex frompi = -1; - PointIndex topi = -1; + PointIndex frompi = PointIndex::INVALID; + PointIndex topi = PointIndex::INVALID; for (pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++) { @@ -1628,7 +1670,7 @@ namespace netgen } - if (topi == -1) + if (!topi.IsValid()) { topi = mesh.AddPoint (top, layer, FIXEDPOINT); meshpoint_tree -> Insert (top, topi); @@ -1638,9 +1680,9 @@ namespace netgen (*geometry.identifications.Get(copyedgeidentification)); - if (csi.Identifyable (mesh[frompi], mesh[topi])) + if (csi.Identifiable (mesh[frompi], mesh[topi])) mesh.GetIdentifications().Add(frompi, topi, copyedgeidentification); - else if (csi.Identifyable (mesh[topi], mesh[frompi])) + else if (csi.Identifiable (mesh[topi], mesh[frompi])) mesh.GetIdentifications().Add(topi, frompi, copyedgeidentification); else { @@ -1741,14 +1783,14 @@ namespace netgen int nsurf = geometry.GetNSurf(); int layer = 0; - Solid * tansol; - Array tansurfind; + // Solid * tansol; + NgArray tansurfind; double size = geometry.MaxSize(); int nsol = geometry.GetNTopLevelObjects(); - BitArray pointatsurface (nsurf); + NgBitArray pointatsurface (nsurf); pointatsurface.Clear(); for (int i = 1; i <= mesh.GetNSeg(); i++) @@ -1799,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(); @@ -1829,7 +1872,7 @@ namespace netgen // seg.invs1 = surfaces[i] -> Inverse(); // seg.invs2 = ! (surfaces[i] -> Inverse()); } - delete tansol; + // delete tansol; } } diff --git a/libsrc/csg/edgeflw.hpp b/libsrc/csg/edgeflw.hpp index f9bfdb97..ac93b4a9 100644 --- a/libsrc/csg/edgeflw.hpp +++ b/libsrc/csg/edgeflw.hpp @@ -26,7 +26,7 @@ namespace netgen points have to be given. */ extern void CalcEdges (const CSGeometry & geometry, - const Array & specpoints, + const NgArray & specpoints, double h, Mesh & mesh); @@ -36,7 +36,7 @@ namespace netgen class EdgeCalculation { const CSGeometry & geometry; - Array & specpoints; + NgArray & specpoints; Point3dTree * searchtree; Point3dTree * meshpoint_tree; int cntedge; @@ -46,7 +46,7 @@ namespace netgen public: EdgeCalculation (const CSGeometry & ageometry, - Array & aspecpoints, + NgArray & aspecpoints, MeshingParameters & amparam); ~EdgeCalculation(); @@ -61,34 +61,34 @@ namespace netgen void FollowEdge (int pi1, int & ep, int & pos, - // const Array & hsp, - const Array & hsp, + // const NgArray & hsp, + const NgArray & hsp, double h, const Mesh & mesh, - Array > & edgepoints, - Array & curvelength); + NgArray > & edgepoints, + NgArray & curvelength); void AnalyzeEdge (int s1, int s2, int s1_rep, int s2_rep, int pos, int layer, - const Array > & edgepoints, - Array & refedges, - Array & refedgesinv); + const NgArray > & edgepoints, + NgArray & refedges, + NgArray & refedgesinv); - void StoreEdge (const Array & refedges, - const Array & refedgesinv, - const Array > & edgepoints, - const Array & curvelength, + void StoreEdge (const NgArray & refedges, + const NgArray & refedgesinv, + const NgArray > & edgepoints, + const NgArray & curvelength, int layer, Mesh & mesh); - void StoreShortEdge (const Array & refedges, - const Array & refedgesinv, - const Array > & edgepoints, - const Array & curvelength, + void StoreShortEdge (const NgArray & refedges, + const NgArray & refedgesinv, + const NgArray > & edgepoints, + const NgArray & curvelength, int layer, Mesh & mesh); - void CopyEdge (const Array & refedges, - const Array & refedgesinv, + void CopyEdge (const NgArray & refedges, + const NgArray & refedgesinv, int copyfromedge, const Point<3> & fromstart, const Point<3> & fromend, const Point<3> & tostart, const Point<3> & toend, diff --git a/libsrc/csg/explicitcurve2d.hpp b/libsrc/csg/explicitcurve2d.hpp index 559030b2..c0fbe5a9 100644 --- a/libsrc/csg/explicitcurve2d.hpp +++ b/libsrc/csg/explicitcurve2d.hpp @@ -13,7 +13,7 @@ namespace netgen /* - Explicit 2D Curve repesentation + Explicit 2D Curve representation */ @@ -70,9 +70,9 @@ namespace netgen class BSplineCurve2d : public ExplicitCurve2d { /// - Array > points; + NgArray > points; /// - Array intervallused; + NgArray intervallused; /// int redlevel; diff --git a/libsrc/csg/extrusion.cpp b/libsrc/csg/extrusion.cpp index 61446b0c..1d3cdafb 100644 --- a/libsrc/csg/extrusion.cpp +++ b/libsrc/csg/extrusion.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -7,7 +8,7 @@ namespace netgen { - Array > project1, project2; + NgArray > project1, project2; @@ -41,6 +42,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; @@ -57,13 +70,13 @@ namespace netgen Init(); } - ExtrusionFace :: ExtrusionFace(const Array & raw_data) + ExtrusionFace :: ExtrusionFace(const NgArray & raw_data) { deletable = true; int pos=0; - Array< Point<2> > p(3); + NgArray< Point<2> > p(3); int ptype = int(raw_data[pos]); pos++; @@ -128,21 +141,26 @@ namespace netgen void ExtrusionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d, int & seg, double & t) const { - if (Dist2 (point3d, latest_point3d) < - 1e-25 * Dist2(path->GetSpline(0).StartPI(), path->GetSpline(0).EndPI())) + static mutex set_latest_point; + + auto eps = 1e-25 * Dist2(path->GetSpline(0).StartPI(), path->GetSpline(0).EndPI()); + if (Dist2 (point3d, latest_point3d) < eps) { - point2d = latest_point2d; - seg = latest_seg; - t = latest_t; - return; + std::lock_guard guard(set_latest_point); + if (Dist2 (point3d, latest_point3d) < eps) + { + point2d = latest_point2d; + seg = latest_seg; + t = latest_t; + return; + } } - latest_point3d = point3d; double cutdist = -1; - Array mindist(path->GetNSplines()); + NgArray mindist(path->GetNSplines()); for(int i = 0; i < path->GetNSplines(); i++) { @@ -201,11 +219,13 @@ namespace netgen point2d = testpoint2d; t = thist; seg = i; - latest_seg = i; - latest_t = t; - latest_point2d = point2d; } } + std::lock_guard guard(set_latest_point); + latest_seg = seg; + latest_t = t; + latest_point2d = point2d; + latest_point3d = point3d; } double ExtrusionFace :: CalcProj(const Point<3> & point3d, Point<2> & point2d, @@ -415,6 +435,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, @@ -453,7 +481,7 @@ namespace netgen v2d(1) = v * loc_z_dir[seg]; Vec<2> n(v2d(1),-v2d(0)); - Array < Point<2> > ips; + NgArray < Point<2> > ips; profile->LineIntersections(v2d(1), @@ -593,7 +621,7 @@ namespace netgen } - void ExtrusionFace :: GetRawData(Array & data) const + void ExtrusionFace :: GetRawData(NgArray & data) const { data.DeleteAll(); profile->GetRawData(data); @@ -648,20 +676,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); @@ -695,7 +738,7 @@ namespace netgen INSOLID_TYPE Extrusion :: PointInSolid (const Point<3> & p, const double eps, - Array * const facenums) const + NgArray * const facenums) const { Vec<3> random_vec(-0.4561,0.7382,0.4970247); @@ -737,11 +780,21 @@ 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 { - Array facenums; + NgArray facenums; INSOLID_TYPE pInSolid = PointInSolid(p,eps,&facenums); if(pInSolid != DOES_INTERSECT) @@ -838,7 +891,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 2d6b3bbe..e29d5660 100644 --- a/libsrc/csg/extrusion.hpp +++ b/libsrc/csg/extrusion.hpp @@ -12,14 +12,16 @@ namespace netgen const SplineSeg<2> * profile; const SplineGeometry<3> * path; Vec<3> glob_z_direction; + Array angles; bool deletable; + int tangential_plane_seg; - Array< const SplineSeg3<3> * > spline3_path; - Array< const LineSeg<3> * > line_path; + NgArray< const SplineSeg3<3> * > spline3_path; + NgArray< const LineSeg<3> * > line_path; - mutable Array < Vec<3> > x_dir, y_dir, z_dir, loc_z_dir; - mutable Array < Point<3> > p0; + mutable NgArray < Vec<3> > x_dir, y_dir, z_dir, loc_z_dir; + mutable NgArray < Point<3> > p0; mutable Vec<3> profile_tangent; mutable double profile_par; @@ -48,13 +50,13 @@ namespace netgen const SplineGeometry<3> * path_in, const Vec<3> & z_direction); - ExtrusionFace(const Array & raw_data); + ExtrusionFace(const NgArray & raw_data); // default constructor for archive ExtrusionFace() {} ~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; @@ -102,7 +107,7 @@ namespace netgen const Vec<3> & GetProfileTangent (void) const {return profile_tangent;} double GetProfilePar(void) const {return profile_par;} - void GetRawData(Array & data) const; + void GetRawData(NgArray & data) const; void CalcLocalCoordinates (int seg, double t, Vec<3> & ex, Vec<3> & ey, Vec<3> & ez) 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,53 +128,56 @@ 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; - Array faces; + NgArray faces; 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, - Array * const facenums) const; - virtual INSOLID_TYPE VecInSolid (const Point<3> & p, - const Vec<3> & v, - double eps) const; + NgArray * const facenums) 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/genmesh.cpp b/libsrc/csg/genmesh.cpp index 4683c4e2..da790fc4 100644 --- a/libsrc/csg/genmesh.cpp +++ b/libsrc/csg/genmesh.cpp @@ -10,15 +10,19 @@ namespace netgen { - Array specpoints; - static Array spoints; + DLL_HEADER NgArray global_specpoints; // for visualization + //static NgArray spoints; + #define TCL_OK 0 #define TCL_ERROR 1 - static void FindPoints (CSGeometry & geom, Mesh & mesh) + static void FindPoints (CSGeometry & geom, + NgArray & specpoints, + NgArray & spoints, + Mesh & mesh) { PrintMessage (1, "Start Findpoints"); @@ -32,7 +36,11 @@ namespace netgen auto pnum = mesh.AddPoint(up); mesh.Points().Last().Singularity (geom.GetUserPointRefFactor(i)); mesh.AddLockedPoint (PointIndex (i+1)); - mesh.pointelements.Append (Element0d(pnum, up.GetIndex())); + int index = up.GetIndex(); + if (index == -1) + index = mesh.AddCD3Name (up.GetName())+1; + // cout << "adding 0d element, pnum = " << pnum << ", material index = " << index << endl; + mesh.pointelements.Append (Element0d(pnum, index)); } SpecialPointCalculation spc; @@ -44,7 +52,13 @@ namespace netgen PrintMessage (2, "Analyze spec points"); spc.AnalyzeSpecialPoints (geom, spoints, specpoints); - + + { + static mutex mut; + lock_guard guard(mut); + global_specpoints = specpoints; + } + PrintMessage (5, "done"); (*testout) << specpoints.Size() << " special points:" << endl; @@ -63,7 +77,10 @@ namespace netgen - static void FindEdges (CSGeometry & geom, Mesh & mesh, MeshingParameters & mparam, + static void FindEdges (CSGeometry & geom, Mesh & mesh, + NgArray & specpoints, + NgArray & spoints, + MeshingParameters & mparam, const bool setmeshsize = false) { EdgeCalculation ec (geom, specpoints, mparam); @@ -140,7 +157,7 @@ namespace netgen } } - Array loc; + NgArray loc; if (!ec.point_on_edge_problem) for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++) { @@ -234,17 +251,17 @@ namespace netgen const char * savetask = multithread.task; multithread.task = "Surface meshing"; - Array segments; + NgArray segments; int noldp = mesh.GetNP(); double starttime = GetTime(); // find master faces from identified - Array masterface(mesh.GetNFD()); + NgArray masterface(mesh.GetNFD()); for (int i = 1; i <= mesh.GetNFD(); i++) masterface.Elem(i) = i; - Array fpairs; + NgArray fpairs; bool changed; do { @@ -382,7 +399,7 @@ namespace netgen for (int j = 0; j < geom.singfaces.Size(); j++) { - Array surfs; + NgArray surfs; geom.GetIndependentSurfaceIndices (geom.singfaces[j]->GetSolid(), geom.BoundingBox(), surfs); for (int k = 1; k <= mesh.GetNFD(); k++) @@ -422,7 +439,7 @@ namespace netgen geom.GetSurface((mesh.GetFaceDescriptor(k).SurfNr())); - Meshing2Surfaces meshing(*surf, mparam, geom.BoundingBox()); + Meshing2Surfaces meshing(geom, *surf, mparam, geom.BoundingBox()); meshing.SetStartTime (starttime); double eps = 1e-8 * geom.MaxSize(); @@ -500,10 +517,12 @@ namespace netgen } if (multithread.terminate) return; - + for (SurfaceElementIndex sei = oldnf; sei < mesh.GetNSE(); sei++) mesh[sei].SetIndex (k); + auto n_illegal_trigs = mesh.FindIllegalTrigs(); + PrintMessage (3, n_illegal_trigs, " illegal triangles"); // mesh.CalcSurfacesOfNode(); @@ -521,48 +540,48 @@ namespace netgen if (multithread.terminate) return; { - MeshOptimize2dSurfaces meshopt(geom); + MeshOptimize2d meshopt(mesh); meshopt.SetFaceIndex (k); meshopt.SetImproveEdges (0); meshopt.SetMetricWeight (mparam.elsizeweight); meshopt.SetWriteStatus (0); - meshopt.EdgeSwapping (mesh, (i > mparam.optsteps2d/2)); + meshopt.EdgeSwapping (i > mparam.optsteps2d/2); } if (multithread.terminate) return; { // mesh.CalcSurfacesOfNode(); - MeshOptimize2dSurfaces meshopt(geom); + MeshOptimize2d meshopt(mesh); meshopt.SetFaceIndex (k); meshopt.SetImproveEdges (0); meshopt.SetMetricWeight (mparam.elsizeweight); meshopt.SetWriteStatus (0); - meshopt.ImproveMesh (mesh, mparam); + meshopt.ImproveMesh(mparam); } { - MeshOptimize2dSurfaces meshopt(geom); + MeshOptimize2d meshopt(mesh); meshopt.SetFaceIndex (k); meshopt.SetImproveEdges (0); meshopt.SetMetricWeight (mparam.elsizeweight); meshopt.SetWriteStatus (0); - meshopt.CombineImprove (mesh); + meshopt.CombineImprove(); // mesh.CalcSurfacesOfNode(); } if (multithread.terminate) return; { - MeshOptimize2dSurfaces meshopt(geom); + MeshOptimize2d meshopt(mesh); meshopt.SetFaceIndex (k); meshopt.SetImproveEdges (0); meshopt.SetMetricWeight (mparam.elsizeweight); meshopt.SetWriteStatus (0); - meshopt.ImproveMesh (mesh, mparam); + meshopt.ImproveMesh(mparam); } } } @@ -663,6 +682,10 @@ namespace netgen int CSGGenerateMesh (CSGeometry & geom, shared_ptr & mesh, MeshingParameters & mparam) { + NgArray specpoints; + NgArray spoints; + + if (mesh && mesh->GetNSE() && !geom.GetNSolids()) { @@ -680,7 +703,7 @@ namespace netgen mesh->SetGlobalH (mparam.maxh); mesh->SetMinimalH (mparam.minh); - Array maxhdom(geom.GetNTopLevelObjects()); + NgArray maxhdom(geom.GetNTopLevelObjects()); for (int i = 0; i < maxhdom.Size(); i++) maxhdom[i] = geom.GetTopLevelObject(i)->GetMaxH(); @@ -699,7 +722,7 @@ namespace netgen } spoints.SetSize(0); - FindPoints (geom, *mesh); + FindPoints (geom, specpoints, spoints, *mesh); PrintMessage (5, "find points done"); @@ -717,7 +740,7 @@ namespace netgen if (mparam.perfstepsstart <= MESHCONST_MESHEDGES) { - FindEdges (geom, *mesh, mparam, true); + FindEdges (geom, *mesh, specpoints, spoints, mparam, true); if (multithread.terminate) return TCL_OK; #ifdef LOG_STREAM (*logout) << "Edges meshed" << endl @@ -734,16 +757,16 @@ namespace netgen mesh->CalcLocalH(mparam.grading); mesh->DeleteMesh(); - FindPoints (geom, *mesh); + FindPoints (geom, specpoints, spoints, *mesh); if (multithread.terminate) return TCL_OK; - FindEdges (geom, *mesh, mparam, true); + FindEdges (geom, *mesh, specpoints, spoints, mparam, true); if (multithread.terminate) return TCL_OK; mesh->DeleteMesh(); - FindPoints (geom, *mesh); + FindPoints (geom, specpoints, spoints, *mesh); if (multithread.terminate) return TCL_OK; - FindEdges (geom, *mesh, mparam); + FindEdges (geom, *mesh, specpoints, spoints, mparam); if (multithread.terminate) return TCL_OK; } } diff --git a/libsrc/csg/identify.cpp b/libsrc/csg/identify.cpp index 95a6bb13..08e7c027 100644 --- a/libsrc/csg/identify.cpp +++ b/libsrc/csg/identify.cpp @@ -28,7 +28,7 @@ ostream & operator<< (ostream & ost, Identification & ident) /* -void Identification :: IdentifySpecialPoints (Array & points) +void Identification :: IdentifySpecialPoints (NgArray & points) { ; } @@ -36,24 +36,24 @@ void Identification :: IdentifySpecialPoints (Array & points int Identification :: -Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, +Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const { - cout << "Identification::Identifyable called for base-class" << endl; + cout << "Identification::Identifiable called for base-class" << endl; return 0; } int Identification :: -Identifyable (const Point<3> & p1, const Point<3> & sp2) const +Identifiable (const Point<3> & p1, const Point<3> & sp2) const { - cout << "Identification::Identifyable called for base-class" << endl; + cout << "Identification::Identifiable called for base-class" << endl; return 0; } int Identification :: -IdentifyableCandidate (const SpecialPoint & sp1) const +IdentifiableCandidate (const SpecialPoint & sp1) const { return 1; } @@ -84,7 +84,7 @@ void Identification :: IdentifyFaces (class Mesh & mesh) } void Identification :: -BuildSurfaceElements (Array & segs, +BuildSurfaceElements (NgArray & segs, Mesh & mesh, const Surface * surf) { cout << "Identification::BuildSurfaceElements called for base-class" << endl; @@ -93,14 +93,14 @@ BuildSurfaceElements (Array & segs, void Identification :: -BuildVolumeElements (Array & surfels, +BuildVolumeElements (NgArray & surfels, class Mesh & mesh) { ; } void Identification :: -GetIdentifiedFaces (Array & idfaces) const +GetIdentifiedFaces (NgArray & idfaces) const { idfaces.SetSize(0); for (int i = 1; i <= identfaces.GetNBags(); i++) @@ -136,7 +136,7 @@ PeriodicIdentification :: ~PeriodicIdentification () /* void PeriodicIdentification :: IdentifySpecialPoints -(Array & points) +(NgArray & points) { int i, j; int bestj; @@ -196,7 +196,7 @@ void PeriodicIdentification :: IdentifySpecialPoints */ int PeriodicIdentification :: -Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, +Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const { @@ -252,7 +252,7 @@ Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, } int PeriodicIdentification :: -Identifyable (const Point<3> & p1, const Point<3> & p2) const +Identifiable (const Point<3> & p1, const Point<3> & p2) const { return (s1->PointOnSurface (p1) && s2->PointOnSurface (p2)); @@ -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); /* @@ -446,7 +450,7 @@ void PeriodicIdentification :: IdentifyFaces (class Mesh & mesh) void PeriodicIdentification :: -BuildSurfaceElements (Array & segs, +BuildSurfaceElements (NgArray & segs, Mesh & mesh, const Surface * surf) { int found = 0; @@ -458,7 +462,7 @@ BuildSurfaceElements (Array & segs, if (geom.GetSurface(surfnr) == s1 || geom.GetSurface(surfnr) == s2) { - Array copy_points; + NgArray copy_points; for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) { @@ -609,7 +613,7 @@ void CloseSurfaceIdentification :: GetData (ostream & ost) const /* void CloseSurfaceIdentification :: IdentifySpecialPoints -(Array & points) +(NgArray & points) { int i, j; int bestj; @@ -668,7 +672,7 @@ void CloseSurfaceIdentification :: IdentifySpecialPoints */ int CloseSurfaceIdentification :: -Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, +Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const { @@ -677,7 +681,7 @@ Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, if (!dom_surf_valid) { const_cast (dom_surf_valid) = 1; - Array & hsurf = const_cast&> (domain_surfaces); + NgArray & hsurf = const_cast&> (domain_surfaces); if (domain) { @@ -826,7 +830,7 @@ Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, } int CloseSurfaceIdentification :: -Identifyable (const Point<3> & p1, const Point<3> & p2) const +Identifiable (const Point<3> & p1, const Point<3> & p2) const { // if (domain) // if (!domain->GetSolid()->IsIn (p1) || !domain->GetSolid()->IsIn (p2)) @@ -838,7 +842,7 @@ Identifyable (const Point<3> & p1, const Point<3> & p2) const int CloseSurfaceIdentification :: -IdentifyableCandidate (const SpecialPoint & sp1) const +IdentifiableCandidate (const SpecialPoint & sp1) const { if (domain) if (!domain->GetSolid()->IsIn (sp1.p)) @@ -887,7 +891,7 @@ GetIdentifiedPoint (class Mesh & mesh, int pi) const Surface *snew; const Point<3> & p = mesh.Point (pi); - Array identmap(mesh.GetNP()); + NgArray identmap(mesh.GetNP()); mesh.GetIdentifications().GetMap (nr, identmap); if (identmap.Get(pi)) return identmap.Get(pi); @@ -958,13 +962,13 @@ void CloseSurfaceIdentification :: IdentifyPoints (Mesh & mesh) { int np = mesh.GetNP(); - Array points_on_surf2; + NgArray points_on_surf2; for (int i2 = 1; i2 <= np; i2++) if (s2->PointOnSurface (mesh.Point(i2))) points_on_surf2.Append (i2); - Array surfs_of_p1; + NgArray surfs_of_p1; for (int i1 = 1; i1 <= np; i1++) { @@ -1080,7 +1084,7 @@ void CloseSurfaceIdentification :: IdentifyFaces (class Mesh & mesh) s2rep = geom.GetSurfaceClassRepresentant(i); } - Array segs_on_face1, segs_on_face2; + NgArray segs_on_face1, segs_on_face2; identfaces.DeleteData(); @@ -1219,13 +1223,13 @@ void CloseSurfaceIdentification :: IdentifyFaces (class Mesh & mesh) void CloseSurfaceIdentification :: -BuildSurfaceElements (Array & segs, +BuildSurfaceElements (NgArray & segs, Mesh & mesh, const Surface * surf) { bool found = 0; int cntquads = 0; - Array identmap; + NgArray identmap; identmap = 0; mesh.GetIdentifications().GetMap (nr, identmap); @@ -1240,7 +1244,7 @@ BuildSurfaceElements (Array & segs, //(*testout) << "segs = " << endl << segs << endl; //(*testout) << "identmap = " << endl << identmap << endl; - //Array foundseg(segs.Size()); + //NgArray foundseg(segs.Size()); //foundseg = false; // insert quad layer: @@ -1301,7 +1305,7 @@ BuildSurfaceElements (Array & segs, { PrintMessage(3, "insert quad layer of ", cntquads, " elements at face ", segs.Get(1).si); - //Array aux; + //NgArray aux; //for(int i=0; i & segs, void CloseSurfaceIdentification :: -BuildSurfaceElements2 (Array & segs, +BuildSurfaceElements2 (NgArray & segs, Mesh & mesh, const Surface * surf) { // copy mesh @@ -1420,7 +1424,7 @@ BuildSurfaceElements2 (Array & segs, void CloseSurfaceIdentification :: -BuildVolumeElements (Array & surfels, +BuildVolumeElements (NgArray & surfels, class Mesh & mesh) { ; @@ -1481,7 +1485,7 @@ void CloseEdgesIdentification :: GetData (ostream & ost) const /* void CloseEdgesIdentification :: IdentifySpecialPoints -(Array & points) +(NgArray & points) { int i, j; int bestj; @@ -1540,7 +1544,7 @@ void CloseEdgesIdentification :: IdentifySpecialPoints */ int CloseEdgesIdentification :: -Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, +Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const { @@ -1633,7 +1637,7 @@ void CloseEdgesIdentification :: IdentifyPoints (Mesh & mesh) } void CloseEdgesIdentification :: -BuildSurfaceElements (Array & segs, +BuildSurfaceElements (NgArray & segs, Mesh & mesh, const Surface * surf) { int found = 0; diff --git a/libsrc/csg/identify.hpp b/libsrc/csg/identify.hpp index 96ae5bce..90e5f4b0 100644 --- a/libsrc/csg/identify.hpp +++ b/libsrc/csg/identify.hpp @@ -34,17 +34,17 @@ namespace netgen DLL_HEADER virtual void GetData (ostream & ost) const = 0; /// obsolete - // virtual void IdentifySpecialPoints (Array & points); + // virtual void IdentifySpecialPoints (NgArray & points); /// can identify both special points (fixed direction) /// (identified points, same tangent) - virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, + virtual int Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const; /// - virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const; + virtual int Identifiable (const Point<3> & p1, const Point<3> & sp2) const; /// is it possible to identify sp1 with some other ? - virtual int IdentifyableCandidate (const SpecialPoint & sp1) const; + virtual int IdentifiableCandidate (const SpecialPoint & sp1) const; /// are points (if connected) by a short edge (direction anyhow) ? virtual int ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const; @@ -59,16 +59,16 @@ namespace netgen virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1); /// copy surfaces, or fill rectangles - virtual void BuildSurfaceElements (Array & segs, + virtual void BuildSurfaceElements (NgArray & segs, class Mesh & mesh, const Surface * surf); /// insert volume elements in thin layers - virtual void BuildVolumeElements (Array & surfels, + virtual void BuildVolumeElements (NgArray & surfels, class Mesh & mesh); /// get list of identified faces - virtual void GetIdentifiedFaces (Array & idfaces) const; + virtual void GetIdentifiedFaces (NgArray & idfaces) const; friend ostream & operator<< (ostream & ost, Identification & ident); }; @@ -91,16 +91,16 @@ namespace netgen virtual void GetData (ostream & ost) const override; - // virtual void IdentifySpecialPoints (Array & points); - virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, + // virtual void IdentifySpecialPoints (NgArray & points); + virtual int Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const override; - virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const override; + virtual int Identifiable (const Point<3> & p1, const Point<3> & sp2) const override; virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1) override; virtual void IdentifyPoints (class Mesh & mesh) override; virtual void IdentifyFaces (class Mesh & mesh) override; - virtual void BuildSurfaceElements (Array & segs, + virtual void BuildSurfaceElements (NgArray & segs, class Mesh & mesh, const Surface * surf) override; }; @@ -125,7 +125,7 @@ namespace netgen double eps_n; Array slices; /// used only for domain-local identification: - Array domain_surfaces; + NgArray domain_surfaces; /// bool dom_surf_valid; @@ -146,25 +146,25 @@ namespace netgen virtual void GetData (ostream & ost) const; - // virtual void IdentifySpecialPoints (Array & points); - virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, + // virtual void IdentifySpecialPoints (NgArray & points); + virtual int Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const; - virtual int Identifyable (const Point<3> & p1, const Point<3> & sp2) const; - virtual int IdentifyableCandidate (const SpecialPoint & sp1) const; + virtual int Identifiable (const Point<3> & p1, const Point<3> & sp2) const; + virtual int IdentifiableCandidate (const SpecialPoint & sp1) const; virtual int ShortEdge (const SpecialPoint & sp1, const SpecialPoint & sp2) const; virtual int GetIdentifiedPoint (class Mesh & mesh, int pi1); const Array & GetSlices () const { return slices; } virtual void IdentifyPoints (class Mesh & mesh); virtual void IdentifyFaces (class Mesh & mesh); - virtual void BuildSurfaceElements (Array & segs, + virtual void BuildSurfaceElements (NgArray & segs, class Mesh & mesh, const Surface * surf); - void BuildSurfaceElements2 (Array & segs, + void BuildSurfaceElements2 (NgArray & segs, class Mesh & mesh, const Surface * surf); - virtual void BuildVolumeElements (Array & surfels, + virtual void BuildVolumeElements (NgArray & surfels, class Mesh & mesh); int RefLevels () const { return ref_levels; } @@ -196,14 +196,14 @@ namespace netgen virtual void Print (ostream & ost) const; virtual void GetData (ostream & ost) const; - // virtual void IdentifySpecialPoints (Array & points); - virtual int Identifyable (const SpecialPoint & sp1, const SpecialPoint & sp2, + // virtual void IdentifySpecialPoints (NgArray & points); + virtual int Identifiable (const SpecialPoint & sp1, const SpecialPoint & sp2, const TABLE & specpoint2solid, const TABLE & specpoint2surface) const; virtual void IdentifyPoints (class Mesh & mesh); - virtual void BuildSurfaceElements (Array & segs, + virtual void BuildSurfaceElements (NgArray & segs, class Mesh & mesh, const Surface * surf); }; diff --git a/libsrc/csg/meshsurf.cpp b/libsrc/csg/meshsurf.cpp index 54a70af8..234c7c40 100644 --- a/libsrc/csg/meshsurf.cpp +++ b/libsrc/csg/meshsurf.cpp @@ -14,49 +14,44 @@ Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurface) ; } */ -Meshing2Surfaces :: Meshing2Surfaces (const Surface & asurf, - const MeshingParameters & mp, - const Box<3> & abb) - : Meshing2(mp, abb), surface(asurf), mparam (mp) -{ - ; -} + Meshing2Surfaces :: Meshing2Surfaces (const CSGeometry& geo, + const Surface & asurf, + const MeshingParameters & mp, + const Box<3> & abb) + : Meshing2(geo, mp, abb), surface(asurf), mparam (mp) + { + ; + } -void Meshing2Surfaces :: DefineTransformation (const Point3d & p1, const Point3d & p2, +void Meshing2Surfaces :: DefineTransformation (const Point<3> & p1, const Point<3> & p2, const PointGeomInfo * geominfo1, const PointGeomInfo * geominfo2) { ((Surface&)surface).DefineTangentialPlane (p1, p2); } -void Meshing2Surfaces :: TransformToPlain (const Point3d & locpoint, +void Meshing2Surfaces :: TransformToPlain (const Point<3> & locpoint, const MultiPointGeomInfo & geominfo, - Point2d & planepoint, + Point<2> & planepoint, double h, int & zone) { - Point<2> hp; - surface.ToPlane (locpoint, hp, h, zone); - planepoint.X() = hp(0); - planepoint.Y() = hp(1); + surface.ToPlane (locpoint, planepoint, h, zone); } -int Meshing2Surfaces :: TransformFromPlain (Point2d & planepoint, - Point3d & locpoint, - PointGeomInfo & gi, - double h) +int Meshing2Surfaces :: TransformFromPlain (const Point<2> & planepoint, + Point<3> & locpoint, + PointGeomInfo & gi, + double h) { - Point<3> hp; - Point<2> hp2 (planepoint.X(), planepoint.Y()); - surface.FromPlane (hp2, hp, h); - locpoint = hp; + surface.FromPlane (planepoint, locpoint, h); gi.trignum = 1; return 0; } -double Meshing2Surfaces :: CalcLocalH (const Point3d & p, double gh) const +double Meshing2Surfaces :: CalcLocalH (const Point<3> & p, double gh) const { return surface.LocH (p, 3, 1, mparam, gh); /* @@ -65,147 +60,4 @@ double Meshing2Surfaces :: CalcLocalH (const Point3d & p, double gh) const return loch; */ } - - - - - - -MeshOptimize2dSurfaces :: MeshOptimize2dSurfaces (const CSGeometry & ageometry) - : MeshOptimize2d(), geometry(ageometry) -{ - ; -} - - -void MeshOptimize2dSurfaces :: ProjectPoint (INDEX surfind, Point<3> & p) const -{ - Point<3> hp = p; - geometry.GetSurface(surfind)->Project (hp); - p = hp; -} - -void MeshOptimize2dSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, - Point<3> & p) const -{ - Point<3> hp = p; - ProjectToEdge ( geometry.GetSurface(surfind), - geometry.GetSurface(surfind2), hp); - p = hp; -} - - -void MeshOptimize2dSurfaces :: -GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const -{ - Vec<3> hn = n; - geometry.GetSurface(surfind)->CalcGradient (p, hn); - hn.Normalize(); - n = hn; - - /* - if (geometry.GetSurface(surfind)->Inverse()) - n *= -1; - */ -} - - - - - - - -RefinementSurfaces :: RefinementSurfaces (const CSGeometry & ageometry) - : Refinement(), geometry(ageometry) -{ - if(geometry.GetNSurf() == 0) - *testout << endl - << "WARNING: Initializing 2D refinement with 0-surface geometry" << endl - << "==========================================================" << endl - << endl << endl; -} - -RefinementSurfaces :: ~RefinementSurfaces () -{ - ; -} - -void RefinementSurfaces :: -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 -{ - Point<3> hnewp; - hnewp = p1+secpoint*(p2-p1); - if (surfi != -1) - { - geometry.GetSurface (surfi) -> Project (hnewp); - newgi.trignum = 1; - } - - newp = hnewp; -} - -void RefinementSurfaces :: -PointBetween (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 -{ - Point<3> hnewp = p1+secpoint*(p2-p1); - //(*testout) << "hnewp " << hnewp << " s1 " << surfi1 << " s2 " << surfi2 << endl; - if (surfi1 != -1 && surfi2 != -1 && surfi1 != surfi2) - { - netgen::ProjectToEdge (geometry.GetSurface(surfi1), - geometry.GetSurface(surfi2), - hnewp); - // (*testout) << "Pointbetween, newp = " << hnewp << endl - // << ", err = " << sqrt (sqr (hnewp(0))+ sqr(hnewp(1)) + sqr (hnewp(2))) - 1 << endl; - newgi.edgenr = 1; - //(*testout) << "hnewp (a1) " << hnewp << endl; - } - else if (surfi1 != -1) - { - geometry.GetSurface (surfi1) -> Project (hnewp); - //(*testout) << "hnewp (a2) " << hnewp << endl; - } - - newp = hnewp; -}; - -Vec<3> RefinementSurfaces :: GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & ap1) const -{ - Vec<3> n1 = geometry.GetSurface (surfi1)->GetNormalVector (p); - Vec<3> n2 = geometry.GetSurface (surfi2)->GetNormalVector (p); - Vec<3> tau = Cross (n1, n2).Normalize(); - return tau; -} - -Vec<3> RefinementSurfaces :: GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const -{ - return geometry.GetSurface (surfi1)->GetNormalVector (p); -} - - - -void RefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi) const -{ - if (surfi != -1) - geometry.GetSurface (surfi) -> Project (p); -}; - -void RefinementSurfaces :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const -{ - netgen::ProjectToEdge (geometry.GetSurface(surfi1), - geometry.GetSurface(surfi2), - p); - -} - - } diff --git a/libsrc/csg/meshsurf.hpp b/libsrc/csg/meshsurf.hpp index 39fb24bb..11fec3bf 100644 --- a/libsrc/csg/meshsurf.hpp +++ b/libsrc/csg/meshsurf.hpp @@ -16,83 +16,30 @@ namespace netgen /// // Meshing2Surfaces (const Surface & asurf); /// - Meshing2Surfaces (const Surface & asurf, const MeshingParameters & mp, + Meshing2Surfaces (const CSGeometry& geo, + const Surface & asurf, + const MeshingParameters & mp, const Box<3> & aboundingbox); protected: /// - virtual void DefineTransformation (const Point3d & p1, const Point3d & p2, - const PointGeomInfo * geominfo1, - const PointGeomInfo * geominfo2); + void DefineTransformation(const Point<3> & p1, + const Point<3> & p2, + const PointGeomInfo * geominfo1, + const PointGeomInfo * geominfo2) override; /// - virtual void TransformToPlain (const Point3d & locpoint, - const MultiPointGeomInfo & geominfo, - Point2d & plainpoint, - double h, int & zone); + void TransformToPlain(const Point<3> & locpoint, + const MultiPointGeomInfo & geominfo, + Point<2> & plainpoint, + double h, int & zone) override; /// - virtual int TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, - PointGeomInfo & gi, - double h); + int TransformFromPlain(const Point<2>& plainpoint, + Point<3>& locpoint, + PointGeomInfo & gi, + double h) override; /// - virtual double CalcLocalH (const Point3d & p, double gh) const; + double CalcLocalH(const Point<3> & p, double gh) const override; }; - - - - /// - class MeshOptimize2dSurfaces : public MeshOptimize2d - { - /// - const CSGeometry & geometry; - - public: - /// - MeshOptimize2dSurfaces (const CSGeometry & ageometry); - - /// - virtual void ProjectPoint (INDEX surfind, Point<3> & p) const; - /// - virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const; - /// - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const; - }; - - - - class RefinementSurfaces : public Refinement - { - const CSGeometry & geometry; - - public: - RefinementSurfaces (const CSGeometry & ageometry); - virtual ~RefinementSurfaces (); - - 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; - - virtual void PointBetween (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; - - virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & ap1) const; - - virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const; - - - virtual void ProjectToSurface (Point<3> & p, int surfi) const; - - virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const; - - }; - } #endif diff --git a/libsrc/csg/polyhedra.cpp b/libsrc/csg/polyhedra.cpp index 1bb1d1d2..93ec0746 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 Array > & 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, - Array & 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 -{ - Array 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 triangle, perturbed 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 triangle, perturbed 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, - Array & 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, - Array & 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 (Array & /* 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(Array < Array * > & 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 (Array > & pts) const -{ - for (int i = 0; i < points.Size(); i++) - pts.Append (points[i]); -} - - -void Polyhedra :: AnalyzeSpecialPoint (const Point<3> & /* pt */, - Array > & /* 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 0a785042..4ba183d3 100644 --- a/libsrc/csg/polyhedra.hpp +++ b/libsrc/csg/polyhedra.hpp @@ -35,64 +35,81 @@ namespace netgen Face () { ; } Face (int pi1, int pi2, int pi3, - const Array > & points, + const NgArray > & points, int ainputnr); }; - Array > points; - Array faces; - Array planes; + NgArray > points; + NgArray faces; + NgArray planes; Box<3> poly_bbox; double eps_base1; 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, - Array & 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, - Array & surfind, double eps) const; + NgArray & surfind, double eps) const override; - virtual void CalcSpecialPoints (Array > & pts) const; + virtual void CalcSpecialPoints (NgArray > & pts) const override; virtual void AnalyzeSpecialPoint (const Point<3> & pt, - Array > & 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, Array & coeffs) const; - virtual void SetPrimitiveData (Array & 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); - void GetPolySurfs(Array < Array * > & polysurfs); + void GetPolySurfs(NgArray < NgArray * > & polysurfs); protected: int FaceBoxIntersection (int fnr, const BoxSphere<3> & box) const; diff --git a/libsrc/csg/python_csg.cpp b/libsrc/csg/python_csg.cpp index c82db668..f66f61c6 100644 --- a/libsrc/csg/python_csg.cpp +++ b/libsrc/csg/python_csg.cpp @@ -1,10 +1,14 @@ #ifdef NG_PYTHON -#include <../general/ngpython.hpp> -#include +#include "../general/ngpython.hpp" +#include "../core/python_ngcore.hpp" +#include "csg.hpp" +#include "../meshing/python_mesh.hpp" +#include "../general/gzstream.h" using namespace netgen; +using namespace pybind11::literals; namespace netgen { @@ -27,9 +31,11 @@ class SPSolid double red = 0, green = 0, blue = 1; bool transp = false; public: - enum optyp { TERM, SECTION, UNION, SUB }; + enum optyp { TERM, SECTION, UNION, SUB, EXISTING }; SPSolid (Solid * as) : solid(as), owner(true), op(TERM) { ; } + SPSolid (Solid * as, int /*dummy*/) + : solid(as), owner(false), op(EXISTING) { ; } ~SPSolid () { ; // if (owner) delete solid; @@ -166,7 +172,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) @@ -174,16 +181,16 @@ DLL_HEADER void ExportCSG(py::module &m) self.geompoints.Append (GeomPoint<2> (Point<2> (x,y))); return self.geompoints.Size()-1; })) - .def ("AddSegment", FunctionPointer - ([] (SplineGeometry<2> & self, int i1, int i2) - { - self.splines.Append (new LineSeg<2> (self.geompoints[i1], self.geompoints[i2])); - })) - .def ("AddSegment", FunctionPointer - ([] (SplineGeometry<2> & self, int i1, int i2, int i3) - { - self.splines.Append (new SplineSeg3<2> (self.geompoints[i1], self.geompoints[i2], self.geompoints[i3])); - })) + .def ("AddSegment", [] (SplineGeometry<2> & self, int i1, int i2, + string bcname, double maxh) + { + self.splines.Append (new LineSeg<2> (self.geompoints[i1], self.geompoints[i2], maxh, bcname)); + }, "p1"_a, "p2"_a, "bcname"_a="default", "maxh"_a=1e99) + .def ("AddSegment", [] (SplineGeometry<2> & self, int i1, int i2, + int i3, string bcname, double maxh) + { + self.splines.Append (new SplineSeg3<2> (self.geompoints[i1], self.geompoints[i2], self.geompoints[i3], bcname, maxh)); + }, "p1"_a, "p2"_a, "p3"_a, "bcname"_a="default", "maxh"_a=1e99) ; py::class_,shared_ptr>> (m,"SplineCurve3d") @@ -208,10 +215,10 @@ DLL_HEADER void ExportCSG(py::module &m) py::class_> (m, "SplineSurface", "A surface for co dim 2 integrals on the splines") - .def("__init__", FunctionPointer ([](SplineSurface* instance, shared_ptr base, py::list cuts) + .def(py::init([](shared_ptr base, py::list cuts) { auto primitive = dynamic_cast (base->GetSolid()->GetPrimitive()); - auto acuts = make_shared>>(); + auto acuts = make_shared>>(); for(int i = 0; i> sps(cuts[i]); @@ -221,12 +228,11 @@ DLL_HEADER void ExportCSG(py::module &m) if(sp) acuts->Append(shared_ptr(sp)); else - throw NgException("Cut must be SurfacePrimitive in constructor of SplineSurface!"); + throw Exception("Cut must be SurfacePrimitive in constructor of SplineSurface!"); } if(!primitive) - throw NgException("Base is not a SurfacePrimitive in constructor of SplineSurface!"); - new (instance) SplineSurface(shared_ptr(primitive),acuts); - py::object obj = py::cast(instance); + throw Exception("Base is not a SurfacePrimitive in constructor of SplineSurface!"); + return make_shared(shared_ptr(primitive),acuts); }),py::arg("base"), py::arg("cuts")=py::list()) .def("AddPoint", FunctionPointer ([] (SplineSurface & self, double x, double y, double z, bool hpref) @@ -319,21 +325,38 @@ DLL_HEADER void ExportCSG(py::module &m) Solid * sol = new Solid (torus); return make_shared (sol); })); - m.def ("Revolution", FunctionPointer([](Point<3> p1, Point<3> p2, - const SplineGeometry<2> & spline) - { - Revolution * rev = new Revolution (p1, p2, spline); - 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 ("Revolution", [](Point<3> p1, Point<3> p2, + shared_ptr> spline) + { + Revolution * rev = new Revolution (p1, p2, spline); + Solid * sol = new Solid(rev); + 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) { @@ -351,6 +374,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) { @@ -409,7 +461,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! if (py::extract(val).check()) mod_nr = py::extract (val)(); if (py::extract(val).check()) bcname = new string ( py::extract (val)()); - Array si; + NgArray si; mod_solid -> GetSolid() -> GetSurfaceIndices (si); // cout << "change bc on surfaces: " << si << " to " << mod_nr << endl; @@ -459,7 +511,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! self.GetTopLevelObject(tlonr) -> SetBCProp(surf->GetBase()->GetBCProperty()); self.GetTopLevelObject(tlonr) -> SetBCName(surf->GetBase()->GetBCName()); self.GetTopLevelObject(tlonr) -> SetMaxH(surf->GetBase()->GetMaxH()); - Array> non_midpoints; + NgArray> non_midpoints; for(auto spline : surf->GetSplines()) { non_midpoints.Append(spline->GetPoint(0)); @@ -494,12 +546,9 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! .def("CloseSurfaces", FunctionPointer ([] (CSGeometry & self, shared_ptr s1, shared_ptr s2, py::list aslices ) { - Array si1, si2; + NgArray si1, si2; s1->GetSolid()->GetSurfaceIndices (si1); s2->GetSolid()->GetSurfaceIndices (si2); - cout << "surface ids1 = " << si1 << endl; - cout << "surface ids2 = " << si2 << endl; - Flags flags; try @@ -531,7 +580,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! ([] (CSGeometry & self, shared_ptr s1, shared_ptr s2, int reflevels, shared_ptr domain_solid) { - Array si1, si2; + NgArray si1, si2; s1->GetSolid()->GetSurfaceIndices (si1); s2->GetSolid()->GetSurfaceIndices (si2); cout << "surface ids1 = " << si1 << endl; @@ -556,7 +605,7 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! ([] (CSGeometry & self, shared_ptr s1, shared_ptr s2, Transformation<3> trafo) { - Array si1, si2; + NgArray si1, si2; s1->GetSolid()->GetSurfaceIndices (si1); s2->GetSolid()->GetSurfaceIndices (si2); cout << "identify surfaces " << si1[0] << " and " << si2[0] << endl; @@ -569,10 +618,22 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! py::arg("solid1"), py::arg("solid2"), py::arg("trafo")=Transformation<3>(Vec<3>(0,0,0)) ) - - .def("AddPoint", [] (CSGeometry & self, Point<3> p, int index) -> CSGeometry& + .def("NameEdge", [] (CSGeometry & self, shared_ptr s1, shared_ptr s2, string name) { - self.AddUserPoint(CSGeometry::UserPoint(p, index)); + Array surfs1, surfs2; + s1->GetSolid()->ForEachSurface( [&surfs1] (Surface * s, bool inv) { surfs1.Append(s); }); + s2->GetSolid()->ForEachSurface( [&surfs2] (Surface * s, bool inv) { surfs2.Append(s); }); + for (auto s1 : surfs1) + for (auto s2 : surfs2) + self.named_edges[tuple(s1,s2)] = name; + }) + + .def("AddPoint", [] (CSGeometry & self, Point<3> p, variant index) -> CSGeometry& + { + if (auto pint = std::get_if (&index)) + self.AddUserPoint(CSGeometry::UserPoint(p, *pint)); + if (auto pstr = std::get_if (&index)) + self.AddUserPoint(CSGeometry::UserPoint(p, *pstr)); return self; }) @@ -615,11 +676,18 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! .def("Draw", FunctionPointer ([] (shared_ptr self) { - self->FindIdenticSurfaces(1e-6); + self->FindIdenticSurfaces(1e-8 * self->MaxSize()); self->CalcTriangleApproximation(0.01, 20); ng_geometry = self; }) ) + .def("GetSolids", [](CSGeometry& self) + { + py::list lst; + for(auto i : Range(self.GetSolids().Size())) + lst.append(make_shared(self.GetSolids()[i], 1234)); + return lst; + }) .def_property_readonly ("ntlo", &CSGeometry::GetNTopLevelObjects) .def("_visualizationData", [](shared_ptr csg_geo) { @@ -638,8 +706,8 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! auto surf = csg_geo->GetSurface(i); surfnames.push_back(surf->GetBCName()); } - csg_geo->FindIdenticSurfaces(1e-6); - csg_geo->CalcTriangleApproximation(0.01,100); + csg_geo->FindIdenticSurfaces(1e-8 * csg_geo->MaxSize()); + csg_geo->CalcTriangleApproximation(0.01,20); auto nto = csg_geo->GetNTopLevelObjects(); size_t np = 0; size_t ntrig = 0; @@ -683,26 +751,27 @@ However, when r = 0, the top part becomes a point(tip) and meshing fails! res["max"] = MoveToNumpy(max); return res; }, py::call_guard()) - ; - - m.def("GenerateMesh", FunctionPointer - ([](shared_ptr geo, MeshingParameters & param) + .def("GenerateMesh", [](shared_ptr geo, + MeshingParameters* pars, py::kwargs kwargs) { - auto dummy = make_shared(); - SetGlobalMesh (dummy); - dummy->SetGeometry(geo); + MeshingParameters mp; + if(pars) mp = *pars; + { + py::gil_scoped_acquire aq; + CreateMPfromKwargs(mp, kwargs); + } + auto mesh = make_shared(); + SetGlobalMesh (mesh); + mesh->SetGeometry(geo); ng_geometry = geo; geo->FindIdenticSurfaces(1e-8 * geo->MaxSize()); - try - { - geo->GenerateMesh (dummy, param); - } - catch (NgException ex) - { - cout << "Caught NgException: " << ex.What() << endl; - } - return dummy; - }),py::call_guard()) + auto result = geo->GenerateMesh (mesh, mp); + if(result != 0) + throw Exception("Meshing failed!"); + return mesh; + }, py::arg("mp") = nullptr, + meshingparameter_description.c_str(), + py::call_guard()) ; m.def("Save", FunctionPointer diff --git a/libsrc/csg/revolution.cpp b/libsrc/csg/revolution.cpp index dac2e277..cc872cec 100644 --- a/libsrc/csg/revolution.cpp +++ b/libsrc/csg/revolution.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -48,17 +49,19 @@ namespace netgen isfirst(first), islast(last), spline(&spline_in), p0(p), v_axis(vec), id(id_in) { deletable = false; + maxh = spline_in.GetMaxh(); + bcname = spline_in.GetBCName(); Init(); } - RevolutionFace :: RevolutionFace(const Array & raw_data) + RevolutionFace :: RevolutionFace(const NgArray & raw_data) { deletable = true; int pos = 0; - Array< Point<2> > p(3); + NgArray< Point<2> > p(3); int stype = int(raw_data[pos]); pos++; @@ -305,6 +308,7 @@ namespace netgen } else { + hesse = 0; (*testout) << "hesse4: " << hesse <MaxCurvature(); - Array < Point<2> > checkpoints; + NgArray < Point<2> > checkpoints; const SplineSeg3<2> * ss3 = dynamic_cast *>(spline); const LineSeg<2> * ls = dynamic_cast *>(spline); @@ -385,7 +389,7 @@ namespace netgen // find smallest y value of spline: - Array testt; + NgArray testt; if(!isfirst) testt.Append(0); @@ -623,7 +627,7 @@ namespace netgen - void RevolutionFace :: GetRawData(Array & data) const + void RevolutionFace :: GetRawData(NgArray & data) const { data.DeleteAll(); spline->GetRawData(data); @@ -639,10 +643,10 @@ namespace netgen Revolution :: Revolution(const Point<3> & p0_in, const Point<3> & p1_in, - const SplineGeometry<2> & spline_in) : - p0(p0_in), p1(p1_in) + shared_ptr> spline_in) : + p0(p0_in), p1(p1_in), splinegeo(spline_in) { - auto nsplines = spline_in.GetNSplines(); + auto nsplines = spline_in->GetNSplines(); surfaceactive.SetSize(0); surfaceids.SetSize(0); @@ -650,25 +654,42 @@ namespace netgen v_axis.Normalize(); - if(spline_in.GetSpline(0).StartPI()(1) <= 0. && - spline_in.GetSpline(nsplines-1).EndPI()(1) <= 0.) + if(spline_in->GetSpline(0).StartPI()(1) <= 0. && + spline_in->GetSpline(nsplines-1).EndPI()(1) <= 0.) type = 2; - else if (Dist(spline_in.GetSpline(0).StartPI(), - spline_in.GetSpline(nsplines-1).EndPI()) < 1e-7) + else if (Dist(spline_in->GetSpline(0).StartPI(), + spline_in->GetSpline(nsplines-1).EndPI()) < 1e-7) type = 1; else cerr << "Surface of revolution cannot be constructed" << endl; - for(int i=0; iGetNSplines(); i++) { - RevolutionFace * face = new RevolutionFace(spline_in.GetSpline(i), - p0,v_axis, - type==2 && i==0, - type==2 && i==spline_in.GetNSplines()-1); - faces.Append(face); - surfaceactive.Append(1); + faces.Append(new RevolutionFace + (spline_in->GetSpline(i), + p0,v_axis, + type==2 && i==0, + type==2 && i==spline_in->GetNSplines()-1)); + 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() @@ -717,7 +738,7 @@ namespace netgen return DOES_INTERSECT; else { - Array < Point<3> > pext(2); + NgArray < Point<3> > pext(2); Point<3> p; pext[0] = box.PMin(); @@ -763,15 +784,16 @@ 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; const double b = -randomx; const double c = -a*p2d(0)-b*p2d(1); - Array < Point<2> > points; + NgArray < Point<2> > points; //(*testout) << "face intersections at: " << endl; for(int i=0; i & p, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { for (int j = 0; j < faces.Size(); j++) if (faces[j] -> PointInFace(p, eps)) @@ -822,7 +844,7 @@ namespace netgen return pInSolid; } - Array intersecting_faces; + NgArray intersecting_faces; for(int i=0; iPointInFace(p,eps)) // == DOES_INTERSECT) @@ -929,6 +951,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 879829fd..39ab04ee 100644 --- a/libsrc/csg/revolution.hpp +++ b/libsrc/csg/revolution.hpp @@ -23,9 +23,9 @@ namespace netgen mutable Vector spline_coefficient_shifted; - Array < Vec<2>* > checklines_vec; - Array < Point<2>* > checklines_start; - Array < Vec<2>* > checklines_normal; + NgArray < Vec<2>* > checklines_vec; + NgArray < Point<2>* > checklines_start; + NgArray < Vec<2>* > checklines_normal; private: void Init (void); @@ -44,7 +44,7 @@ namespace netgen bool last = false, const int id_in = 0); - RevolutionFace(const Array & raw_data); + RevolutionFace(const NgArray & raw_data); // default constructor for archive RevolutionFace() {} @@ -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; @@ -87,7 +90,7 @@ namespace netgen /* INSOLID_TYPE */ bool PointInFace (const Point<3> & p, const double eps) const; - void GetRawData(Array & data) const; + void GetRawData(NgArray & data) const; }; @@ -112,13 +115,14 @@ namespace netgen Array faces; + shared_ptr> splinegeo; mutable int intersecting_face; public: Revolution(const Point<3> & p0_in, const Point<3> & p1_in, - const SplineGeometry<2> & spline_in); + shared_ptr> spline_in); // default constructor for archive Revolution() {} @@ -143,7 +147,7 @@ namespace netgen double eps) const; virtual void GetTangentialSurfaceIndices (const Point<3> & p, - Array & surfind, double eps) const; + NgArray & surfind, double eps) const; virtual INSOLID_TYPE VecInSolid (const Point<3> & p, const Vec<3> & v, @@ -155,7 +159,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 0ad13db9..8dc1b7e5 100644 --- a/libsrc/csg/singularref.cpp +++ b/libsrc/csg/singularref.cpp @@ -41,7 +41,7 @@ void SingularEdge :: FindPointsOnEdge (class Mesh & mesh) segms.SetSize(0); - Array si1, si2; + NgArray si1, si2; sol1->GetSurfaceIndices (si1); sol2->GetSurfaceIndices (si2); @@ -150,7 +150,7 @@ SingularPoint :: SingularPoint (double abeta, void SingularPoint :: FindPoints (class Mesh & mesh) { points.SetSize(0); - Array surfk, surf; + NgArray surfk, surf; for (PointIndex pi = PointIndex::BASE; @@ -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/singularref.hpp b/libsrc/csg/singularref.hpp index c6984dc1..6615c93d 100644 --- a/libsrc/csg/singularref.hpp +++ b/libsrc/csg/singularref.hpp @@ -19,7 +19,7 @@ namespace netgen /** Singular Face. - Causes a bounday layer mesh refinement. + Causes a boundary layer mesh refinement. All elements in subdomain domnr will get a boundary layer on faces sharing the solid sol */ @@ -29,8 +29,8 @@ namespace netgen int domnr; const Solid *sol; double factor; - // Array > points; - // Array segms; + // NgArray > points; + // NgArray segms; public: SingularFace (int adomnr, const Solid * asol, double sf) : domnr(adomnr), sol(asol), factor(sf) { ; } @@ -47,8 +47,8 @@ namespace netgen int domnr; const CSGeometry& geom; const Solid *sol1, *sol2; - Array > points; - Array segms; + NgArray > points; + NgArray segms; double factor; double maxhinit; @@ -68,7 +68,7 @@ namespace netgen public: double beta; const Solid *sol1, *sol2, *sol3; - Array > points; + NgArray > points; double factor; public: diff --git a/libsrc/csg/solid.cpp b/libsrc/csg/solid.cpp index c61f4e39..a0ae3b0b 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,73 @@ 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); + } + throw Exception("PointInSolid: invalid op"); + } + + + 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); + } + throw Exception("VecInSolid: invalid op"); + } + + // 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); + } + throw Exception("VecInSolid2: invalid op"); + } + + + 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 +262,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 +286,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 +312,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 +340,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 +377,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 @@ -557,14 +664,14 @@ namespace netgen - void Solid :: Boundaries (const Point<3> & p, Array & bounds) const + void Solid :: Boundaries (const Point<3> & p, NgArray & bounds) const { int in, strin; bounds.SetSize (0); RecBoundaries (p, bounds, in, strin); } - void Solid :: RecBoundaries (const Point<3> & p, Array & bounds, + void Solid :: RecBoundaries (const Point<3> & p, NgArray & bounds, int & in, int & strin) const { switch (op) @@ -585,7 +692,7 @@ namespace netgen case SECTION: { int i, in1, in2, strin1, strin2; - Array bounds1, bounds2; + NgArray bounds1, bounds2; s1 -> RecBoundaries (p, bounds1, in1, strin1); s2 -> RecBoundaries (p, bounds2, in2, strin2); @@ -604,7 +711,7 @@ namespace netgen case UNION: { int i, in1, in2, strin1, strin2; - Array bounds1, bounds2; + NgArray bounds1, bounds2; s1 -> RecBoundaries (p, bounds1, in1, strin1); s2 -> RecBoundaries (p, bounds2, in2, strin2); @@ -638,18 +745,20 @@ namespace netgen } - void Solid :: TangentialSolid (const Point<3> & p, Solid *& tansol, Array & 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, Array & surfids, - int & in, int & strin, double eps) const + void Solid :: RecTangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const { tansol = NULL; @@ -671,7 +780,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 +795,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 +821,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 +849,24 @@ namespace netgen - void Solid :: TangentialSolid2 (const Point<3> & p, - const Vec<3> & t, - Solid *& tansol, Array & 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, Array & surfids, - int & in, int & strin, double eps) const + Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const { - tansol = NULL; + tansol = nullptr; switch (op) { @@ -774,8 +885,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 +897,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 +912,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 +933,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 +965,28 @@ namespace netgen - void Solid :: TangentialSolid3 (const Point<3> & p, - const Vec<3> & t, const Vec<3> & t2, - Solid *& tansol, Array & 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, Array & surfids, - int & in, int & strin, double eps) const + Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const { - tansol = NULL; + tansol = nullptr; switch (op) { @@ -882,8 +996,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 +1008,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 +1023,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 +1044,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 +1079,13 @@ namespace netgen - void Solid :: TangentialEdgeSolid (const Point<3> & p, - const Vec<3> & t, const Vec<3> & t2, const Vec<3> & m, - Solid *& tansol, Array & 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 +1093,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, Array & surfids, - int & in, int & strin, double eps) const + Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const { tansol = NULL; @@ -1005,8 +1122,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 +1134,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 +1149,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 +1170,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 +1212,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 +1262,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 +1277,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 +1292,7 @@ namespace netgen } case SUB: { - int in1, strin1; + bool in1, strin1; s1 -> RecEdge (p, v, in1, strin1, faces, eps); in = !strin1; strin = !in1; @@ -1450,13 +1571,13 @@ namespace netgen return 0; } - void Solid :: GetSurfaceIndices (Array & surfind) const + void Solid :: GetSurfaceIndices (NgArray & surfind) const { surfind.SetSize (0); RecGetSurfaceIndices (surfind); } - void Solid :: RecGetSurfaceIndices (Array & surfind) const + void Solid :: RecGetSurfaceIndices (NgArray & surfind) const { switch (op) { @@ -1533,13 +1654,13 @@ namespace netgen } - void Solid :: GetTangentialSurfaceIndices (const Point<3> & p, Array & surfind, double eps) const + void Solid :: GetTangentialSurfaceIndices (const Point<3> & p, NgArray & surfind, double eps) const { surfind.SetSize (0); RecGetTangentialSurfaceIndices (p, surfind, eps); } - void Solid :: RecGetTangentialSurfaceIndices (const Point<3> & p, Array & surfind, double eps) const + void Solid :: RecGetTangentialSurfaceIndices (const Point<3> & p, NgArray & surfind, double eps) const { switch (op) { @@ -1576,14 +1697,14 @@ namespace netgen void Solid :: GetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { surfind.SetSize (0); RecGetTangentialSurfaceIndices2 (p, v, surfind, eps); } void Solid :: RecGetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { switch (op) { @@ -1628,14 +1749,14 @@ namespace netgen void Solid :: GetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { surfind.SetSize (0); RecGetTangentialSurfaceIndices3 (p, v, v2, surfind, eps); } void Solid :: RecGetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { switch (op) { @@ -1697,7 +1818,7 @@ namespace netgen void Solid :: RecGetTangentialEdgeSurfaceIndices (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, const Vec<3> & m, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { switch (op) { @@ -1801,7 +1922,7 @@ namespace netgen } - void Solid :: CalcOnePrimitiveSpecialPoints (const Box<3> & box, Array > & pts) const + void Solid :: CalcOnePrimitiveSpecialPoints (const Box<3> & box, NgArray > & pts) const { double eps = 1e-8 * box.Diam (); @@ -1814,7 +1935,7 @@ namespace netgen } } - void Solid :: RecCalcOnePrimitiveSpecialPoints (Array > & pts) const + void Solid :: RecCalcOnePrimitiveSpecialPoints (NgArray > & pts) const { switch (op) { @@ -1842,5 +1963,6 @@ namespace netgen - BlockAllocator Solid :: ball(sizeof (Solid)); + // BlockAllocator Solid :: ball(sizeof (Solid)); + shared_ptr Solid :: ball = make_shared(sizeof (Solid)); } diff --git a/libsrc/csg/solid.hpp b/libsrc/csg/solid.hpp index 193cb363..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 { @@ -81,14 +101,14 @@ namespace netgen void IterateSolid (SolidIterator & it, bool only_once = 0); - void Boundaries (const Point<3> & p, Array & bounds) const; + void Boundaries (const Point<3> & p, NgArray & bounds) const; int NumPrimitives () const; - void GetSurfaceIndices (Array & surfind) const; + void GetSurfaceIndices (NgArray & surfind) const; void GetSurfaceIndices (IndexSet & iset) const; - void GetTangentialSurfaceIndices (const Point<3> & p, Array & surfids, double eps) const; - void GetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, Array & surfids, double eps) const; - void GetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, Array & surfids, double eps) const; + void GetTangentialSurfaceIndices (const Point<3> & p, NgArray & surfids, double eps) const; + void GetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, NgArray & surfids, double eps) const; + void GetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, NgArray & surfids, double eps) const; void ForEachSurface (const std::function & lambda, bool inv = false) const; @@ -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, Array & 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, Array & 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, Array & 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,12 +164,12 @@ 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, Array & 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, Array > & pts) const; + void CalcOnePrimitiveSpecialPoints (const Box<3> & box, NgArray > & pts) const; /// int Edge (const Point<3> & p, const Vec<3> & v, double eps) const; @@ -161,58 +192,58 @@ namespace netgen static Solid * CreateSolid (istream & ist, const SymbolTable & solids); - static BlockAllocator ball; + static shared_ptr ball; void * operator new(size_t /* s */) { - return ball.Alloc(); + return ball->Alloc(); } void operator delete (void * p) { - ball.Free (p); + ball->Free (p); } protected: /// - void RecBoundaries (const Point<3> & p, Array & bounds, + void RecBoundaries (const Point<3> & p, NgArray & bounds, int & in, int & strin) const; /// - void RecTangentialSolid (const Point<3> & p, Solid *& tansol, Array & surfids, - int & in, int & strin, double eps) const; + void RecTangentialSolid (const Point<3> & p, Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const; void RecTangentialSolid2 (const Point<3> & p, const Vec<3> & vec, - Solid *& tansol, Array & surfids, - int & in, int & strin, double eps) const; + Solid *& tansol, NgArray & surfids, + bool & in, bool & strin, double eps) const; /// void RecTangentialSolid3 (const Point<3> & p, const Vec<3> & vec,const Vec<3> & vec2, - Solid *& tansol, Array & surfids, - int & in, int & strin, double eps) const; + Solid *& tansol, NgArray & surfids, + 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, Array & surfids, - int & in, int & strin, double eps) const; + Solid *& tansol, NgArray & surfids, + 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); /// Solid * RecGetReducedSolid (const BoxSphere<3> & box, INSOLID_TYPE & in) const; /// - void RecGetSurfaceIndices (Array & surfind) const; - void RecGetTangentialSurfaceIndices (const Point<3> & p, Array & surfids, double eps) const; - void RecGetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, Array & surfids, double eps) const; + void RecGetSurfaceIndices (NgArray & surfind) const; + void RecGetTangentialSurfaceIndices (const Point<3> & p, NgArray & surfids, double eps) const; + void RecGetTangentialSurfaceIndices2 (const Point<3> & p, const Vec<3> & v, NgArray & surfids, double eps) const; void RecGetTangentialSurfaceIndices3 (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, - Array & surfids, double eps) const; + NgArray & surfids, double eps) const; void RecGetTangentialEdgeSurfaceIndices (const Point<3> & p, const Vec<3> & v, const Vec<3> & v2, const Vec<3> & m, - Array & surfids, double eps) const; + NgArray & surfids, double eps) const; void RecGetSurfaceIndices (IndexSet & iset) const; - void RecCalcOnePrimitiveSpecialPoints (Array > & pts) const; + void RecCalcOnePrimitiveSpecialPoints (NgArray > & pts) const; friend class SolidIterator; friend class ClearVisitedIt; @@ -234,7 +265,7 @@ namespace netgen class ReducePrimitiveIterator : public SolidIterator { - const BoxSphere<3> & box; + BoxSphere<3> box; public: ReducePrimitiveIterator (const BoxSphere<3> & abox) : SolidIterator(), box(abox) { ; } diff --git a/libsrc/csg/specpoin.cpp b/libsrc/csg/specpoin.cpp index 13187ace..b99e054c 100644 --- a/libsrc/csg/specpoin.cpp +++ b/libsrc/csg/specpoin.cpp @@ -21,7 +21,7 @@ namespace netgen { - Array > boxes; + DLL_HEADER NgArray > boxes; // for visualization void ProjectToEdge (const Surface * f1, const Surface * f2, Point<3> & hp); @@ -64,7 +64,7 @@ namespace netgen } - static Array numprim_hist; + // static NgArray numprim_hist; SpecialPointCalculation :: SpecialPointCalculation () { @@ -73,10 +73,10 @@ namespace netgen void SpecialPointCalculation :: CalcSpecialPoints (const CSGeometry & ageometry, - Array & apoints) + NgArray & apoints) { - static int timer = NgProfiler::CreateTimer ("CSG: find special points"); - NgProfiler::RegionTimer reg (timer); + // static int timer = NgProfiler::CreateTimer ("CSG: find special points"); + // NgProfiler::RegionTimer reg (timer); geometry = &ageometry; @@ -100,8 +100,8 @@ namespace netgen box.CalcDiamCenter(); PrintMessage (3, "main-solids: ", geometry->GetNTopLevelObjects()); - numprim_hist.SetSize (geometry->GetNSurf()+1); - numprim_hist = 0; + // numprim_hist.SetSize (geometry->GetNSurf()+1); + // numprim_hist = 0; for (int i = 0; i < geometry->GetNTopLevelObjects(); i++) { @@ -112,7 +112,7 @@ namespace netgen if (tlo->GetSolid()) { - Array > hpts; + NgArray > hpts; tlo->GetSolid()->CalcOnePrimitiveSpecialPoints (box, hpts); // if (hpts.Size()) // cout << "oneprimitivespecialpoints = " << hpts << endl; @@ -161,10 +161,12 @@ namespace netgen PrintMessage (3, "Found points ", apoints.Size()); + /* for (int i = 0; i < boxesinlevel.Size(); i++) (*testout) << "level " << i << " has " << boxesinlevel[i] << " boxes" << endl; (*testout) << "numprim_histogramm = " << endl << numprim_hist << endl; + */ } @@ -184,8 +186,8 @@ namespace netgen if (multithread.terminate) { - *testout << "boxes = " << boxes << endl; - *testout << "boxesinlevel = " << boxesinlevel << endl; + // *testout << "boxes = " << boxes << endl; + // *testout << "boxesinlevel = " << boxesinlevel << endl; throw NgException ("Meshing stopped"); } @@ -210,17 +212,18 @@ namespace netgen bool possiblecrossp, possibleexp; // possible cross or extremalpoint bool surecrossp = 0, sureexp = 0; // sure ... - // static Array locsurf; // attention: array is static - ArrayMem locsurf; + // static NgArray locsurf; // attention: array is static + NgArrayMem locsurf; // static int cntbox = 0; // cntbox++; - + /* if (level <= boxesinlevel.Size()) boxesinlevel.Elem(level)++; else boxesinlevel.Append (1); - + */ + /* numprim = sol -> NumPrimitives(); sol -> GetSurfaceIndices (locsurf); @@ -233,7 +236,7 @@ namespace netgen (*testout) << "numprim = " << numprim << endl; #endif - numprim_hist[numprim]++; + // numprim_hist[numprim]++; Point<3> p = box.Center(); @@ -273,8 +276,8 @@ namespace netgen if (nquad == numprim && nplane >= numprim-1) { - Array > pts; - Array surfids; + NgArray > pts; + NgArray surfids; for (int k1 = 0; k1 < numprim - 2; k1++) for (int k2 = k1 + 1; k2 < numprim - 1; k2++) @@ -286,36 +289,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 +327,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 +360,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; } } } @@ -392,11 +375,9 @@ namespace netgen if (nsphere == numprim) // && calccp == false) { - Array > pts; - Array surfids; + 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 +390,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 +406,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 +423,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 +482,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; } @@ -541,7 +528,7 @@ namespace netgen BoxSphere<3> boxp (pp, pp); boxp.Increase (1e-3*size); boxp.CalcDiamCenter(); - Array locsurf2; + NgArray locsurf2; geometry -> GetIndependentSurfaceIndices (sol, boxp, locsurf2); @@ -608,9 +595,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 +877,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); @@ -1132,7 +1128,7 @@ namespace netgen ComputeCrossPoints (const Plane * plane1, const Plane * plane2, const Plane * plane3, - Array > & pts) + NgArray > & pts) { Mat<3> mat; Vec<3> rhs, sol; @@ -1174,7 +1170,7 @@ namespace netgen ComputeCrossPoints (const Plane * plane1, const Plane * plane2, const QuadraticSurface * quadric, - Array > & pts) + NgArray > & pts) { Mat<2,3> mat; Mat<3,2> inv; @@ -1244,7 +1240,7 @@ namespace netgen ComputeCrossPoints (const Sphere * sphere1, const Sphere * sphere2, const Sphere * sphere3, - Array > & pts) + NgArray > & pts) { Mat<2,3> mat; Mat<3,2> inv; @@ -1327,7 +1323,7 @@ namespace netgen void SpecialPointCalculation :: ComputeExtremalPoints (const Plane * plane, const QuadraticSurface * quadric, - Array > & pts) + NgArray > & pts) { // 3 equations: // surf1 = 0 <===> plane_a + plane_b x = 0; @@ -1416,7 +1412,7 @@ namespace netgen void SpecialPointCalculation :: ComputeExtremalPoints (const Sphere * sphere1, const Sphere * sphere2, - Array > & pts) + NgArray > & pts) { // 3 equations: // surf1 = 0 <===> |x-c1|^2 - r1^2 = 0; @@ -1534,7 +1530,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; + } @@ -1666,19 +1716,19 @@ namespace netgen void SpecialPointCalculation :: AnalyzeSpecialPoints (const CSGeometry & ageometry, - Array & apoints, - Array & specpoints) + NgArray & apoints, + NgArray & specpoints) { static int timer = NgProfiler::CreateTimer ("CSG: analyze special points"); NgProfiler::RegionTimer reg (timer); - Array surfind, rep_surfind, surfind2, rep_surfind2, surfind3; + NgArray surfind, rep_surfind, surfind2, rep_surfind2, surfind3; - Array > normalvecs; + NgArray > normalvecs; Vec<3> nsurf = 0.0; - Array specpoint2point; + NgArray specpoint2point; specpoints.SetSize (0); geometry = &ageometry; @@ -1698,7 +1748,7 @@ namespace netgen */ Vec<3> dir(1.2, 1.7, 0.9); - Array coord(apoints.Size()); + NgArray coord(apoints.Size()); for (int i = 0; i < apoints.Size(); i++) coord[i] = dir * Vec<3> (apoints[i]); @@ -1717,7 +1767,7 @@ namespace netgen (*testout) << "points = " << apoints << endl; Point3dTree searchtree (bbox.PMin(), bbox.PMax()); - Array locsearch; + NgArray locsearch; for (int si = 0; si < ageometry.GetNTopLevelObjects(); si++) { @@ -1739,9 +1789,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 +1817,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 +1866,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 +1911,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); @@ -1882,7 +1933,7 @@ namespace netgen #ifdef DEVELOP (*testout) << "surfind2 = " << endl << surfind2 << endl; #endif - Array surfind2_aux(surfind2); + NgArray surfind2_aux(surfind2); ageometry.GetIndependentSurfaceIndices (surfind2_aux); #ifdef DEVELOP (*testout) << "surfind2,rep = " << endl << surfind2_aux << endl; @@ -1911,31 +1962,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 +1999,6 @@ namespace netgen if (cnt_tang_faces < 1) ok = false; - delete locsol2; if (!ok) continue; } @@ -1973,16 +2023,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 +2073,8 @@ namespace netgen !locsol->VectorStrictIn (p, t2b)); */ - if (isface) - { - cnts++; - } + if (isface) + cnts++; } if (cnts < 2) isedge = 0; } @@ -2014,7 +2093,7 @@ namespace netgen for (int m = 0; m < locsearch.Size(); m++) { - if (Dist2 (specpoints[locsearch[m]].p, apoints[i]) < 1e-10*geomsize + if (Dist2 (specpoints[locsearch[m]].p, apoints[i]) < sqr(1e-8*geomsize) && Abs2(specpoints[locsearch[m]].v - t) < 1e-8) { spi = locsearch[m]; @@ -2054,42 +2133,11 @@ namespace netgen } } - - delete locsol; } } - /* - BitArray testuncond (specpoints.Size()); - testuncond.Clear(); - for(int i = 0; i same; - same.Append(i); - - for(int j = i+1; j * points; + NgArray * points; /// - Array boxesinlevel; + NgArray boxesinlevel; /// double size; /// - double relydegtest; // maximal dimension of bisection intervall for + double relydegtest; // maximal dimension of bisection interval for /// test of degeneration parameters double cpeps1, epeps1, epeps2, epspointdist2; @@ -102,11 +102,11 @@ namespace netgen /// void CalcSpecialPoints (const CSGeometry & ageometry, - Array & points); + NgArray & points); /// void AnalyzeSpecialPoints (const CSGeometry & geometry, - Array & points, - Array & specpoints); + NgArray & points, + NgArray & specpoints); protected: /// @@ -161,27 +161,30 @@ namespace netgen void ComputeExtremalPoints (const Plane * plane, const QuadraticSurface * quadric, - Array > & pts); + NgArray > & pts); void ComputeExtremalPoints (const Sphere * sphere1, const Sphere * sphere2, - Array > & pts); + NgArray > & pts); + bool ComputeExtremalPoints (const RevolutionFace * rev1, + const RevolutionFace * rev2, + NgArray > & pts); void ComputeCrossPoints (const Plane * plane1, const Plane * plane2, const Plane * plane3, - Array > & pts); + NgArray > & pts); void ComputeCrossPoints (const Plane * plane1, const Plane * plane2, const QuadraticSurface * quadratic, - Array > & pts); + NgArray > & pts); void ComputeCrossPoints (const Sphere * sphere1, const Sphere * sphere2, const Sphere * sphere3, - Array > & pts); + NgArray > & pts); }; } diff --git a/libsrc/csg/spline3d.hpp b/libsrc/csg/spline3d.hpp index db3a40c5..39508678 100644 --- a/libsrc/csg/spline3d.hpp +++ b/libsrc/csg/spline3d.hpp @@ -27,7 +27,7 @@ namespace netgen class spline3d { /// - Array segments; + NgArray segments; public: /// diff --git a/libsrc/csg/splinesurface.cpp b/libsrc/csg/splinesurface.cpp index 6134b1f4..861838c2 100644 --- a/libsrc/csg/splinesurface.cpp +++ b/libsrc/csg/splinesurface.cpp @@ -1,5 +1,6 @@ #include +#include namespace netgen { @@ -36,11 +37,11 @@ void SplineSurface :: AppendPoint(const Point<3> & p, const double reffac, const return "default"; } - const shared_ptr>> SplineSurface :: CreateCuttingSurfaces() + const shared_ptr>> SplineSurface :: CreateCuttingSurfaces() { if(all_cuts) return all_cuts; - auto cuttings = make_shared>>(); + auto cuttings = make_shared>>(); for (auto cut : *cuts) cuttings->Append(cut); for(int i = 0; i> geompoints; - Array>> splines; - Array bcnames; - Array maxh; + NgArray> geompoints; + NgArray>> splines; + NgArray bcnames; + NgArray maxh; shared_ptr baseprimitive; - shared_ptr>> cuts; - shared_ptr>> all_cuts; + shared_ptr>> cuts; + shared_ptr>> all_cuts; public: - SplineSurface(shared_ptr abaseprimitive, shared_ptr>> acuts) : + SplineSurface(shared_ptr abaseprimitive, shared_ptr>> acuts) : OneSurfacePrimitive(), baseprimitive(abaseprimitive), cuts(acuts) { ; } // default constructor for archive @@ -25,7 +25,7 @@ namespace netgen const auto & GetSplines() const { return splines; } int GetNSplines() const { return splines.Size(); } - const Array>& GetPoints() const { return geompoints; } + const NgArray>& GetPoints() const { return geompoints; } string GetSplineType(const int i) const { return splines[i]->GetType(); } SplineSeg<3> & GetSpline(const int i) { return *splines[i]; } const SplineSeg<3> & GetSpline(const int i) const { return *splines[i]; } @@ -37,8 +37,8 @@ namespace netgen DLL_HEADER void AppendPoint(const Point<3> & p, const double reffac = 1., const bool hpref=false); void AppendSegment(shared_ptr> spline, string & bcname, double amaxh = -1); - const shared_ptr>> CreateCuttingSurfaces(); - const shared_ptr>> GetCuts() const { return all_cuts; } + const shared_ptr>> CreateCuttingSurfaces(); + const shared_ptr>> GetCuts() const { return all_cuts; } const shared_ptr GetBase() const { return baseprimitive; } virtual void Project (Point<3> & p3d) const { baseprimitive->Project(p3d); } @@ -50,7 +50,7 @@ namespace netgen { return baseprimitive->HesseNorm(); } virtual Point<3> GetSurfacePoint () const { return baseprimitive->GetSurfacePoint(); } - virtual void CalcSpecialPoints(Array> & pts) const + virtual void CalcSpecialPoints(NgArray> & pts) const { baseprimitive->CalcSpecialPoints(pts); } virtual INSOLID_TYPE BoxInSolid(const BoxSphere<3> & box) const @@ -67,7 +67,7 @@ namespace netgen virtual void CalcGradient (const Point<3> & point, Vec<3> & grad) const; virtual double HesseNorm () const; virtual Point<3> GetSurfacePoint () const; - virtual void CalcSpecialPoints(Array> & pts) const; + virtual void CalcSpecialPoints(NgArray> & pts) const; virtual INSOLID_TYPE BoxInSolid(const BoxSphere<3> & box) const; diff --git a/libsrc/csg/surface.cpp b/libsrc/csg/surface.cpp index f829840b..e31e9fce 100644 --- a/libsrc/csg/surface.cpp +++ b/libsrc/csg/surface.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -16,6 +17,7 @@ Surface :: Surface () strcpy (name, "noname"); bcprop = -1; bcname = "default"; + inverse = false; } Surface :: ~Surface() @@ -230,13 +232,13 @@ void Primitive :: SetSurfaceId (int i, int id) void Primitive :: GetPrimitiveData (const char *& classname, - Array & coeffs) const + NgArray & coeffs) const { classname = "undef"; coeffs.SetSize (0); } -void Primitive :: SetPrimitiveData (Array & coeffs) +void Primitive :: SetPrimitiveData (NgArray & coeffs) { ; } @@ -256,7 +258,7 @@ Primitive * Primitive :: CreatePrimitive (const char * classname) stringstream ost; - ost << "Primitve::CreatePrimitive not implemented for " << classname << endl; + ost << "Primitive::CreatePrimitive not implemented for " << classname << endl; throw NgException (ost.str()); } @@ -264,7 +266,7 @@ Primitive * Primitive :: CreatePrimitive (const char * classname) Primitive * Primitive :: Copy () const { stringstream ost; - ost << "Primitve::Copy not implemented for " << typeid(*this).name() << endl; + ost << "Primitive::Copy not implemented for " << typeid(*this).name() << endl; throw NgException (ost.str()); } @@ -272,12 +274,12 @@ Primitive * Primitive :: Copy () const void Primitive :: Transform (Transformation<3> & trans) { stringstream ost; - ost << "Primitve::Transform not implemented for " << typeid(*this).name() << endl; + ost << "Primitive::Transform not implemented for " << typeid(*this).name() << endl; throw NgException (ost.str()); } void Primitive :: GetTangentialSurfaceIndices (const Point<3> & p, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { for (int j = 0; j < GetNSurfaces(); j++) if (fabs (GetSurface(j).CalcFunctionValue (p)) < eps) @@ -288,7 +290,7 @@ void Primitive :: GetTangentialSurfaceIndices (const Point<3> & p, void Primitive :: GetTangentialVecSurfaceIndices (const Point<3> & p, const Vec<3> & v, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { cout << "get tangvecsurfind not implemented" << endl; surfind.SetSize (0); @@ -296,7 +298,7 @@ GetTangentialVecSurfaceIndices (const Point<3> & p, const Vec<3> & v, void Primitive :: GetTangentialVecSurfaceIndices2 (const Point<3> & p, const Vec<3> & v1, const Vec<3> & v2, - Array & surfind, double eps) const + NgArray & surfind, double eps) const { for (int j = 0; j < GetNSurfaces(); j++) { @@ -427,11 +429,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 39a6ceee..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 @@ -241,8 +239,8 @@ namespace netgen class Primitive { protected: - Array surfaceids; - Array surfaceactive; + NgArray surfaceids; + NgArray surfaceactive; public: @@ -268,7 +266,7 @@ namespace netgen double eps) const = 0; virtual void GetTangentialSurfaceIndices (const Point<3> & p, - Array & surfind, double eps) const; + NgArray & surfind, double eps) const; virtual INSOLID_TYPE VecInSolid (const Point<3> & p, const Vec<3> & v, @@ -293,16 +291,20 @@ 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, - Array & surfind, double eps) const; + 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, - Array & surfind, double eps) const; + NgArray & surfind, double eps) const; - virtual void CalcSpecialPoints (Array > & /* pts */) const { ; } + virtual void CalcSpecialPoints (NgArray > & /* pts */) const { ; } virtual void AnalyzeSpecialPoint (const Point<3> & /* pt */, - Array > & /* specpts */) const { ; } + NgArray > & /* specpts */) const { ; } virtual Vec<3> SpecialPointTangentialVector (const Point<3> & /* p */, int /* s1 */, int /* s2 */) const { return Vec<3> (0,0,0); } @@ -318,8 +320,8 @@ namespace netgen virtual int SurfaceInverted (int /* i */ = 0) const { return 0; } virtual void GetPrimitiveData (const char *& classname, - Array & coeffs) const; - virtual void SetPrimitiveData (Array & coeffs); + NgArray & coeffs) const; + virtual void SetPrimitiveData (NgArray & coeffs); static Primitive * CreatePrimitive (const char * classname); diff --git a/libsrc/csg/triapprox.cpp b/libsrc/csg/triapprox.cpp index 0c4f2b14..a45211c6 100644 --- a/libsrc/csg/triapprox.cpp +++ b/libsrc/csg/triapprox.cpp @@ -28,8 +28,8 @@ namespace netgen void TriangleApproximation :: RemoveUnusedPoints () { - BitArray used(GetNP()); - Array map (GetNP()); + NgBitArray used(GetNP()); + NgArray map (GetNP()); int i, j; int cnt = 0; diff --git a/libsrc/csg/triapprox.hpp b/libsrc/csg/triapprox.hpp index dab9a833..e97b62bc 100644 --- a/libsrc/csg/triapprox.hpp +++ b/libsrc/csg/triapprox.hpp @@ -36,9 +36,9 @@ namespace netgen class TriangleApproximation { - Array > points; - Array > normals; - Array trigs; + NgArray > points; + NgArray > normals; + NgArray trigs; public: TriangleApproximation(); diff --git a/libsrc/csg/vscsg.cpp b/libsrc/csg/vscsg.cpp index f28a49bc..ed511e79 100644 --- a/libsrc/csg/vscsg.cpp +++ b/libsrc/csg/vscsg.cpp @@ -17,9 +17,11 @@ namespace netgen /* *********************** Draw Geometry **************** */ - extern shared_ptr mesh; - extern Array specpoints; - extern Array > boxes; + DLL_HEADER extern shared_ptr mesh; + DLL_HEADER extern NgArray global_specpoints; + NgArray & specpoints = global_specpoints; + + DLL_HEADER extern NgArray > boxes; @@ -498,7 +500,7 @@ namespace netgen #ifdef NG_PYTHON #include <../general/ngpython.hpp> -DLL_HEADER void ExportCSGVis(py::module &m) +NGGUI_API void ExportCSGVis(py::module &m) { using namespace netgen; diff --git a/libsrc/csg/vscsg.hpp b/libsrc/csg/vscsg.hpp index dc705a80..1a18295e 100644 --- a/libsrc/csg/vscsg.hpp +++ b/libsrc/csg/vscsg.hpp @@ -10,10 +10,10 @@ namespace netgen { - class DLL_HEADER VisualSceneGeometry : public VisualScene + class NGGUI_API VisualSceneGeometry : public VisualScene { class CSGeometry * geometry; - Array trilists; + NgArray trilists; int selsurf; public: VisualSceneGeometry (); diff --git a/libsrc/csg/zrefine.cpp b/libsrc/csg/zrefine.cpp index 81800cef..30594d5b 100644 --- a/libsrc/csg/zrefine.cpp +++ b/libsrc/csg/zrefine.cpp @@ -211,7 +211,7 @@ namespace netgen { int i, j; int nseg = mesh.GetNSeg(); - Array edgesonpoint(mesh.GetNP()); + NgArray edgesonpoint(mesh.GetNP()); for (i = 1; i <= mesh.GetNP(); i++) edgesonpoint.Elem(i) = 0; @@ -251,11 +251,11 @@ namespace netgen // markers for z-refinement: p1, p2, levels // p1-p2 is an edge to be refined - Array ref_uniform; - Array ref_singular; - Array ref_slices; + NgArray ref_uniform; + NgArray ref_singular; + NgArray ref_slices; - BitArray first_id(geom->identifications.Size()); + NgBitArray first_id(geom->identifications.Size()); first_id.Set(); @@ -287,7 +287,7 @@ namespace netgen } else { - //const Array & slices = csid->GetSlices(); + //const NgArray & slices = csid->GetSlices(); INDEX_4 i4; i4[0] = pair.I1(); i4[1] = pair.I2(); @@ -301,7 +301,7 @@ namespace netgen - Array epgi; + NgArray epgi; while (1) { @@ -389,12 +389,12 @@ namespace netgen edge.Sort(); if (!refedges.Used(edge)) { - const Array & slices = csid->GetSlices(); + const auto& slices = csid->GetSlices(); //(*testout) << "idnr " << idnr << " i " << i << endl; //(*testout) << "slices " << slices << endl; - double slicefac = slices.Get(slicenr); + double slicefac = slices[slicenr-1]; double slicefaclast = - (slicenr == slices.Size()) ? 1 : slices.Get(slicenr+1); + (slicenr == slices.Size()) ? 1 : slices[slicenr]; Point3d np = p1 + (slicefac / slicefaclast) * (p2-p1); //(*testout) << "slicenr " << slicenr << " slicefac " << slicefac << " quot " << (slicefac / slicefaclast) << " np " << np << endl; diff --git a/libsrc/general/CMakeLists.txt b/libsrc/general/CMakeLists.txt index 3f6c2aad..eb97a1c0 100644 --- a/libsrc/general/CMakeLists.txt +++ b/libsrc/general/CMakeLists.txt @@ -1,17 +1,22 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(gen INTERFACE) -set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) -target_sources(gen INTERFACE - ${sdir}/array.cpp ${sdir}/bitarray.cpp ${sdir}/dynamicmem.cpp ${sdir}/flags.cpp - ${sdir}/hashtabl.cpp ${sdir}/mystring.cpp ${sdir}/optmem.cpp ${sdir}/parthreads.cpp - ${sdir}/seti.cpp ${sdir}/sort.cpp ${sdir}/spbita2d.cpp ${sdir}/table.cpp - ${sdir}/mpi_interface.cpp ${sdir}/gzstream.cpp - ) +target_sources(nglib PRIVATE + dynamicmem.cpp + gzstream.cpp + hashtabl.cpp + mystring.cpp + ngarray.cpp + ngbitarray.cpp + optmem.cpp + parthreads.cpp + seti.cpp + sort.cpp + spbita2d.cpp + table.cpp +) install(FILES - array.hpp autodiff.hpp autoptr.hpp bitarray.hpp - dynamicmem.hpp flags.hpp hashtabl.hpp mpi_interface.hpp myadt.hpp - ngsimd.hpp mystring.hpp netgenout.hpp ngpython.hpp + ngarray.hpp autodiff.hpp autoptr.hpp ngbitarray.hpp + dynamicmem.hpp hashtabl.hpp myadt.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/autoptr.hpp b/libsrc/general/autoptr.hpp index 3de595ad..6089ea50 100644 --- a/libsrc/general/autoptr.hpp +++ b/libsrc/general/autoptr.hpp @@ -1,3 +1,5 @@ +braucht man nicht mehr + #ifndef FILE_AUTOPTR #define FILE_AUTOPTR diff --git a/libsrc/general/bitarray.hpp b/libsrc/general/bitarray.hpp deleted file mode 100644 index ce0ba348..00000000 --- a/libsrc/general/bitarray.hpp +++ /dev/null @@ -1,227 +0,0 @@ -#ifndef FILE_BitArray -#define FILE_BitArray - -/**************************************************************************/ -/* File: bitarray.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 01. Jun. 95 */ -/**************************************************************************/ - -#include - -namespace netgen -{ - - -/** - data type BitArray - - BitArray is a compressed array of Boolean information. By Set and Clear - the whole array or one bit can be set or reset, respectively. - Test returns the state of the occurring bit. - No range checking is done. - - index ranges from 0 to size-1 -*/ -class BitArray -{ - INDEX size; - unsigned char * data; - -public: - BitArray (); - /// - BitArray (INDEX asize); - /// - ~BitArray (); - - /// - void SetSize (INDEX asize); - /// - INDEX Size () const - { - return size; - } - - /// - void Set (); - /// - void Set (INDEX i) - { - data[Addr(i)] |= Mask(i); - } - - void Clear (); - - - void Clear (INDEX i) - { - data[Addr(i)] &= ~Mask(i); - } - - bool Test (INDEX i) const - { - return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? true : false; - } - - /// - void Invert (); - /// - void And (const BitArray & ba2); - /// - void Or (const BitArray & ba2); -private: - /// - inline unsigned char Mask (INDEX i) const - { - return char(1) << (i % CHAR_BIT); - } - /// - inline INDEX Addr (INDEX i) const - { - return (i / CHAR_BIT); - } - - /// - BitArray & operator= (BitArray &); - /// - BitArray (const BitArray &); -}; - - - -// print bitarray -inline ostream & operator<< (ostream & s, const BitArray & a) -{ - for (int i = 1; i <= a.Size(); i++) - { - s << int (a.Test(i)); - if (i % 40 == 0) s << "\n"; - } - if (a.Size() % 40 != 0) s << "\n"; - return s; -} - - -/* -inline -INDEX BitArray :: Size () const - { - return size; - } - -inline -unsigned char BitArray :: Mask (INDEX i) const - { - return char(1) << (i % CHAR_BIT); - } - -inline -INDEX BitArray :: Addr (INDEX i) const - { - return (i / CHAR_BIT); - } -inline -void BitArray :: Set (INDEX i) - { - data[Addr(i)] |= Mask(i); - } - -inline -void BitArray :: Clear (INDEX i) - { - data[Addr(i)] &= ~Mask(i); - } - - -inline -int BitArray :: Test (INDEX i) const - { - return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? 1 : 0; - } - -*/ - - - - - - -/** - data type BitArrayChar - - BitArray is an array of Boolean information. By Set and Clear - the whole array or one bit can be set or reset, respectively. - Test returns the state of the occurring bit. - No range checking is done. -*/ -template -class BitArrayChar -{ - /// - Array data; - -public: - /// - BitArrayChar () - { ; } - /// - BitArrayChar (int asize) - : data(asize) - { ; } - /// - ~BitArrayChar () - { ; } - - /// - void SetSize (int asize) - { data.SetSize(asize); } - - /// - inline int Size () const - { return data.Size(); } - - /// - void Set (); - /// - inline void Set (int i) - { data[i] = 1; } - /// - void Clear (); - /// - inline void Clear (int i) - { data[i] = 0; } - /// - inline int Test (int i) const - { return data[i]; } - /// - void Invert (); - /// - void And (const BitArrayChar & ba2); - /// - void Or (const BitArrayChar & ba2); -private: - /// copy bitarray is not supported - BitArrayChar & operator= (BitArrayChar &) { return *this; } - /// copy bitarray is not supported - BitArrayChar (const BitArrayChar &) { ; } -}; - - - - -template -inline ostream & operator<< (ostream & s, const BitArrayChar & a) -{ - for (int i = BASE; i < a.Size()+BASE; i++) - { - s << a.Test(i); - if ( (i-BASE) % 40 == 39) s << "\n"; - } - if (a.Size() % 40 != 0) s << "\n"; - return s; -} - -} - -#endif diff --git a/libsrc/general/flags.cpp b/libsrc/general/flags.cpp deleted file mode 100644 index 993413ed..00000000 --- a/libsrc/general/flags.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/**************************************************************************/ -/* File: flags.cc */ -/* Author: Joachim Schoeberl */ -/* Date: 10. Oct. 96 */ -/**************************************************************************/ - -/* - Datatype Flags -*/ - -#include -#include - -namespace netgen -{ - //using namespace netgen; - - Flags :: Flags () - { - ; - } - - Flags :: ~Flags () - { - DeleteFlags (); - } - - void Flags :: DeleteFlags () - { - for (int i = 0; i < strflags.Size(); i++) - delete [] strflags[i]; - for (int i = 0; i < numlistflags.Size(); i++) - delete numlistflags[i]; - strflags.DeleteAll(); - numflags.DeleteAll(); - defflags.DeleteAll(); - strlistflags.DeleteAll(); - numlistflags.DeleteAll(); - } - - void Flags :: SetFlag (const char * name, const char * val) - { - char * hval = new char[strlen (val) + 1]; - strcpy (hval, val); - strflags.Set (name, hval); - } - - void Flags :: SetFlag (const char * name, double val) - { - numflags.Set (name, val); - } - - void Flags :: SetFlag (const char * name) - { - defflags.Set (name, 1); - } - - - void Flags :: SetFlag (const char * name, const Array & val) - { - Array * strarray = new Array; - for (int i = 1; i <= val.Size(); i++) - { - strarray->Append (new char[strlen(val.Get(i))+1]); - strcpy (strarray->Last(), val.Get(i)); - } - strlistflags.Set (name, strarray); - } - - void Flags :: SetFlag (const char * name, const Array & val) - { - Array * numarray = new Array; - for (int i = 1; i <= val.Size(); i++) - numarray->Append (val.Get(i)); - numlistflags.Set (name, numarray); - } - - - - - - const char * - Flags :: GetStringFlag (const char * name, const char * def) const - { - if (strflags.Used (name)) - return strflags[name]; - else - return def; - } - - double Flags :: GetNumFlag (const char * name, double def) const - { - if (numflags.Used (name)) - return numflags[name]; - else - return def; - } - - const double * Flags :: GetNumFlagPtr (const char * name) const - { - if (numflags.Used (name)) - return & ((SymbolTable&)numflags)[name]; - else - return NULL; - } - - double * Flags :: GetNumFlagPtr (const char * name) - { - if (numflags.Used (name)) - return & ((SymbolTable&)numflags)[name]; - else - return NULL; - } - - bool Flags :: GetDefineFlag (const char * name) const - { - return defflags.Used (name); - } - - - const Array & - Flags :: GetStringListFlag (const char * name) const - { - if (strlistflags.Used (name)) - return *strlistflags[name]; - else - { - static Array dummy_array(0); - return dummy_array; - } - } - - const Array & - Flags ::GetNumListFlag (const char * name) const - { - if (numlistflags.Used (name)) - return *numlistflags[name]; - else - { - static Array dummy_array(0); - return dummy_array; - } - } - - - bool Flags :: StringFlagDefined (const char * name) const - { - return strflags.Used (name); - } - - bool Flags :: NumFlagDefined (const char * name) const - { - return numflags.Used (name); - } - - bool Flags :: StringListFlagDefined (const char * name) const - { - return strlistflags.Used (name); - } - - bool Flags :: NumListFlagDefined (const char * name) const - { - return numlistflags.Used (name); - } - - - void Flags :: SaveFlags (const char * filename) const - { - int i; - ofstream outfile (filename); - - for (i = 1; i <= strflags.Size(); i++) - outfile << strflags.GetName(i) << " = " << strflags[i] << endl; - for (i = 1; i <= numflags.Size(); i++) - outfile << numflags.GetName(i) << " = " << numflags[i] << endl; - for (i = 1; i <= defflags.Size(); i++) - outfile << defflags.GetName(i) << endl; - } - - - - void Flags :: PrintFlags (ostream & ost) const - { - int i; - - for (i = 1; i <= strflags.Size(); i++) - ost << strflags.GetName(i) << " = " << strflags[i] << endl; - for (i = 1; i <= numflags.Size(); i++) - ost << numflags.GetName(i) << " = " << numflags[i] << endl; - for (i = 1; i <= defflags.Size(); i++) - ost << defflags.GetName(i) << endl; - } - - - void Flags :: LoadFlags (const char * filename) - { - char name[100], str[100]; - char ch; - double val; - ifstream infile(filename); - - // (*logout) << "Load flags from " << filename << endl << endl; - while (infile.good()) - { - infile >> name; - if (strlen (name) == 0) break; - - if (name[0] == '/' && name[1] == '/') - { - // (*logout) << "comment: "; - ch = 0; - while (ch != '\n' && infile.good()) - { - ch = infile.get(); - // (*logout) << ch; - } - continue; - } - - // (*logout) << name; - ch = 0; - infile >> ch; - if (ch != '=') - { - // (*logout) << endl; - infile.putback (ch); - SetFlag (name); - } - else - { - infile >> val; - if (!infile.good()) - { - infile.clear(); - infile >> str; - SetFlag (name, str); - // (*logout) << " = " << str << endl; - } - else - { - SetFlag (name, val); - // (*logout) << " = " << val << endl; - } - } - } - // (*logout) << endl; - } - - - void Flags :: SetCommandLineFlag (const char * st) - { - // cout << "clflag = " << st << endl; - istringstream inst( (char *)st); - // istrstream defined with char * (not const char * ?????) - - char name[100]; - double val; - - - if (st[0] != '-') - { - cerr << "flag must start with '-'" << endl; - return; - } - - const char * pos = strchr (st, '='); - - if (!pos) - { - // (cout) << "Add def flag: " << st+1 << endl; - SetFlag (st+1); - } - else - { - // cout << "pos = " << pos << endl; - - strncpy (name, st+1, (pos-st)-1); - name[pos-st-1] = 0; - - // cout << "name = " << name << endl; - - pos++; - char * endptr = NULL; - - val = strtod (pos, &endptr); - - // cout << "val = " << val << endl; - - if (endptr == pos) - { - // (cout) << "Add String Flag: " << name << " = " << pos << endl; - SetFlag (name, pos); - } - else - { - // (cout) << "Add Num Flag: " << name << " = " << val << endl; - SetFlag (name, val); - } - } - - - /* - inst >> name; - (*mycout) << "name = " << name << endl; - - ch = 0; - inst >> ch; - if (ch != '=') - { - SetFlag (name); - } - else - { - inst >> val; - if (!inst.good()) - { - inst.clear(); - inst >> str; - SetFlag (name, str); - (*mycout) << "str = " << str << endl; - } - else - { - SetFlag (name, val); - (*mycout) << "val = " << val << endl; - } - } - */ - } -} diff --git a/libsrc/general/flags.hpp b/libsrc/general/flags.hpp deleted file mode 100644 index e7ba0382..00000000 --- a/libsrc/general/flags.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef FILE_FLAGS -#define FILE_FLAGS - - -/**************************************************************************/ -/* File: flags.hh */ -/* Author: Joachim Schoeberl */ -/* Date: 10. Oct. 96 */ -/**************************************************************************/ - -namespace netgen -{ - -/** - Flag - Table. - A flag table maintains string variables, numerical - variables and boolean flags. -*/ -class Flags -{ - /// - SymbolTable strflags; - /// - SymbolTable numflags; - /// - SymbolTable defflags; - /// - SymbolTable*> strlistflags; - /// - SymbolTable*> numlistflags; -public: - /// - DLL_HEADER Flags (); - /// - DLL_HEADER ~Flags (); - - /// Deletes all flags - DLL_HEADER void DeleteFlags (); - /// Sets string flag, overwrite if exists - DLL_HEADER void SetFlag (const char * name, const char * val); - /// Sets numerical flag, overwrite if exists - DLL_HEADER void SetFlag (const char * name, double val); - /// Sets boolean flag - DLL_HEADER void SetFlag (const char * name); - /// Sets string arary flag - DLL_HEADER void SetFlag (const char * name, const Array & val); - /// Sets double array flag - DLL_HEADER void SetFlag (const char * name, const Array & val); - - /// Save flags to file - DLL_HEADER void SaveFlags (const char * filename) const; - /// write flags to stream - DLL_HEADER void PrintFlags (ostream & ost) const; - /// Load flags from file - DLL_HEADER void LoadFlags (const char * filename); - /// set flag of form -name=hello -val=0.5 -defined - DLL_HEADER void SetCommandLineFlag (const char * st); - - /// Returns string flag, default value if not exists - DLL_HEADER const char * GetStringFlag (const char * name, const char * def) const; - /// Returns numerical flag, default value if not exists - DLL_HEADER double GetNumFlag (const char * name, double def) const; - /// Returns address of numerical flag, null if not exists - DLL_HEADER const double * GetNumFlagPtr (const char * name) const; - /// Returns address of numerical flag, null if not exists - DLL_HEADER double * GetNumFlagPtr (const char * name); - /// Returns boolean flag - DLL_HEADER bool GetDefineFlag (const char * name) const; - /// Returns string list flag, empty array if not exist - DLL_HEADER const Array & GetStringListFlag (const char * name) const; - /// Returns num list flag, empty array if not exist - DLL_HEADER const Array & GetNumListFlag (const char * name) const; - - - /// Test, if string flag is defined - DLL_HEADER bool StringFlagDefined (const char * name) const; - /// Test, if num flag is defined - DLL_HEADER bool NumFlagDefined (const char * name) const; - /// Test, if string list flag is defined - DLL_HEADER bool StringListFlagDefined (const char * name) const; - /// Test, if num list flag is defined - DLL_HEADER bool NumListFlagDefined (const char * name) const; -}; - -} - -#endif - diff --git a/libsrc/general/gzstream.cpp b/libsrc/general/gzstream.cpp index 9e8f5cb8..49003377 100644 --- a/libsrc/general/gzstream.cpp +++ b/libsrc/general/gzstream.cpp @@ -28,7 +28,7 @@ #include #include -//#include "gzstream.h" +#include "gzstream.h" //#include //#include // for memcpy @@ -44,7 +44,7 @@ namespace GZSTREAM_NAMESPACE { // class gzstreambuf: // -------------------------------------- -gzstreambuf* gzstreambuf::open( const char* name, int open_mode) { +gzstreambuf* gzstreambuf::open( const filesystem::path & name, int open_mode) { if ( is_open()) return (gzstreambuf*)0; mode = open_mode; @@ -60,7 +60,11 @@ gzstreambuf* gzstreambuf::open( const char* name, int open_mode) { *fmodeptr++ = 'w'; *fmodeptr++ = 'b'; *fmodeptr = '\0'; - file = gzopen( name, fmode); +#ifdef WIN32 + file = gzopen_w( name.c_str(), fmode); +#else // WIN32 + file = gzopen( name.c_str(), fmode); +#endif // WIN32 if (file == 0) return (gzstreambuf*)0; opened = 1; @@ -139,17 +143,17 @@ int gzstreambuf::sync() { // class gzstreambase: // -------------------------------------- -gzstreambase::gzstreambase( const char* name, int mode) { +gzstreambase::gzstreambase( const filesystem::path & name, int mode) { init( &buf); - open( name, mode); + open( name.c_str(), mode); } gzstreambase::~gzstreambase() { buf.close(); } -void gzstreambase::open( const char* name, int open_mode) { - if ( ! buf.open( name, open_mode)) +void gzstreambase::open( const filesystem::path & name, int open_mode) { + if ( ! buf.open( name.c_str(), open_mode)) clear( rdstate() | std::ios::badbit); } diff --git a/libsrc/general/gzstream.h b/libsrc/general/gzstream.h index 74854a80..7528b5f0 100644 --- a/libsrc/general/gzstream.h +++ b/libsrc/general/gzstream.h @@ -62,7 +62,7 @@ public: // ASSERT: both input & output capabilities will not be used together } int is_open() { return opened; } - gzstreambuf* open( const char* name, int open_mode); + gzstreambuf* open( const filesystem::path & name, int open_mode); gzstreambuf* close(); ~gzstreambuf() { close(); } @@ -71,14 +71,14 @@ public: virtual int sync(); }; -class gzstreambase : virtual public std::ios { +class DLL_HEADER gzstreambase : virtual public std::ios { protected: gzstreambuf buf; public: gzstreambase() { init(&buf); } - DLL_HEADER gzstreambase( const char* name, int open_mode); - DLL_HEADER ~gzstreambase(); - void open( const char* name, int open_mode); + gzstreambase( const filesystem::path & name, int open_mode); + ~gzstreambase(); + void open( const filesystem::path & name, int open_mode); void close(); gzstreambuf* rdbuf() { return &buf; } }; @@ -92,10 +92,10 @@ public: class DLL_HEADER igzstream : public gzstreambase, public std::istream { public: igzstream() : std::istream( &buf) {} - igzstream( const char* name, int open_mode = std::ios::in) + igzstream( const filesystem::path & name, int open_mode = std::ios::in) : gzstreambase( name, open_mode), std::istream( &buf) {} gzstreambuf* rdbuf() { return gzstreambase::rdbuf(); } - void open( const char* name, int open_mode = std::ios::in) { + void open( const filesystem::path & name, int open_mode = std::ios::in) { gzstreambase::open( name, open_mode); } }; @@ -103,10 +103,10 @@ public: class DLL_HEADER ogzstream : public gzstreambase, public std::ostream { public: ogzstream() : std::ostream( &buf) {} - ogzstream( const char* name, int mode = std::ios::out) + ogzstream( const filesystem::path & name, int mode = std::ios::out) : gzstreambase( name, mode), std::ostream( &buf) {} gzstreambuf* rdbuf() { return gzstreambase::rdbuf(); } - void open( const char* name, int open_mode = std::ios::out) { + void open( const filesystem::path & name, int open_mode = std::ios::out) { gzstreambase::open( name, open_mode); } }; diff --git a/libsrc/general/hashtabl.hpp b/libsrc/general/hashtabl.hpp index 4d8fadc8..34c3f641 100644 --- a/libsrc/general/hashtabl.hpp +++ b/libsrc/general/hashtabl.hpp @@ -149,7 +149,14 @@ public: int pos = Position (bnr, ahash); return cont.Get (bnr, pos); } - + + T & Get (const INDEX_2 & ahash) + { + int bnr = HashValue (ahash); + int pos = Position (bnr, ahash); + return cont.Get (bnr, pos); + } + /// bool Used (const INDEX_2 & ahash) const { @@ -214,9 +221,14 @@ public: int BagNr() const { return bagnr; } int Pos() const { return pos; } - void operator++ (int) + Iterator operator++ (int) + { + Iterator it(ht, bagnr, pos); + ++(*this); + return it; + } + Iterator& operator++() { - // cout << "begin Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; pos++; while (bagnr < ht.GetNBags() && pos == ht.GetBagSize(bagnr+1)) @@ -224,7 +236,12 @@ public: pos = 0; bagnr++; } - // cout << "end Operator ++: bagnr = " << bagnr << " - pos = " << pos << endl; + return *this; + } + + std::pair operator*() + { + return std::make_pair(ht.hash[bagnr][pos], ht.cont[bagnr][pos]); } bool operator != (int i) const @@ -246,6 +263,18 @@ public: return GetNBags(); } + Iterator begin () const + { + Iterator it(*this, 0, -1); + it++; + return it; + } + + int end() const + { + return GetNBags(); + } + void GetData (const Iterator & it, INDEX_2 & ahash, T & acont) const { @@ -480,7 +509,7 @@ class BASE_INDEX_CLOSED_HASHTABLE protected: /// // MoveableArray hash; - Array hash; + NgArray hash; /// int invalid; public: @@ -556,7 +585,7 @@ class INDEX_CLOSED_HASHTABLE : public BASE_INDEX_CLOSED_HASHTABLE { /// // MoveableArray cont; - Array cont; + NgArray cont; public: /// @@ -653,13 +682,13 @@ class BASE_INDEX_2_CLOSED_HASHTABLE protected: /// // MoveableArray hash; - Array hash; + NgArray hash; /// int invalid; size_t mask; public: /// - BASE_INDEX_2_CLOSED_HASHTABLE (size_t size); + DLL_HEADER BASE_INDEX_2_CLOSED_HASHTABLE (size_t size); int Size() const { return hash.Size(); } bool UsedPos0 (int pos) const { return ! (hash[pos].I1() == invalid); } @@ -709,16 +738,16 @@ public: protected: /// - int Position2 (const INDEX_2 & ind) const; - bool PositionCreate2 (const INDEX_2 & ind, int & apos); - void BaseSetSize (int asize); + DLL_HEADER int Position2 (const INDEX_2 & ind) const; + DLL_HEADER bool PositionCreate2 (const INDEX_2 & ind, int & apos); + DLL_HEADER void BaseSetSize (int asize); }; template class INDEX_2_CLOSED_HASHTABLE : public BASE_INDEX_2_CLOSED_HASHTABLE { - Array cont; + NgArray cont; public: INDEX_2_CLOSED_HASHTABLE (size_t size) : BASE_INDEX_2_CLOSED_HASHTABLE(size), cont(RoundUp2(size)) @@ -743,6 +772,15 @@ public: int pos = Position0 (ahash); return (pos != -1); } + + inline optional GetIfUsed (const INDEX_2 & ahash) const + { + int pos = Position0 (ahash); + if (pos != -1) + return cont[pos]; + else + return nullopt; + } inline void SetData0 (int pos, const INDEX_2 & ahash, const T & acont) { @@ -813,7 +851,7 @@ inline ostream & operator<< (ostream & ost, const INDEX_2_CLOSED_HASHTABLE & class BASE_INDEX_3_CLOSED_HASHTABLE { protected: - Array hash; + NgArray hash; int invalid; size_t mask; @@ -922,7 +960,7 @@ template class INDEX_3_CLOSED_HASHTABLE : public BASE_INDEX_3_CLOSED_HASHTABLE { // MoveableArray cont; - Array cont; + NgArray cont; public: INDEX_3_CLOSED_HASHTABLE (int size) @@ -1361,6 +1399,10 @@ inline void SetInvalid (INDEX_2 & i2) { i2[0] = -1; } inline bool IsInvalid (INDEX_2 i2) { return i2[0] == -1; } inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+size_t(i2[1])) % size; } +inline void SetInvalid (INDEX_3 & i3) { i3[0] = -1; } +inline bool IsInvalid (INDEX_3 i3) { return i3[0] == -1; } +inline size_t HashValue (INDEX_3 i3, size_t size) { return (i3[0]+15*size_t(i3[1])+41*size_t(i3[2])) % size; } + /** A closed hash-table. @@ -1376,9 +1418,9 @@ inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+si /// size_t used; /// - Array hash; + NgArray hash; /// - Array cont; + NgArray cont; public: /// ClosedHashTable (size_t asize = 128) @@ -1390,8 +1432,7 @@ inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+si ClosedHashTable (ClosedHashTable && ht2) = default; - // who needs that ? - ClosedHashTable (FlatArray _hash, FlatArray _cont) + ClosedHashTable (NgFlatArray _hash, NgFlatArray _cont) : size(_hash.Size()), used(0), hash(_hash.Size(), _hash.Addr(0)), cont(_cont.Size(), _cont.Addr(0)) { for (auto & v : hash) @@ -1417,13 +1458,6 @@ inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+si size_t UsedElements () const { return used; - /* - size_t cnt = 0; - for (size_t i = 0; i < size; i++) - if (hash[i] != invalid) - cnt++; - return cnt; - */ } size_t Position (const T_HASH ind) const @@ -1443,7 +1477,7 @@ inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+si ClosedHashTable tmp(2*Size()); for (auto both : *this) tmp[both.first] = both.second; - *this = move(tmp); + *this = std::move(tmp); } // returns true if new position is created @@ -1565,6 +1599,13 @@ inline size_t HashValue (INDEX_2 i2, size_t size) { return (113*size_t(i2[0])+si pos = nextpos; } } + + void DeleteData() + { + for (auto & v : hash) + SetInvalid(v); + used = 0; + } class Iterator { diff --git a/libsrc/general/mpi_interface.cpp b/libsrc/general/mpi_interface.cpp index ad688995..8fd6fb96 100644 --- a/libsrc/general/mpi_interface.cpp +++ b/libsrc/general/mpi_interface.cpp @@ -4,6 +4,7 @@ /* Date: 04. Apr. 97 */ /**************************************************************************/ +#ifdef OLD #include #include @@ -50,3 +51,4 @@ namespace netgen +#endif diff --git a/libsrc/general/mpi_interface.hpp b/libsrc/general/mpi_interface.hpp index 6dc3070f..edc9c5f1 100644 --- a/libsrc/general/mpi_interface.hpp +++ b/libsrc/general/mpi_interface.hpp @@ -1,8 +1,12 @@ +braucht keiner mehr + + +#ifdef XXXXXX + #ifndef FILE_PARALLEL #define FILE_PARALLEL - #ifdef VTRACE #include "vt_user.h" #else @@ -14,65 +18,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,47 +40,37 @@ namespace netgen typedef int MPI_Datatype; template inline MPI_Datatype MyGetMPIType ( ) { return 0; } #endif - -#ifdef PARALLEL - inline MPI_Comm MyMPI_SubCommunicator(MPI_Comm comm, Array & procs) - { - MPI_Comm subcomm; - MPI_Group gcomm, gsubcomm; - MPI_Comm_group(comm, &gcomm); - MPI_Group_incl(gcomm, procs.Size(), &(procs[0]), &gsubcomm); - MPI_Comm_create_group(comm, gsubcomm, 6969, &subcomm); - return subcomm; - } -#else - inline MPI_Comm MyMPI_SubCommunicator(MPI_Comm comm, Array & procs) - { return comm; } #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 */) +#ifdef PARALLEL + + [[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; @@ -145,34 +82,37 @@ namespace netgen - template - inline void MyMPI_Send (FlatArray 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 ( FlatArray 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 ( Array & s, int src, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("use ngcore - Array instead")]] + 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 ( Array & s, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("use ngcore - Array instead")]] + inline int MyMPI_Recv ( NgArray & s, int tag, MPI_Comm comm) { MPI_Status status; int len; @@ -180,10 +120,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; } @@ -191,39 +131,41 @@ namespace netgen /* template - inline void MyMPI_ISend (FlatArray s, int dest, int tag, MPI_Request & request) + inline void MyMPI_ISend (NgFlatArray s, int dest, int tag, MPI_Request & request) { MPI_Isend( &s.First(), s.Size(), MyGetMPIType(), dest, tag, MPI_COMM_WORLD, & request); } template - inline void MyMPI_IRecv (FlatArray s, int dest, int tag, MPI_Request & request) + inline void MyMPI_IRecv (NgFlatArray s, int dest, int tag, MPI_Request & request) { MPI_Irecv( &s.First(), s.Size(), MyGetMPIType(), dest, tag, MPI_COMM_WORLD, & request); } */ template - inline MPI_Request MyMPI_ISend (FlatArray s, int dest, int tag, MPI_Comm comm /* = ng_comm */) + [[deprecated("mympi_isend ngflatarray, use comm.send instead")]] + [[deprecated("use ngcore - Array 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 (FlatArray 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 (FlatArray s, int dest, int tag) + inline void MyMPI_ISend (NgFlatArray s, int dest, int tag) { MPI_Request request; MPI_Isend( &s.First(), s.Size(), MyGetMPIType(), dest, tag, MPI_COMM_WORLD, &request); @@ -232,7 +174,7 @@ namespace netgen template - inline void MyMPI_IRecv (FlatArray s, int dest, int tag) + inline void MyMPI_IRecv (NgFlatArray s, int dest, int tag) { MPI_Request request; MPI_Irecv( &s.First(), s.Size(), MyGetMPIType(), dest, tag, MPI_COMM_WORLD, &request); @@ -247,44 +189,12 @@ namespace netgen receive-table entries will be set */ - /* template + [[deprecated("do we need that ? ")]] 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); - - Array 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(); @@ -292,61 +202,88 @@ namespace netgen 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)); 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); + } + + + template + [[deprecated("do we need that ? ")]] + inline void MyMPI_ExchangeTable (DynamicTable & send_data, + DynamicTable & recv_data, int tag, + const NgMPI_Comm & comm) + { + int rank = comm.Rank(); + int ntasks = comm.Size(); + + 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); + + // for (int i = 0; i < ntasks; i++) + // recv_data.SetEntrySize (i, recv_sizes[i], sizeof(T)); + recv_data = DynamicTable (recv_sizes, true); + + Array requests; + for (int dest = 0; dest < ntasks; dest++) + if (dest != rank && send_data[dest].Size()) + 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 (comm.IRecv (FlatArray(recv_data[dest]), dest, tag)); + + MyMPI_WaitAll (requests); } - - - - - extern void MyMPI_SendCmd (const char * cmd); + + [[deprecated("do we still send commands?")]] + DLL_HEADER void MyMPI_SendCmd (const char * cmd); + [[deprecated("do we still send commands?")]] extern string MyMPI_RecvCmd (); - - template - inline void MyMPI_Bcast (T & s, MPI_Comm comm /* = ng_comm */) + [[deprecated("use comm.BCast instead")]] + 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 (Array & s, NgMPI_Comm comm /* = ng_comm */) + [[deprecated("use comm.BCast instead")]] + inline void MyMPI_Bcast (NgArray & s, NgMPI_Comm comm) { int size = s.Size(); - MyMPI_Bcast (size, comm); + // MyMPI_Bcast (size, comm); + comm.Bcast(size); // 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 (Array & s, int root, MPI_Comm comm /* = ng_comm */) + [[deprecated("use comm.BCast instead")]] + inline void MyMPI_Bcast (NgArray & s, int root, MPI_Comm comm) { int id; MPI_Comm_rank(comm, &id); @@ -355,70 +292,45 @@ 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, FlatArray 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 (FlatArray send, FlatArray 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); - } - -// template -// inline void MyMPI_Alltoall_Block (FlatArray send, FlatArray 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); + MPI_Alltoall( &send[0], 1, GetMPIType(), &recv[0], 1, GetMPIType(), comm); } - inline void MyMPI_Recv ( int *& s, int & len, int src, int tag) +#else + template + [[deprecated("do we need that ? ")]] + inline void MyMPI_ExchangeTable (TABLE & send_data, + TABLE & recv_data, int tag, + const NgMPI_Comm & comm) { - 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); - } - */ - + template + [[deprecated("do we need that ? ")]] + inline void MyMPI_ExchangeTable (DynamicTable & send_data, + DynamicTable & recv_data, int tag, + const NgMPI_Comm & comm) + { ; } #endif // PARALLEL } #endif + + +#endif diff --git a/libsrc/general/myadt.hpp b/libsrc/general/myadt.hpp index 4284a42f..871de4f1 100644 --- a/libsrc/general/myadt.hpp +++ b/libsrc/general/myadt.hpp @@ -17,7 +17,7 @@ #include "../include/mydefs.hpp" -#include "../core/ngcore.hpp" +#include namespace netgen { using namespace ngcore; @@ -28,27 +28,23 @@ namespace netgen #include "dynamicmem.hpp" #include "template.hpp" -#include "array.hpp" +#include "ngarray.hpp" #include "table.hpp" #include "hashtabl.hpp" -#include "bitarray.hpp" -#include "flags.hpp" +#include "ngbitarray.hpp" #include "spbita2d.hpp" #include "seti.hpp" #include "optmem.hpp" -#include "autoptr.hpp" +// #include "autoptr.hpp" #include "sort.hpp" #include "stack.hpp" #include "mystring.hpp" -#include "mpi_interface.hpp" +// #include "mpi_interface.hpp" #include "netgenout.hpp" -#include "gzstream.h" - -#include "ngsimd.hpp" #endif diff --git a/libsrc/general/mystring.cpp b/libsrc/general/mystring.cpp index c5525da1..7d7dcd01 100644 --- a/libsrc/general/mystring.cpp +++ b/libsrc/general/mystring.cpp @@ -105,7 +105,7 @@ MyStr::MyStr(const MyStr& s) MyStr::MyStr(int i) { char buffer[32]; - sprintf(buffer, "%d", i); + snprintf(buffer, 32, "%d", i); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -129,7 +129,7 @@ MyStr::MyStr(unsigned int i) MyStr::MyStr(void * p) { char buffer[32]; - sprintf(buffer, "%p", p); + snprintf(buffer, 32, "%p", p); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -155,7 +155,7 @@ MyStr::MyStr(long l) MyStr::MyStr(size_t l) { char buffer[32]; - sprintf(buffer, "%ld", l); + snprintf(buffer, 32, "%ld", l); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -168,7 +168,7 @@ MyStr::MyStr(double d) { char buffer[32]; //if (fabs(d) < 1E-100) {d = 0;} - sprintf(buffer, "%g", d); + snprintf(buffer, 32, "%g", d); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -181,7 +181,7 @@ MyStr::MyStr(const Point3d& p) { char buffer[80]; //if (fabs(d) < 1E-100) {d = 0;} - sprintf(buffer, "[%g, %g, %g]", p.X(), p.Y(), p.Z()); + snprintf(buffer, 80, "[%g, %g, %g]", p.X(), p.Y(), p.Z()); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -194,7 +194,7 @@ MyStr::MyStr(const Vec3d& p) { char buffer[80]; //if (fabs(d) < 1E-100) {d = 0;} - sprintf(buffer, "[%g, %g, %g]", p.X(), p.Y(), p.Z()); + snprintf(buffer, 80, "[%g, %g, %g]", p.X(), p.Y(), p.Z()); length = unsigned(strlen(buffer)); if (length > SHORTLEN) str = new char[length + 1]; @@ -223,6 +223,10 @@ MyStr::MyStr(const string & st) strcpy (str, st.c_str()); } +MyStr::MyStr(const filesystem::path & path) + : MyStr(path.string()) +{ } + MyStr MyStr::Left(unsigned r) diff --git a/libsrc/general/mystring.hpp b/libsrc/general/mystring.hpp index 0fb2bbd2..ee364d77 100644 --- a/libsrc/general/mystring.hpp +++ b/libsrc/general/mystring.hpp @@ -19,6 +19,8 @@ #ifndef MYSTRING__H #define MYSTRING__H +#include + namespace netgen { @@ -58,6 +60,7 @@ public: MyStr(const Point3d& p); MyStr(const Vec3d& p); MyStr(const string & st); + MyStr(const filesystem::path & st); ~MyStr(); MyStr Left(unsigned); diff --git a/libsrc/general/netgenout.hpp b/libsrc/general/netgenout.hpp index 076a6df1..eaf214cf 100644 --- a/libsrc/general/netgenout.hpp +++ b/libsrc/general/netgenout.hpp @@ -4,12 +4,12 @@ // #include // #include // #include -#include "mpi_interface.hpp" +// #include "mpi_interface.hpp" namespace netgen { -DLL_HEADER extern int printmessage_importance; + //DLL_HEADER extern int printmessage_importance; DLL_HEADER extern int printdots; @@ -39,13 +39,13 @@ public: class Procs { - const netgen::FlatArray procs; + const netgen::NgFlatArray procs; public: - Procs ( const netgen::FlatArray & aprocs ) : procs (aprocs) { ; } + Procs ( const netgen::NgFlatArray & aprocs ) : procs (aprocs) { ; } - const netgen::FlatArray & GetProcs () const { return procs; } + const netgen::NgFlatArray & GetProcs () const { return procs; } }; diff --git a/libsrc/general/array.cpp b/libsrc/general/ngarray.cpp similarity index 86% rename from libsrc/general/array.cpp rename to libsrc/general/ngarray.cpp index d3f48d36..37a8e118 100644 --- a/libsrc/general/array.cpp +++ b/libsrc/general/ngarray.cpp @@ -1,5 +1,5 @@ -#ifndef FILE_NGSTD_ArrayCPP -#define FILE_NGSTD_ArrayCPP +#ifndef FILE_NGSTD_NgArrayCPP +#define FILE_NGSTD_NgArrayCPP // necessary for SGI ???? /**************************************************************************/ @@ -9,7 +9,7 @@ /**************************************************************************/ /* - Abstract data type Array + Abstract data type NgArray */ #include @@ -65,11 +65,11 @@ namespace netgen { if (!actsize) { - throw Exception ("Array should not be empty"); - // cerr << "Array souldn't be empty"; + throw Exception ("NgArray should not be empty"); + // cerr << "NgArray shouldn't be empty"; } } #endif } -#endif +#endif // FILE_NGSTD_NgArrayCPP diff --git a/libsrc/general/array.hpp b/libsrc/general/ngarray.hpp similarity index 74% rename from libsrc/general/array.hpp rename to libsrc/general/ngarray.hpp index df0cd0a5..6e244ec8 100644 --- a/libsrc/general/array.hpp +++ b/libsrc/general/ngarray.hpp @@ -1,8 +1,8 @@ -#ifndef FILE_Array -#define FILE_Array +#ifndef NGARRAY_HPP_INCLUDED +#define NGARRAY_HPP_INCLUDED /**************************************************************************/ -/* File: array.hpp */ +/* File: ngarray.hpp */ /* Author: Joachim Schoeberl */ /* Date: 01. Jun. 95 */ /**************************************************************************/ @@ -39,22 +39,23 @@ namespace netgen T Size() const { return next-first; } T operator[] (T i) const { return first+i; } bool Contains (T i) const { return ((i >= first) && (i < next)); } - + T_Range Modify (int inc_begin, int inc_end) const + { return T_Range(first+inc_begin, next+inc_end); } ArrayRangeIterator begin() const { return first; } ArrayRangeIterator end() const { return next; } }; template - class FlatArray; + class NgFlatArray; template class ArrayIterator { - FlatArray ar; + NgFlatArray ar; TIND ind; public: - ArrayIterator (FlatArray aar, TIND ai) : ar(aar), ind(ai) { ; } + ArrayIterator (NgFlatArray aar, TIND ai) : ar(aar), ind(ai) { ; } ArrayIterator operator++ (int) { return ArrayIterator(ar, ind++); } ArrayIterator operator++ () { return ArrayIterator(ar, ++ind); } T operator*() const { return ar[ind]; } @@ -67,14 +68,14 @@ namespace netgen /** A simple array container. - Array represented by size and data-pointer. + NgArray represented by size and data-pointer. No memory allocation and deallocation, must be provided by user. Helper functions for printing. Optional range check by macro RANGE_CHECK */ template - class FlatArray + class NgFlatArray { protected: /// the size @@ -83,9 +84,10 @@ namespace netgen T * data; public: typedef T TELEM; + using index_type = TIND; /// provide size and memory - FlatArray (size_t asize, T * adata) + NgFlatArray (size_t asize, T * adata) : size(asize), data(adata) { ; } /// the size @@ -96,10 +98,15 @@ namespace netgen ArrayIterator end() const { return ArrayIterator (*this, BASE+size); } - TIND Begin() const { return TIND(BASE); } - TIND End() const { return TIND(size+BASE); } + // TIND Begin() const { return TIND(BASE); } + // TIND End() const { return TIND(size+BASE); } T_Range Range() const { return T_Range(BASE, size+BASE); } + [[deprecated("Use *Range().begin() instead")]] + auto Begin() const { return *Range().begin(); } + [[deprecated("Use *Range().end() instead")]] + auto End() const { return *Range().end(); } + /// Access array. BASE-based T & operator[] (TIND i) const { @@ -112,9 +119,9 @@ namespace netgen } template - IndirectArray > operator[] (const FlatArray & ia) const + IndirectArray > operator[] (const NgFlatArray & ia) const { - return IndirectArray > (*this, ia); + return IndirectArray > (*this, ia); } @@ -124,7 +131,7 @@ namespace netgen { #ifdef DEBUG if (i < 1 || i > size) - cout << "Array<" << typeid(T).name() + cout << "NgArray<" << typeid(T).name() << ">::Elem out of range, i = " << i << ", s = " << size << endl; #endif @@ -133,11 +140,12 @@ namespace netgen } /// Access array, one-based (old fashioned) + // [[deprecated("Use operator[] instead")]] const T & Get (int i) const { #ifdef DEBUG if (i < 1 || i > size) - cout << "Array<" << typeid(T).name() << ">::Get out of range, i = " << i + cout << "NgArray<" << typeid(T).name() << ">::Get out of range, i = " << i << ", s = " << size << endl; #endif @@ -149,7 +157,7 @@ namespace netgen { #ifdef DEBUG if (i < 1 || i > size) - cout << "Array<" << typeid(T).name() << ">::Set out of range, i = " << i + cout << "NgArray<" << typeid(T).name() << ">::Set out of range, i = " << i << ", s = " << size << endl; #endif @@ -170,7 +178,7 @@ namespace netgen } /// Fill array with value val - FlatArray & operator= (const T & val) + NgFlatArray & operator= (const T & val) { for (int i = 0; i < size; i++) data[i] = val; @@ -178,9 +186,9 @@ namespace netgen } /// takes range starting from position start of end-start elements - const FlatArray Range (TIND start, TIND end) + const NgFlatArray Range (TIND start, TIND end) { - return FlatArray (end-start, data+start); + return NgFlatArray (end-start, data+start); } /// first position of element elem, returns -1 if element not contained in array @@ -197,70 +205,75 @@ namespace netgen { return ( Pos(elem) >= 0 ); } + + operator FlatArray () const + { + static_assert (BASE==0); + return FlatArray(size, data); + } }; // print array template - inline ostream & operator<< (ostream & s, const FlatArray & a) + inline ostream & operator<< (ostream & s, const NgFlatArray & a) { - for (TIND i = a.Begin(); i < a.End(); i++) + // for (TIND i = a.Begin(); i < a.End(); i++) + for (auto i : a.Range()) s << i << ": " << a[i] << endl; return s; } - /** Dynamic array container. - Array is an automatically increasing array container. + NgArray is an automatically increasing array container. The allocated memory doubles on overflow. Either the container takes care of memory allocation and deallocation, or the user provides one block of data. */ template - class Array : public FlatArray + class NgArray : public NgFlatArray { protected: - using FlatArray::size; - using FlatArray::data; + using NgFlatArray::size; + using NgFlatArray::data; /// physical size of array - size_t allocsize; + size_t allocsize = 0; /// memory is responsibility of container bool ownmem; public: /// Generate array of logical and physical size asize - explicit Array() - : FlatArray (0, NULL) + explicit NgArray() + : NgFlatArray (0, NULL) { allocsize = 0; ownmem = 1; } - explicit Array(size_t asize) - : FlatArray (asize, asize ? new T[asize] : nullptr) + explicit NgArray(size_t asize) + : NgFlatArray (asize, asize ? new T[asize] : nullptr) { allocsize = asize; - if(asize) - ownmem = 1; + ownmem = (asize == 0) ? 0 : 1; } /// Generate array in user data - Array(TIND asize, T* adata) - : FlatArray (asize, adata) + NgArray(TIND asize, T* adata) + : NgFlatArray (asize, adata) { allocsize = asize; ownmem = 0; } /// array copy - explicit Array (const Array & a2) - : FlatArray (a2.Size(), a2.Size() ? new T[a2.Size()] : 0) + explicit NgArray (const NgArray & a2) + : NgFlatArray (a2.Size(), a2.Size() ? new T[a2.Size()] : 0) { allocsize = size; ownmem = 1; @@ -269,8 +282,8 @@ namespace netgen } /// array move - Array (Array && a2) - : FlatArray (a2.size, a2.data), allocsize(a2.allocsize), ownmem(a2.ownmem) + NgArray (NgArray && a2) + : NgFlatArray (a2.size, a2.data), allocsize(a2.allocsize), ownmem(a2.ownmem) { a2.size = 0; a2.data = nullptr; @@ -280,7 +293,7 @@ namespace netgen /// if responsible, deletes memory - ~Array() + ~NgArray() { if (ownmem) delete [] data; @@ -294,6 +307,11 @@ namespace netgen size = nsize; } + void SetSize0() + { + size = 0; + } + /// Change physical size. Keeps logical size. Keeps contents. void SetAllocSize (size_t nallocsize) { @@ -313,7 +331,7 @@ namespace netgen } template - void Append (FlatArray a2) + void Append (NgFlatArray a2) { if (size+a2.Size() > allocsize) ReSize (size+a2.Size()); @@ -363,14 +381,14 @@ namespace netgen } /// Fill array with val - Array & operator= (const T & val) + NgArray & operator= (const T & val) { - FlatArray::operator= (val); + NgFlatArray::operator= (val); return *this; } /// array copy - Array & operator= (const Array & a2) + NgArray & operator= (const NgArray & a2) { SetSize (a2.Size()); for (TIND i (BASE); i < size+BASE; i++) @@ -379,7 +397,7 @@ namespace netgen } /// array copy - Array & operator= (const FlatArray & a2) + NgArray & operator= (const NgFlatArray & a2) { SetSize (a2.Size()); for (TIND i = BASE; i < size+BASE; i++) @@ -387,12 +405,12 @@ namespace netgen return *this; } - Array & operator= (Array && a2) + NgArray & operator= (NgArray && a2) { - Swap (data, a2.data); - Swap (size, a2.size); - Swap (allocsize, a2.allocsize); - Swap (ownmem, a2.ownmem); + ngcore::Swap (data, a2.data); + ngcore::Swap (size, a2.size); + ngcore::Swap (allocsize, a2.allocsize); + ngcore::Swap (ownmem, a2.ownmem); return *this; } @@ -430,16 +448,11 @@ namespace netgen T * p = new T[nsize]; size_t mins = (nsize < size) ? nsize : size; - // memcpy (p, data, mins * sizeof(T)); -#if defined(__GNUG__) && __GNUC__ < 5 && !defined(__clang__) - for (size_t i = 0; i < mins; i++) p[i] = move(data[i]); -#else - if (std::is_trivially_copyable::value) + if constexpr(std::is_trivially_copyable::value) memcpy (p, data, sizeof(T)*mins); else - for (size_t i = 0; i < mins; i++) p[i] = move(data[i]); -#endif + for (size_t i = 0; i < mins; i++) p[i] = std::move(data[i]); if (ownmem) delete [] data; @@ -459,19 +472,19 @@ namespace netgen template - class ArrayMem : public Array + class NgArrayMem : public NgArray { - using Array::size; - using Array::data; - using Array::ownmem; + using NgArray::size; + using NgArray::data; + using NgArray::ownmem; T mem[S]; // Intel C++ calls dummy constructor // char mem[S*sizeof(T)]; // double mem[(S*sizeof(T)+7) / 8]; public: /// Generate array of logical and physical size asize - explicit ArrayMem(size_t asize = 0) - : Array (S, static_cast (static_cast(&mem[0]))) + explicit NgArrayMem(size_t asize = 0) + : NgArray (S, static_cast (static_cast(&mem[0]))) { size = asize; if (asize > S) @@ -482,14 +495,14 @@ namespace netgen // SetSize (asize); } - ArrayMem & operator= (const T & val) + NgArrayMem & operator= (const T & val) { - Array::operator= (val); + NgArray::operator= (val); return *this; } /// array copy - ArrayMem & operator= (const FlatArray & a2) + NgArrayMem & operator= (const NgFlatArray & a2) { this->SetSize (a2.Size()); for (size_t i = 0; i < size; i++) @@ -506,11 +519,11 @@ namespace netgen template class IndirectArray { - const FlatArray & array; - const FlatArray & ia; + const NgFlatArray & array; + const NgFlatArray & ia; public: - IndirectArray (const FlatArray & aa, const FlatArray & aia) + IndirectArray (const NgFlatArray & aa, const NgFlatArray & aia) : array(aa), ia(aia) { ; } int Size() const { return ia.Size(); } const T & operator[] (int i) const { return array[ia[i]]; } @@ -527,10 +540,15 @@ namespace netgen IndirectArray (const TA1 & aa, const TA2 & aia) : array(aa), ia(aia) { ; } int Size() const { return ia.Size(); } + [[deprecated("Use *Range().begin() instead")]] int Begin() const { return ia.Begin(); } + [[deprecated("Use *Range().end() instead")]] int End() const { return ia.End(); } const typename TA1::TELEM & operator[] (int i) const { return array[ia[i]]; } + auto Range() const { return ia.Range(); } + // auto begin() const { return ia.begin(); } + // auto end() const { return ia.end(); } }; @@ -704,7 +722,7 @@ namespace netgen /// bubble sort array template - inline void BubbleSort (const FlatArray & data) + inline void BubbleSort (const NgFlatArray & data) { for (int i = 0; i < data.Size(); i++) for (int j = i+1; j < data.Size(); j++) @@ -718,7 +736,7 @@ namespace netgen /// bubble sort array template - inline void BubbleSort (FlatArray & data, FlatArray & 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++) @@ -728,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 (FlatArray & data, - FlatArray & slave, + void QuickSortRec (NgFlatArray & data, + NgFlatArray & index, int left, int right) { int i = left; @@ -751,21 +769,21 @@ namespace netgen if (i <= j) { - Swap (data[i], data[j]); - Swap (slave[i], slave[j]); + ngcore::Swap (data[i], data[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 (FlatArray & data, FlatArray & 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); } @@ -777,8 +795,8 @@ namespace netgen template - void Intersection (const FlatArray & in1, const FlatArray & in2, - Array & out) + void Intersection (const NgFlatArray & in1, const NgFlatArray & in2, + NgArray & out) { out.SetSize(0); for(int i=0; i - void Intersection (const FlatArray & in1, const FlatArray & in2, const FlatArray & in3, - Array & out) + void Intersection (const NgFlatArray & in1, const NgFlatArray & in2, const NgFlatArray & in3, + NgArray & out) { out.SetSize(0); for(int i=0; i @@ -16,25 +16,25 @@ namespace netgen { //using namespace netgen; - BitArray :: BitArray () + NgBitArray :: NgBitArray () { size = 0; data = NULL; } - BitArray :: BitArray (int asize) + NgBitArray :: NgBitArray (int asize) { size = 0; data = NULL; SetSize (asize); } - BitArray :: ~BitArray () + NgBitArray :: ~NgBitArray () { delete [] data; } - void BitArray :: SetSize (int asize) + void NgBitArray :: SetSize (int asize) { if (size == asize) return; delete [] data; @@ -43,14 +43,14 @@ namespace netgen data = new unsigned char [Addr (size)+1]; } - void BitArray :: Set () + void NgBitArray :: Set () { if (!size) return; for (int i = 0; i <= Addr (size); i++) data[i] = UCHAR_MAX; } - void BitArray :: Clear () + void NgBitArray :: Clear () { if (!size) return; for (int i = 0; i <= Addr (size); i++) @@ -59,14 +59,14 @@ namespace netgen - void BitArray :: Invert () + void NgBitArray :: Invert () { if (!size) return; for (int i = 0; i <= Addr (size); i++) data[i] ^= 255; } - void BitArray :: And (const BitArray & ba2) + void NgBitArray :: And (const NgBitArray & ba2) { if (!size) return; for (int i = 0; i <= Addr (size); i++) @@ -74,7 +74,7 @@ namespace netgen } - void BitArray :: Or (const BitArray & ba2) + void NgBitArray :: Or (const NgBitArray & ba2) { if (!size) return; for (int i = 0; i <= Addr (size); i++) @@ -82,51 +82,4 @@ namespace netgen } - - - - - - - - - - template - void BitArrayChar :: Set () - { - data = 1; - } - - template - void BitArrayChar :: Clear () - { - data = 0; - } - - - template - void BitArrayChar :: Invert () - { - for (int i = BASE; i < data.Size()+BASE; i++) - data[i] = 1 - data[i]; - } - - template - void BitArrayChar :: And (const BitArrayChar & ba2) - { - for (int i = BASE; i < data.Size()+BASE; i++) - data[i] &= ba2.data[i]; - } - - - template - void BitArrayChar :: Or (const BitArrayChar & ba2) - { - for (int i = BASE; i < data.Size()+BASE; i++) - data[i] |= ba2.data[i]; - } - - - template class BitArrayChar<0>; - template class BitArrayChar<1>; } diff --git a/libsrc/general/ngbitarray.hpp b/libsrc/general/ngbitarray.hpp new file mode 100644 index 00000000..637d1814 --- /dev/null +++ b/libsrc/general/ngbitarray.hpp @@ -0,0 +1,147 @@ +#ifndef FILE_BitArray +#define FILE_BitArray + +/**************************************************************************/ +/* File: bitarray.hpp */ +/* Author: Joachim Schoeberl */ +/* Date: 01. Jun. 95 */ +/**************************************************************************/ + +#include + +namespace netgen +{ + + +/** + data type NgBitArray + + NgBitArray is a compressed array of Boolean information. By Set and Clear + the whole array or one bit can be set or reset, respectively. + Test returns the state of the occurring bit. + No range checking is done. + + index ranges from 0 to size-1 +*/ +class NgBitArray +{ + INDEX size; + unsigned char * data; + +public: + DLL_HEADER NgBitArray (); + /// + DLL_HEADER NgBitArray (INDEX asize); + /// + DLL_HEADER ~NgBitArray (); + + /// + DLL_HEADER void SetSize (INDEX asize); + /// + INDEX Size () const + { + return size; + } + + /// + DLL_HEADER void Set (); + /// + void Set (INDEX i) + { + data[Addr(i)] |= Mask(i); + } + + DLL_HEADER void Clear (); + + + void Clear (INDEX i) + { + data[Addr(i)] &= ~Mask(i); + } + + bool Test (INDEX i) const + { + return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? true : false; + } + + /// + void Invert (); + /// + void And (const NgBitArray & ba2); + /// + void Or (const NgBitArray & ba2); +private: + /// + inline unsigned char Mask (INDEX i) const + { + return char(1) << (i % CHAR_BIT); + } + /// + inline INDEX Addr (INDEX i) const + { + return (i / CHAR_BIT); + } + + /// + NgBitArray & operator= (NgBitArray &); + /// + NgBitArray (const NgBitArray &); +}; + + + +// print bitarray +inline ostream & operator<< (ostream & s, const NgBitArray & a) +{ + for (int i = 1; i <= a.Size(); i++) + { + s << int (a.Test(i)); + if (i % 40 == 0) s << "\n"; + } + if (a.Size() % 40 != 0) s << "\n"; + return s; +} + + +/* +inline +INDEX NgBitArray :: Size () const + { + return size; + } + +inline +unsigned char NgBitArray :: Mask (INDEX i) const + { + return char(1) << (i % CHAR_BIT); + } + +inline +INDEX NgBitArray :: Addr (INDEX i) const + { + return (i / CHAR_BIT); + } +inline +void NgBitArray :: Set (INDEX i) + { + data[Addr(i)] |= Mask(i); + } + +inline +void NgBitArray :: Clear (INDEX i) + { + data[Addr(i)] &= ~Mask(i); + } + + +inline +int NgBitArray :: Test (INDEX i) const + { + return (data[i / CHAR_BIT] & (char(1) << (i % CHAR_BIT) ) ) ? 1 : 0; + } + +*/ + +} + +#endif diff --git a/libsrc/general/ngpython.hpp b/libsrc/general/ngpython.hpp index 722709c5..ef02d19f 100644 --- a/libsrc/general/ngpython.hpp +++ b/libsrc/general/ngpython.hpp @@ -1,13 +1,14 @@ #ifdef NG_PYTHON -#include +#include + #include #include #include -namespace py = pybind11; #include #include +using namespace ngcore; template py::array MoveToNumpy(std::vector& vec) diff --git a/libsrc/general/ngsimd.hpp b/libsrc/general/ngsimd.hpp deleted file mode 100644 index 97e92052..00000000 --- a/libsrc/general/ngsimd.hpp +++ /dev/null @@ -1,707 +0,0 @@ -#ifndef FILE_NGSIMD -#define FILE_NGSIMD -/**************************************************************************/ -/* File: ngsimd.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 25. Mar. 16 */ -/**************************************************************************/ - -#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); } - - -#ifdef __AVX__ - template - class AlignedAlloc - { - protected: - static void * aligned_malloc(size_t s) - { - // Assume 16 byte alignment of standard library - if(alignof(T)<=16) - return malloc(s); - else - return _mm_malloc(s, alignof(T)); - } - - static void aligned_free(void *p) - { - if(alignof(T)<=16) - free(p); - else - _mm_free(p); - } - - public: - void * operator new (size_t s, void *p) { return p; } - void * operator new (size_t s) { return aligned_malloc(s); } - void * operator new[] (size_t s) { return aligned_malloc(s); } - void operator delete (void * p) { aligned_free(p); } - void operator delete[] (void * p) { aligned_free(p); } - }; -#else - // it's only a dummy without AVX - template - class AlignedAlloc { ; }; - -#endif - -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 // : public AlignedAlloc> - { - __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 : public AlignedAlloc> - { - __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 : public AlignedAlloc> - { - __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 : public AlignedAlloc> - { - 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> : public AlignedAlloc> - { - 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.cpp b/libsrc/general/optmem.cpp index 73ca693b..0b84decb 100644 --- a/libsrc/general/optmem.cpp +++ b/libsrc/general/optmem.cpp @@ -5,7 +5,7 @@ /**************************************************************************/ /* - Abstract data type Array + Abstract data type NgArray */ @@ -14,7 +14,7 @@ namespace netgen { - static mutex block_allocator_mutex; + // static mutex block_allocator_mutex; BlockAllocator :: BlockAllocator (unsigned asize, unsigned ablocks) : bablocks (0) @@ -28,6 +28,7 @@ namespace netgen BlockAllocator :: ~BlockAllocator () { + lock_guard guard(block_allocator_mutex); // cout << "****************** delete BlockAllocator " << endl; for (int i = 0; i < bablocks.Size(); i++) delete [] bablocks[i]; diff --git a/libsrc/general/optmem.hpp b/libsrc/general/optmem.hpp index 658bf055..b2be31d3 100644 --- a/libsrc/general/optmem.hpp +++ b/libsrc/general/optmem.hpp @@ -22,42 +22,17 @@ private: /// void * freelist; /// - Array bablocks; + NgArray bablocks; + 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 05521df1..d7f42a4a 100644 --- a/libsrc/general/parthreads.hpp +++ b/libsrc/general/parthreads.hpp @@ -96,14 +96,8 @@ void ParallelFor( int first, int next, const TFunc & f ) - template - inline atomic & AsAtomic (T & d) - { - return reinterpret_cast&> (d); - } - - 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) { @@ -114,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) { @@ -127,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/seti.hpp b/libsrc/general/seti.hpp index 4adbb09c..1f576de3 100644 --- a/libsrc/general/seti.hpp +++ b/libsrc/general/seti.hpp @@ -16,8 +16,8 @@ namespace netgen */ class IndexSet { - Array set; - BitArray flags; + NgArray set; + NgBitArray flags; public: IndexSet (int maxind); @@ -41,7 +41,7 @@ public: void Del (int ind); void Clear (); - const Array & GetArray() { return set; } + const NgArray & GetArray() { return set; } }; } diff --git a/libsrc/general/sort.cpp b/libsrc/general/sort.cpp index a6adda1a..b44f391b 100644 --- a/libsrc/general/sort.cpp +++ b/libsrc/general/sort.cpp @@ -16,8 +16,8 @@ namespace netgen { - void Sort (const Array & values, - Array & order) + void Sort (const NgArray & values, + NgArray & order) { int n = values.Size(); int i, j; @@ -35,8 +35,8 @@ namespace netgen } - void QuickSortRec (const Array & values, - Array & order, + void QuickSortRec (const NgArray & values, + NgArray & order, int left, int right) { int i, j; @@ -62,8 +62,8 @@ namespace netgen if (i < right) QuickSortRec (values, order, i, right); } - void QuickSort (const Array & values, - Array & order) + void QuickSort (const NgArray & values, + NgArray & order) { int i, n = values.Size(); order.SetSize (n); diff --git a/libsrc/general/sort.hpp b/libsrc/general/sort.hpp index 3a9b41db..c9250586 100644 --- a/libsrc/general/sort.hpp +++ b/libsrc/general/sort.hpp @@ -11,11 +11,11 @@ namespace netgen { // order(i) is sorted index of element i -extern void Sort (const Array & values, - Array & order); +extern void Sort (const NgArray & values, + NgArray & order); -extern void QuickSort (const Array & values, - Array & order); +extern void QuickSort (const NgArray & values, + NgArray & order); @@ -35,10 +35,11 @@ inline void BubbleSort (int size, T * data) } template -inline void BubbleSort (Array & data) +inline void BubbleSort (NgArray & data) { if(data.Size() > 0) - BubbleSort (data.Size(), &data[data.Begin()]); + // BubbleSort (data.Size(), &data[data.Begin()]); + BubbleSort (data.Size(), &data[*data.Range().begin()]); } } diff --git a/libsrc/general/stack.hpp b/libsrc/general/stack.hpp index 83adee64..7e86b524 100644 --- a/libsrc/general/stack.hpp +++ b/libsrc/general/stack.hpp @@ -41,7 +41,7 @@ public: private: /// - Array elems; + NgArray elems; /// INDEX size; }; diff --git a/libsrc/general/table.cpp b/libsrc/general/table.cpp index c964c470..732d63c3 100644 --- a/libsrc/general/table.cpp +++ b/libsrc/general/table.cpp @@ -27,7 +27,7 @@ namespace netgen oneblock = NULL; } - BASE_TABLE :: BASE_TABLE (const FlatArray & entrysizes, int elemsize) + BASE_TABLE :: BASE_TABLE (const NgFlatArray & entrysizes, int elemsize) : data(entrysizes.Size()) { size_t cnt = 0; diff --git a/libsrc/general/table.hpp b/libsrc/general/table.hpp index 6a08b3dc..a62277fa 100644 --- a/libsrc/general/table.hpp +++ b/libsrc/general/table.hpp @@ -29,26 +29,26 @@ protected: }; /// - Array data; + NgArray data; char * oneblock; public: /// BASE_TABLE (BASE_TABLE && table2) - : data(move(table2.data)), oneblock(table2.oneblock) + : data(std::move(table2.data)), oneblock(table2.oneblock) { table2.oneblock = nullptr; } - BASE_TABLE (int size); + DLL_HEADER BASE_TABLE (int size); /// - BASE_TABLE (const FlatArray & entrysizes, int elemsize); + DLL_HEADER BASE_TABLE (const NgFlatArray & entrysizes, int elemsize); /// - ~BASE_TABLE (); + DLL_HEADER ~BASE_TABLE (); BASE_TABLE & operator= (BASE_TABLE && table2) { - data = move(table2.data); + data = std::move(table2.data); Swap (oneblock, table2.oneblock); return *this; } @@ -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 FlatArray & entrysizes) - : BASE_TABLE (FlatArray (entrysizes.Size(), const_cast(&entrysizes[BASE])), + 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) @@ -190,6 +201,8 @@ public: inline const T & Get (int i, int nr) const { return ((T*)data.Get(i).col)[nr-1]; } + inline T & Get (int i, int nr) + { return ((T*)data.Get(i).col)[nr-1]; } /** Returns pointer to the first element in row i. */ inline const T * GetLine (int i) const @@ -219,7 +232,7 @@ public: inline void PrintMemInfo (ostream & ost) const { int els = AllocatedElements(); - ost << "table: allocaed " << els + ost << "table: allocated " << els << " a " << sizeof(T) << " Byts = " << els * sizeof(T) << " bytes in " << Size() << " bags." @@ -228,14 +241,14 @@ public: } /// Access entry. - FlatArray operator[] (int i) const + NgFlatArray operator[] (int i) const { #ifdef DEBUG if (i-BASE < 0 || i-BASE >= data.Size()) cout << "table out of range, i = " << i << ", s = " << data.Size() << endl; #endif - return FlatArray (data[i-BASE].size, (T*)data[i-BASE].col); + return NgFlatArray (data[i-BASE].size, (T*)data[i-BASE].col); } void DoArchive (Archive & ar) @@ -251,7 +264,7 @@ inline ostream & operator<< (ostream & ost, const TABLE & table) for (int i = BASE; i < table.Size()+BASE; i++) { ost << i << ": "; - FlatArray row = table[i]; + NgFlatArray row = table[i]; ost << "(" << row.Size() << ") "; for (int j = 0; j < row.Size(); j++) ost << row[j] << " "; diff --git a/libsrc/general/template.hpp b/libsrc/general/template.hpp index 8f824a9b..61166f54 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 @@ -23,7 +25,6 @@ namespace netgen // #include /** output stream for testing. testout is opened by main */ -DLL_HEADER extern ostream * testout; /** use instead of cout */ DLL_HEADER extern ostream * mycout; @@ -41,26 +42,6 @@ DLL_HEADER extern void MyError (const char * ch); DLL_HEADER extern void MyBeep (int nr = 1); -template -inline void Swap (T & a, T & b) -{ - T temp = a; - a = b; - b = temp; -} - -/* -template -inline void swap (T & a, T & b) -{ - T temp = a; - a = b; - b = temp; -} -*/ - - - /** INDEX is a typedef for (at least) 4-byte integer */ @@ -187,13 +168,19 @@ public: friend ostream & operator<<(ostream & s, const INDEX_2 & i2); }; - + /* inline INDEX_2 Sort (const INDEX_2 & i2) { INDEX_2 tmp = i2; tmp.Sort(); return tmp; } + */ +inline INDEX_2 Sort (INDEX_2 i2) +{ + i2.Sort(); + return i2; +} inline bool operator< (const INDEX_2 ia, const INDEX_2 ib) { @@ -400,7 +387,7 @@ inline bool operator< (const INDEX_4 & a, const INDEX_4 & b) - +/* @@ -438,6 +425,7 @@ inline T max3 (T a, T b, T c) /// + /// template inline int sgn (T a) @@ -458,6 +446,7 @@ inline T pow3 (const T a) { return a * a * a; } +*/ diff --git a/libsrc/geom2d/CMakeLists.txt b/libsrc/geom2d/CMakeLists.txt index 43c619a0..0766c698 100644 --- a/libsrc/geom2d/CMakeLists.txt +++ b/libsrc/geom2d/CMakeLists.txt @@ -1,24 +1,16 @@ -add_definitions(-DNGLIB_EXPORTS) -add_library(geom2d ${NG_LIB_TYPE} genmesh2d.cpp geom2dmesh.cpp geometry2d.cpp python_geom2d.cpp ) -if(APPLE) - set_target_properties( geom2d PROPERTIES SUFFIX ".so") -endif(APPLE) - -target_link_libraries(geom2d mesh ${PYTHON_LIBRARIES}) -install( TARGETS geom2d ${NG_INSTALL_DIR}) - -target_link_libraries(geom2d ngcore) +target_sources(nglib PRIVATE + csg2d.cpp + genmesh2d.cpp + geometry2d.cpp + python_geom2d.cpp +) if(USE_GUI) - add_library(geom2dvis ${NG_LIB_TYPE} vsgeom2d.cpp) - if(NOT WIN32) - target_link_libraries(geom2dvis geom2d) - install( TARGETS geom2dvis ${NG_INSTALL_DIR}) - endif(NOT WIN32) + target_sources(nggui PRIVATE vsgeom2d.cpp geom2dpkg.cpp) endif(USE_GUI) install(FILES - geom2dmesh.hpp geometry2d.hpp spline2d.hpp - vsgeom2d.hpp + geometry2d.hpp spline2d.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..fdefb59c --- /dev/null +++ b/libsrc/geom2d/csg2d.cpp @@ -0,0 +1,2240 @@ +#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 // degenerate 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: Additionally 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 either (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 constructing 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); + geo->SetDomainMaxh(dom, s.maxh); + if(s.layer != 1) + geo->SetDomainLayer(dom, s.layer); + } + 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..dcc06132 --- /dev/null +++ b/libsrc/geom2d/csg2d.hpp @@ -0,0 +1,750 @@ +#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; + + int layer = 1; + string name = MAT_DEFAULT; + double maxh = MAXH_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) + { + this->maxh = 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; + } + + Solid2d & Layer(int layer_) + { + layer = layer_; + 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 fde8b16c..b2ed2dc3 100644 --- a/libsrc/geom2d/genmesh2d.cpp +++ b/libsrc/geom2d/genmesh2d.cpp @@ -14,33 +14,48 @@ namespace netgen // double l, MeshingParameters & mp, Mesh & mesh, // double h, double h1, double h2, double hcurve, - double elto0, Array & points) + double elto0, NgArray & points) { double fperel, oldf, f; - int n = 10000; - - Array > xi(n); - Array 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); @@ -98,7 +113,7 @@ namespace netgen int n = 100; Point<2> mark, oldmark; - Array curvepoints; + NgArray curvepoints; double edgelength, edgelengthold; CalcPartition (spline, mp, mesh, elto0, curvepoints); @@ -111,7 +126,7 @@ namespace netgen double lold = 0; oldmark = pold; edgelengthold = 0; - Array locsearch; + NgArray locsearch; for (int i = 1; i <= n; i++) { @@ -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); } } } @@ -308,55 +337,50 @@ namespace netgen void SplineGeometry2d :: CopyEdgeMesh (int from, int to, Mesh & mesh, Point3dTree & searchtree) { - // const int D = 2; - - Array mappoints (mesh.GetNP()); - Array param (mesh.GetNP()); - mappoints = -1; + Array mappoints (mesh.GetNP()); + Array param (mesh.GetNP()); + mappoints = PointIndex::INVALID; param = 0; Point3d pmin, pmax; mesh.GetBox (pmin, pmax); double diam2 = Dist2(pmin, pmax); - if (printmessage_importance>0) - cout << "copy edge, from = " << from << " to " << to << endl; + PrintMessage(3, string("Copy edge, from ") + ToString(from) + " to " + ToString(to)); - for (int i = 1; i <= mesh.GetNSeg(); i++) + for (const auto& seg : mesh.LineSegments()) { - const Segment & seg = mesh.LineSegment(i); if (seg.edgenr == from) { - mappoints.Elem(seg[0]) = 1; - param.Elem(seg[0]) = seg.epgeominfo[0].dist; + mappoints[seg[0]] = 1; + param[seg[0]] = seg.epgeominfo[0].dist; - mappoints.Elem(seg[1]) = 1; - param.Elem(seg[1]) = seg.epgeominfo[1].dist; + mappoints[seg[1]] = 1; + param[seg[1]] = seg.epgeominfo[1].dist; } } bool mapped = false; - for (int i = 1; i <= mappoints.Size(); i++) + for (auto i : Range(mappoints)) { - if (mappoints.Get(i) != -1) + if (mappoints[i].IsValid()) { - Point<2> newp = splines.Get(to)->GetPoint (param.Get(i)); + Point<2> newp = splines.Get(to)->GetPoint (param[i]); Point<3> newp3 (newp(0), newp(1), 0); - int npi = -1; + PointIndex npi = PointIndex::INVALID; - for (PointIndex pi = PointIndex::BASE; - pi < mesh.GetNP()+PointIndex::BASE; pi++) + for (auto pi : Range(mesh.Points())) if (Dist2 (mesh.Point(pi), newp3) < 1e-12 * diam2) npi = pi; - if (npi == -1) + if (!npi.IsValid()) { npi = mesh.AddPoint (newp3); searchtree.Insert (newp3, npi); } - mappoints.Elem(i) = npi; + mappoints[i] = npi; mesh.GetIdentifications().Add (i, npi, to); mapped = true; @@ -375,15 +399,15 @@ namespace netgen Segment nseg; nseg.edgenr = to; nseg.si = GetSpline(to-1).bc; // splines.Get(to)->bc; - nseg[0] = mappoints.Get(seg[0]); - nseg[1] = mappoints.Get(seg[1]); + nseg[0] = mappoints[seg[0]]; + nseg[1] = mappoints[seg[1]]; nseg.domin = GetSpline(to-1).leftdom; nseg.domout = GetSpline(to-1).rightdom; nseg.epgeominfo[0].edgenr = to; - nseg.epgeominfo[0].dist = param.Get(seg[0]); + nseg.epgeominfo[0].dist = param[seg[0]]; nseg.epgeominfo[1].edgenr = to; - nseg.epgeominfo[1].dist = param.Get(seg[1]); + nseg.epgeominfo[1].dist = param[seg[1]]; mesh.AddSegment (nseg); } } @@ -397,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 (); - + bbox.Increase (1e-2*bbox.Diam()); + t_h.Start(); if (bbox.Diam() < mp.maxh) mp.maxh = bbox.Diam(); @@ -417,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) @@ -439,6 +472,7 @@ namespace netgen } (*mesh)[mpi].Singularity(geometry.GetPoint(i).hpref); } + t_hpref.Stop(); int maxdomnr = 0; @@ -448,11 +482,21 @@ 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)); - // set Array bcnames... + // set NgArray bcnames... // number of bcnames int maxsegmentindex = 0; for (SegmentIndex si = 0; si < mesh->GetNSeg(); si++) @@ -463,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(); @@ -477,10 +519,11 @@ namespace netgen for (int domnr = 1; domnr <= maxdomnr; domnr++) if (geometry.GetDomainTensorMeshing (domnr)) { // tensor product mesh + RegionTimer rt(t_tensor); - Array nextpi(bnp); - Array si1(bnp), si2(bnp); - PointIndex firstpi; + NgArray nextpi(bnp); + NgArray si1(bnp), si2(bnp); + // PointIndex firstpi; nextpi = -1; si1 = -1; @@ -518,7 +561,7 @@ namespace netgen - Array pts ( (nex+1) * (ney+1) ); // x ... inner loop + NgArray pts ( (nex+1) * (ney+1) ); // x ... inner loop pts = -1; for (PointIndex pi = c1, i = 0; pi != c2; pi = nextpi[pi], i++) @@ -532,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++) @@ -550,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; @@ -570,21 +625,45 @@ namespace netgen mp.quad = hquad || geometry.GetDomainQuadMeshing (domnr); - Meshing2 meshing (mp, Box<3> (pmin, pmax)); + Meshing2 meshing (geometry, mp, Box<3> (pmin, pmax)); - Array 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) @@ -598,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); @@ -616,6 +713,8 @@ namespace netgen mesh->SetMaterial (domnr, material); } + mesh->Compress(); + mp.quad = hquad; diff --git a/libsrc/geom2d/geom2dmesh.cpp b/libsrc/geom2d/geom2dmesh.cpp deleted file mode 100644 index be1fc6c9..00000000 --- a/libsrc/geom2d/geom2dmesh.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include -#include - -namespace netgen -{ - - Refinement2d :: Refinement2d (const SplineGeometry2d & ageometry) - : Refinement(), geometry(ageometry) - { - ; - } - - Refinement2d :: ~Refinement2d () - { - ; - } - - - void Refinement2d :: - 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 - { - newp = p1+secpoint*(p2-p1); - newgi.trignum = 1; - } - - - - void Refinement2d :: - PointBetween (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 - { - Point<2> p2d; - double newdist; - auto spline = geometry.GetSplines().Get(ap1.edgenr); - if( (ap1.dist == 0.0) && (ap2.dist == 0.0) ) - { - // used for manually generated meshes - const SplineSeg3<2> * ss3; - const LineSeg<2> * ls; - auto ext = dynamic_cast(spline); - if(ext) - { - ss3 = dynamic_cast *>(ext->seg); - ls = dynamic_cast *>(ext->seg); - } - else - { - ss3 = dynamic_cast *>(spline); - ls = dynamic_cast *>(spline); - } - Point<2> p12d(p1(0),p1(1)), p22d(p2(0),p2(1)); - Point<2> p1_proj(0.0,0.0), p2_proj(0.0,0.0); - double t1_proj = 0.0; - double t2_proj = 0.0; - if(ss3) - { - ss3->Project(p12d,p1_proj,t1_proj); - ss3->Project(p22d,p2_proj,t2_proj); - } - else if(ls) - { - ls->Project(p12d,p1_proj,t1_proj); - ls->Project(p22d,p2_proj,t2_proj); - } - p2d = spline->GetPoint (((1-secpoint)*t1_proj+secpoint*t2_proj)); - newdist = (1-secpoint)*t1_proj+secpoint*t2_proj; - } - else - { - p2d = spline->GetPoint (((1-secpoint)*ap1.dist+secpoint*ap2.dist)); - newdist = (1-secpoint)*ap1.dist+secpoint*ap2.dist; - } - - // (*testout) << "refine 2d line, ap1.dist, ap2.dist = " << ap1.dist << ", " << ap2.dist << endl; - // (*testout) << "p1, p2 = " << p1 << p2 << ", newp = " << p2d << endl; - - newp = Point3d (p2d(0), p2d(1), 0); - newgi.edgenr = ap1.edgenr; - newgi.dist = newdist; - }; - - - - Vec<3> Refinement2d :: GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & ap1) const - { - Vec<2> t2d = geometry.GetSplines().Get(ap1.edgenr) -> GetTangent(ap1.dist); - return Vec<3> (t2d(0), t2d(1), 0); - } - - Vec<3> Refinement2d :: GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const - { - return Vec<3> (0,0,1); - } - - - void Refinement2d :: ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const - { - p(2) = 0; - } - - - void Refinement2d :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & egi) const - { - Point<2> p2d (p(0), p(1)), pp; - double t; - geometry.GetSplines().Get(egi.edgenr) -> Project (p2d, pp, t); - p = Point<3> (pp(0), pp(1), 0); - } -} diff --git a/libsrc/geom2d/geom2dmesh.hpp b/libsrc/geom2d/geom2dmesh.hpp deleted file mode 100644 index 9b72216d..00000000 --- a/libsrc/geom2d/geom2dmesh.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef FILE_GEOM2DMESH -#define FILE_GEOM2DMESH - -/**************************************************************************/ -/* File: geom2dmesh.hh */ -/* Author: Joachim Schoeberl */ -/* Date: 22. Jan. 01 */ -/**************************************************************************/ - - -namespace netgen -{ - - class Refinement2d : public Refinement - { - const class SplineGeometry2d & geometry; - - public: - Refinement2d (const class SplineGeometry2d & ageometry); - virtual ~Refinement2d (); - - 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; - - virtual void PointBetween (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; - - - virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & ap1) const; - - virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const; - - virtual void ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const; - - virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & egi) const; - }; - - -} - - - -#endif diff --git a/libsrc/geom2d/geom2dpkg.cpp b/libsrc/geom2d/geom2dpkg.cpp index 735e15e9..87392192 100644 --- a/libsrc/geom2d/geom2dpkg.cpp +++ b/libsrc/geom2d/geom2dpkg.cpp @@ -21,7 +21,7 @@ namespace netgen class SplineGeometryVisRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const { return NULL; } + virtual NetgenGeometry * Load (const filesystem::path & filename) const { return NULL; } virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; }; diff --git a/libsrc/geom2d/geometry2d.cpp b/libsrc/geom2d/geometry2d.cpp index f18dd335..f3332258 100644 --- a/libsrc/geom2d/geometry2d.cpp +++ b/libsrc/geom2d/geometry2d.cpp @@ -6,6 +6,7 @@ #include #include +#include namespace netgen { @@ -20,8 +21,78 @@ namespace netgen delete [] materials[i]; } + void SplineGeometry2d :: 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 + { + Point<2> p2d; + double newdist; + auto spline = GetSplines().Get(ap1.edgenr); + if( (ap1.dist == 0.0) && (ap2.dist == 0.0) ) + { + // used for manually generated meshes + const SplineSeg3<2> * ss3; + const LineSeg<2> * ls; + auto ext = dynamic_cast(spline); + if(ext) + { + ss3 = dynamic_cast *>(ext->seg); + ls = dynamic_cast *>(ext->seg); + } + else + { + ss3 = dynamic_cast *>(spline); + ls = dynamic_cast *>(spline); + } + Point<2> p12d(p1(0),p1(1)), p22d(p2(0),p2(1)); + Point<2> p1_proj(0.0,0.0), p2_proj(0.0,0.0); + double t1_proj = 0.0; + double t2_proj = 0.0; + if(ss3) + { + ss3->Project(p12d,p1_proj,t1_proj); + ss3->Project(p22d,p2_proj,t2_proj); + } + else if(ls) + { + ls->Project(p12d,p1_proj,t1_proj); + ls->Project(p22d,p2_proj,t2_proj); + } + p2d = spline->GetPoint (((1-secpoint)*t1_proj+secpoint*t2_proj)); + newdist = (1-secpoint)*t1_proj+secpoint*t2_proj; + } + else + { + p2d = spline->GetPoint (((1-secpoint)*ap1.dist+secpoint*ap2.dist)); + newdist = (1-secpoint)*ap1.dist+secpoint*ap2.dist; + } - void SplineGeometry2d :: Load (const char * filename) + // (*testout) << "refine 2d line, ap1.dist, ap2.dist = " << ap1.dist << ", " << ap2.dist << endl; + // (*testout) << "p1, p2 = " << p1 << p2 << ", newp = " << p2d << endl; + + newp = Point3d (p2d(0), p2d(1), 0); + newgi.edgenr = ap1.edgenr; + newgi.dist = newdist; + }; + + + + Vec<3> SplineGeometry2d :: GetTangent(const Point<3> & p, int surfi1, int surfi2, + const EdgePointGeomInfo & ap1) const + { + Vec<2> t2d = GetSplines().Get(ap1.edgenr) -> GetTangent(ap1.dist); + return Vec<3> (t2d(0), t2d(1), 0); + } + + Vec<3> SplineGeometry2d :: GetNormal(int surfi1, const Point<3> & p, + const PointGeomInfo* gi) const + { + return Vec<3> (0,0,1); + } + + void SplineGeometry2d :: Load (const filesystem::path & filename) { ifstream infile; @@ -33,7 +104,7 @@ namespace netgen if ( ! infile.good() ) throw NgException(string ("Input file '") + - string (filename) + + filename.string() + string ("' not available!")); TestComment ( infile ); @@ -138,7 +209,7 @@ namespace netgen geompoints.Append (GeomPoint(x, hd)); geompoints.Last().hpref = flags.GetDefineFlag ("hpref"); - geompoints.Last().hmax = 1e99; + geompoints.Last().hmax = flags.GetNumFlag("hmax", 1e99); } PrintMessage (3, nump, " points loaded"); @@ -187,7 +258,7 @@ namespace netgen { int npts; infile >> npts; - Array< Point > pts(npts); + NgArray< Point > pts(npts); for (int j = 0; j < npts; j++) for(int k=0; k> pts[j](k); @@ -201,7 +272,6 @@ namespace netgen infile >> spex->reffak; spex -> leftdom = leftdom; spex -> rightdom = rightdom; - spex -> hmax = 1e99; splines.Append (spex); @@ -233,6 +303,7 @@ namespace netgen delete bcnames[mybc]; bcnames[mybc] = new string (flags.GetStringFlag("bcname","") ); } + spex -> hmax = flags.GetNumFlag("hmax", 1e99); } } @@ -358,7 +429,7 @@ namespace netgen { int npts; infile >> npts; - Array< Point > pts(npts); + NgArray< Point > pts(npts); for (int j = 0; j < npts; j++) for(int k=0; k> pts[j](k); @@ -489,8 +560,8 @@ namespace netgen string keyword; - Array < GeomPoint > infilepoints (0); - Array pointnrs (0); + NgArray < GeomPoint > infilepoints (0); + NgArray pointnrs (0); nump = 0; int numdomains = 0; @@ -654,7 +725,7 @@ namespace netgen { int npts; infile >> npts; - Array< Point > pts(npts); + NgArray< Point > pts(npts); for (int j = 0; j < npts; j++) for(int k=0; k> pts[j](k); @@ -666,7 +737,7 @@ namespace netgen int npts,order; infile >> npts; infile >> order; - Array< Point > pts(npts); + NgArray< Point > pts(npts); for (int j = 0; j < npts; j++) for(int k=0; k> pts[j](k); @@ -830,7 +901,7 @@ namespace netgen /* void CalcPartition (const SplineSegExt & spline, double l, double h, double h1, double h2, - double hcurve, double elto0, Array & points) + double hcurve, double elto0, NgArray & points) { double fperel, oldf, f; @@ -988,36 +1059,36 @@ namespace netgen int SplineGeometry2d :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) { - MeshFromSpline2D (*this, mesh, mparam); + if(restricted_h.Size()) + { + // copy so that we don't change mparam outside + MeshingParameters mp = mparam; + for(const auto& [pnt, maxh] : restricted_h) + mp.meshsize_points.Append({pnt, maxh}); + MeshFromSpline2D (*this, mesh, mp); + } + else + MeshFromSpline2D (*this, mesh, mparam); return 0; } - - Refinement & SplineGeometry2d :: GetRefinement () const - { - return * new Refinement2d (*this); - } - - - class SplineGeometryRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const; + virtual NetgenGeometry * Load (const filesystem::path & filename) const; }; - NetgenGeometry * SplineGeometryRegister :: Load (string filename) const + NetgenGeometry * SplineGeometryRegister :: Load (const filesystem::path & filename) const { - const char * cfilename = filename.c_str(); - if (strcmp (&cfilename[strlen(cfilename)-4], "in2d") == 0) + string ext = ToLower(filename.extension()); + if (ext == ".in2d") { - PrintMessage (1, "Load 2D-Spline geometry file ", cfilename); - + PrintMessage (1, "Load 2D-Spline geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); SplineGeometry2d * hgeom = new SplineGeometry2d(); - hgeom -> Load (cfilename); + hgeom -> Load (filename); return hgeom; } diff --git a/libsrc/geom2d/geometry2d.hpp b/libsrc/geom2d/geometry2d.hpp index 0d4bcb00..d6b62b88 100644 --- a/libsrc/geom2d/geometry2d.hpp +++ b/libsrc/geom2d/geometry2d.hpp @@ -9,11 +9,11 @@ #include #include +#include // #include "../gprim/spline.hpp" // #include "../gprim/splinegeometry.hpp" -#include "geom2dmesh.hpp" namespace netgen { @@ -94,7 +94,7 @@ namespace netgen seg->GetCoeff (coeffs); } - virtual void GetPoints (int n, Array > & points) const + virtual void GetPoints (int n, NgArray > & points) const { seg->GetPoints (n, points); } @@ -128,35 +128,63 @@ namespace netgen - class SplineGeometry2d : public SplineGeometry<2>, public NetgenGeometry + class DLL_HEADER SplineGeometry2d : public SplineGeometry<2>, public NetgenGeometry { protected: - Array materials; - Array maxh; - Array quadmeshing; + NgArray materials; + NgArray maxh; + NgArray quadmeshing; Array tensormeshing; - Array layer; - Array bcnames; + NgArray layer; + NgArray bcnames; double elto0 = 1.0; public: - DLL_HEADER virtual ~SplineGeometry2d(); + virtual ~SplineGeometry2d(); - DLL_HEADER void Load (const char * filename); + void Load (const filesystem::path & filename); - DLL_HEADER void LoadData( ifstream & infile ); - DLL_HEADER void LoadDataNew ( ifstream & infile ); - DLL_HEADER void LoadDataV2 ( ifstream & infile ); + void LoadData( ifstream & infile ); + void LoadDataNew ( ifstream & infile ); + void LoadDataV2 ( ifstream & infile ); void TestComment ( ifstream & infile ) ; - void DoArchive(Archive& ar) + void DoArchive(Archive& ar) override { SplineGeometry<2>::DoArchive(ar); ar & materials & maxh & quadmeshing & tensormeshing & layer & bcnames & elto0; } + bool ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const override + { + p(2) = 0.0; + return true; + } + + 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 + { + newp = p1+secpoint*(p2-p1); + newgi.trignum = 1; + } + + 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; + + + Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2, + const EdgePointGeomInfo & ap1) const override; + Vec<3> GetNormal(int surfi1, const Point<3> & p, + const PointGeomInfo* gi) const override; + const SplineSegExt & GetSpline (const int i) const { return dynamic_cast (*splines[i]); @@ -168,7 +196,7 @@ namespace netgen } - DLL_HEADER virtual int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam); + int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) override; void PartitionBoundary (MeshingParameters & mp, double h, Mesh & mesh2d); @@ -187,17 +215,52 @@ 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() old_size) + { + layer.SetSize(domnr); + for(size_t i = old_size; i < domnr; i++) + layer[i] = 1; + } + layer[domnr-1] = layernr; + } string GetBCName (int bcnr) const; void SetBCName (int bcnr, string name); @@ -205,9 +268,6 @@ namespace netgen int AddBCName (string name); string * BCNamePtr ( const int bcnr ); - - - DLL_HEADER virtual Refinement & GetRefinement () const; }; } diff --git a/libsrc/geom2d/python_geom2d.cpp b/libsrc/geom2d/python_geom2d.cpp index f2b20127..ab232f13 100644 --- a/libsrc/geom2d/python_geom2d.cpp +++ b/libsrc/geom2d/python_geom2d.cpp @@ -1,11 +1,15 @@ #ifdef NG_PYTHON -#include <../general/ngpython.hpp> +#include "../general/ngpython.hpp" +#include "../core/python_ngcore.hpp" +#include "../meshing/python_mesh.hpp" -#include -#include +#include "../include/meshing.hpp" +#include "../include/geometry2d.hpp" +#include "csg2d.hpp" using namespace netgen; +using namespace pybind11::literals; namespace netgen { @@ -13,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") @@ -45,6 +49,7 @@ DLL_HEADER void ExportGeom2d(py::module &m) })) .def(NGSPickle()) .def("Load",&SplineGeometry2d::Load) + .def("SetDomainLayer", &SplineGeometry2d::SetDomainLayer) .def("AppendPoint", FunctionPointer ([](SplineGeometry2d &self, double px, double py, double maxh, double hpref, string name) { @@ -60,57 +65,76 @@ DLL_HEADER void ExportGeom2d(py::module &m) }), py::arg("x"), py::arg("y"), py::arg("maxh") = 1e99, py::arg("hpref")=0, py::arg("name")="") .def("Append", FunctionPointer([](SplineGeometry2d &self, py::list segment, int leftdomain, int rightdomain, - py::object bc, py::object copy, double maxh, double hpref) + optional> bc, optional copy, double maxh, + double hpref, double hprefleft, double hprefright) { - py::extract segtype(segment[0]); - SplineSegExt * seg; - if (segtype().compare("line") == 0) + if(py::isinstance(segment[0])) { - py::extract point_index1(segment[1]); - py::extract point_index2(segment[2]); - //point_index1.check() - - LineSeg<2> * l = new LineSeg<2>(self.GetPoint(point_index1()), self.GetPoint(point_index2())); - seg = new SplineSegExt(*l); - } - else if (segtype().compare("spline3") == 0) - { - py::extract point_index1(segment[1]); - py::extract point_index2(segment[2]); - py::extract point_index3(segment[3]); - - SplineSeg3<2> * seg3 = new SplineSeg3<2>(self.GetPoint(point_index1()), self.GetPoint(point_index2()), self.GetPoint(point_index3())); - 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 { - cout << "Appended segment is not a line or a spline3" << endl; + 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; - seg->hpref_left = hpref; - seg->hpref_right = hpref; + seg->hpref_left = max(hpref, hprefleft); + seg->hpref_right = max(hpref,hprefright); seg->reffak = 1; seg->copyfrom = -1; - if (py::extract(copy).check()) - seg->copyfrom = py::extract(copy)()+1; - - if (py::extract(bc).check()) - seg->bc = py::extract(bc)(); - else if (py::extract(bc).check()) + if (copy.has_value()) + seg->copyfrom = *copy+1; + + if (bc.has_value()) { - string bcname = py::extract(bc)(); - seg->bc = self.GetNSplines()+1; - self.SetBCName(seg->bc, bcname); + if(auto intptr = get_if(&*bc); intptr) + seg->bc = *intptr; + else + { + auto bcname = get_if(&*bc); + seg->bc = self.GetNSplines() + 1; + self.SetBCName(seg->bc, *bcname); + } } else seg->bc = self.GetNSplines()+1; self.AppendSegment(seg); return self.GetNSplines()-1; }), py::arg("point_indices"), py::arg("leftdomain") = 1, py::arg("rightdomain") = py::int_(0), - py::arg("bc")=NGDummyArgument(), py::arg("copy")=NGDummyArgument(), py::arg("maxh")=1e99, py::arg("hpref")=0) + py::arg("bc")=nullopt, py::arg("copy")=nullopt, py::arg("maxh")=1e99, + py::arg("hpref")=0,py::arg("hprefleft")=0,py::arg("hprefright")=0) .def("AppendSegment", FunctionPointer([](SplineGeometry2d &self, py::list point_indices, int leftdomain, int rightdomain) @@ -130,6 +154,8 @@ DLL_HEADER void ExportGeom2d(py::module &m) seg = new SplineSegExt(*seg3); } + else + throw Exception("Can only append segments with 2 or 3 points!"); seg->leftdom = leftdomain; seg->rightdom = rightdomain; seg->hmax = 1e99; @@ -138,6 +164,47 @@ DLL_HEADER void ExportGeom2d(py::module &m) self.AppendSegment(seg); }), py::arg("point_indices"), py::arg("leftdomain") = 1, py::arg("rightdomain") = py::int_(0)) + + .def("AddCurve", + [] (SplineGeometry2d & self, py::object func, + int leftdomain, int rightdomain, py::object bc, double maxh) + { + int n = 1000; + NgArray> points; + for (int i = 0; i <= n; i++) + { + double t = double(i)/n; + py::tuple xy = func(t); + double x = py::cast(xy[0]); + double y = py::cast(xy[1]); + points.Append (Point<2>(x,y)); + } + auto spline = new DiscretePointsSeg<2> (points); + SplineSegExt * spex = new SplineSegExt (*spline); + + spex -> leftdom = leftdomain; + spex -> rightdom = rightdomain; + spex->hmax = maxh; + spex->reffak = 1; + spex->copyfrom = -1; + + if (py::extract(bc).check()) + spex->bc = py::extract(bc)(); + else if (py::extract(bc).check()) + { + string bcname = py::extract(bc)(); + spex->bc = self.GetNSplines()+1; + self.SetBCName(spex->bc, bcname); + } + else + spex->bc = self.GetNSplines()+1; + + + self.AppendSegment (spex); + }, py::arg("func"), py::arg("leftdomain") = 1, py::arg("rightdomain") = py::int_(0), + py::arg("bc")=NGDummyArgument(), py::arg("maxh")=1e99, + "Curve is given as parametrization on the interval [0,1]") + .def("SetMaterial", &SplineGeometry2d::SetMaterial) .def("SetDomainMaxH", &SplineGeometry2d::SetDomainMaxh) @@ -222,6 +289,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)); @@ -320,18 +388,115 @@ DLL_HEADER void ExportGeom2d(py::module &m) //cout << i << " : " << self.splines[i]->GetPoint(0.1) << " , " << self.splines[i]->GetPoint(0.5) << endl; } })) - .def("GenerateMesh", [](shared_ptr self, MeshingParameters & mparam) + .def("Draw", FunctionPointer + ([] (shared_ptr self) + { + ng_geometry = self; + py::module::import("netgen").attr("Redraw")(); + }) + ) + + .def("GenerateMesh", [](shared_ptr self, + optional pars, py::kwargs kwargs) { - shared_ptr mesh = make_shared (); + MeshingParameters mp; + if(pars) mp = *pars; + { + py::gil_scoped_acquire aq; + CreateMPfromKwargs(mp, kwargs); + } + auto mesh = make_shared(); mesh->SetGeometry(self); SetGlobalMesh (mesh); ng_geometry = self; - self->GenerateMesh(mesh, mparam); + auto result = self->GenerateMesh(mesh, mp); + if(result != 0) + throw Exception("Meshing failed!"); return mesh; - },py::call_guard()) - - ; + }, 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("Layer", &Solid2d::Layer) + + .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/geom2d/spline2d.hpp b/libsrc/geom2d/spline2d.hpp index 52ec0df1..dadd793e 100644 --- a/libsrc/geom2d/spline2d.hpp +++ b/libsrc/geom2d/spline2d.hpp @@ -85,13 +85,13 @@ public: virtual void GetCoeff (Vector & coeffs) const = 0; - virtual void GetPoints (int n, Array > & points); + virtual void GetPoints (int n, NgArray > & points); /** calculates lineintersections: - for lines $$ a x + b y + c = 0 $$ the interecting points are calculated + for lines $$ a x + b y + c = 0 $$ the intersecting points are calculated and stored in points */ virtual void LineIntersections (const double a, const double b, const double c, - Array < Point<2> > & points, const double eps) const + NgArray < Point<2> > & points, const double eps) const {points.SetSize(0);} virtual double MaxCurvature(void) const = 0; @@ -123,7 +123,7 @@ public: virtual string GetType(void) const {return "line";} virtual void LineIntersections (const double a, const double b, const double c, - Array < Point<2> > & points, const double eps) const; + NgArray < Point<2> > & points, const double eps) const; virtual double MaxCurvature(void) const {return 0;} }; @@ -154,7 +154,7 @@ public: const GeomPoint2d & TangentPoint (void) const { return p2; } virtual void LineIntersections (const double a, const double b, const double c, - Array < Point<2> > & points, const double eps) const; + NgArray < Point<2> > & points, const double eps) const; virtual double MaxCurvature(void) const; }; @@ -195,7 +195,7 @@ public: virtual string GetType(void) const {return "circle";} virtual void LineIntersections (const double a, const double b, const double c, - Array < Point<2> > & points, const double eps) const; + NgArray < Point<2> > & points, const double eps) const; virtual double MaxCurvature(void) const {return 1./radius;} }; @@ -208,11 +208,11 @@ public: /// class DiscretePointsSegment : public SplineSegment { - Array > pts; + NgArray > pts; GeomPoint2d p1, p2; public: /// - DiscretePointsSegment (const Array > & apts); + DiscretePointsSegment (const NgArray > & apts); /// virtual ~DiscretePointsSegment (); /// diff --git a/libsrc/geom2d/vsgeom2d.cpp b/libsrc/geom2d/vsgeom2d.cpp index f0cff330..ebfc7923 100644 --- a/libsrc/geom2d/vsgeom2d.cpp +++ b/libsrc/geom2d/vsgeom2d.cpp @@ -52,7 +52,7 @@ namespace netgen glColor3f (0, 0, 1); - Array > points, otherpoints; + NgArray > points, otherpoints; for (int i = 1; i <= geometry2d->GetSplines().Size(); i++) { diff --git a/libsrc/geom2d/vsgeom2d.hpp b/libsrc/geom2d/vsgeom2d.hpp index c7cd034e..190c386b 100644 --- a/libsrc/geom2d/vsgeom2d.hpp +++ b/libsrc/geom2d/vsgeom2d.hpp @@ -10,7 +10,7 @@ namespace netgen { - class DLL_HEADER VisualSceneGeometry2d : public VisualScene + class NGGUI_API VisualSceneGeometry2d : public VisualScene { const class SplineGeometry2d * geometry2d; public: diff --git a/libsrc/gprim/CMakeLists.txt b/libsrc/gprim/CMakeLists.txt index 402973dc..b23480a4 100644 --- a/libsrc/gprim/CMakeLists.txt +++ b/libsrc/gprim/CMakeLists.txt @@ -1,13 +1,16 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(gprim INTERFACE) -set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) -target_sources(gprim INTERFACE - ${sdir}/adtree.cpp ${sdir}/geom2d.cpp ${sdir}/geom3d.cpp ${sdir}/geomfuncs.cpp - ${sdir}/geomtest3d.cpp ${sdir}/transform3d.cpp ${sdir}/spline.cpp ${sdir}/splinegeometry.cpp - ) +target_sources(nglib PRIVATE + adtree.cpp + geom2d.cpp + geom3d.cpp + geomfuncs.cpp + geomtest3d.cpp + spline.cpp + splinegeometry.cpp + transform3d.cpp +) install(FILES - adtree.hpp geom2d.hpp geom3d.hpp geomfuncs.hpp geomobjects2.hpp + adtree.hpp geom2d.hpp geom3d.hpp geomfuncs.hpp geomobjects.hpp geomops2.hpp geomops.hpp geomtest3d.hpp gprim.hpp splinegeometry.hpp spline.hpp transform3d.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/gprim COMPONENT netgen_devel diff --git a/libsrc/gprim/adtree.cpp b/libsrc/gprim/adtree.cpp index cba845a7..a3ecdf28 100644 --- a/libsrc/gprim/adtree.cpp +++ b/libsrc/gprim/adtree.cpp @@ -206,7 +206,7 @@ namespace netgen } - void ADTree :: GetMatch (Array & matches) + void ADTree :: GetMatch (NgArray & matches) { int nodenr; @@ -398,25 +398,25 @@ namespace netgen void ADTree3 :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); - static Array stackdir(1000); + ArrayMem stack(1000); + ArrayMem stackdir(1000); ADTreeNode3 * node; int dir, stacks; - stack.SetSize (1000); - stackdir.SetSize(1000); + // stack.SetSize (1000); + // stackdir.SetSize(1000); pis.SetSize(0); - stack.Elem(1) = root; - stackdir.Elem(1) = 0; - stacks = 1; + stack[0] = root; + stackdir[0] = 0; + stacks = 0; - while (stacks) + while (stacks >= 0) { - node = stack.Get(stacks); - dir = stackdir.Get(stacks); + node = stack[stacks]; + dir = stackdir[stacks]; stacks--; if (node->pi != -1) @@ -436,14 +436,14 @@ namespace netgen if (node->left && bmin[dir] <= node->sep) { stacks++; - stack.Elem(stacks) = node->left; - stackdir.Elem(stacks) = ndir; + stack[stacks] = node->left; + stackdir[stacks] = ndir; } if (node->right && bmax[dir] >= node->sep) { stacks++; - stack.Elem(stacks) = node->right; - stackdir.Elem(stacks) = ndir; + stack[stacks] = node->right; + stackdir[stacks] = ndir; } } } @@ -658,10 +658,10 @@ namespace netgen void ADTree3Div :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); - static Array stackdir(1000); + static NgArray stack(1000); + static NgArray stackdir(1000); ADTreeNode3Div * node; int dir, i, stacks; @@ -917,10 +917,10 @@ namespace netgen void ADTree3M :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); - static Array stackdir(1000); + static NgArray stack(1000); + static NgArray stackdir(1000); ADTreeNode3M * node; int dir, i, stacks; @@ -1163,9 +1163,9 @@ namespace netgen void ADTree3F :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); + static NgArray stack(1000); ADTreeNode3F * node; int dir, i, stacks; @@ -1427,9 +1427,9 @@ namespace netgen void ADTree3FM :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); + static NgArray stack(1000); ADTreeNode3FM * node; int dir, i, stacks; @@ -1700,11 +1700,11 @@ namespace netgen void ADTree6 :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - // static Array stack(10000); + // static NgArray stack(10000); // stack.SetSize (10000); - ArrayMem stack(10000); + NgArrayMem stack(10000); pis.SetSize(0); stack[0].node = root; @@ -1794,7 +1794,7 @@ namespace netgen - + /* template T_ADTree :: T_ADTree (Point acmin, Point acmax) // : ela(0) @@ -1805,14 +1805,18 @@ namespace netgen root = new T_ADTreeNode; root->sep = (cmin[0] + cmax[0]) / 2; } + */ + /* template T_ADTree :: ~T_ADTree () { root->DeleteChilds(); delete root; } + */ + /* template void T_ADTree :: Insert (Point p, T pi) { @@ -1883,7 +1887,9 @@ namespace netgen node = node->father; } } + */ + /* template void T_ADTree :: DeleteElement (T pi) { @@ -1899,7 +1905,9 @@ namespace netgen node = node->father; } } + */ + /* template void T_ADTree :: PrintMemInfo (ostream & ost) const { @@ -1908,8 +1916,9 @@ namespace netgen << Elements() * sizeof(T_ADTreeNode) << endl; ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode*) * ela.Size() << " Bytes" << endl; } + */ - + /* template class inttn { public: @@ -1920,11 +1929,11 @@ namespace netgen template void T_ADTree :: GetIntersecting (Point bmin, Point bmax, - Array & pis) const + NgArray & pis) const { - // static Array stack(10000); + // static NgArray stack(10000); // stack.SetSize (10000); - ArrayMem,10000> stack(10000); + NgArrayMem,10000> stack(10000); pis.SetSize(0); stack[0].node = root; @@ -1948,19 +1957,18 @@ namespace netgen found = false; if (found) pis.Append (node->pi); - /* - if (node->data[0] > bmax[0] || - node->data[1] > bmax[1] || - node->data[2] > bmax[2] || - node->data[3] < bmin[3] || - node->data[4] < bmin[4] || - node->data[5] < bmin[5]) - ; - else - { - pis.Append (node->pi); - } - */ + + // if (node->data[0] > bmax[0] || + // node->data[1] > bmax[1] || + // node->data[2] > bmax[2] || + // node->data[3] < bmin[3] || + // node->data[4] < bmin[4] || + // node->data[5] < bmin[5]) + // ; + // else + // { + // pis.Append (node->pi); + // } } int ndir = (dir+1) % dim; @@ -1979,7 +1987,9 @@ namespace netgen } } } +*/ + /* template void T_ADTree :: PrintRec (ostream & ost, const T_ADTreeNode * node) const { @@ -2021,8 +2031,7 @@ namespace netgen els += ElementsRec(node->right); return els; } - - +*/ @@ -2195,9 +2204,9 @@ namespace netgen void ADTree6F :: GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const + NgArray & pis) const { - static Array stack(1000); + static NgArray stack(1000); ADTreeNode6F * node; int dir, i, stacks; @@ -2329,7 +2338,7 @@ namespace netgen } void Point3dTree :: GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, - Array & pis) const + NgArray & pis) const { float pmi[3], pma[3]; for (int i = 0; i < 3; i++) @@ -2346,7 +2355,7 @@ namespace netgen - + /* template BoxTree :: BoxTree (const Box & abox) { @@ -2360,7 +2369,9 @@ namespace netgen } tree = new T_ADTree<2*dim,T> (tpmin, tpmax); } + */ + /* template BoxTree :: BoxTree (const Point & apmin, const Point & apmax) { @@ -2394,10 +2405,12 @@ namespace netgen tree->Insert (tp, pi); } + */ + /* template void BoxTree ::GetIntersecting (const Point & pmin, const Point & pmax, - Array & pis) const + NgArray & pis) const { Point<2*dim> tpmin, tpmax; double tol = Tolerance(); @@ -2412,8 +2425,9 @@ namespace netgen tree->GetIntersecting (tpmin, tpmax, pis); } - - + */ + + /* template<> BlockAllocator T_ADTreeNode<4,INDEX> :: ball(sizeof (T_ADTreeNode<4,INDEX>)); template class T_ADTree<4,INDEX>; template class BoxTree<2,INDEX>; @@ -2426,4 +2440,6 @@ namespace netgen template<> BlockAllocator T_ADTreeNode<6,INDEX> :: ball(sizeof (T_ADTreeNode<6,INDEX>)); template class T_ADTree<6,INDEX>; template class BoxTree<3,INDEX>; + */ + } diff --git a/libsrc/gprim/adtree.hpp b/libsrc/gprim/adtree.hpp index 11694b67..594cf76d 100644 --- a/libsrc/gprim/adtree.hpp +++ b/libsrc/gprim/adtree.hpp @@ -51,11 +51,11 @@ class ADTree int dim; ADTreeNode * root; float *cmin, *cmax; - Array ela; + NgArray ela; const ADTreeCriterion * criterion; - Array stack; - Array stackdir; + NgArray stack; + NgArray stackdir; int stackindex; public: @@ -65,11 +65,11 @@ public: void Insert (const float * p, int pi); // void GetIntersecting (const float * bmin, const float * bmax, - // Array & pis) const; + // NgArray & pis) const; void SetCriterion (ADTreeCriterion & acriterion); void Reset (); int Next (); - void GetMatch (Array & matches); + void GetMatch (NgArray & matches); void DeleteElement (int pi); @@ -105,7 +105,7 @@ class ADTree3 { ADTreeNode3 * root; float cmin[3], cmax[3]; - Array ela; + NgArray ela; public: ADTree3 (const float * acmin, @@ -114,7 +114,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -155,7 +155,7 @@ class ADTree3Div { ADTreeNode3Div * root; float cmin[3], cmax[3]; - Array ela; + NgArray ela; public: ADTree3Div (const float * acmin, @@ -164,7 +164,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -204,7 +204,7 @@ class ADTree3M { ADTreeNode3M * root; float cmin[3], cmax[3]; - Array ela; + NgArray ela; public: ADTree3M (const float * acmin, @@ -213,7 +213,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -253,7 +253,7 @@ class ADTree3F { ADTreeNode3F * root; float cmin[3], cmax[3]; - Array ela; + NgArray ela; public: ADTree3F (const float * acmin, @@ -262,7 +262,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -300,7 +300,7 @@ class ADTree3FM { ADTreeNode3FM * root; float cmin[3], cmax[3]; - Array ela; + NgArray ela; public: ADTree3FM (const float * acmin, @@ -309,7 +309,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -351,7 +351,7 @@ class ADTree6 { ADTreeNode6 * root; float cmin[6], cmax[6]; - Array ela; + NgArray ela; public: ADTree6 (const float * acmin, @@ -360,7 +360,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -404,56 +404,203 @@ public: nchilds = 0; } - void DeleteChilds () + void DeleteChilds (BlockAllocator & ball) { if (left) { - left->DeleteChilds(); - delete left; + left->DeleteChilds(ball); + ball.Free(left); left = NULL; } if (right) { - right->DeleteChilds(); - delete right; + right->DeleteChilds(ball); + ball.Free(right); right = NULL; } } - - // friend class T_ADTree; - - static BlockAllocator ball; - void * operator new(size_t) - { - return ball.Alloc(); - } - - void operator delete (void * p) - { - ball.Free(p); - } }; - template class T_ADTree { T_ADTreeNode * root; // float cmin[dim], cmax[dim]; Point cmin, cmax; - // Array*> ela; + // NgArray*> ela; ClosedHashTable*> ela; + + BlockAllocator ball{sizeof(T_ADTreeNode)}; public: - T_ADTree (Point acmin, Point acmax); - ~T_ADTree (); + T_ADTree (Point acmin, Point acmax) + { + cmin = acmin; + cmax = acmax; + + root = new (ball.Alloc()) T_ADTreeNode; + root->sep = (cmin[0] + cmax[0]) / 2; + } + + ~T_ADTree () + { + root->DeleteChilds(ball); + ball.Free(root); + } + + void Insert (Point p, T pi) + { + T_ADTreeNode *node(NULL); + T_ADTreeNode *next; + int dir; + int lr(0); + + Point bmin = cmin; + Point bmax = cmax; + + next = root; + dir = 0; + while (next) + { + node = next; + + if (IsInvalid(node->pi)) + { + // memcpy (node->data, p, dim * sizeof(float)); + node->data = p; + node->pi = pi; + + // if (ela.Size() < pi+1) + // ela.SetSize (pi+1); + ela[pi] = node; + + return; + } + + if (node->sep > p[dir]) + { + next = node->left; + bmax(dir) = node->sep; + lr = 0; + } + else + { + next = node->right; + bmin(dir) = node->sep; + lr = 1; + } + + dir++; + if (dir == dim) dir = 0; + } + + + next = new (ball.Alloc()) T_ADTreeNode; + next->data = p; + next->pi = pi; + next->sep = (bmin[dir] + bmax[dir]) / 2; + + // if (ela.Size() < pi+1) + // ela.SetSize (pi+1); + ela[pi] = next; + + if (lr) + node->right = next; + else + node->left = next; + next -> father = node; + + while (node) + { + node->nchilds++; + node = node->father; + } + } + + + class inttn { + public: + int dir; + T_ADTreeNode * node; + }; - void Insert (Point p, T pi); void GetIntersecting (Point bmin, Point bmax, - Array & pis) const; + NgArray & pis) const + { + NgArrayMem stack(10000); + pis.SetSize(0); + + stack[0].node = root; + stack[0].dir = 0; + int stacks = 0; + + while (stacks >= 0) + { + T_ADTreeNode * node = stack[stacks].node; + int dir = stack[stacks].dir; + + stacks--; + if (!IsInvalid(node->pi)) // != -1) + { + bool found = true; + for (int i = 0; i < dim/2; i++) + if (node->data[i] > bmax[i]) + found = false; + for (int i = dim/2; i < dim; i++) + if (node->data[i] < bmin[i]) + found = false; + if (found) + pis.Append (node->pi); + /* + if (node->data[0] > bmax[0] || + node->data[1] > bmax[1] || + node->data[2] > bmax[2] || + node->data[3] < bmin[3] || + node->data[4] < bmin[4] || + node->data[5] < bmin[5]) + ; + else + { + pis.Append (node->pi); + } + */ + } + + int ndir = (dir+1) % dim; + + if (node->left && bmin[dir] <= node->sep) + { + stacks++; + stack[stacks].node = node->left; + stack[stacks].dir = ndir; + } + if (node->right && bmax[dir] >= node->sep) + { + stacks++; + stack[stacks].node = node->right; + stack[stacks].dir = ndir; + } + } + } + + + + void DeleteElement (T pi) + { + T_ADTreeNode * node = ela[pi]; + ela.Delete(pi); + + SetInvalid(node->pi); // = -1; + + node = node->father; + while (node) + { + node->nchilds--; + node = node->father; + } + } - void DeleteElement (T pi); void Print (ostream & ost) const { PrintRec (ost, root); } @@ -462,11 +609,54 @@ public: int Elements () const { return ElementsRec (root); } - void PrintRec (ostream & ost, const T_ADTreeNode * node) const; - int DepthRec (const T_ADTreeNode * node) const; - int ElementsRec (const T_ADTreeNode * node) const; + void PrintRec (ostream & ost, const T_ADTreeNode * node) const + { + + // if (node->data) // true anyway + { + ost << node->pi << ": "; + ost << node->nchilds << " childs, "; + for (int i = 0; i < dim; i++) + ost << node->data[i] << " "; + ost << endl; + } + if (node->left) + PrintRec (ost, node->left); + if (node->right) + PrintRec (ost, node->right); + } + + int DepthRec (const T_ADTreeNode * node) const + { + int ldepth = 0; + int rdepth = 0; + + if (node->left) + ldepth = DepthRec(node->left); + if (node->right) + rdepth = DepthRec(node->right); + return 1 + max2 (ldepth, rdepth); + } + + int ElementsRec (const T_ADTreeNode * node) const + { + int els = 1; + if (node->left) + els += ElementsRec(node->left); + if (node->right) + els += ElementsRec(node->right); + return els; + } + + + void PrintMemInfo (ostream & ost) const + { + ost << Elements() << " elements a " << sizeof(ADTreeNode6) + << " Bytes = " + << Elements() * sizeof(T_ADTreeNode) << endl; + ost << "maxind = " << ela.Size() << " = " << sizeof(T_ADTreeNode*) * ela.Size() << " Bytes" << endl; + } - void PrintMemInfo (ostream & ost) const; }; @@ -501,7 +691,7 @@ class ADTree6F { ADTreeNode6F * root; float cmin[6], cmax[6]; - Array ela; + NgArray ela; public: ADTree6F (const float * acmin, @@ -510,7 +700,7 @@ public: void Insert (const float * p, int pi); void GetIntersecting (const float * bmin, const float * bmax, - Array & pis) const; + NgArray & pis) const; void DeleteElement (int pi); @@ -547,34 +737,651 @@ public: void DeleteElement (int pi) { tree->DeleteElement(pi); } DLL_HEADER void GetIntersecting (const Point<3> & pmin, const Point<3> & pmax, - Array & pis) const; + NgArray & pis) const; const ADTree3 & Tree() const { return *tree; }; }; +template +class BoxTree +{ +public: + // Number of entries per leaf + static constexpr int N = 100; - template - class BoxTree + struct Node; + + struct Leaf { - T_ADTree<2*dim,T> * tree; - Point boxpmin, boxpmax; - public: - BoxTree (const Box & abox); - BoxTree (const Point & apmin, const Point & apmax); - ~BoxTree (); - void Insert (const Point & bmin, const Point & bmax, T pi); - void Insert (const Box & box, T pi) + Point<2*dim> p[N]; + T index[N]; + int n_elements; + + Leaf() : n_elements(0) + { } + + void Add( ClosedHashTable &leaf_index, const Point<2*dim> &ap, T aindex ) + { + p[n_elements] = ap; + index[n_elements] = aindex; + n_elements++; + leaf_index[aindex] = this; + } + }; + + struct Node + { + union + { + Node *children[2]; + Leaf *leaf; + }; + double sep; + int level; + + Node() + : children{nullptr,nullptr} + { } + + ~Node() + { } + + Leaf *GetLeaf() const + { + return children[1] ? nullptr : leaf; + } + }; + +private: + Node root; + + ClosedHashTable leaf_index; + + Point global_min, global_max; + double tol; + size_t n_leaves; + size_t n_nodes; + BlockAllocator ball_nodes; + BlockAllocator ball_leaves; + +public: + + BoxTree (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.level = 0; + tol = 1e-7 * Dist(pmax, pmin); + } + + BoxTree (const Box & box) + : BoxTree(box.PMin(), box.PMax()) + { } + + void SetTolerance(double _tol) { tol = _tol; } + double GetTolerance() { return tol; } + + 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("BoxTree::GetIntersecting"); RegionTimer rt(timer); + // static Timer timer1("BoxTree::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 GetIntersecting(const Point & pmin, + const Point & pmax, + Array & pis) const + { + pis.SetSize0(); + 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 DeleteElement (T pi) - { tree->DeleteElement(pi); } - void GetIntersecting (const Point & pmin, const Point & pmax, - Array & pis) const; - double Tolerance() const { return 1e-7 * Dist(boxpmax, boxpmin); } // single precision - const auto & Tree() const { return *tree; }; - auto & Tree() { return *tree; }; + + void Insert (const Point & pmin, const Point & pmax, T pi) + { + // static Timer timer("BoxTree::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(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(); + + for (auto i : order.Range(isplit)) + leaf1->Add(leaf_index, leaf->p[i], leaf->index[i] ); + for (auto i : order.Range(isplit, N)) + leaf2->Add(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( leaf_index, p, pi ); + else + leaf2->Add( leaf_index, p, pi ); + + ball_leaves.Free(leaf); + n_leaves++; + n_nodes+=2; + } + } + + void DeleteElement (T pi) + { + // static Timer timer("BoxTree::DeleteElement"); RegionTimer rt(timer); + Leaf *leaf = leaf_index[pi]; + leaf_index.Delete(pi); + 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; + } + } + } }; +// template +// class BoxTree +// { +// T_ADTree<2*dim,T> * tree; +// Point boxpmin, boxpmax; +// public: +// BoxTree (const Box & abox) +// { +// boxpmin = abox.PMin(); +// boxpmax = abox.PMax(); +// Point<2*dim> tpmin, tpmax; +// for (int i = 0; i < dim; i++) +// { +// tpmin(i) = tpmin(i+dim) = boxpmin(i); +// tpmax(i) = tpmax(i+dim) = boxpmax(i); +// } +// tree = new T_ADTree<2*dim,T> (tpmin, tpmax); +// } +// +// BoxTree (const Point & apmin, const Point & apmax) +// { +// boxpmin = apmin; +// boxpmax = apmax; +// Point<2*dim> tpmin, tpmax; +// for (int i = 0; i < dim; i++) +// { +// tpmin(i) = tpmin(i+dim) = boxpmin(i); +// tpmax(i) = tpmax(i+dim) = boxpmax(i); +// } +// tree = new T_ADTree<2*dim,T> (tpmin, tpmax); +// } +// +// ~BoxTree () +// { +// delete tree; +// } +// +// void Insert (const Point & bmin, const Point & bmax, T pi) +// { +// Point<2*dim> tp; +// +// for (size_t i = 0; i < dim; i++) +// { +// tp(i) = bmin(i); +// tp(i+dim) = bmax(i); +// } +// +// tree->Insert (tp, pi); +// } +// +// void Insert (const Box & box, T pi) +// { +// Insert (box.PMin(), box.PMax(), pi); +// } +// +// void DeleteElement (T pi) +// { +// tree->DeleteElement(pi); +// } +// +// void GetIntersecting (const Point & pmin, const Point & pmax, +// NgArray & pis) const +// { +// Point<2*dim> tpmin, tpmax; +// double tol = Tolerance(); +// for (size_t i = 0; i < dim; i++) +// { +// tpmin(i) = boxpmin(i); +// tpmax(i) = pmax(i)+tol; +// +// tpmin(i+dim) = pmin(i)-tol; +// tpmax(i+dim) = boxpmax(i); +// } +// +// tree->GetIntersecting (tpmin, tpmax, pis); +// } +// +// +// double Tolerance() const { return 1e-7 * Dist(boxpmax, boxpmin); } // single precision +// const auto & Tree() const { return *tree; }; +// 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()) + { } + + double GetTolerance() { return tol; } + + 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 334df09c..890a456b 100644 --- a/libsrc/gprim/geom2d.hpp +++ b/libsrc/gprim/geom2d.hpp @@ -609,40 +609,6 @@ namespace netgen #endif - - class Polygon2d - { - protected: - Array 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/geom3d.cpp b/libsrc/gprim/geom3d.cpp index cb1f92c2..bad0657b 100644 --- a/libsrc/gprim/geom3d.cpp +++ b/libsrc/gprim/geom3d.cpp @@ -711,8 +711,8 @@ void referencetransform :: ToPlain (const Point3d & p, Point3d & pp) const pp.Z() = (ez_h * v); } -void referencetransform :: ToPlain (const Array & p, - Array & pp) const +void referencetransform :: ToPlain (const NgArray & p, + NgArray & pp) const { Vec3d v; int i; diff --git a/libsrc/gprim/geom3d.hpp b/libsrc/gprim/geom3d.hpp index 05216d8f..068848d5 100644 --- a/libsrc/gprim/geom3d.hpp +++ b/libsrc/gprim/geom3d.hpp @@ -302,7 +302,7 @@ namespace netgen friend inline void Cross (const Vec3d & v1, const Vec3d & v2, Vec3d & prod); /// Returns one normal-vector to n - void GetNormal (Vec3d & n) const; + DLL_HEADER void GetNormal (Vec3d & n) const; /// friend double Angle (const Vec3d & v); /// @@ -578,15 +578,15 @@ namespace netgen /// Box3d () { }; /// - Box3d ( double aminx, double amaxx, + DLL_HEADER Box3d ( double aminx, double amaxx, double aminy, double amaxy, double aminz, double amaxz ); /// - Box3d ( const Box3d & b2 ); + DLL_HEADER Box3d ( const Box3d & b2 ); /// - Box3d (const Point3d& p1, const Point3d& p2); + DLL_HEADER Box3d (const Point3d& p1, const Point3d& p2); /// - Box3d (const Box<3> & b2); + DLL_HEADER Box3d (const Box<3> & b2); /// double MinX () const { return minx[0]; } /// @@ -735,7 +735,7 @@ namespace netgen /// void ToPlain (const Point3d & p, Point3d & pp) const; /// - void ToPlain (const Array & p, Array & pp) const; + void ToPlain (const NgArray & p, NgArray & pp) const; /// void FromPlain (const Point3d & pp, Point3d & p) const; }; 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..2a647b25 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); @@ -163,9 +177,34 @@ namespace netgen double Det (const Mat<3,3> & m); // eigenvalues of a symmetric matrix - void EigenValues (const Mat<3,3> & m, Vec<3> & ev); - void EigenValues (const Mat<2,2> & m, Vec<3> & ev); + DLL_HEADER void EigenValues (const Mat<3,3> & m, Vec<3> & ev); + DLL_HEADER 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 eb2f0030..32a22318 100644 --- a/libsrc/gprim/geomobjects.hpp +++ b/libsrc/gprim/geomobjects.hpp @@ -17,7 +17,7 @@ namespace netgen template - class Point : public ngsimd::AlignedAlloc> + class Point { protected: @@ -43,7 +43,7 @@ namespace netgen Point (const Point & p2) { for (int i = 0; i < D; i++) x[i] = p2(i); } - explicit Point (const Vec & v) + explicit Point (const Vec & v) { for (int i = 0; i < D; i++) x[i] = v(i); } @@ -63,6 +63,9 @@ namespace netgen T & operator() (int i) { return x[i]; } const T & operator() (int i) const { return x[i]; } + T& operator[] (int i) { return x[i]; } + const T& operator[] (int i) const { return x[i]; } + operator const T* () const { return x; } void DoArchive(Archive& archive) @@ -73,7 +76,7 @@ namespace netgen }; template - class Vec : public ngsimd::AlignedAlloc> + class Vec { protected: @@ -101,9 +104,8 @@ namespace netgen explicit Vec (const Point & p) { for (int i = 0; i < D; i++) x[i] = p(i); } - Vec (const Vec & p1, const Vec & p2) - { for(int i=0; i& p1, const Point& p2) + { for(int i=0; i Vec & operator= (const Vec & p2) @@ -118,9 +120,20 @@ namespace netgen return *this; } + bool operator== (const Vec &a) const + { + bool res = true; + for (auto i : Range(D)) + res &= (x[i]==a.x[i]); + return res; + } + T & operator() (int i) { return x[i]; } const T & operator() (int i) const { return x[i]; } + T& operator[] (int i) { return x[i]; } + const T& operator[] (int i) const { return x[i]; } + operator const T* () const { return x; } void DoArchive(Archive& archive) @@ -157,12 +170,56 @@ namespace netgen Vec GetNormal () const; }; + template + inline Vec operator-(const Point& p1, const Point& p2) + { + Vec result; + for(auto i : Range(D)) + result[i] = p1[i] - p2[i]; + return result; + } + template + inline Vec operator*(const Vec& v, double d) + { + Vec result; + for(auto i : Range(D)) + result[i] = d*v[i]; + return result; + } + inline double Cross2(const Vec<2>& v1, const Vec<2>& v2) + { + return v1[0] * v2[1] - v1[1] * v2[0]; + } + + // are points clockwise? + inline bool CW(const Point<2>& p1, const Point<2>& p2, + const Point<2>& p3) + { + return Cross2(p2-p1, p3-p2) < 0; + } + + // are points counterclockwise? + inline bool CCW(const Point<2>& p1, const Point<2>& p2, + const Point<2>& p3) + { + return Cross2(p2-p1, p3-p2) > 0; + } + + // are strictly points counterclockwise? + inline bool CCW(const Point<2>& p1, const Point<2>& p2, + const Point<2>& p3, double eps) + { + auto v1 = p2-p1; + auto v2 = p3-p2; + return Cross2(v1, v2) > eps*eps*max2(v1.Length2(), + v2.Length2()); + } template - class Mat : public ngsimd::AlignedAlloc> + class Mat { protected: @@ -212,6 +269,11 @@ namespace netgen CalcInverse (*this, inv); sol = inv * rhs; } + + void DoArchive(Archive & ar) + { + ar.Do(x, H*W); + } }; @@ -278,15 +340,18 @@ namespace netgen template void Set (const IndirectArray & points) { - Set (points[points.Begin()]); - for (int i = points.Begin()+1; i < points.End(); i++) + // Set (points[points.Begin()]); + Set (points[*points.Range().begin()]); + // for (int i = points.Begin()+1; i < points.End(); i++) + for (int i : points.Range().Modify(1,0)) Add (points[i]); } template void Add (const IndirectArray & points) { - for (int i = points.Begin(); i < points.End(); i++) + // for (int i = points.Begin(); i < points.End(); i++) + for (int i : points.Range()) Add (points[i]); } @@ -324,8 +389,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; } @@ -338,6 +411,13 @@ namespace netgen } } + void Scale (double factor) + { + auto center = Center(); + pmin = center + factor*(pmin-center); + pmax = center + factor*(pmax-center); + } + void DoArchive(Archive& archive) { archive & pmin & pmax; } }; @@ -418,7 +498,7 @@ namespace netgen }; -#ifdef PARALLEL +#ifdef PARALLEL_OLD template <> inline MPI_Datatype MyGetMPIType > () { diff --git a/libsrc/gprim/geomobjects2.hpp b/libsrc/gprim/geomobjects2.hpp deleted file mode 100644 index 014a3852..00000000 --- a/libsrc/gprim/geomobjects2.hpp +++ /dev/null @@ -1,366 +0,0 @@ -#ifndef FILE_OBJECTS -#define FILE_OBJECTS - -/* *************************************************************************/ -/* File: geomobjects.hpp */ -/* Author: Joachim Schoeberl */ -/* Date: 20. Jul. 02 */ -/* *************************************************************************/ - -template -class VecExpr -{ -public: - VecExpr () { ; } - double operator() (int i) const { return static_cast (*this) (i); } -}; - - -template class Vec; -template class Point; - - -template -class Point : public VecExpr > -{ - -protected: - double x[D]; - -public: - Point () { ; } - Point (double ax) { x[0] = ax; } - Point (double ax, double ay) { x[0] = ax; x[1] = ay; } - Point (double ax, double ay, double az) - { x[0] = ax; x[1] = ay; x[2] = az; } - Point (double ax, double ay, double az, double au) - { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au;} - - Point (const Point & p2) - { for (int i = 0; i < D; i++) x[i] = p2.x[i]; } - - explicit Point (const Vec & v) - { for (int i = 0; i < D; i++) x[i] = v(i); } - - - template - explicit Point (const VecExpr & expr) - { - for (int i = 0; i < D; i++) x[i] = expr(i); - } - - Point & operator= (const Point & p2) - { - for (int i = 0; i < D; i++) x[i] = p2.x[i]; - return *this; - } - - template - Point & operator= (const VecExpr & expr) - { - for (int i = 0; i < D; i++) x[i] = expr(i); - return *this; - } - - double & operator() (int i) { return x[i]; } - const double & operator() (int i) const { return x[i]; } - - operator const double* () const { return x; } -}; - - - - - -template -class Vec : public VecExpr > -{ - -protected: - double x[D]; - -public: - Vec () { ; } - Vec (double ax) { for (int i = 0; i < D; i++) x[i] = ax; } - Vec (double ax, double ay) { x[0] = ax; x[1] = ay; } - Vec (double ax, double ay, double az) - { x[0] = ax; x[1] = ay; x[2] = az; } - Vec (double ax, double ay, double az, double au) - { x[0] = ax; x[1] = ay; x[2] = az; x[3] = au; } - - Vec (const Vec & p2) - { for (int i = 0; i < D; i++) x[i] = p2.x[i]; } - - explicit Vec (const Point & p) - { for (int i = 0; i < D; i++) x[i] = p(i); } - - template - explicit Vec (const VecExpr & expr) - { - for (int i = 0; i < D; i++) x[i] = expr(i); - } - - - Vec & operator= (const Vec & p2) - { - for (int i = 0; i < D; i++) x[i] = p2.x[i]; - return *this; - } - - template - Vec & operator= (const VecExpr & expr) - { - for (int i = 0; i < D; i++) x[i] = expr(i); - return *this; - } - - Vec & operator= (double s) - { - for (int i = 0; i < D; i++) x[i] = s; - return *this; - } - - double & operator() (int i) { return x[i]; } - const double & operator() (int i) const { return x[i]; } - - operator const double* () const { return x; } - - double Length () const - { - double l = 0; - for (int i = 0; i < D; i++) - l += x[i] * x[i]; - return sqrt (l); - } - - double Length2 () const - { - double l = 0; - for (int i = 0; i < D; i++) - l += x[i] * x[i]; - return l; - } - - const Vec & Normalize () - { - double l = Length(); - if (l != 0) - for (int i = 0; i < D; i++) - x[i] /= l; - return *this; - } - - Vec GetNormal () const; -}; - - - - - -template -class Mat -{ - -protected: - double x[H*W]; - -public: - Mat () { ; } - Mat (const Mat & b) - { for (int i = 0; i < H*W; i++) x[i] = b.x[i]; } - - Mat & operator= (double s) - { - for (int i = 0; i < H*W; i++) x[i] = s; - return *this; - } - - Mat & operator= (const Mat & b) - { - for (int i = 0; i < H*W; i++) x[i] = b.x[i]; - return *this; - } - - double & operator() (int i, int j) { return x[i*W+j]; } - const double & operator() (int i, int j) const { return x[i*W+j]; } - - Vec Col (int i) const - { - Vec hv; - for (int j = 0; j < H; j++) - hv(j) = x[j*W+i]; - return hv; - } - - Vec Row (int i) const - { - Vec hv; - for (int j = 0; j < W; j++) - hv(j) = x[i*W+j]; - return hv; - } - - void Solve (const Vec & rhs, Vec & sol) const - { - Mat inv; - CalcInverse (*this, inv); - sol = inv * rhs; - } -}; - - - - -template -class Box -{ -protected: - Point pmin, pmax; -public: - Box () { ; } - Box ( const Point & p1, const Point & p2) - { - for (int i = 0; i < D; i++) - { - pmin(i) = min2(p1(i), p2(i)); - pmax(i) = max2(p1(i), p2(i)); - } - } - - const Point & PMin () const { return pmin; } - const Point & PMax () const { return pmax; } - - void Set (const Point & p) - { pmin = pmax = p; } - - void Add (const Point & p) - { - for (int i = 0; i < D; i++) - { - if (p(i) < pmin(i)) pmin(i) = p(i); - else if (p(i) > pmax(i)) pmax(i) = p(i); - } - } - - Point Center () const - { - Point c; - for (int i = 0; i < D; i++) - c(i) = 0.5 * (pmin(i)+pmax(i)); - return c; - } - double Diam () const { return Abs (pmax-pmin); } - - Point GetPointNr (int nr) const - { - Point p; - for (int i = 0; i < D; i++) - { - p(i) = (nr & 1) ? pmax(i) : pmin(i); - nr >>= 1; - } - return p; - } - - - bool Intersect (const Box & box2) const - { - for (int i = 0; i < D; i++) - if (pmin(i) > box2.pmax(i) || - pmax(i) < box2.pmin(i)) return 0; - return 1; - } - - - 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; - } - - - void Increase (double dist) - { - for (int i = 0; i < D; i++) - { - pmin(i) -= dist; - pmax(i) += dist; - } - } -}; - - - - -template -class BoxSphere : public Box -{ -protected: - /// - Point c; - /// - double diam; - /// - double inner; -public: - /// - BoxSphere () { }; - /// - BoxSphere ( Point pmin, Point pmax ) - : Box (pmin, pmax) - { - CalcDiamCenter(); - } - - /// - const Point & Center () const { return c; } - /// - double Diam () const { return diam; } - /// - double Inner () const { return inner; } - - - /// - void GetSubBox (int nr, BoxSphere & sbox) const - { - for (int i = 0; i < D; i++) - { - if (nr & 1) - { - sbox.pmin(i) = c(i); - sbox.pmax(i) = this->pmax(i); - } - else - { - sbox.pmin(i) = this->pmin(i); - sbox.pmax(i) = c(i); - } - sbox.c(i) = 0.5 * (sbox.pmin(i) + sbox.pmax(i)); - nr >>= 1; - } - sbox.diam = 0.5 * diam; - sbox.inner = 0.5 * inner; - } - - - /// - void CalcDiamCenter () - { - c = Box::Center (); - diam = Dist (this->pmin, this->pmax); - - inner = this->pmax(0) - this->pmin(0); - for (int i = 1; i < D; i++) - if (this->pmax(i) - this->pmin(i) < inner) - inner = this->pmax(i) - this->pmin(i); - } - -}; - - - - - - -#endif diff --git a/libsrc/gprim/geomops.hpp b/libsrc/gprim/geomops.hpp index 6b06cb6e..fcb127ed 100644 --- a/libsrc/gprim/geomops.hpp +++ b/libsrc/gprim/geomops.hpp @@ -78,6 +78,15 @@ namespace netgen return res; } + template + inline Vec> operator* (SIMD s, Vec b) + { + Vec> res; + for (int i = 0; i < D; i++) + res(i) = s * b(i); + return res; + } + template inline double operator* (Vec a, Vec b) 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..954d32e7 100644 --- a/libsrc/gprim/geomtest3d.hpp +++ b/libsrc/gprim/geomtest3d.hpp @@ -69,13 +69,13 @@ extern double ComputeCylinderRadius (const Vec3d & n1, const Vec3d & n2, double h1, double h2); /// Minimal distance of point p to the line segment [lp1,lp2] -extern double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2d & p); +DLL_HEADER double MinDistLP2 (const Point2d & lp1, const Point2d & lp2, const Point2d & p); /// Minimal distance of point p to the line segment [lp1,lp2] -extern double MinDistLP2 (const Point3d & lp1, const Point3d & lp2, const Point3d & p); +DLL_HEADER 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 3f8ccf98..ed11ef96 100644 --- a/libsrc/gprim/spline.cpp +++ b/libsrc/gprim/spline.cpp @@ -9,6 +9,8 @@ Spline curve for Mesh generator #include #include "spline.hpp" +#include + namespace netgen { @@ -35,14 +37,14 @@ namespace netgen template <> void CircleSeg<3> :: LineIntersections (const double a, const double b, const double c, - Array < Point<3> > & points, const double eps) const + NgArray < Point<3> > & points, const double eps) const { cerr << "CircleSeg<3>::LineIntersections not implemented" << endl; } template <> void CircleSeg<2> :: LineIntersections (const double a, const double b, const double c, - Array < Point<2> > & points, const double eps) const + NgArray < Point<2> > & points, const double eps) const { points.SetSize(0); @@ -62,7 +64,7 @@ namespace netgen if(discr < 0) return; - Array t; + NgArray t; if(fabs(discr) < 1e-20) t.Append(-0.5*c2/c1); @@ -89,8 +91,10 @@ namespace netgen template SplineSeg3 :: SplineSeg3 (const GeomPoint & ap1, const GeomPoint & ap2, - const GeomPoint & ap3) - : p1(ap1), p2(ap2), p3(ap3) + const GeomPoint & ap3, + string bcname, + double maxh) + : SplineSeg(maxh, bcname), p1(ap1), p2(ap2), p3(ap3) { weight = Dist (p1, p3) / sqrt (0.5 * (Dist2 (p1, p2) + Dist2 (p2, p3))); // weight = sqrt(2); @@ -98,6 +102,18 @@ namespace netgen proj_latest_t = 0.5; } + template + SplineSeg3 :: SplineSeg3 (const GeomPoint & ap1, + const GeomPoint & ap2, + const GeomPoint & ap3, + double aweight, + string bcname, + double maxh) + : SplineSeg(maxh, bcname), p1(ap1), p2(ap2), p3(ap3), weight(aweight) + { + proj_latest_t = 0.5; + } + template Point SplineSeg3 :: GetPoint (double t) const { @@ -488,7 +504,7 @@ namespace netgen template void SplineSeg3 :: LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const + NgArray < Point > & points, const double eps) const { points.SetSize(0); @@ -535,7 +551,7 @@ namespace netgen template < int D > - void SplineSeg3 :: GetRawData (Array & data) const + void SplineSeg3 :: GetRawData (NgArray & data) const { data.Append(3); for(int i=0; i class SplineSeg { + double maxh; + string bcname; public: - SplineSeg () { ; } + SplineSeg (double amaxh = 1e99, string abcname = "default") + : maxh(amaxh), bcname(abcname) { ; } /// virtual ~SplineSeg() { ; } /// calculates length of curve @@ -94,13 +97,13 @@ namespace netgen virtual void GetCoeff (Vector & coeffs) const = 0; virtual void GetCoeff (Vector & coeffs, Point p0) const { ; } - virtual void GetPoints (int n, Array > & points) const; + virtual void GetPoints (int n, NgArray > & points) const; /** calculates (2D) lineintersections: - for lines $$ a x + b y + c = 0 $$ the interecting points are calculated + for lines $$ a x + b y + c = 0 $$ the intersecting points are calculated and stored in points */ virtual void LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const + NgArray < Point > & points, const double eps) const {points.SetSize(0);} // is the point in the convex hull (increased by eps) of the spline ? @@ -113,9 +116,11 @@ namespace netgen virtual void Project (const Point point, Point & point_on_curve, double & t) const { cerr << "Project not implemented for spline base-class" << endl;} - virtual void GetRawData (Array & data) const + virtual void GetRawData (NgArray & data) const { cerr << "GetRawData not implemented for spline base-class" << endl;} + double GetMaxh() const { return maxh; } + string GetBCName() const { return bcname; } }; @@ -127,7 +132,8 @@ namespace netgen GeomPoint p1, p2; public: /// - LineSeg (const GeomPoint & ap1, const GeomPoint & ap2); + LineSeg (const GeomPoint & ap1, const GeomPoint & ap2, + double maxh=1e99, string bcname="default"); /// // default constructor for archive LineSeg() {} @@ -157,7 +163,7 @@ namespace netgen virtual string GetType(void) const {return "line";} virtual void LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const; + NgArray < Point > & points, const double eps) const; virtual bool InConvexHull (Point p, double eps) const { @@ -168,7 +174,7 @@ namespace netgen virtual void Project (const Point point, Point & point_on_curve, double & t) const; - virtual void GetRawData (Array & data) const; + virtual void GetRawData (NgArray & data) const; }; @@ -182,9 +188,17 @@ namespace netgen mutable double proj_latest_t; public: /// - SplineSeg3 (const GeomPoint & ap1, + DLL_HEADER SplineSeg3 (const GeomPoint & ap1, const GeomPoint & ap2, - const GeomPoint & ap3); + const GeomPoint & ap3, + string bcname="default", + double maxh=1e99); + DLL_HEADER SplineSeg3 (const GeomPoint & ap1, + const GeomPoint & ap2, + const GeomPoint & ap3, + double aweight, + string bcname="default", + double maxh=1e99); // default constructor for archive SplineSeg3() {} /// @@ -192,9 +206,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,19 +220,19 @@ 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";} const GeomPoint & TangentPoint (void) const { return p2; } DLL_HEADER virtual void LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const; + NgArray < Point > & points, const double eps) const; virtual bool InConvexHull (Point p, double eps) const { @@ -225,7 +243,7 @@ namespace netgen DLL_HEADER virtual void Project (const Point point, Point & point_on_curve, double & t) const; - DLL_HEADER virtual void GetRawData (Array & data) const; + DLL_HEADER virtual void GetRawData (NgArray & data) const; }; @@ -271,7 +289,7 @@ namespace netgen virtual string GetType(void) const {return "circle";} virtual void LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const; + NgArray < Point > & points, const double eps) const; virtual bool InConvexHull (Point p, double eps) const { @@ -290,11 +308,11 @@ namespace netgen template class DiscretePointsSeg : public SplineSeg { - Array > pts; + NgArray > pts; GeomPoint p1n, p2n; public: /// - DiscretePointsSeg (const Array > & apts); + DiscretePointsSeg (const NgArray > & apts); // default constructor for archive DiscretePointsSeg() {} virtual void DoArchive(Archive& ar) @@ -346,7 +364,7 @@ namespace netgen template - void SplineSeg :: GetPoints (int n, Array > & points) const + void SplineSeg :: GetPoints (int n, NgArray > & points) const { points.SetSize (n); if (n >= 2) @@ -376,8 +394,9 @@ namespace netgen template LineSeg :: LineSeg (const GeomPoint & ap1, - const GeomPoint & ap2) - : p1(ap1), p2(ap2) + const GeomPoint & ap2, + double maxh, string bcname) + : SplineSeg(maxh, bcname), p1(ap1), p2(ap2) { ; } @@ -445,7 +464,7 @@ namespace netgen template void LineSeg :: LineIntersections (const double a, const double b, const double c, - Array < Point > & points, const double eps) const + NgArray < Point > & points, const double eps) const { points.SetSize(0); @@ -478,7 +497,7 @@ namespace netgen template - void LineSeg :: GetRawData (Array & data) const + void LineSeg :: GetRawData (NgArray & data) const { data.Append(2); for(int i=0; i - DiscretePointsSeg :: DiscretePointsSeg (const Array > & apts) + DiscretePointsSeg :: DiscretePointsSeg (const NgArray > & apts) : pts (apts) { for(int i=0; i class BSplineSeg : public SplineSeg { - Array > pts; + NgArray > pts; GeomPoint p1n, p2n; - Array ti; + NgArray ti; public: /// - BSplineSeg (const Array > & apts); + BSplineSeg (const NgArray > & apts); /// //default constructor for archive BSplineSeg() {} @@ -680,7 +699,7 @@ namespace netgen // Constructor template - BSplineSeg :: BSplineSeg (const Array > & apts) + BSplineSeg :: BSplineSeg (const NgArray > & apts) : pts (apts) { /* diff --git a/libsrc/gprim/splinegeometry.cpp b/libsrc/gprim/splinegeometry.cpp index 213f5224..8c1eaf4b 100644 --- a/libsrc/gprim/splinegeometry.cpp +++ b/libsrc/gprim/splinegeometry.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "splinegeometry.hpp" namespace netgen @@ -23,7 +24,7 @@ namespace netgen template - void SplineGeometry :: GetRawData (Array & raw_data) const + void SplineGeometry :: GetRawData (NgArray & raw_data) const { raw_data.Append(D); // raw_data.Append(elto0); @@ -36,7 +37,7 @@ namespace netgen template - int SplineGeometry :: Load (const Array & raw_data, const int startpos) + int SplineGeometry :: Load (const NgArray & raw_data, const int startpos) { int pos = startpos; if(raw_data[pos] != D) @@ -49,7 +50,7 @@ namespace netgen splines.SetSize(int(raw_data[pos])); pos++; - Array< Point > pts(3); + NgArray< Point > pts(3); for(int i=0; i > points; + NgArray > points; for (int i = 0; i < splines.Size(); i++) { splines[i]->GetPoints (20, points); diff --git a/libsrc/gprim/splinegeometry.hpp b/libsrc/gprim/splinegeometry.hpp index 14a28b4b..bf9978ba 100644 --- a/libsrc/gprim/splinegeometry.hpp +++ b/libsrc/gprim/splinegeometry.hpp @@ -22,27 +22,27 @@ namespace netgen template < int D > - class SplineGeometry + class DLL_HEADER SplineGeometry { // protected: public: - Array < GeomPoint > geompoints; - Array < SplineSeg* > splines; + NgArray < GeomPoint > geompoints; + NgArray < SplineSeg* > splines; SplineGeometry() : geompoints{}, splines{} { ; } - DLL_HEADER ~SplineGeometry(); + virtual ~SplineGeometry(); - DLL_HEADER int Load (const Array & raw_data, const int startpos = 0); + int Load (const NgArray & raw_data, const int startpos = 0); virtual void DoArchive(Archive& ar) { ar & geompoints & splines; } - DLL_HEADER void GetRawData (Array & raw_data) const; + void GetRawData (NgArray & raw_data) const; - const Array*> & GetSplines () const + const NgArray*> & GetSplines () const { return splines; } int GetNSplines (void) const { return splines.Size(); } @@ -50,7 +50,7 @@ namespace netgen SplineSeg & GetSpline (const int i) {return *splines[i];} const SplineSeg & GetSpline (const int i) const {return *splines[i];} - DLL_HEADER void GetBoundingBox (Box & box) const; + void GetBoundingBox (Box & box) const; Box GetBoundingBox () const { Box box; GetBoundingBox (box); return box; } @@ -58,7 +58,7 @@ namespace netgen const GeomPoint & GetPoint(int i) const { return geompoints[i]; } // void SetGrading (const double grading); - DLL_HEADER void AppendPoint (const Point & p, const double reffac = 1., const bool hpref = false); + void AppendPoint (const Point & p, const double reffac = 1., const bool hpref = false); void AppendSegment(SplineSeg * spline) { diff --git a/libsrc/gprim/transform3d.cpp b/libsrc/gprim/transform3d.cpp index 7a2ded0f..2d1e9472 100644 --- a/libsrc/gprim/transform3d.cpp +++ b/libsrc/gprim/transform3d.cpp @@ -163,7 +163,31 @@ ostream & operator<< (ostream & ost, Transformation3d & trans) return ost; } + template <> + Transformation<3> :: Transformation (const Point<3> & c, const Vec<3> & axes, double angle) + { + Vec<3> vc(c); + Transformation<3> tc(vc); + Transformation<3> tcinv(-vc); + Transformation<3> r, ht, ht2; + // r.SetAxisRotation (3, alpha); + Vec<3> naxes = axes; + naxes.Normalize(); + Vec<3> n1 = naxes.GetNormal(); + Vec<3> n2 = Cross(naxes, n1); + r.v = Vec<3>(0,0,0); + double co = cos(angle); + double si = sin(angle); + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + r.m(i,j) = naxes(i)*naxes(j) + co*(n1(i)*n1(j)+n2(i)*n2(j)) + si*( (n2(i)*n1(j)-n2(j)*n1(i)) ); + + ht.Combine (tc, r); + Combine (ht, tcinv); + } + + template Transformation :: Transformation (const Point * pp) { diff --git a/libsrc/gprim/transform3d.hpp b/libsrc/gprim/transform3d.hpp index 7c966801..b5886d29 100644 --- a/libsrc/gprim/transform3d.hpp +++ b/libsrc/gprim/transform3d.hpp @@ -102,6 +102,8 @@ public: m(i,i) = 1; } + Transformation (const Point & c, const Vec<3> & axes, double angle); + // rotation with ... Transformation (const Point & c, double alpha, double beta, double gamma) { @@ -128,6 +130,14 @@ public: // (*testout) << "Rotation - Transformation:" << (*this) << endl; } + Mat & GetMatrix() { return m; } + Vec & GetVector() { return v; } + + void DoArchive(Archive& ar) + { + ar & m & v; + } + /// Transformation CalcInverse () const { diff --git a/libsrc/include/incopengl.hpp b/libsrc/include/incopengl.hpp index b3fcc71b..1f42f12f 100644 --- a/libsrc/include/incopengl.hpp +++ b/libsrc/include/incopengl.hpp @@ -2,10 +2,13 @@ #define INCOPENGL_HPP___ #define GL_GLEXT_PROTOTYPES +#include #include -# if defined(TOGL_AGL) || defined(TOGL_AGL_CLASSIC) || defined(TOGL_NSOPENGL) -# include +# ifdef __APPLE__ +#define GL_SILENCE_DEPRECATION +#define GL_DO_NOT_WARN_IF_MULTI_GL_VERSION_HEADERS_INCLUDED +# include # include # else # include @@ -27,6 +30,11 @@ #define GL_ARRAY_BUFFER 0x8892 #define GL_ELEMENT_ARRAY_BUFFER 0x8893 #define GL_STATIC_DRAW 0x88E4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_COLOR_ATTACHMENT0 0x8CE0 typedef ptrdiff_t GLintptr; typedef ptrdiff_t GLsizeiptr; extern void (*glBindBuffer) (GLenum a, GLuint b); @@ -34,6 +42,16 @@ extern void (*glDeleteBuffers) (GLsizei a, const GLuint *b); extern void (*glGenBuffers) (GLsizei a, GLuint *b); extern void (*glBufferData) (GLenum a, GLsizeiptr b, const GLvoid *c, GLenum d); extern void (*glBufferSubData) (GLenum a, GLintptr b, GLsizeiptr c, const GLvoid *d); + +extern GLenum (*glCheckFramebufferStatus) (GLenum target); +extern void (*glBindFramebuffer) (GLenum target, GLuint framebuffer); +extern void (*glBindRenderbuffer) (GLenum target, GLuint renderbuffer); +extern void (*glDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); +extern void (*glDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers); +extern void (*glGenFramebuffers) (GLsizei n, GLuint *framebuffers); +extern void (*glGenRenderbuffers) (GLsizei n, GLuint *renderbuffers); +extern void (*glRenderbufferStorage) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +extern void (*glFramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); #endif DLL_HEADER void LoadOpenGLFunctionPointers(); #endif // INCOPENGL_HPP___ diff --git a/libsrc/include/mydefs.hpp b/libsrc/include/mydefs.hpp index f2368ce7..c0fd99e6 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(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/mystdlib.h b/libsrc/include/mystdlib.h index b55ba3da..f79e0ad2 100644 --- a/libsrc/include/mystdlib.h +++ b/libsrc/include/mystdlib.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/libsrc/include/nginterface.h b/libsrc/include/nginterface.h index 752228f9..b4459087 100644 --- a/libsrc/include/nginterface.h +++ b/libsrc/include/nginterface.h @@ -11,26 +11,14 @@ /* Date: 20. Nov. 99 */ /**************************************************************************/ +#include "mydefs.hpp" +#include + /* Application program interface to Netgen */ -#ifdef WIN32 - #if NGINTERFACE_EXPORTS || NGLIB_EXPORTS || nglib_EXPORTS - #define DLL_HEADER __declspec(dllexport) - #else - #define DLL_HEADER __declspec(dllimport) - #endif -#else - #if __GNUC__ >= 4 - #define DLL_HEADER __attribute__ ((visibility ("default"))) - #else - #define DLL_HEADER - #endif -#endif - - // max number of nodes per element #define NG_ELEMENT_MAXPOINTS 20 @@ -86,7 +74,7 @@ extern "C" { // number of surface triangles DLL_HEADER int Ng_GetNSE (); - // Get Point coordintes, index from 1 .. np + // Get Point coordinates, index from 1 .. np DLL_HEADER void Ng_GetPoint (int pi, double * p); // Get Element Points @@ -232,11 +220,13 @@ extern "C" { DLL_HEADER int Ng_GetNEdges(); DLL_HEADER int Ng_GetNFaces(); - + [[deprecated("orientation is not supported anymore")]] DLL_HEADER int Ng_GetElement_Edges (int elnr, int * edges, int * orient = 0); + // [[deprecated("orientation is not supported anymore")]] DLL_HEADER int Ng_GetElement_Faces (int elnr, int * faces, int * orient = 0); - + [[deprecated("orientation is not supported anymore")]] DLL_HEADER int Ng_GetSurfaceElement_Edges (int selnr, int * edges, int * orient = 0); + // [[deprecated("orientation is not supported anymore")]] DLL_HEADER int Ng_GetSurfaceElement_Face (int selnr, int * orient = 0); DLL_HEADER void Ng_GetSurfaceElementNeighbouringDomains(const int selnr, int & in, int & out); @@ -292,7 +282,7 @@ extern "C" { int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * pnums ); int NgPar_GetNDistantNodeNums ( int nodetype, int locnum ); - int NgPar_GetGlobalNodeNum (int nodetype, int locnum); + DLL_HEADER int NgPar_GetGlobalNodeNum (int nodetype, int locnum); #endif @@ -317,6 +307,9 @@ extern "C" { struct Ng_SolutionData { std::string name; // name of gridfunction + std::string title = ""; // name of gridfunction ( printed on top of window ) + std::string number_format = "%.3e"; // printf-style string to format colormap values + std::string unit = ""; // string to append to last number in colormap (ASCII only) double * data; // solution values int components; // relevant (double) components in solution vector int dist; // # doubles per entry alignment! @@ -462,15 +455,6 @@ extern "C" { return value is number of nodes */ DLL_HEADER int Ng_GetElementClosureNodes (int dim, int elementnr, int nodeset, int * nodes); - - - struct Ng_Tcl_Interp; - typedef int (Ng_Tcl_CmdProc) (Ng_Tcl_Interp *interp, int argc, const char *argv[]); - - DLL_HEADER void Ng_Tcl_CreateCommand (Ng_Tcl_Interp * interp, - const char * cmdName, Ng_Tcl_CmdProc * proc); - - void Ng_Tcl_SetResult (Ng_Tcl_Interp * interp, const char * result); } @@ -479,10 +463,9 @@ extern "C" { #ifdef __cplusplus #include -namespace netgen +namespace ngcore { - DLL_HEADER extern std::ostream * testout; - DLL_HEADER extern int printmessage_importance; + NGCORE_API extern int printmessage_importance; } #endif diff --git a/libsrc/include/nginterface_v2.hpp b/libsrc/include/nginterface_v2.hpp index 24b9b3e9..6c70a9ef 100644 --- a/libsrc/include/nginterface_v2.hpp +++ b/libsrc/include/nginterface_v2.hpp @@ -8,6 +8,8 @@ /* Date: May 09 */ /**************************************************************************/ +#include "mydefs.hpp" + /* C++ interface to Netgen */ @@ -39,7 +41,8 @@ namespace netgen // extern DLL_HEADER NgMPI_Comm ng_comm; static constexpr int POINTINDEX_BASE = 1; - + + /* struct T_EDGE2 { // int orient:1; @@ -52,6 +55,9 @@ namespace netgen // int nr:29; // 0-based int nr; // 0-based }; + */ + typedef int T_EDGE2; + typedef int T_FACE2; template class Ng_Buffer @@ -112,7 +118,7 @@ namespace netgen const T_EDGE2 * ptr; size_t Size() const { return num; } - int operator[] (size_t i) const { return ptr[i].nr; } + int operator[] (size_t i) const { return ptr[i]; } }; class Ng_Faces @@ -122,7 +128,7 @@ namespace netgen const T_FACE2 * ptr; size_t Size() const { return num; } - int operator[] (size_t i) const { return ptr[i].nr; } + int operator[] (size_t i) const { return ptr[i]; } }; class Ng_Facets @@ -274,7 +280,7 @@ namespace netgen void UpdateTopology (); void DoArchive (Archive & archive); - NgMPI_Comm GetCommunicator() const; + const NgMPI_Comm & GetCommunicator() const; virtual ~Ngx_Mesh(); @@ -282,7 +288,8 @@ namespace netgen int GetDimension() const; int GetNLevels() const; - + size_t GetNVLevel (int level) const; + int GetNElements (int dim) const; int GetNNodes (int nt) const; @@ -340,8 +347,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); @@ -351,6 +361,10 @@ namespace netgen int GetParentElement (int ei) const; int GetParentSElement (int ei) const; + bool HasParentEdges() const; + std::tuple> GetParentEdges (int enr) const; + std::tuple> GetParentFaces (int fnr) const; + int GetNIdentifications() const; int GetIdentificationType(int idnr) const; Ng_Buffer GetPeriodicVertices(int idnr) const; @@ -364,8 +378,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 5b1efd6d..a0c1cf01 100644 --- a/libsrc/include/nginterface_v2_impl.hpp +++ b/libsrc/include/nginterface_v2_impl.hpp @@ -28,8 +28,10 @@ NGX_INLINE DLL_HEADER int Ngx_Mesh :: GetElementIndex<1> (size_t nr) const template <> NGX_INLINE DLL_HEADER int Ngx_Mesh :: GetElementIndex<2> (size_t nr) const { - int ind = (*mesh)[SurfaceElementIndex(nr)].GetIndex(); - return mesh->GetFaceDescriptor(ind).BCProperty(); + // int ind = (*mesh)[SurfaceElementIndex(nr)].GetIndex(); + // return mesh->GetFaceDescriptor(ind).BCProperty(); + const Element2d & el = (*mesh)[SurfaceElementIndex(nr)]; + return mesh->GetFaceDescriptor(el).BCProperty(); } template <> @@ -106,16 +108,22 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<1> (size_t nr) const ret.vertices.ptr = (int*)&(el[0]); ret.edges.num = 1; - ret.edges.ptr = (T_EDGE2*)mesh->GetTopology().GetSegmentElementEdgesPtr (nr); + ret.edges.ptr = mesh->GetTopology().GetSegmentElementEdgesPtr (nr); 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; - ret.facets.ptr = (int*)ret.edges.ptr; + ret.facets.ptr = ret.edges.ptr; } else { @@ -133,12 +141,11 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<1> (size_t nr) const template <> NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<2> (size_t nr) const { - // const Element2d & el = mesh->SurfaceElement (SurfaceElementIndex (nr)); const Element2d & el = mesh->SurfaceElements()[nr]; Ng_Element ret; ret.type = NG_ELEMENT_TYPE(el.GetType()); - const FaceDescriptor & fd = mesh->GetFaceDescriptor(el.GetIndex()); + const FaceDescriptor & fd = mesh->GetFaceDescriptor(el); // .GetIndex()); ret.index = fd.BCProperty(); if (mesh->GetDimension() == 3) ret.mat = &fd.GetBCName(); @@ -151,22 +158,22 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<2> (size_t nr) const ret.vertices.ptr = (int*)&(el[0]); ret.edges.num = MeshTopology::GetNEdges (el.GetType()); - ret.edges.ptr = (T_EDGE2*)mesh->GetTopology().GetSurfaceElementEdgesPtr (nr); + ret.edges.ptr = mesh->GetTopology().GetSurfaceElementEdgesPtr (nr); ret.faces.num = MeshTopology::GetNFaces (el.GetType()); - ret.faces.ptr = (T_FACE2*)mesh->GetTopology().GetSurfaceElementFacesPtr (nr); + ret.faces.ptr = mesh->GetTopology().GetSurfaceElementFacesPtr (nr); if (mesh->GetDimension() == 3) { ret.facets.num = ret.faces.num; ret.facets.base = 0; - ret.facets.ptr = (int*)ret.faces.ptr; + ret.facets.ptr = ret.faces.ptr; } else { ret.facets.num = ret.edges.num; ret.facets.base = 0; - ret.facets.ptr = (int*)ret.edges.ptr; + ret.facets.ptr = ret.edges.ptr; } ret.is_curved = el.IsCurved(); return ret; @@ -175,7 +182,6 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<2> (size_t nr) const template <> NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<3> (size_t nr) const { - // const Element & el = mesh->VolumeElement (ElementIndex (nr)); const Element & el = mesh->VolumeElements()[nr]; Ng_Element ret; @@ -189,14 +195,14 @@ NGX_INLINE DLL_HEADER Ng_Element Ngx_Mesh :: GetElement<3> (size_t nr) const ret.vertices.ptr = (int*)&(el[0]); ret.edges.num = MeshTopology::GetNEdges (el.GetType()); - ret.edges.ptr = (T_EDGE2*)mesh->GetTopology().GetElementEdgesPtr (nr); + ret.edges.ptr = mesh->GetTopology().GetElementEdgesPtr (nr); ret.faces.num = MeshTopology::GetNFaces (el.GetType()); - ret.faces.ptr = (T_FACE2*)mesh->GetTopology().GetElementFacesPtr (nr); + ret.faces.ptr = mesh->GetTopology().GetElementFacesPtr (nr); ret.facets.num = ret.faces.num; ret.facets.base = 0; - ret.facets.ptr = (int*)ret.faces.ptr; + ret.facets.ptr = ret.faces.ptr; ret.is_curved = el.IsCurved(); return ret; @@ -250,35 +256,35 @@ template <> NGX_INLINE DLL_HEADER const Ng_Node<0> Ngx_Mesh :: GetNode<0> (int v { case 3: { - FlatArray ia = mesh->GetTopology().GetVertexElements(vnr); + auto ia = mesh->GetTopology().GetVertexElements(vnr); node.elements.ne = ia.Size(); - node.elements.ptr = (int*)&ia[0]; + node.elements.ptr = (int*)ia.Data(); - FlatArray bia = mesh->GetTopology().GetVertexSurfaceElements(vnr); + auto bia = mesh->GetTopology().GetVertexSurfaceElements(vnr); node.bnd_elements.ne = bia.Size(); - node.bnd_elements.ptr = (int*)&bia[0]; + node.bnd_elements.ptr = (int*)bia.Data(); break; } case 2: { - FlatArray ia = mesh->GetTopology().GetVertexSurfaceElements(vnr); + auto ia = mesh->GetTopology().GetVertexSurfaceElements(vnr); node.elements.ne = ia.Size(); - node.elements.ptr = (int*)&ia[0]; + node.elements.ptr = (int*)ia.Data(); - FlatArray bia = mesh->GetTopology().GetVertexSegments(vnr); + auto bia = mesh->GetTopology().GetVertexSegments(vnr); node.bnd_elements.ne = bia.Size(); - node.bnd_elements.ptr = (int*)&bia[0]; + node.bnd_elements.ptr = (int*)bia.Data(); break; } case 1: { - FlatArray ia = mesh->GetTopology().GetVertexSegments(vnr); + auto ia = mesh->GetTopology().GetVertexSegments(vnr); node.elements.ne = ia.Size(); - node.elements.ptr = (int*)&ia[0]; + node.elements.ptr = (int*)ia.Data(); - FlatArray bia = mesh->GetTopology().GetVertexPointElements(vnr); + auto bia = mesh->GetTopology().GetVertexPointElements(vnr); node.bnd_elements.ne = bia.Size(); - node.bnd_elements.ptr = (int*)&bia[0]; + node.bnd_elements.ptr = (int*)bia.Data(); break; } default: @@ -290,14 +296,14 @@ template <> NGX_INLINE DLL_HEADER const Ng_Node<0> Ngx_Mesh :: GetNode<0> (int v template <> NGX_INLINE DLL_HEADER const Ng_Node<1> Ngx_Mesh :: GetNode<1> (int nr) const { Ng_Node<1> node; - node.vertices.ptr = mesh->GetTopology().GetEdgeVerticesPtr(nr); + node.vertices.ptr = (const int*)mesh->GetTopology().GetEdgeVerticesPtr(nr); return node; } template <> NGX_INLINE DLL_HEADER const Ng_Node<2> Ngx_Mesh :: GetNode<2> (int nr) const { Ng_Node<2> node; - node.vertices.ptr = mesh->GetTopology().GetFaceVerticesPtr(nr); + node.vertices.ptr = (const int*)mesh->GetTopology().GetFaceVerticesPtr(nr); node.vertices.nv = (node.vertices.ptr[3] == 0) ? 3 : 4; node.surface_el = mesh->GetTopology().GetFace2SurfaceElement (nr+1)-1; return node; @@ -306,7 +312,7 @@ template <> NGX_INLINE DLL_HEADER const Ng_Node<2> Ngx_Mesh :: GetNode<2> (int n NGX_INLINE DLL_HEADER Ng_Buffer Ngx_Mesh :: GetPeriodicVertices(int idnr) const { - Array apairs; + NgArray apairs; mesh->GetIdentifications().GetPairs (idnr+1, apairs); for(auto& ind : apairs) { @@ -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 8c8d1eb2..9126484f 100644 --- a/libsrc/interface/CMakeLists.txt +++ b/libsrc/interface/CMakeLists.txt @@ -1,15 +1,10 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(interface ${NG_LIB_TYPE} +target_sources(nglib PRIVATE nginterface.cpp nginterface_v2.cpp 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 writevtk.cpp - wuchemnitz.cpp writegmsh2.cpp writeOpenFOAM15x.cpp - ) - -target_link_libraries(interface mesh csg geom2d) -target_link_libraries(interface visual) -install( TARGETS interface ${NG_INSTALL_DIR}) + wuchemnitz.cpp writegmsh2.cpp writeOpenFOAM15x.cpp rw_cgns.cpp +) install(FILES writeuser.hpp diff --git a/libsrc/interface/nginterface.cpp b/libsrc/interface/nginterface.cpp index 8df20a88..4da0eed9 100644 --- a/libsrc/interface/nginterface.cpp +++ b/libsrc/interface/nginterface.cpp @@ -8,6 +8,8 @@ #include "../sockets/sockets.hpp" #endif +#include "../general/gzstream.h" + #include "nginterface.h" // #include "../visualization/soldata.hpp" // #include @@ -58,8 +60,8 @@ namespace netgen #ifdef SOCKETS extern AutoPtr clientsocket; - //extern Array< AutoPtr < ServerInfo > > servers; - extern Array< ServerInfo* > servers; + //extern NgArray< AutoPtr < ServerInfo > > servers; + extern NgArray< ServerInfo* > servers; #endif @@ -102,15 +104,8 @@ void Ng_LoadMeshFromStream ( istream & input ) mesh -> Load(input); SetGlobalMesh (mesh); - for (int i = 0; i < geometryregister.Size(); i++) - { - NetgenGeometry * hgeom = geometryregister[i]->LoadFromMeshFile (input); - if (hgeom) - { - ng_geometry.reset (hgeom); - break; - } - } + ng_geometry = geometryregister.LoadFromMeshFile (input); + if (!ng_geometry) ng_geometry = make_shared(); mesh->SetGeometry (ng_geometry); @@ -146,28 +141,38 @@ void Ng_LoadMesh (const char * filename, ngcore::NgMPI_Comm comm) if( id == 0) { - string fn(filename); - if (fn.substr (fn.length()-3, 3) == ".gz") - infile = new igzstream (filename); - else - infile = new ifstream (filename); mesh.reset (new Mesh()); mesh->SetCommunicator(comm); - mesh -> Load(*infile); - SetGlobalMesh (mesh); - - // make string from rest of file (for geometry info!) - // (this might be empty, in which case we take the global ng_geometry) - stringstream geom_part; - geom_part << infile->rdbuf(); - string geom_part_string = geom_part.str(); - strs = geom_part_string.size(); - // buf = new char[strs]; - buf.SetSize(strs); - memcpy(&buf[0], geom_part_string.c_str(), strs*sizeof(char)); - - delete infile; + + string fn(filename); + if (fn.length() > 8 && fn.substr (fn.length()-8, 8) == ".vol.bin") + { + mesh -> Load(fn); + SetGlobalMesh (mesh); + } + else + { + if (fn.substr (fn.length()-3, 3) == ".gz") + infile = new igzstream (filename); + else + infile = new ifstream (filename); + mesh -> Load(*infile); + SetGlobalMesh (mesh); + + // make string from rest of file (for geometry info!) + // (this might be empty, in which case we take the global ng_geometry) + stringstream geom_part; + geom_part << infile->rdbuf(); + string geom_part_string = geom_part.str(); + strs = geom_part_string.size(); + // buf = new char[strs]; + buf.SetSize(strs); + memcpy(buf.Data(), geom_part_string.c_str(), strs*sizeof(char)); + + delete infile; + } + if (ntasks > 1) { @@ -191,9 +196,9 @@ void Ng_LoadMesh (const char * filename, ngcore::NgMPI_Comm comm) bool endfile = false; int n, dummy; - Array segment_weights; - Array surface_weights; - Array volume_weights; + NgArray segment_weights; + NgArray surface_weights; + NgArray volume_weights; while (weightsfile.good() && !endfile) { @@ -238,22 +243,28 @@ void Ng_LoadMesh (const char * filename, ngcore::NgMPI_Comm comm) mesh->SendRecvMesh(); } + /* if(ntasks>1) { #ifdef PARALLEL - /** Scatter the geometry-string (no dummy-implementation in mpi_interface) **/ + // Scatter the geometry-string (no dummy-implementation in mpi_interface) int strs = buf.Size(); MyMPI_Bcast(strs, comm); if(strs>0) MyMPI_Bcast(buf, comm); -#endif - } + #endif + } + */ + comm.Bcast(buf); shared_ptr geo; if(buf.Size()) { // if we had geom-info in the file, take it istringstream geom_infile(string((const char*)&buf[0], buf.Size())); geo = geometryregister.LoadFromMeshFile(geom_infile); } - if(geo!=nullptr) mesh->SetGeometry(geo); + if(geo!=nullptr) { + ng_geometry = geo; + mesh->SetGeometry(geo); + } else if(ng_geometry!=nullptr) mesh->SetGeometry(ng_geometry); } @@ -476,14 +487,14 @@ const char * Ng_GetDomainMaterial (int dom) int Ng_GetUserDataSize (char * id) { - Array da; + NgArray da; mesh->GetUserData (id, da); return da.Size(); } void Ng_GetUserData (char * id, double * data) { - Array da; + NgArray da; mesh->GetUserData (id, da); for (int i = 0; i < da.Size(); i++) data[i] = da[i]; @@ -557,7 +568,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()); } @@ -637,12 +648,12 @@ int Ng_FindElementOfPoint (double * p, double * lami, int build_searchtree, const int * const indices, const int numind) { - Array * dummy(NULL); + NgArray * dummy(NULL); int ind = -1; if(indices != NULL) { - dummy = new Array(numind); + dummy = new NgArray(numind); for(int i=0; i * dummy(NULL); + NgArray * dummy(NULL); int ind = -1; if(indices != NULL) { - dummy = new Array(numind); + dummy = new NgArray(numind); for(int i=0; i +[[deprecated("Use GetDistantNodeNums(locnum) -> FlatArray instead!")]] int NgPar_GetDistantNodeNums ( int nodetype, int locnum, int * distnums ) { int size = NgPar_GetNDistantNodeNums (nodetype, locnum); @@ -928,6 +940,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++; @@ -941,6 +954,7 @@ int NgPar_GetNDistantNodeNums ( int nodetype, int locnum ) return -1; } +[[deprecated("Use GetDistantNodeNums(locnum) -> FlatArray instead!")]] int NgPar_GetGlobalNodeNum (int nodetype, int locnum) { locnum++; @@ -1547,7 +1561,7 @@ int Ng_GetSurfaceElement_Edges (int elnr, int * edges, int * orient) /* int i, ned; const MeshTopology & topology = mesh->GetTopology(); - Array ia; + NgArray ia; topology.GetSurfaceElementEdges (elnr, ia); ned = ia.Size(); for (i = 1; i <= ned; i++) @@ -1578,7 +1592,7 @@ int Ng_GetSurfaceElement_Face (int selnr, int * orient) int Ng_GetFace_Vertices (int fnr, int * vert) { const MeshTopology & topology = mesh->GetTopology(); - ArrayMem ia; + NgArrayMem ia; topology.GetFaceVertices (fnr, ia); for (int i = 0; i < ia.Size(); i++) vert[i] = ia[i]; @@ -1590,7 +1604,7 @@ int Ng_GetFace_Vertices (int fnr, int * vert) int Ng_GetFace_Edges (int fnr, int * edge) { const MeshTopology & topology = mesh->GetTopology(); - ArrayMem ia; + NgArrayMem ia; topology.GetFaceEdges (fnr, ia); for (int i = 0; i < ia.Size(); i++) edge[i] = ia[i]; @@ -1635,19 +1649,19 @@ void Ng_GetVertexElements (int vnr, int * els) { case 3: { - FlatArray ia = mesh->GetTopology().GetVertexElements(vnr); + auto ia = mesh->GetTopology().GetVertexElements(vnr); for (int i = 0; i < ia.Size(); i++) els[i] = ia[i]+1; break; } case 2: { - FlatArray ia = mesh->GetTopology().GetVertexSurfaceElements(vnr); + auto ia = mesh->GetTopology().GetVertexSurfaceElements(vnr); for (int i = 0; i < ia.Size(); i++) els[i] = ia[i]+1; break; } case 1: { - FlatArray ia = mesh->GetTopology().GetVertexSegments(vnr); + auto ia = mesh->GetTopology().GetVertexSegments(vnr); for (int i = 0; i < ia.Size(); i++) els[i] = ia[i]+1; break; /* @@ -1721,7 +1735,9 @@ void Ng_SetSurfaceElementOrders (int enr, int ox, int oy) int Ng_GetNLevels () { - return (mesh) ? mesh->mglevels : 0; + if (!mesh) return 0; + return max(size_t(1), mesh -> level_nv.Size()); + // return (mesh) ? mesh->mglevels : 0; } @@ -1797,7 +1813,7 @@ int Ng_GetClusterRepElement (int pi) int Ng_GetNPeriodicVertices (int idnr) { - Array apairs; + NgArray apairs; mesh->GetIdentifications().GetPairs (idnr, apairs); return apairs.Size(); } @@ -1806,7 +1822,7 @@ int Ng_GetNPeriodicVertices (int idnr) // pairs should be an integer array of 2*npairs void Ng_GetPeriodicVertices (int idnr, int * pairs) { - Array apairs; + NgArray apairs; mesh->GetIdentifications().GetPairs (idnr, apairs); for (int i = 0; i < apairs.Size(); i++) { @@ -1820,7 +1836,7 @@ void Ng_GetPeriodicVertices (int idnr, int * pairs) int Ng_GetNPeriodicEdges (int idnr) { - Array map; + NgArray map; //const MeshTopology & top = mesh->GetTopology(); int nse = mesh->GetNSeg(); @@ -1847,7 +1863,7 @@ int Ng_GetNPeriodicEdges (int idnr) void Ng_GetPeriodicEdges (int idnr, int * pairs) { - Array map; + NgArray map; const MeshTopology & top = mesh->GetTopology(); int nse = mesh->GetNSeg(); @@ -1865,8 +1881,10 @@ void Ng_GetPeriodicEdges (int idnr, int * pairs) if (other1 && other2 && mesh->IsSegment (other1, other2)) { SegmentIndex otherseg = mesh->SegmentNr (other1, other2); - pairs[cnt++] = top.GetSegmentEdge (si+1); - pairs[cnt++] = top.GetSegmentEdge (otherseg+1); + // pairs[cnt++] = top.GetSegmentEdge (si+1); + // pairs[cnt++] = top.GetSegmentEdge (otherseg+1); + pairs[cnt++] = top.GetEdge (si)+1; + pairs[cnt++] = top.GetEdge (otherseg)+1; } } } @@ -1925,8 +1943,9 @@ int Ng_IsRunning() int Ng_GetVertex_Elements( int vnr, int* elems ) { const MeshTopology& topology = mesh->GetTopology(); - ArrayMem indexArray; - topology.GetVertexElements( vnr, indexArray ); + // ArrayMem indexArray; + // topology.GetVertexElements( vnr, indexArray ); + auto indexArray = topology.GetVertexElements( vnr ); for( int i=0; iGetTopology(); - ArrayMem indexArray; - topology.GetVertexSurfaceElements( vnr, indexArray ); + // ArrayMem indexArray; + // topology.GetVertexSurfaceElements( vnr, indexArray ); + auto indexArray = topology.GetVertexSurfaceElements( vnr ); for( int i=0; iGetTopology(); + /* ArrayMem indexArray; topology.GetVertexElements( vnr, indexArray ); - return indexArray.Size(); + */ + return topology.GetVertexElements(vnr).Size(); } ///// Added by Roman Stainko .... @@ -1988,8 +2010,9 @@ int Ng_GetVertex_NSurfaceElements( int vnr ) case 3: { const MeshTopology& topology = mesh->GetTopology(); - ArrayMem indexArray; - topology.GetVertexSurfaceElements( vnr, indexArray ); + // ArrayMem indexArray; + // topology.GetVertexSurfaceElements( vnr, indexArray ); + auto indexArray = topology.GetVertexSurfaceElements( vnr ); return indexArray.Size(); } case 2: @@ -2154,9 +2177,9 @@ int Ng_Bisect_WithInfo ( const char * refinementfile, double ** qualityloss, int mesh->LocalHFunction().SetGrading (mparam.grading); - Array * qualityloss_arr = NULL; + NgArray * qualityloss_arr = NULL; if(qualityloss != NULL) - qualityloss_arr = new Array; + qualityloss_arr = new NgArray; ref -> Bisect (*mesh, biopt, qualityloss_arr); @@ -2233,8 +2256,9 @@ int Ng_GetClosureNodes (int nt, int nodenr, int nodeset, int * nodes) if (nodeset & 2) // Edges { int edges[12]; - int ned; - ned = mesh->GetTopology().GetElementEdges (nodenr+1, edges, 0); + // int ned; + // ned = mesh->GetTopology().GetElementEdges (nodenr+1, edges, 0); + int ned = mesh->GetTopology().GetEdges (ElementIndex(nodenr)).Size(); for (int i = 0; i < ned; i++) { nodes[cnt++] = 1; @@ -2353,13 +2377,6 @@ void Ng_GetArgs (int & argc, char ** &argv) -void LinkFunction () -{ - Ng_Redraw(); -} - - - void Ng_TclCmd(string cmd) { lock_guard guard(tcl_todo_mutex); diff --git a/libsrc/interface/nginterface_v2.cpp b/libsrc/interface/nginterface_v2.cpp index e5fb36ec..a953dce0 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(MPI_COMM_NULL); } + 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 { @@ -130,7 +134,15 @@ namespace netgen int Ngx_Mesh :: GetNLevels() const { - return mesh -> mglevels; + return max(size_t(1), mesh -> level_nv.Size()); + } + + size_t Ngx_Mesh :: GetNVLevel(int level) const + { + if (level >= mesh->level_nv.Size()) + return mesh->GetNV(); + else + return mesh->level_nv[level]; } int Ngx_Mesh :: GetNElements (int dim) const @@ -689,9 +701,9 @@ namespace netgen { int hpelnr = -1; if (mesh->GetDimension() == 2) - hpelnr = mesh->SurfaceElement(ei).hp_elnr; + hpelnr = mesh->SurfaceElement(ei).GetHpElnr(); else - hpelnr = mesh->VolumeElement(ei).hp_elnr; + hpelnr = mesh->VolumeElement(ei).GetHpElnr(); if (hpelnr < 0) throw NgException("Ngx_Mesh::GetHPElementLevel: Wrong hp-element number!"); @@ -756,7 +768,7 @@ namespace netgen Ng_BufferMS Ngx_Mesh::GetFaceEdges (int fnr) const { const MeshTopology & topology = mesh->GetTopology(); - ArrayMem ia; + NgArrayMem ia; topology.GetFaceEdges (fnr+1, ia); Ng_BufferMS res(ia.Size()); for (size_t i = 0; i < ia.Size(); i++) @@ -767,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++) { @@ -816,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++) { @@ -851,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]; @@ -900,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++) { @@ -935,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++) { @@ -970,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++) { @@ -991,10 +1001,6 @@ namespace netgen } } - -#endif - - @@ -1029,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]; @@ -1052,6 +1066,7 @@ namespace netgen return si; } } + } } break; case 3: @@ -1070,7 +1085,7 @@ namespace netgen int * const indices, int numind) const { - Array dummy(numind); + NgArray dummy(numind); for (int i = 0; i < numind; i++) dummy[i] = indices[i]+1; double lam3[3]; @@ -1089,7 +1104,7 @@ namespace netgen if (ind > 0) { - if(mesh->SurfaceElement(ind).GetType()==QUAD) + if(mesh->SurfaceElement(ind).GetType()==QUAD || mesh->SurfaceElement(ind).GetType()==TRIG6) { lami[0] = lam3[0]; lami[1] = lam3[1]; @@ -1111,7 +1126,7 @@ namespace netgen int * const indices, int numind) const { - Array dummy(numind); + NgArray dummy(numind); for (int i = 0; i < numind; i++) dummy[i] = indices[i]+1; Point<3> p3d(p[0], p[1], p[2]); @@ -1126,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) { @@ -1139,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); @@ -1149,20 +1175,22 @@ 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) biopt.refine_hp = 1; biopt.task_manager = task_manager; biopt.tracer = tracer; - - const Refinement & ref = mesh->GetGeometry()->GetRefinement(); - ref.Bisect (*mesh, biopt); + mesh->GetGeometry()->GetRefinement().Bisect (*mesh, biopt); (*tracer)("call updatetop", false); mesh -> UpdateTopology(task_manager, tracer); (*tracer)("call updatetop", true); - mesh -> GetCurvedElements().SetIsHighOrder (false); + if(mesh->GetCurvedElements().IsHighOrder()) + mesh->GetCurvedElements() + .BuildCurvedElements(&mesh->GetGeometry()->GetRefinement(), + mesh->GetCurvedElements().GetOrder()); } @@ -1282,33 +1310,38 @@ 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: - { - FlatArray dn = mesh->GetParallelTopology().GetDistantPNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + // return mesh->GetParallelTopology().GetDistantPNums(locnum); + return mesh->GetParallelTopology().GetDistantProcs(locnum+PointIndex::BASE); case 1: - { - FlatArray dn = mesh->GetParallelTopology().GetDistantEdgeNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + // return mesh->GetParallelTopology().GetDistantEdgeNums(locnum); + return mesh->GetParallelTopology().GetDistantEdgeProcs(locnum); case 2: - { - FlatArray dn = mesh->GetParallelTopology().GetDistantFaceNums(locnum); - return std::tuple(dn.Size(), &dn[0]); - } + // return mesh->GetParallelTopology().GetDistantFaceNums(locnum); + return mesh->GetParallelTopology().GetDistantFaceProcs(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 d1cff5fc..39ad99ea 100644 --- a/libsrc/interface/read_fnf_mesh.cpp +++ b/libsrc/interface/read_fnf_mesh.cpp @@ -53,16 +53,16 @@ namespace netgen string name; string placement; string valuetype; - Array places; + NgArray places; }; void ReadFNFFormat (Mesh & mesh, - const string & filename) + const filesystem::path & filename) { - ifstream fin (filename.c_str()); + ifstream fin (filename); string buf; @@ -94,7 +94,7 @@ namespace netgen char ch; string name; sbuf >> ch >> name; - cout << "Title: " << name << endl; + PrintMessage(3, "Title: ", name); } else if (token == "%STATISTICS") { @@ -106,7 +106,7 @@ namespace netgen } else { - cout << "SECTION HEADER, unknown field: " << buf << endl; + PrintMessage(1, "SECTION HEADER, unknown field: ",buf); } } } @@ -142,7 +142,7 @@ namespace netgen } else { - cout << "SECTION ELEM_TYPE, unknown field: " << buf << endl; + PrintMessage(1, "SECTION ELEM_TYPE, unknown field: ", buf); } } } @@ -169,12 +169,26 @@ namespace netgen } } - + else if (token == "ANALYSIS") + { + // ignore this section + while (1) + { + ReadLine (fin, buf); + stringstream sbuf(buf); + string token; + sbuf >> token; + if (token == "%END_SECT") + { + break; + } + } + } else if (token == "MATERIALS") { *testout << "parse materials" << endl; - Array young_modulus, poisson_ratio, mass_density; + NgArray young_modulus, poisson_ratio, mass_density; while (1) { @@ -194,7 +208,9 @@ namespace netgen sbuf >> nr >> prop >> ch; if (prop == "DEF") { - ; + string name; + sbuf >> name; + mesh.SetMaterial(nr, name); } else { @@ -219,7 +235,7 @@ namespace netgen } else { - cout << "SECTION MATERIALS, unknown field: " << buf << endl; + PrintMessage(1, "SECTION MATERIALS, unknown field: ", buf); } } } @@ -250,7 +266,7 @@ namespace netgen string propid; sbuf >> elnr >> def >> ch; sbuf >> typid >> matid >> propid; - Array pnums; + NgArray pnums; while (1) { int pn; @@ -271,7 +287,7 @@ namespace netgen } else { - cout << "SECTION MESH, unknown: " << buf << endl; + PrintMessage(1, "SECTION MESH, unknown: ", buf); } } } @@ -291,7 +307,7 @@ namespace netgen sbuf >> nr >> kw >> ch; if (kw == "NODES") { - Array enums; + NgArray enums; while (1) { int en; @@ -315,7 +331,7 @@ namespace netgen sbuf >> nr >> kw >> ch; if (kw == "FACES") { - Array fnums; + NgArray fnums; while (1) { int fn; @@ -335,6 +351,8 @@ namespace netgen int fnr = fnums[j+1]; const Element & el = mesh.VolumeElement (elnr); + if(j == 0) + mesh.GetFaceDescriptor(nr).SetDomainIn(el.GetIndex()); Element2d el2d; el.GetFace (fnr, el2d); el2d.SetIndex (nr); @@ -349,7 +367,7 @@ namespace netgen } else { - cout << "SECTION MESH, unknown: " << buf << endl; + PrintMessage(1, "SECTION MESH, unknown: ", buf); } } } @@ -359,7 +377,7 @@ namespace netgen else if (token == "LOADS") { - Array loadtypes; + NgArray loadtypes; while (1) { @@ -378,10 +396,10 @@ namespace netgen sbuf >> lt->id >> def >> ch >> lt->name >> lt->placement >> lt->valuetype; if (lt->name == "DISPLACEMENT") - cout << "loadtype DISPLACEMENT found" << endl; + PrintMessage(3, "loadtype DISPLACEMENT found"); if (lt->placement != "FACE" && lt->placement != "EDGE" && lt->placement != "NODE") - cout << "unsupported placement " << lt->placement << endl; + PrintMessage(1, "unsupported placement ", lt->placement); loadtypes.Append (lt); } @@ -406,33 +424,37 @@ namespace netgen if (load_type_id == loadtypes[i]->id) loadtypes[i]->places.Append (placement); } - } + } + else if (token == "%CON_CASE") + { ; } else if (token == "%END_SECT") { for (int i = 0; i < loadtypes.Size(); i++) { + stringstream str; + str << loadtypes[i]->places; if (loadtypes[i]->placement == "FACE" && loadtypes[i]->name == "DISPLACEMENT") { mesh.SetUserData ("CONSTRAINT_DISP_FACE", loadtypes[i]->places); - cout << "constrained faces: " << loadtypes[i]->places << endl; + PrintMessage(3, "constrained faces: ", str.str()); } if (loadtypes[i]->placement == "EDGE" && loadtypes[i]->name == "DISPLACEMENT") { mesh.SetUserData ("CONSTRAINT_DISP_EDGE", loadtypes[i]->places); - cout << "constrained edges: " << loadtypes[i]->places << endl; + PrintMessage(3,"constrained edges: ", str.str()); } if (loadtypes[i]->placement == "NODE" && loadtypes[i]->name == "DISPLACEMENT") { mesh.SetUserData ("CONSTRAINT_DISP_NODE", loadtypes[i]->places); - cout << "constrained nodes: " << loadtypes[i]->places << endl; + PrintMessage(3, "constrained nodes: ", str.str()); } } break; } else { - cout << "SECTION LOADS, unknown field: " << buf << endl; + PrintMessage(1, "SECTION LOADS, unknown field: ", buf); } } } @@ -441,11 +463,12 @@ namespace netgen else { - cout << "unknown section " << token << endl; + PrintMessage(1, "unknown section ", token); } } else - cout << "parse line: (" << buf << ")" << endl; + PrintMessage(3, "parse line: (", buf, ")"); } + mesh.ComputeNVertices(); } } diff --git a/libsrc/interface/readtetmesh.cpp b/libsrc/interface/readtetmesh.cpp index db18f2ed..1ffcce69 100644 --- a/libsrc/interface/readtetmesh.cpp +++ b/libsrc/interface/readtetmesh.cpp @@ -20,10 +20,8 @@ namespace netgen void ReadTETFormat (Mesh & mesh, - const string & hfilename) + const filesystem::path & filename) { - const char * filename = hfilename.c_str(); - cout << "Reading .tet mesh" << endl; ifstream in (filename); @@ -49,17 +47,17 @@ namespace netgen Point3d p; int numObj3D,numObj2D,numObj1D,numObj0D; // bool nullstarted; - Array eldom; + NgArray eldom; int minId3D = -1, minId2D = -1; int maxId3D(-1), maxId2D(-1), maxId1D(-1), maxId0D(-1); - Array *> segmentdata; - Array tris; + NgArray *> segmentdata; + NgArray tris; - Array userdata_int; // just save data for 1:1 output - Array userdata_double; - Array point_pids; - Array tetfacedata; - Array uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D; + NgArray userdata_int; // just save data for 1:1 output + NgArray userdata_double; + NgArray point_pids; + NgArray tetfacedata; + NgArray uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D; while(!done) { @@ -154,7 +152,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(7); + segmentdata[i] = new NgArray(7); *segmentdata[i] = -1; in >> dummyint; in >> (*segmentdata[i])[0] >> (*segmentdata[i])[1]; @@ -243,7 +241,7 @@ namespace netgen break; case 16: - // MasterEdgeID, SlaveEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2) + // MasterEdgeID, MinionEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2) for(int i=0; i> dummyint >> dummyint >> dummyint; break; @@ -254,7 +252,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 +264,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,15 +341,15 @@ 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; for(int i=0; i nodes1(3),nodes2(3); - Array sortval1(3),sortval2(3); + NgArray nodes1(3),nodes2(3); + NgArray sortval1(3),sortval2(3); in >> tri1 >> tri2 >> transl; if(transl > maxtransl) @@ -455,7 +453,7 @@ namespace netgen cout << endl; - // Array indextodescriptor(maxId2D+1); + // NgArray indextodescriptor(maxId2D+1); // for(int i=1; i<=mesh.GetNFD(); i++) // indextodescriptor[mesh.GetFaceDescriptor(i).SurfNr()] = i; @@ -538,7 +536,7 @@ namespace netgen case 27: // Object2D GroupID, #Faces FaceID List { - Array ports; + NgArray ports; //int totnum = 0; uid_to_group_2D.SetSize(maxId2D+1); uid_to_group_2D = -1; @@ -665,7 +663,7 @@ namespace netgen mesh.SetUserData("TETmesh:uid_to_group_0D",uid_to_group_0D); - Array surfindices(tris.Size()); + NgArray surfindices(tris.Size()); surfindices = -1; for(int i=0; i indextodescriptor(maxId2D+1); + // NgArray indextodescriptor(maxId2D+1); // for(int i=1; i<=mesh.GetNFD(); i++) // indextodescriptor[mesh.GetFaceDescriptor(i).SurfNr()] = i; diff --git a/libsrc/interface/readuser.cpp b/libsrc/interface/readuser.cpp index a92ef843..c3aac4ae 100644 --- a/libsrc/interface/readuser.cpp +++ b/libsrc/interface/readuser.cpp @@ -4,36 +4,32 @@ #include - - #include #include #include +#include #include +#include #include "writeuser.hpp" namespace netgen { void ReadFile (Mesh & mesh, - const string & hfilename) + const filesystem::path & filename) { - cout << "Read User File" << endl; + PrintMessage(3, "Read User File"); - const char * filename = hfilename.c_str(); + auto ext = filename.extension(); char reco[100]; int np, nbe; - // ".surf" - mesh - - if ( (strlen (filename) > 5) && - strcmp (&filename[strlen (filename)-5], ".surf") == 0 ) - + if ( ext == ".surf" ) { - cout << "Surface file" << endl; + cout << IM(3) << "Surface file" << endl; ifstream in (filename); @@ -43,12 +39,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"); @@ -78,57 +73,101 @@ namespace netgen } - cout << "points: " << np << " faces: " << nbe << endl; + cout << IM(3) << "points: " << np << " faces: " << nbe << endl; } - - - - if ( (strlen (filename) > 4) && - strcmp (&filename[strlen (filename)-4], ".unv") == 0 ) + if ( ext == ".unv" ) { char reco[100]; // int invert; + // read files that are stored with D instead of E as exponent prefix + // such files are for example exported by GMSH + bool Dnotation; + bool DnotationSet = false; ifstream in(filename); mesh.ClearFaceDescriptors(); mesh.AddFaceDescriptor (FaceDescriptor(0,1,0,0)); + mesh.GetFaceDescriptor(1).SetBCProperty (1); + // map from unv element nr to our element number + an index if it is vol (0), bnd(1), ... + std::map> element_map; + int dim = 3; + int bccounter = 0; - + NgArray tmp_segments; while (in.good()) { in >> reco; - cout << "reco = " << reco << endl; + if (strcmp(reco, "-1") == 0) + continue; - if (strcmp (reco, "2411") == 0) + else if (strcmp (reco, "2411") == 0) { - cout << "nodes found" << endl; + cout << IM(3) << "nodes found" << endl; while (1) { int pi, hi; - Point<3> p; + Point<3> p; + string p1tmp, p2tmp, p3tmp; in >> pi; if (pi == -1) break; in >> hi >> hi >> hi; - in >> p(0) >> p(1) >> p(2); + // check if D in first line + if (DnotationSet == false) { + in >> p1tmp >> p2tmp >> p3tmp; + if (p1tmp.find("D") != std::string::npos){ + Dnotation = true; + cout << IM(3) << "Attention: in your UNV file, D is used as an exponent prefix instead of E" << endl; + std::replace(p1tmp.begin(), p1tmp.end(), 'D', 'E'); + std::replace(p2tmp.begin(), p2tmp.end(), 'D', 'E'); + std::replace(p3tmp.begin(), p3tmp.end(), 'D', 'E'); + } + p(0) = std::stod(p1tmp); + p(1) = std::stod(p2tmp); + p(2) = std::stod(p3tmp); + mesh.AddPoint(p); - cout << "p(" << pi << ") = " - << p << endl; + DnotationSet = true; + continue; + } - mesh.AddPoint (p); + if (Dnotation == true) { + in >> p1tmp >> p2tmp >> p3tmp; + std::replace(p1tmp.begin(), p1tmp.end(), 'D', 'E'); + std::replace(p2tmp.begin(), p2tmp.end(), 'D', 'E'); + std::replace(p3tmp.begin(), p3tmp.end(), 'D', 'E'); + p(0) = std::stod(p1tmp); + p(1) = std::stod(p2tmp); + p(2) = std::stod(p3tmp); + } + else{ + in >> p(0) >> p(1) >> p(2); + } + mesh.AddPoint(p); } - cout << "read " << mesh.GetNP() << " points" << endl; + cout << IM(3) << "read " << mesh.GetNP() << " points" << endl; + Point3d pmin, pmax; + cout << IM(5) << "Get Box" << endl; + mesh.GetBox (pmin, pmax); + cout << IM(5) << "Pmin: " << pmin << " Pmax: " << pmax << endl; + if(fabs(pmin.Z() - pmax.Z()) < 1e-10 * Dist(pmin, pmax)) + { + cout << IM(5) << "Set Dimension to 2." << endl; + mesh.SetDimension(2); + dim = 2 ; + } + } - if (strcmp (reco, "2412") == 0) + else if (strcmp (reco, "2412") == 0) { - cout << "elements found" << endl; + cout << IM(3) << "elements found" << endl; while (1) { @@ -140,8 +179,6 @@ namespace netgen if (label == -1) break; in >> fe_id >> phys_prop >> mat_prop >> color >> nnodes; - cout << "fe_id = " << fe_id << " col = " << color << ", nnodes = " << nnodes << endl; - if (fe_id >= 11 && fe_id <= 32) in >> hi >> hi >> hi; @@ -151,45 +188,263 @@ namespace netgen switch (fe_id) { - case 41: + case 11: // (Rod) SEGM + { + Segment el; + el[0] = nodes[0]; + el[1] = nodes[1]; + el[2] = -1; + + if(dim == 3){ + auto nr = tmp_segments.Size(); + tmp_segments.Append(el); + element_map[label] = std::make_tuple(nr+1, 2); + } + else if(dim == 2){ + el.si = -1; // add label to segment, will be changed later when BC's are assigned + auto nr = mesh.AddSegment(el); + element_map[label] = std::make_tuple(nr+1, 2); + } + break; + } + + case 22: // (Tapered beam) SEGM + { + Segment el; + el[0] = nodes[0]; + el[1] = nodes[2]; + el[2] = nodes[1]; + + if(dim == 3){ + auto nr = tmp_segments.Size(); + tmp_segments.Append(el); + element_map[label] = std::make_tuple(nr+1, 2); + } + else if(dim == 2){ + el.si = -1; // add label to segment, will be changed later when BC's are assigned + auto nr = mesh.AddSegment(el); + element_map[label] = std::make_tuple(nr+1, 2); + } + + break; + } + case 41: // TRIG { Element2d el (TRIG); el.SetIndex (1); for (int j = 0; j < nnodes; j++) el[j] = nodes[j]; - mesh.AddSurfaceElement (el); - + auto nr = mesh.AddSurfaceElement (el); + element_map[label] = std::make_tuple(nr+1, 1); break; } - case 111: + case 42: // TRIG6 + { + Element2d el(TRIG6); + el.SetIndex(1); + int jj = 0; + for(auto j : {0,2,4,3,5,1}) + el[jj++] = nodes[j]; + auto nr = mesh.AddSurfaceElement(el); + element_map[label] = std::make_tuple(nr+1, 1); + break; + } + case 111: // TET { Element el (TET); el.SetIndex (1); for (int j = 0; j < nnodes; j++) el[j] = nodes[j]; - mesh.AddVolumeElement (el); - + auto nr = mesh.AddVolumeElement (el); + element_map[label] = std::make_tuple(nr+1, 0); break; } + case 118: // TET10 + { + Element el(TET10); + el.SetIndex(1); + int jj = 0; + for(auto j : {0,2,4,9,1,5,6,3,7,8}) + el[jj++] = nodes[j]; + auto nr = mesh.AddVolumeElement(el); + element_map[label] = std::make_tuple(nr+1, 0); + break; + } + default: + cout << IM(3) << "Do not know fe_id = " << fe_id << ", skipping it." << endl; + break; } } + cout << IM(3) << mesh.GetNE() << " elements found" << endl; + cout << IM(3) << mesh.GetNSE() << " surface elements found" << endl; + + } + else if(strcmp (reco, "2467") == 0) + { + int matnr = 1; + cout << IM(3) << "Groups found" << endl; + while(in.good()) + { + int len; + string name; + in >> len; + if(len == -1) + break; + for(int i=0; i < 7; i++) + in >> len; + in >> name; + cout << IM(3) << len << " element are in group " << name << endl; + int hi, index; + int fdnr, ednr; + + in >> hi >> index >> hi >> hi; + int codim = get<1>(element_map[index]); + // use first element to determine if boundary or volume + + switch (codim) + { + case 0: + { + mesh.SetMaterial(++matnr, name); + mesh.VolumeElement(get<0>(element_map[index])).SetIndex(matnr); + break; + } + case 1: + { + if(dim == 3) + { + int bcpr = mesh.GetNFD(); + fdnr = mesh.AddFaceDescriptor(FaceDescriptor(bcpr, 0,0,0)); + mesh.GetFaceDescriptor(fdnr).SetBCProperty(bcpr+1); + mesh.SetBCName(bcpr, name); + mesh.SurfaceElement(get<0>(element_map[index])).SetIndex(fdnr); + bccounter++; + } + else if(dim == 2) + { + mesh.SetMaterial(matnr, name); + fdnr = mesh.AddFaceDescriptor(FaceDescriptor(matnr, 0,0,0)); + mesh.SurfaceElement(get<0>(element_map[index])).SetIndex(matnr); + mesh.GetFaceDescriptor(fdnr).SetBCProperty(matnr); + matnr++; + } + break; + + } + case 2: + { + if(dim == 3) + { + int bcpr = mesh.GetNCD2Names()+1; + auto ed = EdgeDescriptor(); + ed.SetSurfNr(0,bcpr);//? + ednr = mesh.AddEdgeDescriptor(ed); + mesh.SetCD2Name(bcpr, name); + auto nr = mesh.AddSegment(tmp_segments[get<0>(element_map[index])-1]); + mesh[nr].edgenr = ednr+1; + } + else if(dim == 2) + { + Segment & seg = mesh.LineSegment(get<0>(element_map[index])); + seg.si = bccounter + 1; + mesh.SetBCName(bccounter, name); + bccounter++; + } + break; + + } + default: + { + cout << IM(3) << "Codim " << codim << " not implemented yet!" << endl; + } + } + + for(int i=0; i> hi >> index >> hi >> hi; + switch (codim) + { + case 0: + mesh.VolumeElement(get<0>(element_map[index])).SetIndex(matnr); + break; + case 1: + if(dim == 3) mesh.SurfaceElement(get<0>(element_map[index])).SetIndex(fdnr); + else if (dim == 2){ + mesh.SurfaceElement(get<0>(element_map[index])).SetIndex(matnr-1); + mesh.GetFaceDescriptor(fdnr).SetBCProperty(matnr); + } + break; + case 2: + if(dim == 3) + { + auto nr = mesh.AddSegment(tmp_segments[get<0>(element_map[index])-1]); + mesh[nr].edgenr = ednr+1; + } + else if(dim == 2) + { + Segment & seg = mesh.LineSegment(get<0>(element_map[index])); + seg.si = bccounter; + } + break; + default: + break; + } + } + } + } + else + { + cout << IM(3) << "Do not know data field type " << reco << ", skipping it" << endl; + while(in.good()) + { + in >> reco; + if(strcmp(reco, "-1") == 0) + break; + } } } + + if(dim == 2){ + // loop through segments to assign default BC to unmarked edges + int bccounter_tmp = bccounter; + for(int index=1; index <= mesh.GetNSeg(); index++){ + Segment & seg = mesh.LineSegment(index); + if(seg.si == -1){ + seg.si = bccounter + 1; + if(bccounter_tmp == bccounter) mesh.SetBCName(bccounter, "default"); // could be more efficient + bccounter_tmp++; + } + } + if(bccounter_tmp > bccounter) bccounter++; + } + cout << IM(5) << "Finalize mesh" << endl; Point3d pmin, pmax; + cout << IM(5) << "ComputeNVertices" << endl; + mesh.ComputeNVertices(); + cout << IM(5) << "RebuildSurfaceElementLists" << endl; + mesh.RebuildSurfaceElementLists(); + cout << IM(5) << "GetBox" << endl; mesh.GetBox (pmin, pmax); - cout << "bounding-box = " << pmin << "-" << pmax << endl; + cout << IM(5) << "UpdateTopology" << endl; + mesh.UpdateTopology(); + cout << IM(5) << "increment bccounter" << endl; + if(dim == 3) bccounter++; + cout << IM(5) << "bounding-box = " << pmin << "-" << pmax << endl; + cout << IM(5) << "Created " << bccounter << " boundaries." << endl; + for(int i=0; i 7) && - strcmp (&filename[strlen (filename)-7], ".mesh2d") == 0 ) + if ( ext == ".mesh2d" ) { - cout << "Reading FEPP2D Mesh" << endl; + cout << IM(3) << "Reading FEPP2D Mesh" << endl; char buf[100]; int np, ne, nseg, i, j; @@ -228,10 +483,9 @@ namespace netgen } - else if ( (strlen (filename) > 5) && - strcmp (&filename[strlen (filename)-5], ".mesh") == 0 ) + else if ( ext == ".mesh" ) { - cout << "Reading Neutral Format" << endl; + cout << IM(3) << "Reading Neutral Format" << endl; int np, ne, nse, i, j; @@ -289,11 +543,11 @@ namespace netgen do { in >> buf; - cout << "buf = " << buf << endl; + cout << IM(5) << "buf = " << buf << endl; if (strcmp (buf, "points") == 0) { in >> np; - cout << "np = " << np << endl; + cout << IM(5) << "np = " << np << endl; } } while (in.good()); @@ -301,23 +555,19 @@ namespace netgen } - if ( (strlen (filename) > 4) && - strcmp (&filename[strlen (filename)-4], ".emt") == 0 ) + if ( ext == ".emt" ) { ifstream inemt (filename); - string pktfile = filename; - int len = strlen (filename); - pktfile[len-3] = 'p'; - pktfile[len-2] = 'k'; - pktfile[len-1] = 't'; - cout << "pktfile = " << pktfile << endl; + auto pktfile = filename; + pktfile.replace_extension("pkt"); + cout << IM(3) << "pktfile = " << pktfile << endl; int np, nse, i; int bcprop; - ifstream inpkt (pktfile.c_str()); + ifstream inpkt (pktfile); inpkt >> np; - Array values(np); + NgArray values(np); for (i = 1; i <= np; i++) { Point3d p(0,0,0); @@ -352,7 +602,7 @@ namespace netgen p3++; if (p1 < 1 || p1 > np || p2 < 1 || p2 > np || p3 < 1 || p3 > np) { - cout << "p1 = " << p1 << " p2 = " << p2 << " p3 = " << p3 << endl; + cout << IM(3) << "p1 = " << p1 << " p2 = " << p2 << " p3 = " << p3 << endl; } if (i > 110354) Swap (p2, p3); @@ -382,7 +632,7 @@ namespace netgen ifstream incyl ("ngusers/guenter/cylinder.surf"); int npcyl, nsecyl; incyl >> npcyl; - cout << "npcyl = " << npcyl << endl; + cout << IM(3) << "npcyl = " << npcyl << endl; for (i = 1; i <= npcyl; i++) { Point3d p(0,0,0); @@ -390,7 +640,7 @@ namespace netgen mesh.AddPoint (p); } incyl >> nsecyl; - cout << "nsecyl = " << nsecyl << endl; + cout << IM(3) << "nsecyl = " << nsecyl << endl; for (i = 1; i <= nsecyl; i++) { incyl >> p1 >> p2 >> p3; @@ -408,20 +658,42 @@ namespace netgen // .tet mesh - if ( (strlen (filename) > 4) && - strcmp (&filename[strlen (filename)-4], ".tet") == 0 ) - { + if ( ext == ".tet" ) ReadTETFormat (mesh, filename); - } - // .fnf mesh (FNF - PE neutral format) - if ( (strlen (filename) > 4) && - strcmp (&filename[strlen (filename)-4], ".fnf") == 0 ) - { + if ( ext == ".fnf" ) ReadFNFFormat (mesh, filename); - } + // .cgns file - CFD General Notation System + if ( ext == ".cgns" ) + ReadCGNSMesh (mesh, filename); + + if ( ext == ".stl" || ext == ".stlb" ) + { + ifstream ist{filename}; + auto geom = shared_ptr(STLGeometry::Load(ist)); + + mesh.SetDimension (3); + + auto & points = geom->GetPoints(); + + for (auto & p : points) + mesh.AddPoint(MeshPoint(p)); + + mesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 1)); + + for (auto ti : IntRange(geom->GetNT())) + { + Element2d el(TRIG); + for (auto i : IntRange(3)) + el[i] = int((*geom)[STLTrigId(ti+IndexBASE())][i]); + + el.SetIndex(1); + + mesh.AddSurfaceElement(el); + } + } } } diff --git a/libsrc/interface/rw_cgns.cpp b/libsrc/interface/rw_cgns.cpp new file mode 100644 index 00000000..ad0869a7 --- /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 filesystem::path & filename, Array> & zones) + { + mesh.SetDimension(3); + static Timer tall("CGNS::ReadMesh"); RegionTimer rtall(tall); + int fn; + cg_open(filename.string().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 filesystem::path & filename) + { + Array> zones; + int fn = ReadCGNSMesh(mesh, filename, zones); + cg_close(fn); + } + + // Reads mesh and solutions of .csns file + tuple, vector, vector>, vector> ReadCGNSFile(const filesystem::path & 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 filesystem::path & filename) + { + static Timer tall("CGNS::WriteMesh"); RegionTimer rtall(tall); + int fn, base, zone; + cg_open(filename.string().c_str(),CG_MODE_WRITE,&fn); + + WriteCGNSMesh(mesh, fn, base, zone); + + cg_close(fn); + } + + + void WriteCGNSFile(shared_ptr mesh, const filesystem::path & filename, vector fields, vector> values, vector locations) + { + static Timer tall("CGNS::WriteFile"); RegionTimer rtall(tall); + int fn, base, zone; + cg_open(filename.string().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 filesystem::path & filename) + { + PrintMessage(1, "Could not import CGNS mesh: Netgen was built without CGNS support"); + } + + tuple, vector, vector>, vector> ReadCGNSFile(const filesystem::path & filename, int base) + { + throw Exception("Netgen was built without CGNS support"); + } + + void WriteCGNSMesh (const Mesh & mesh, const filesystem::path & filename) + { + PrintMessage(1, "Could not write CGNS mesh: Netgen was built without CGNS support"); + } + + void WriteCGNSFile(shared_ptr mesh, const filesystem::path & filename, vector fields, vector> values, vector locations) + { + throw Exception("Netgen was built without CGNS support"); + } + +} + +#endif // NG_CGNS diff --git a/libsrc/interface/writeOpenFOAM15x.cpp b/libsrc/interface/writeOpenFOAM15x.cpp index 574fc2ec..8b185d75 100644 --- a/libsrc/interface/writeOpenFOAM15x.cpp +++ b/libsrc/interface/writeOpenFOAM15x.cpp @@ -30,6 +30,7 @@ #include #include +#include "../general/gzstream.h" namespace netgen { @@ -39,11 +40,11 @@ namespace netgen // Global arrays used to maintain the owner, neighbour and face lists // so that they are accessible across functions - static Array owner_facelist; - static Array owner_celllist; - static Array neighbour_celllist; - static Array surfelem_bclist; - static Array surfelem_lists; + static NgArray owner_facelist; + static NgArray owner_celllist; + static NgArray neighbour_celllist; + static NgArray surfelem_bclist; + static NgArray surfelem_lists; @@ -118,17 +119,17 @@ namespace netgen // Initialise arrays to zero if required neighbour_celllist = 0; - // Array used to keep track of Faces which have already been + // NgArray used to keep track of Faces which have already been // processed and added to the Owner list... In addition, also the // location where the face appears in the Owner list is also stored // to speed up creation of the Neighbour list - Array ownerfaces(totfaces); + NgArray ownerfaces(totfaces); ownerfaces = 0; - // Array to hold the set of local faces of each volume element + // NgArray to hold the set of local faces of each volume element // while running through the set of volume elements // NOTE: The size is set automatically by the Netgen topology function - Array locfaces; + NgArray locfaces; // Secondary indices used to independently advance the owner // and boundary condition arrays within the main loop @@ -383,9 +384,9 @@ namespace netgen *outfile << "(\n"; - // Array to hold the indices of the points of each face to + // NgArray to hold the indices of the points of each face to // flip if required - Array facepnts; + NgArray facepnts; // Write the faces in the order specified in the owners lists of the // internal cells and the boundary cells @@ -545,7 +546,7 @@ namespace netgen *outfile << "\n"; - Array bcarray; + NgArray bcarray; int ind = 1; // Since the boundary conditions are already sorted in ascending @@ -596,7 +597,7 @@ namespace netgen - void WriteOpenFOAM15xFormat (const Mesh & mesh, const string & casename, const bool compressed) + void WriteOpenFOAM15xFormat (const Mesh & mesh, const filesystem::path & dirname, const bool compressed) { bool error = false; char casefiles[256]; @@ -638,22 +639,12 @@ namespace netgen } - cout << "Writing OpenFOAM 1.5+ Mesh files to case: " << casename << "\n"; + cout << "Writing OpenFOAM 1.5+ Mesh files to case: " << dirname.string() << "\n"; // Create the case directory if it does not already exist // NOTE: This needs to be improved for the Linux variant....!!! - #ifdef WIN32 - char casedir[256]; - sprintf(casedir, "mkdir %s\\constant\\polyMesh", casename.c_str()); - system(casedir); - #else - char casedir[256]; - mkdir(casename.c_str(), S_IRWXU|S_IRWXG); - sprintf(casedir, "%s/constant", casename.c_str()); - mkdir(casedir, S_IRWXU|S_IRWXG); - sprintf(casedir, "%s/constant/polyMesh", casename.c_str()); - mkdir(casedir, S_IRWXU|S_IRWXG); - #endif + auto mesh_dir = filesystem::path(dirname).append("constant").append("polyMesh"); + filesystem::create_directories(mesh_dir); // Open handles to the five required mesh files // points @@ -661,59 +652,21 @@ namespace netgen // owner // neighbour // boundary - ostream *outfile_pnts; - ostream *outfile_faces; - ostream *outfile_own; - ostream *outfile_nei; - ostream *outfile_bnd; - if(compressed) - { - sprintf(casefiles, "%s/constant/polyMesh/points.gz", casename.c_str()); - outfile_pnts = new ogzstream(casefiles); - } - else - { - sprintf(casefiles, "%s/constant/polyMesh/points", casename.c_str()); - outfile_pnts = new ofstream(casefiles); - } + auto get_name = [compressed, &mesh_dir]( string s ) { + auto p = filesystem::path(mesh_dir).append(s); + if(compressed) + p.concat(".gz"); + return p; + }; - if(compressed) - { - sprintf(casefiles, "%s/constant/polyMesh/faces.gz", casename.c_str()); - outfile_faces = new ogzstream(casefiles); - } - else - { - sprintf(casefiles, "%s/constant/polyMesh/faces", casename.c_str()); - outfile_faces = new ofstream(casefiles); - } + auto outfile_pnts = make_unique(get_name("points")); + auto outfile_faces = make_unique(get_name("faces")); + auto outfile_own = make_unique(get_name("owner")); + auto outfile_nei = make_unique(get_name("neighbor")); - if(compressed) - { - sprintf(casefiles, "%s/constant/polyMesh/owner.gz", casename.c_str()); - outfile_own = new ogzstream(casefiles); - } - else - { - sprintf(casefiles, "%s/constant/polyMesh/owner", casename.c_str()); - outfile_own = new ofstream(casefiles); - } - - if(compressed) - { - sprintf(casefiles, "%s/constant/polyMesh/neighbour.gz", casename.c_str()); - outfile_nei = new ogzstream(casefiles); - } - else - { - sprintf(casefiles, "%s/constant/polyMesh/neighbour", casename.c_str()); - outfile_nei = new ofstream(casefiles); - } - - // Note... the boundary file is not compressed - sprintf(casefiles, "%s/constant/polyMesh/boundary", casename.c_str()); - outfile_bnd = new ofstream(casefiles); + // Note... the boundary file is not compressed + auto outfile_bnd = make_unique(mesh_dir.append("boundary")); ResetTime(); @@ -730,8 +683,7 @@ namespace netgen if(outfile_own->good() && !error) { cout << "Writing the owner file: "; - WriteOwnerFile(outfile_own); - delete outfile_own; + WriteOwnerFile(outfile_own.get()); cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n"; } else @@ -745,8 +697,7 @@ namespace netgen if(outfile_nei->good() && !error) { cout << "Writing the neighbour file: "; - WriteNeighbourFile(outfile_nei); - delete outfile_nei; + WriteNeighbourFile(outfile_nei.get()); cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n"; } else @@ -760,8 +711,7 @@ namespace netgen if(outfile_faces->good() && !error) { cout << "Writing the faces file: "; - WriteFacesFile(outfile_faces, mesh); - delete outfile_faces; + WriteFacesFile(outfile_faces.get(), mesh); cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n"; } else @@ -775,8 +725,7 @@ namespace netgen if(outfile_pnts->good() && !error) { cout << "Writing the points file: "; - WritePointsFile(outfile_pnts,mesh); - delete outfile_pnts; + WritePointsFile(outfile_pnts.get(),mesh); cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n"; } else @@ -790,8 +739,7 @@ namespace netgen if(outfile_bnd->good() && !error) { cout << "Writing the boundary file: "; - WriteBoundaryFile(outfile_bnd); - delete outfile_bnd; + WriteBoundaryFile(outfile_bnd.get()); cout << "Done! (Time Elapsed = " << GetTime() << " sec)\n"; } else diff --git a/libsrc/interface/writeabaqus.cpp b/libsrc/interface/writeabaqus.cpp index 6f2f165c..8d698b0b 100644 --- a/libsrc/interface/writeabaqus.cpp +++ b/libsrc/interface/writeabaqus.cpp @@ -18,13 +18,13 @@ namespace netgen void WriteAbaqusFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { cout << "\nWrite Abaqus Volume Mesh" << endl; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile << "*Heading" << endl; outfile << " " << filename << endl; @@ -124,23 +124,18 @@ void WriteAbaqusFormat (const Mesh & mesh, // periodic identification, implementation for // Helmut J. Boehm, TU Vienna - char cfilename[255]; - strcpy (cfilename, filename.c_str()); - - char mpcfilename[255]; - strcpy (mpcfilename, cfilename); - size_t len = strlen (cfilename); - if (len >= 4 && (strcmp (mpcfilename+len-4, ".inp") == 0)) - strcpy (mpcfilename+len-4, ".mpc"); + auto mpcfilename = filename; + if (filename.extension() == ".inp") + mpcfilename.replace_extension(".mpc"); else - strcat (mpcfilename, ".mpc"); + mpcfilename.concat(".mpc"); ofstream mpc (mpcfilename); int masternode(0); - Array pairs; - BitArray master(np), help(np); + NgArray pairs; + NgBitArray master(np), help(np); master.Set(); for (i = 1; i <= 3; i++) { @@ -158,25 +153,25 @@ void WriteAbaqusFormat (const Mesh & mesh, cout << "masternode = " << masternode << " = " << mesh.Point(masternode) << endl; - Array 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 +185,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,14 +193,14 @@ 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" << "*EQUATION, INPUT=" << mpcfilename << endl; - BitArray eliminated(np); + NgBitArray eliminated(np); eliminated.Clear(); for (i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++) { @@ -223,7 +218,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/writediffpack.cpp b/libsrc/interface/writediffpack.cpp index edf9f70f..1c6ba159 100644 --- a/libsrc/interface/writediffpack.cpp +++ b/libsrc/interface/writediffpack.cpp @@ -21,12 +21,12 @@ namespace netgen void WriteDiffPackFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { // double scale = globflags.GetNumFlag ("scale", 1); double scale = 1; - ofstream outfile(filename.c_str()); + ofstream outfile(filename); outfile.precision(14); @@ -39,8 +39,8 @@ void WriteDiffPackFormat (const Mesh & mesh, int np = mesh.GetNP(); int ne = mesh.GetNE(); int nse = mesh.GetNSE(); - Array BIname; - Array BCsinpoint; + NgArray BIname; + NgArray BCsinpoint; int i, j, k, l; @@ -112,7 +112,7 @@ void WriteDiffPackFormat (const Mesh & mesh, /* for (j = 1; j <= nse; j++) */ - FlatArray sels = point2sel[i]; + NgFlatArray sels = point2sel[i]; for (int jj = 0; jj < sels.Size(); jj++) { for (k = 1; k <= mesh[sels[jj]].GetNP(); k++) @@ -203,8 +203,8 @@ void WriteDiffPackFormat (const Mesh & mesh, int np = mesh.GetNP(); //int ne = mesh.GetNE(); int nse = mesh.GetNSE(); - Array BIname; - Array BCsinpoint; + NgArray BIname; + NgArray BCsinpoint; int i, j, k, l; diff --git a/libsrc/interface/writedolfin.cpp b/libsrc/interface/writedolfin.cpp index 8fd9e749..01b4e91c 100644 --- a/libsrc/interface/writedolfin.cpp +++ b/libsrc/interface/writedolfin.cpp @@ -19,7 +19,7 @@ namespace netgen - void WriteDolfinFormat (const Mesh & mesh, const string & filename) + void WriteDolfinFormat (const Mesh & mesh, const filesystem::path & filename) { cout << "start writing dolfin export" << endl; @@ -30,7 +30,7 @@ namespace netgen // int invertsurf = mparam.inverttrigs; // int i, j; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); // char str[100]; outfile.precision(8); diff --git a/libsrc/interface/writeelmer.cpp b/libsrc/interface/writeelmer.cpp index 805d3dca..ebaa97f7 100644 --- a/libsrc/interface/writeelmer.cpp +++ b/libsrc/interface/writeelmer.cpp @@ -21,9 +21,38 @@ namespace netgen void WriteElmerFormat (const Mesh &mesh, - const string &filename) + const filesystem::path &dirname) { cout << "write elmer mesh files" << endl; + + std::map tmap; + tmap[TRIG] = 303; + tmap[TRIG6] = 306; + tmap[QUAD] = 404; + tmap[QUAD8] = 408; + tmap[TET] = 504; + tmap[TET10] = 510; + tmap[PYRAMID] = 605; + tmap[PYRAMID13] = 613; + tmap[PRISM] = 706; + tmap[PRISM15] = 715; + tmap[HEX] = 808; + tmap[HEX20] = 820; + + std::map> pmap; + pmap[TRIG] = {1,2,3}; + pmap[TRIG6] = {1,2,3, 6,4,5}; + pmap[QUAD] = {1,2,3,4}; + pmap[QUAD8] = {1,2,3,4, 5,8,6,7}; + pmap[TET] = {1,2,3,4}; + pmap[TET10] = {1,2,3,4, 5,8,6,7,9,10}; + pmap[PYRAMID]={1,2,3,4,5}; + pmap[PYRAMID13]= {1,2,3,4,5,6,7,8,9,10,11,12,13}; + pmap[PRISM] = {1,2,3,4,5,6}; + pmap[PRISM15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + pmap[HEX] = {1,2,3,4,5,6,7,8}; + pmap[HEX20] = {1,2,3,4,5,8,6,7,8, 9,12,10,11, 17,20,19,18, 13,16,14,15}; + int np = mesh.GetNP(); int ne = mesh.GetNE(); int nse = mesh.GetNSE(); @@ -33,44 +62,76 @@ void WriteElmerFormat (const Mesh &mesh, int inverttets = mparam.inverttets; int invertsurf = mparam.inverttrigs; -#ifdef WIN32 - char a[256]; - sprintf( a, "mkdir %s", filename.c_str() ); - system( a ); -#else - // int rc = - mkdir(filename.c_str(), S_IRWXU|S_IRWXG); -#endif + filesystem::create_directories(dirname); - sprintf( str, "%s/mesh.header", filename.c_str() ); - ofstream outfile_h(str); - sprintf( str, "%s/mesh.nodes", filename.c_str() ); - ofstream outfile_n(str); - sprintf( str, "%s/mesh.elements", filename.c_str() ); - ofstream outfile_e(str); - sprintf( str, "%s/mesh.boundary", filename.c_str() ); - ofstream outfile_b(str); + auto get_name = [&dirname]( string s ) { + return filesystem::path(dirname).append(s); + }; + + ofstream outfile_h(get_name("mesh.header")); + ofstream outfile_n(get_name("mesh.nodes")); + ofstream outfile_e(get_name("mesh.elements")); + ofstream outfile_b(get_name("mesh.boundary")); + ofstream outfile_names(get_name("mesh.names")); + + for( auto codim : IntRange(0, mesh.GetDimension()-1) ) + { + auto & names = const_cast(mesh).GetRegionNamesCD(codim); + + for (auto i0 : Range(names) ) + { + if(names[i0] == nullptr) + continue; + string name = *names[i0]; + if(name == "" || name == "default") + continue; + outfile_names << "$" << name << "=" << i0+1 << "\n"; + } + } + + auto get3FacePoints = [](const Element2d & el) + { + INDEX_3 i3; + INDEX_4 i4; + auto eltype = el.GetType(); + switch (eltype) + { + case TRIG: + case TRIG6: + i3 = {el[0], el[1], el[2]}; + i3.Sort(); + break; + case QUAD: + case QUAD8: + i4 = {el[0], el[1], el[2], el[3]}; + i4.Sort(); + i3 = {i4[0], i4[1], i4[2]}; + break; + default: + throw Exception("Got invalid type (no face)"); + } + return i3; + }; // fill hashtable + // use lowest three point numbers of lowest-order face to index faces INDEX_3_HASHTABLE face2volelement(ne); - for (i = 1; i <= ne; i++) + for (int i = 1; i <= ne; i++) { const Element & el = mesh.VolumeElement(i); - INDEX_3 i3; - int k, l; - for (j = 1; j <= 4; j++) // loop over faces of tet + + // getface not working for second order elements -> reconstruct linear element here + Element linear_el = el; + linear_el.SetNP(el.GetNV()); // GetNV returns 8 for HEX20 for instance + + for (auto j : Range(1,el.GetNFaces()+1)) { - l = 0; - for (k = 1; k <= 4; k++) - if (k != j) - { - l++; - i3.I(l) = el.PNum(k); - } - i3.Sort(); - face2volelement.Set (i3, i); + Element2d face; + linear_el.GetFace(j, face); + face2volelement.Set (get3FacePoints(face), i); + cout << "set " << get3FacePoints(face) << "\tto " << i << endl; } } @@ -78,10 +139,7 @@ void WriteElmerFormat (const Mesh &mesh, // outfile.setf (ios::fixed, ios::floatfield); // outfile.setf (ios::showpoint); - outfile_h << np << " " << ne << " " << nse << "\n"; - outfile_h << "2" << "\n"; - outfile_h << "303 " << nse << "\n"; - outfile_h << "504 " << ne << "\n"; + std::map elcount; for (i = 1; i <= np; i++) { @@ -97,12 +155,15 @@ void WriteElmerFormat (const Mesh &mesh, { Element el = mesh.VolumeElement(i); if (inverttets) el.Invert(); - sprintf( str, "5%02d", (int)el.GetNP() ); - outfile_e << i << " " << el.GetIndex() << " " << str << " "; + auto eltype = el.GetType(); + elcount[eltype]++; + outfile_e << i << " " << el.GetIndex() << " " << tmap[eltype] << " "; + + auto & map = pmap[eltype]; for (j = 1; j <= el.GetNP(); j++) { outfile_e << " "; - outfile_e << el.PNum(j); + outfile_e << el.PNum(map[j-1]); } outfile_e << "\n"; } @@ -111,23 +172,29 @@ void WriteElmerFormat (const Mesh &mesh, { Element2d el = mesh.SurfaceElement(i); if (invertsurf) el.Invert(); - sprintf( str, "3%02d", (int)el.GetNP() ); - { - INDEX_3 i3; - for (j = 1; j <= 3; j++) i3.I(j) = el.PNum(j); - i3.Sort(); - - int elind = face2volelement.Get(i3); - outfile_b << i << " " << mesh.GetFaceDescriptor(el.GetIndex()).BCProperty() << - " " << elind << " 0 " << str << " "; - } + auto eltype = el.GetType(); + elcount[eltype]++; + + int elind = face2volelement.Get(get3FacePoints(el)); + cout << "get " << get3FacePoints(el) << "\t " << elind << endl; + + outfile_b << i << " " << mesh.GetFaceDescriptor(el.GetIndex()).BCProperty() << + " " << elind << " 0 " << tmap[eltype] << " "; + + auto & map = pmap[el.GetType()]; for (j = 1; j <= el.GetNP(); j++) { outfile_b << " "; - outfile_b << el.PNum(j); + outfile_b << el.PNum(map[j-1]); } outfile_b << "\n"; } + + outfile_h << np << " " << ne << " " << nse << "\n"; + outfile_h << "2" << "\n"; + + for( auto & [eltype,count] : elcount ) + outfile_h << tmap[eltype] << " " << count << "\n"; } } diff --git a/libsrc/interface/writefeap.cpp b/libsrc/interface/writefeap.cpp index dc2574a9..0b5aad61 100644 --- a/libsrc/interface/writefeap.cpp +++ b/libsrc/interface/writefeap.cpp @@ -22,7 +22,7 @@ namespace netgen void WriteFEAPFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { // Feap format by A. Rieger @@ -35,7 +35,7 @@ void WriteFEAPFormat (const Mesh & mesh, double scale = 1; // globflags.GetNumFlag ("scale", 1); - ofstream outfile(filename.c_str()); + ofstream outfile(filename); outfile << "feap" << "\n"; outfile << mesh.GetNP(); @@ -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/writefluent.cpp b/libsrc/interface/writefluent.cpp index 792203d6..43cb5fe3 100644 --- a/libsrc/interface/writefluent.cpp +++ b/libsrc/interface/writefluent.cpp @@ -18,7 +18,7 @@ namespace netgen void WriteFluentFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { cout << "start writing fluent export" << endl; @@ -28,7 +28,7 @@ void WriteFluentFormat (const Mesh & mesh, int nse = mesh.GetNSE(); int i, j; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); char str[100]; outfile.precision(6); @@ -42,11 +42,11 @@ void WriteFluentFormat (const Mesh & mesh, outfile << "(0 \"Nodes:\")" << endl; //number of nodes: - sprintf(str,"(10 (0 1 %x 1))",np); //hexadecimal!!! + snprintf (str, size(str), "(10 (0 1 %x 1))",np); //hexadecimal!!! outfile << str << endl; //nodes of zone 1: - sprintf(str,"(10 (7 1 %x 1)(",np); //hexadecimal!!! + snprintf (str, size(str), "(10 (7 1 %x 1)(",np); //hexadecimal!!! outfile << str << endl; for (i = 1; i <= np; i++) { @@ -65,19 +65,19 @@ void WriteFluentFormat (const Mesh & mesh, Element2d face, face2; int i2, j2; - Array surfaceelp; - Array surfaceeli; - Array locels; + NgArray surfaceelp; + NgArray surfaceeli; + NgArray locels; //no cells=no tets //no faces=2*tets int noverbface = 2*ne-nse/2; - sprintf(str,"(13 (0 1 %x 0))",(noverbface+nse)); //hexadecimal!!! + snprintf (str, size(str), "(13 (0 1 %x 0))",(noverbface+nse)); //hexadecimal!!! outfile << str << endl; - sprintf(str,"(13 (4 1 %x 2 3)(",noverbface); //hexadecimal!!! + snprintf (str, size(str), "(13 (4 1 %x 2 3)(",noverbface); //hexadecimal!!! outfile << str << endl; const_cast (mesh).BuildElementSearchTree(); @@ -156,7 +156,7 @@ void WriteFluentFormat (const Mesh & mesh, } outfile << "))" << endl; - sprintf(str,"(13 (2 %x %x 3 3)(",(noverbface+1),noverbface+nse); //hexadecimal!!! + snprintf (str, size(str), "(13 (2 %x %x 3 3)(",(noverbface+1),noverbface+nse); //hexadecimal!!! outfile << str << endl; for (i = 1; i <= surfaceelp.Size(); i++) @@ -171,10 +171,10 @@ void WriteFluentFormat (const Mesh & mesh, outfile << "(0 \"Cells:\")" << endl; - sprintf(str,"(12 (0 1 %x 0))",ne); //hexadecimal!!! + snprintf (str, size(str), "(12 (0 1 %x 0))",ne); //hexadecimal!!! outfile << str << endl; - sprintf(str,"(12 (1 1 %x 1 2))",ne); //hexadecimal!!! + snprintf (str, size(str), "(12 (1 1 %x 1 2))",ne); //hexadecimal!!! outfile << str << endl << endl; diff --git a/libsrc/interface/writegmsh.cpp b/libsrc/interface/writegmsh.cpp index c4adfbd6..1048f582 100644 --- a/libsrc/interface/writegmsh.cpp +++ b/libsrc/interface/writegmsh.cpp @@ -32,9 +32,9 @@ namespace netgen void WriteGmshFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); outfile.setf (ios::showpoint); diff --git a/libsrc/interface/writegmsh2.cpp b/libsrc/interface/writegmsh2.cpp index e0c84f8d..c80208c4 100644 --- a/libsrc/interface/writegmsh2.cpp +++ b/libsrc/interface/writegmsh2.cpp @@ -49,9 +49,9 @@ namespace netgen */ void WriteGmsh2Format (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); outfile.setf (ios::showpoint); diff --git a/libsrc/interface/writejcm.cpp b/libsrc/interface/writejcm.cpp index 59b64101..7732b413 100644 --- a/libsrc/interface/writejcm.cpp +++ b/libsrc/interface/writejcm.cpp @@ -17,7 +17,7 @@ namespace netgen void WriteJCMFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { if (mesh.GetDimension() != 3) { @@ -33,7 +33,7 @@ void WriteJCMFormat (const Mesh & mesh, int np = mesh.GetNP(); // Identic points - Array identmap1, identmap2, identmap3; + NgArray identmap1, identmap2, identmap3; mesh.GetIdentifications().GetMap(1, identmap1); mesh.GetIdentifications().GetMap(2, identmap2); mesh.GetIdentifications().GetMap(3, identmap3); @@ -95,7 +95,7 @@ void WriteJCMFormat (const Mesh & mesh, int nbquad = 0; // array with 1 if point on any tetra, 0 else // this is needed in order to arrange the prism points in the right order - Array pointsOnTetras; + NgArray pointsOnTetras; pointsOnTetras.SetSize (mesh.GetNP()); pointsOnTetras = 0; for (i = 1; i <= ne; i++) @@ -122,7 +122,7 @@ void WriteJCMFormat (const Mesh & mesh, nbquad++; } - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); outfile.setf (ios::showpoint); diff --git a/libsrc/interface/writepermas.cpp b/libsrc/interface/writepermas.cpp index 7d2013e1..3fbd5845 100644 --- a/libsrc/interface/writepermas.cpp +++ b/libsrc/interface/writepermas.cpp @@ -22,18 +22,18 @@ namespace netgen // This should be the new function to export a PERMAS file - void WritePermasFormat (const Mesh &mesh, const string &filename, + void WritePermasFormat (const Mesh &mesh, const filesystem::path &filename, string &strComp, string &strSitu) { - ofstream outfile (filename.c_str()); + ofstream outfile (filename); addComponent(strComp, strSitu, outfile); WritePermasFormat ( mesh, filename); } - void WritePermasFormat (const Mesh &mesh, const string &filename) + void WritePermasFormat (const Mesh &mesh, const filesystem::path &filename) { string strComp, strSitu; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(8); @@ -99,8 +99,8 @@ namespace netgen outfile << i << " " << el.PNum(1) << " " << el.PNum(2) - << " " << el.PNum(3) - << " " << el.PNum(4) << endl; + << " " << el.PNum(4) + << " " << el.PNum(3) << endl; } } else diff --git a/libsrc/interface/writetecplot.cpp b/libsrc/interface/writetecplot.cpp index 4730b983..2c130efb 100644 --- a/libsrc/interface/writetecplot.cpp +++ b/libsrc/interface/writetecplot.cpp @@ -27,8 +27,8 @@ void WriteTecPlotFormat (const Mesh & mesh, INDEX ne = mesh.GetNE(); INDEX nse = mesh.GetNSE(); - Array sn(np); - ofstream outfile(filename.c_str()); + NgArray sn(np); + ofstream outfile(filename); outfile << "TITLE=\" " << filename << "\"" << endl; diff --git a/libsrc/interface/writetet.cpp b/libsrc/interface/writetet.cpp index bcab41c2..8e952ae9 100644 --- a/libsrc/interface/writetet.cpp +++ b/libsrc/interface/writetet.cpp @@ -23,16 +23,16 @@ namespace netgen cout << "starting .tet export to file " << filename << endl; - Array point_ids,edge_ids,face_ids; - Array elnum(mesh.GetNE()); + NgArray point_ids,edge_ids,face_ids; + NgArray elnum(mesh.GetNE()); elnum = -1; - Array userdata_int; - Array userdata_double; - Array ports; + NgArray userdata_int; + NgArray userdata_double; + NgArray ports; - Array uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D; + NgArray uid_to_group_3D, uid_to_group_2D, uid_to_group_1D, uid_to_group_0D; int pos_int = 0; int pos_double = 0; @@ -98,9 +98,9 @@ namespace netgen INDEX_2_CLOSED_HASHTABLE edgenumbers(6*mesh.GetNE()+3*mesh.GetNSE());; INDEX_3_CLOSED_HASHTABLE facenumbers(4*mesh.GetNE()+mesh.GetNSE()); - Array edge2node; - Array face2edge; - Array element2face; + NgArray edge2node; + NgArray face2edge; + NgArray element2face; int numelems(0),numfaces(0),numedges(0),numnodes(0); @@ -272,7 +272,7 @@ namespace netgen - ofstream outfile(filename.c_str()); + ofstream outfile(filename); outfile.precision(16); @@ -285,7 +285,7 @@ namespace netgen int numObj0D,numObj1D,numObj2D,numObj3D; int numports = ports.Size(); - Array nodenum(point_ids.Size()+1); + NgArray nodenum(point_ids.Size()+1); nodenum = -1; @@ -367,18 +367,18 @@ namespace netgen uidpid = "UID"; - Array< Array* > idmaps; + NgArray< NgArray* > idmaps; for(int i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++) { if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC) { - idmaps.Append(new Array); + idmaps.Append(new NgArray); mesh.GetIdentifications().GetMap(i,*idmaps.Last(),true); } } - Array id_num,id_type; - Array< Array *> id_groups; + NgArray id_num,id_type; + NgArray< NgArray *> id_groups; // sst 2008-03-12: Write problem class... @@ -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"; @@ -449,7 +449,7 @@ namespace netgen if(nodenum[i] == -1) continue; - Array group; + NgArray group; group.Append(i); for(int j=0; j 1) { - id_groups.Append(new Array(group)); + id_groups.Append(new NgArray(group)); if(group.Size() == 2) { id_type[i] = 1; @@ -494,7 +494,8 @@ namespace netgen } - for(PointIndex i = mesh.Points().Begin(); i < mesh.Points().End(); i++) + // for(PointIndex i = mesh.Points().Begin(); i < mesh.Points().End(); i++) + for(PointIndex i : mesh.Points().Range()) { if(nodenum[i] == -1) continue; @@ -514,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* > vertex_to_edge(mesh.GetNP()+1); + NgArray< NgArray* > vertex_to_edge(mesh.GetNP()+1); for(int i=0; i<=mesh.GetNP(); i++) - vertex_to_edge[i] = new Array; + vertex_to_edge[i] = new NgArray; - Array< Array* > idmaps_edge(idmaps.Size()); + NgArray< NgArray* > idmaps_edge(idmaps.Size()); for(int i=0; i(numedges); + idmaps_edge[i] = new NgArray(numedges); (*idmaps_edge[i]) = 0; } - Array possible; + NgArray possible; for(int i=0; i group; + NgArray group; group.Append(i); for(int j=0; j 1) { id_num[i] = 1; - id_groups.Append(new Array(group)); + id_groups.Append(new NgArray(group)); if(group.Size() == 2) { id_type[i] = 1; @@ -701,7 +702,7 @@ namespace netgen continue; - Array group; + NgArray group; group.Append(i); for(int j=0; j 1) { id_num[i] = 1; - id_groups.Append(new Array(group)); + id_groups.Append(new NgArray(group)); if(group.Size() == 2) { id_type[i] = 1; @@ -759,7 +760,7 @@ namespace netgen << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"\ << n2 << "\n" \ << "\n"\ - << "// MasterEdgeID, SlaveEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n"\ + << "// MasterEdgeID, MinionEdgeID, TranslCode (1=dS1 2=dS2 3=dS1+dS2):\n"\ << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; for(int i=0; i* > edge_to_face(numedges+1); + NgArray< NgArray* > edge_to_face(numedges+1); for(int i=0; i; + edge_to_face[i] = new NgArray; for(int i=0; i group; + NgArray group; group.Append(i); for(int j=0; j 1) { id_num[i] = -1; - id_groups.Append(new Array(group)); + id_groups.Append(new NgArray(group)); if(group.Size() == 2) n2++; else @@ -920,7 +921,7 @@ namespace netgen << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"\ << n2 << "\n" \ << "\n"\ - << "// MasterFaceID, SlaveFaceID, TranslCode (1=dS1 2=dS2):\n"\ + << "// MasterFaceID, MinionFaceID, TranslCode (1=dS1 2=dS2):\n"\ << "// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; for(int i=0; i * > groups; + NgArray< NgArray * > groups; int maxg = -1; for(int i = 0; i; + groups[i] = new NgArray; for(ElementIndex i=0; i= 0) @@ -1063,7 +1064,8 @@ namespace netgen for(int i=0; iSetSize(0); - for(PointIndex i = mesh.Points().Begin(); i < mesh.Points().End(); i++) + // for(PointIndex i = mesh.Points().Begin(); i < mesh.Points().End(); i++) + for(PointIndex i : mesh.Points().Range()) { if(i-PointIndex::BASE < point_ids.Size()) { diff --git a/libsrc/interface/writetochnog.cpp b/libsrc/interface/writetochnog.cpp index c9ec6e3c..3068a385 100644 --- a/libsrc/interface/writetochnog.cpp +++ b/libsrc/interface/writetochnog.cpp @@ -20,11 +20,11 @@ namespace netgen void WriteTochnogFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { cout << "\nWrite Tochnog Volume Mesh" << endl; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile << "(Nodes and Elements generated with NETGEN" << endl; outfile << " " << filename << ")" << endl; diff --git a/libsrc/interface/writeuser.cpp b/libsrc/interface/writeuser.cpp index 7a6d9880..55c91e90 100644 --- a/libsrc/interface/writeuser.cpp +++ b/libsrc/interface/writeuser.cpp @@ -11,6 +11,7 @@ #include #include "writeuser.hpp" +#include "../general/gzstream.h" namespace netgen @@ -18,8 +19,8 @@ namespace netgen extern MeshingParameters mparam; - void RegisterUserFormats (Array & names, - Array & extensions) + void RegisterUserFormats (NgArray & names, + NgArray & extensions) { const char *types[] = @@ -44,6 +45,7 @@ namespace netgen "OpenFOAM 1.5+ Compressed", "*", "JCMwave Format", ".jcm", "TET Format", ".tet", + "CGNS Format", ".cgns", // { "Chemnitz Format" }, 0 }; @@ -57,9 +59,9 @@ namespace netgen -bool WriteUserFormat (const string & format, +bool WriteUserFormat (const filesystem::path & format, const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { // cout << "write user &hgeom = " << &hgeom << endl; // const CSGeometry & geom = *dynamic_cast (&hgeom); @@ -148,6 +150,9 @@ bool WriteUserFormat (const string & format, WriteTETFormat( mesh, filename);//, "High Frequency" ); #endif + else if (format == "CGNS Format") + WriteCGNSMesh( mesh, filename); + else { return 1; @@ -166,7 +171,7 @@ bool WriteUserFormat (const string & format, void WriteNeutralFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { cout << "write neutral, new" << endl; int np = mesh.GetNP(); @@ -178,7 +183,7 @@ void WriteNeutralFormat (const Mesh & mesh, int inverttets = mparam.inverttets; int invertsurf = mparam.inverttrigs; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); @@ -282,14 +287,14 @@ void WriteNeutralFormat (const Mesh & mesh, void WriteSurfaceFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { // surface mesh int i, j; cout << "Write Surface Mesh" << endl; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile << "surfacemesh" << endl; @@ -324,16 +329,17 @@ void WriteSurfaceFormat (const Mesh & mesh, */ void WriteSTLFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { cout << "\nWrite STL Surface Mesh" << endl; + auto ext = filename.extension(); ostream *outfile; - if(filename.substr(filename.length()-3,3) == ".gz") - outfile = new ogzstream(filename.c_str()); + if(ext == ".gz") + outfile = new ogzstream(filename); else - outfile = new ofstream(filename.c_str()); + outfile = new ofstream(filename); int i; @@ -381,22 +387,23 @@ void WriteSTLFormat (const Mesh & mesh, * when using a third-party mesher */ void WriteSTLExtFormat (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { cout << "\nWrite STL Surface Mesh (with separated boundary faces)" << endl; + auto ext = filename.extension(); ostream *outfile; - if(filename.substr(filename.length()-3,3) == ".gz") - outfile = new ogzstream(filename.c_str()); + if(ext == ".gz") + outfile = new ogzstream(filename); else - outfile = new ofstream(filename.c_str()); + outfile = new ofstream(filename); outfile->precision(10); int numBCs = 0; - Array faceBCs; + NgArray faceBCs; TABLE faceBCMapping; faceBCs.SetSize(mesh.GetNFD()); @@ -431,15 +438,15 @@ void WriteSTLExtFormat (const Mesh & mesh, for(int faceNr = 1;faceNr <= faceBCMapping.EntrySize(bcInd); faceNr++) { - Array faceSei; + Array faceSei; mesh.GetSurfaceElementsOfFace(faceBCMapping.Get(bcInd,faceNr),faceSei); for (int i = 0; i < faceSei.Size(); i++) { *outfile << "facet normal "; - const Point3d& p1 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(1)); - const Point3d& p2 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(2)); - const Point3d& p3 = mesh.Point(mesh.SurfaceElement(faceSei[i]).PNum(3)); + const Point3d& p1 = mesh.Point(mesh[faceSei[i]].PNum(1)); + const Point3d& p2 = mesh.Point(mesh[faceSei[i]].PNum(2)); + const Point3d& p3 = mesh.Point(mesh[faceSei[i]].PNum(3)); Vec3d normal = Cross(p2-p1,p3-p1); if (normal.Length() != 0) @@ -473,7 +480,7 @@ void WriteSTLExtFormat (const Mesh & mesh, void WriteVRMLFormat (const Mesh & mesh, bool faces, - const string & filename) + const filesystem::path & filename) { if (faces) @@ -486,7 +493,7 @@ void WriteVRMLFormat (const Mesh & mesh, int nse = mesh.GetNSE(); int i, j; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); @@ -562,7 +569,7 @@ void WriteVRMLFormat (const Mesh & mesh, int nse = mesh.GetNSE(); int i, j; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); @@ -638,10 +645,10 @@ void WriteVRMLFormat (const Mesh & mesh, */ void WriteFEPPFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { - ofstream outfile (filename.c_str()); + ofstream outfile (filename); if (mesh.GetDimension() == 3) @@ -769,7 +776,7 @@ void WriteFEPPFormat (const Mesh & mesh, void WriteEdgeElementFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename) + const filesystem::path & filename) { cout << "write edge element format" << endl; @@ -782,9 +789,9 @@ void WriteEdgeElementFormat (const Mesh & mesh, int inverttets = mparam.inverttets; int invertsurf = mparam.inverttrigs; - Array edges; + NgArray edges; - ofstream outfile (filename.c_str()); + ofstream outfile (filename); outfile.precision(6); outfile.setf (ios::fixed, ios::floatfield); @@ -907,8 +914,8 @@ void WriteEdgeElementFormat (const Mesh & mesh, void WriteFile (int typ, const Mesh & mesh, const CSGeometry & geom, - const char * filename, - const char * geomfile, + const filesystem::path & filename, + const filesystem::path & geomfile, double h) { @@ -939,10 +946,10 @@ void WriteFile (int typ, INDEX_2_HASHTABLE edgeht(mesh.GetNP()); // list of edges - Array edgelist; + NgArray edgelist; // edge (point) on boundary ? - BitArray bedge, bpoint(mesh.GetNP()); + NgBitArray bedge, bpoint(mesh.GetNP()); static int eledges[6][2] = { { 1, 2 } , { 1, 3 } , { 1, 4 }, { 2, 3 } , { 2, 4 } , { 3, 4 } }; diff --git a/libsrc/interface/writeuser.hpp b/libsrc/interface/writeuser.hpp index 704d7c8c..037a84ac 100644 --- a/libsrc/interface/writeuser.hpp +++ b/libsrc/interface/writeuser.hpp @@ -13,15 +13,15 @@ DLL_HEADER extern void WriteFile (int typ, const Mesh & mesh, const NetgenGeometry & geom, - const char * filename, - const char * geomfile = NULL, + const filesystem::path & filename, + const filesystem::path & geomfile = "", double h = 0); DLL_HEADER extern void ReadFile (Mesh & mesh, - const string & filename); + const filesystem::path & filename); @@ -31,15 +31,15 @@ void ReadFile (Mesh & mesh, extern void WriteNeutralFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteSurfaceFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteSTLFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); // Philippose - 16 August 2010 @@ -48,30 +48,30 @@ void WriteSTLFormat (const Mesh & mesh, // a separate "solid" entity in the STL file extern void WriteSTLExtFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteVRMLFormat (const Mesh & mesh, bool faces, - const string & filename); + const filesystem::path & filename); extern void WriteFEPPFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteGmshFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); // Philippose - 29/01/2009 // Added GMSH v2.xx Mesh Export support void WriteGmsh2Format (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteVtkFormat (const Mesh & mesh, @@ -81,90 +81,104 @@ void WriteVtkFormat (const Mesh & mesh, // Added OpenFOAM 1.5+ Mesh Export support extern void WriteOpenFOAM15xFormat (const Mesh & mesh, - const string & casename, + const filesystem::path & casename, const bool compressed); extern void WriteUserChemnitz (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteJCMFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteDiffPackFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteTochnogFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteTecPlotFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); extern void WriteAbaqusFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteFluentFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WritePermasFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteFEAPFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteElmerFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void WriteEdgeElementFormat (const Mesh & mesh, const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); #ifdef OLIVER extern void WriteTETFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); #endif extern void ReadTETFormat (Mesh & mesh, - const string & filename); + const filesystem::path & filename); extern void ReadFNFFormat (Mesh & mesh, - const string & filename); + const filesystem::path & filename); +extern void DLL_HEADER ReadCGNSMesh (Mesh & mesh, + const filesystem::path & filename); + +extern void DLL_HEADER WriteCGNSMesh (const Mesh & mesh, + const filesystem::path & filename); + +// read/write mesh and solutions from CGNS file +extern tuple, vector, vector>, vector> +DLL_HEADER ReadCGNSFile(const filesystem::path & filename, int base); + +extern void DLL_HEADER WriteCGNSFile(shared_ptr mesh, const filesystem::path & filename, vector fields, + vector> values, vector locations); + + void WriteDolfinFormat (const Mesh & mesh, - const string & filename); + const filesystem::path & filename); -extern void DLL_HEADER RegisterUserFormats (Array & names, - Array & extensions); +extern void DLL_HEADER RegisterUserFormats (NgArray & names, + NgArray & extensions); -extern bool DLL_HEADER WriteUserFormat (const string & format, +extern bool DLL_HEADER WriteUserFormat (const filesystem::path & format, const Mesh & mesh, // const NetgenGeometry & geom, - const string & filename); + const filesystem::path & filename); } diff --git a/libsrc/interface/wuchemnitz.cpp b/libsrc/interface/wuchemnitz.cpp index 46872fb0..6798993f 100644 --- a/libsrc/interface/wuchemnitz.cpp +++ b/libsrc/interface/wuchemnitz.cpp @@ -54,12 +54,12 @@ namespace netgen int p1, p2; }; - static Array points; - static Array volelements; - static Array surfelements; + static NgArray points; + static NgArray volelements; + static NgArray surfelements; - static Array faces; - static Array edges; + static NgArray faces; + static NgArray edges; void ReadFile (char * filename) @@ -306,9 +306,9 @@ namespace netgen void WriteUserChemnitz (const Mesh & mesh, - const string & filename) + const filesystem::path & filename) { - ofstream outfile (filename.c_str()); + ofstream outfile (filename); ReadFileMesh (mesh); Convert (); diff --git a/libsrc/linalg/CMakeLists.txt b/libsrc/linalg/CMakeLists.txt index a8ab1b5a..b757b2a9 100644 --- a/libsrc/linalg/CMakeLists.txt +++ b/libsrc/linalg/CMakeLists.txt @@ -1,7 +1,9 @@ -add_library( la INTERFACE ) -set(sdir ${CMAKE_CURRENT_SOURCE_DIR}) -target_sources( la INTERFACE - ${sdir}/densemat.cpp ${sdir}/polynomial.cpp ${sdir}/bfgs.cpp ${sdir}/linopt.cpp ${sdir}/linsearch.cpp +target_sources(nglib PRIVATE + bfgs.cpp + densemat.cpp + linopt.cpp + linsearch.cpp + polynomial.cpp ) install(FILES diff --git a/libsrc/linalg/bfgs.cpp b/libsrc/linalg/bfgs.cpp index 65189dee..49fcaa48 100644 --- a/libsrc/linalg/bfgs.cpp +++ b/libsrc/linalg/bfgs.cpp @@ -331,7 +331,7 @@ double BFGS ( { if (LDLtUpdate (l, d, 1 / a1, y) != 0) { - cerr << "BFGS update error1" << endl; + // cerr << "BFGS update error1" << endl; (*testout) << "BFGS update error1" << endl; (*testout) << "l " << endl << l << endl << "d " << d << endl; @@ -341,7 +341,7 @@ double BFGS ( if (LDLtUpdate (l, d, -1 / a2, bs) != 0) { - cerr << "BFGS update error2" << endl; + // cerr << "BFGS update error2" << endl; (*testout) << "BFGS update error2" << endl; (*testout) << "l " << endl << l << endl << "d " << d << endl; diff --git a/libsrc/linalg/densemat.cpp b/libsrc/linalg/densemat.cpp index f8d58c2a..f2cc2fa5 100644 --- a/libsrc/linalg/densemat.cpp +++ b/libsrc/linalg/densemat.cpp @@ -427,7 +427,7 @@ namespace netgen double max, hr; - Array p(n); // pivot-permutation + NgArray p(n); // pivot-permutation Vector hv(n); @@ -1154,7 +1154,7 @@ namespace netgen } - void DenseMatrix :: MultElementMatrix (const Array & pnum, + void DenseMatrix :: MultElementMatrix (const NgArray & pnum, const Vector & hx, Vector & hy) { int i, j; @@ -1180,7 +1180,7 @@ namespace netgen } - void DenseMatrix :: MultTransElementMatrix (const Array & pnum, + void DenseMatrix :: MultTransElementMatrix (const NgArray & pnum, const Vector & hx, Vector & hy) { int i, j; @@ -1380,6 +1380,6 @@ namespace netgen return ost; } - + } diff --git a/libsrc/linalg/densemat.hpp b/libsrc/linalg/densemat.hpp index 9b007202..5b3bb6a5 100644 --- a/libsrc/linalg/densemat.hpp +++ b/libsrc/linalg/densemat.hpp @@ -171,8 +171,14 @@ public: { height = h; data = adata; ownmem = false; } /// MatrixFixWidth (const MatrixFixWidth & m2) - : height(m2.height), data(m2.data), ownmem(false) - { ; } + : height(m2.height), ownmem(true) + { + data = new T[height*WIDTH]; + for (int i = 0; i < WIDTH*height; i++) + data[i] = m2.data[i]; + } + // : height(m2.height), data(m2.data), ownmem(false) + //{ ; } /// ~MatrixFixWidth () { if (ownmem) delete [] data; } @@ -277,6 +283,15 @@ public: /// MatrixFixWidth (int h) { height = h; data = new double[WIDTH*height]; ownmem = true; } + + MatrixFixWidth (const MatrixFixWidth & m2) + : height(m2.height), ownmem(true) + { + data = new double[height*WIDTH]; + for (int i = 0; i < WIDTH*height; i++) + data[i] = m2.data[i]; + } + /// MatrixFixWidth (int h, double * adata) { height = h; data = adata; ownmem = false; } diff --git a/libsrc/meshing/CMakeLists.txt b/libsrc/meshing/CMakeLists.txt index 12fe70ae..7fff8d26 100644 --- a/libsrc/meshing/CMakeLists.txt +++ b/libsrc/meshing/CMakeLists.txt @@ -1,30 +1,21 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(mesh ${NG_LIB_TYPE} +target_sources(nglib PRIVATE adfront2.cpp adfront3.cpp bisect.cpp boundarylayer.cpp clusters.cpp curvedelems.cpp delaunay.cpp delaunay2d.cpp geomsearch.cpp global.cpp hprefinement.cpp improve2.cpp improve2gen.cpp improve3.cpp localh.cpp meshclass.cpp meshfunc.cpp meshfunc2d.cpp meshing2.cpp meshing3.cpp meshtool.cpp meshtype.cpp msghandler.cpp netrule2.cpp - netrule3.cpp parser2.cpp parser3.cpp prism2rls.cpp - pyramid2rls.cpp pyramidrls.cpp quadrls.cpp refine.cpp + netrule3.cpp parser2.cpp parser3.cpp refine.cpp ruler2.cpp ruler3.cpp secondorder.cpp smoothing2.5.cpp - 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 - ../../ng/onetcl.cpp - ${mesh_object_libs} - ) + smoothing2.cpp smoothing3.cpp specials.cpp + topology.cpp validate.cpp bcfunctions.cpp + parallelmesh.cpp paralleltop.cpp basegeom.cpp + python_mesh.cpp surfacegeom.cpp + debugging.cpp fieldlines.cpp visual_interface.cpp + boundarylayer2d.cpp +) -if(APPLE) - set_target_properties( mesh PROPERTIES SUFFIX ".so") -endif(APPLE) - -target_link_libraries( mesh PUBLIC ngcore PRIVATE gprim la gen ) - -target_link_libraries( mesh PUBLIC ${ZLIB_LIBRARIES} ${MPI_CXX_LIBRARIES} ${PYTHON_LIBRARIES} ${METIS_LIBRARY}) -install( TARGETS mesh ${NG_INSTALL_DIR}) +target_link_libraries( nglib PRIVATE netgen_metis "$" ${ZLIB_LIBRARIES} ) install(FILES adfront2.hpp adfront3.hpp basegeom.hpp bcfunctions.hpp bisect.hpp @@ -35,5 +26,7 @@ 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 surfacegeom.hpp delaunay2d.hpp + fieldlines.hpp soldata.hpp visual_interface.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/meshing COMPONENT netgen_devel ) diff --git a/libsrc/meshing/adfront2.cpp b/libsrc/meshing/adfront2.cpp index 11dd7421..ab27334b 100644 --- a/libsrc/meshing/adfront2.cpp +++ b/libsrc/meshing/adfront2.cpp @@ -39,7 +39,8 @@ namespace netgen allflines = 0; minval = 0; - starti = lines.Begin(); + // starti = lines.Begin(); + starti = *lines.Range().begin(); } AdFront2 :: ~AdFront2 () @@ -53,7 +54,8 @@ namespace netgen if (nfl > 0) { ost << nfl << " open front segments left:" << endl; - for (int i = lines.Begin(); i < lines.End(); i++) + // for (int i = lines.Begin(); i < lines.End(); i++) + for (int i : lines.Range()) if (lines[i].Valid()) ost << i << ": " << GetGlobalIndex (lines[i].L().I1()) << "-" @@ -62,7 +64,7 @@ namespace netgen } /* - void AdFront2 :: GetPoints (Array > & apoints) const + void AdFront2 :: GetPoints (NgArray > & apoints) const { apoints.Append (points); // for (int i = 0; i < points.Size(); i++) @@ -219,8 +221,9 @@ namespace netgen int & qualclass) { int baselineindex = -1; - - for (int i = starti; i < lines.End(); i++) + + // for (int i = starti; i < lines.End(); i++) + for (int i = starti; i < *lines.Range().end(); i++) { if (lines[i].Valid()) { @@ -240,7 +243,8 @@ namespace netgen if (baselineindex == -1) { minval = INT_MAX; - for (int i = lines.Begin(); i < lines.End(); i++) + // for (int i = lines.Begin(); i < lines.End(); i++) + for (int i : lines.Range()) if (lines[i].Valid()) { int hi = lines[i].LineClass() + @@ -270,17 +274,15 @@ namespace netgen int AdFront2 :: GetLocals (int baselineindex, - Array & locpoints, - Array & pgeominfo, - Array & loclines, // local index - Array & pindex, - Array & lindex, + NgArray> & locpoints, + NgArray & pgeominfo, + NgArray & loclines, // local index + NgArray & pindex, + NgArray & lindex, double xh) { - static int timer = NgProfiler::CreateTimer ("adfront2::GetLocals"); - NgProfiler::RegionTimer reg (timer); - - + static Timer timer("adfront2::GetLocals"); RegionTimer reg (timer); + int pstind; Point<3> midp, p0; @@ -291,20 +293,21 @@ namespace netgen lindex.Append(baselineindex); ArrayMem nearlines(0); - ArrayMem nearpoints(0); + NgArrayMem nearpoints(0); // dominating costs !! linesearchtree.GetIntersecting (p0 - Vec3d(xh, xh, xh), p0 + Vec3d(xh, xh, xh), nearlines); - pointsearchtree.GetIntersecting (p0 - Vec3d(xh, xh, xh), + // only special points that are not in adfront, + // other points are from linesearchtree + cpointsearchtree.GetIntersecting(p0 - Vec3d(xh, xh, xh), p0 + Vec3d(xh, xh, xh), nearpoints); - - for (int ii = 0; ii < nearlines.Size(); ii++) + + for(auto i : nearlines) { - int i = nearlines[ii]; if (lines[i].Valid() && i != baselineindex) { loclines.Append(lines[i].L()); @@ -312,41 +315,40 @@ namespace netgen } } - // static Array invpindex; + // static NgArray invpindex; invpindex.SetSize (points.Size()); // invpindex = -1; - for (int i = 0; i < nearpoints.Size(); i++) - invpindex[nearpoints[i]] = -1; + for(auto pi : nearpoints) + invpindex[pi] = -1; - for (int i = 0; i < loclines.Size(); i++) + for(const auto& li : loclines) { - invpindex[loclines[i].I1()] = 0; - invpindex[loclines[i].I2()] = 0; + invpindex[li.I1()] = 0; + invpindex[li.I2()] = 0; } - for (int i = 0; i < loclines.Size(); i++) + for(auto& line : loclines) { - for (int j = 0; j < 2; j++) - { - int pi = loclines[i][j]; + for(auto i : Range(2)) + { + auto& pi = line[i]; if (invpindex[pi] == 0) { pindex.Append (pi); invpindex[pi] = pindex.Size(); locpoints.Append (points[pi].P()); - loclines[i][j] = locpoints.Size(); + pi = locpoints.Size(); } else - loclines[i][j] = invpindex[pi]; + pi = invpindex[pi]; } } // double xh2 = xh*xh; - for (int ii = 0; ii < nearpoints.Size(); ii++) + for(auto i : nearpoints) { - int i = nearpoints[ii]; if (points[i].Valid() && points[i].OnSurface() && // Dist2 (points.Get(i).P(), p0) <= xh2 && @@ -420,7 +422,7 @@ namespace netgen if (loclines.Size() == 1) { - cout << "loclines.Size = 1" << endl; + cout << IM(5) << "loclines.Size = 1" << endl; (*testout) << "loclines.size = 1" << endl << " h = " << xh << endl << " nearline.size = " << nearlines.Size() << endl @@ -434,7 +436,8 @@ namespace netgen void AdFront2 :: SetStartFront () { - for (int i = lines.Begin(); i < lines.End(); i++) + // for (int i = lines.Begin(); i < lines.End(); i++) + for (int i : lines.Range()) if (lines[i].Valid()) for (int j = 1; j <= 2; j++) points[lines[i].L().I(j)].DecFrontNr(0); @@ -444,12 +447,14 @@ namespace netgen void AdFront2 :: Print (ostream & ost) const { ost << points.Size() << " Points: " << endl; - for (int i = points.Begin(); i < points.End(); i++) + // for (int i = points.Begin(); i < points.End(); i++) + for (int i : points.Range()) if (points[i].Valid()) ost << i << " " << points[i].P() << endl; ost << nfl << " Lines: " << endl; - for (int i = lines.Begin(); i < lines.End(); i++) + // for (int i = lines.Begin(); i < lines.End(); i++) + for (int i : lines.Range()) if (lines[i].Valid()) ost << lines[i].L().I1() << " - " << lines[i].L().I2() << endl; @@ -498,7 +503,7 @@ namespace netgen } bool AdFront2 :: SameSide (const Point<2> & lp1, const Point<2> & lp2, - const Array * testfaces) const + const FlatArray * testfaces) const { int cnt = 0; diff --git a/libsrc/meshing/adfront2.hpp b/libsrc/meshing/adfront2.hpp index 11d9593b..95914535 100644 --- a/libsrc/meshing/adfront2.hpp +++ b/libsrc/meshing/adfront2.hpp @@ -165,21 +165,21 @@ class AdFront2 { /// - Array points; /// front points - Array lines; /// front lines + NgArray points; /// front points + NgArray lines; /// front lines Box3d boundingbox; BoxTree<3> linesearchtree; /// search tree for lines Point3dTree pointsearchtree; /// search tree for points Point3dTree cpointsearchtree; /// search tree for cone points (not used ???) - Array delpointl; /// list of deleted front points - Array dellinel; /// list of deleted front lines + NgArray delpointl; /// list of deleted front points + NgArray dellinel; /// list of deleted front lines int nfl; /// number of front lines; INDEX_2_HASHTABLE * allflines; /// all front lines ever have been - Array invpindex; + NgArray invpindex; int minval; int starti; @@ -192,7 +192,7 @@ public: ~AdFront2 (); /// - // void GetPoints (Array > & apoints) const; + // void GetPoints (NgArray > & apoints) const; /// void Print (ostream & ost) const; @@ -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, @@ -216,11 +216,11 @@ public: /// int GetLocals (int baseline, - Array & locpoints, - Array & pgeominfo, - Array & loclines, // local index - Array & pindex, - Array & lindex, + NgArray> & locpoints, + NgArray & pgeominfo, + NgArray & loclines, // local index + NgArray & pindex, + NgArray & lindex, double xh); /// @@ -262,7 +262,7 @@ public: bool Inside (const Point<2> & p) const; bool SameSide (const Point<2> & lp1, const Point<2> & lp2, - const Array * /* testfaces */ = NULL) const; + const FlatArray * /* testfaces */ = NULL) const; /* { return Inside (lp1) == Inside (lp2); diff --git a/libsrc/meshing/adfront3.cpp b/libsrc/meshing/adfront3.cpp index d809eb18..588f1d23 100644 --- a/libsrc/meshing/adfront3.cpp +++ b/libsrc/meshing/adfront3.cpp @@ -84,11 +84,15 @@ AdFront3 :: ~AdFront3 () delete connectedpairs; } -void AdFront3 :: GetPoints (Array > & apoints) const +void AdFront3 :: GetPoints (NgArray > & apoints) const { + /* for (PointIndex pi = points.Begin(); pi < points.End(); pi++) apoints.Append (points[pi].P()); + */ + for (auto & p : points) + apoints.Append(p.P()); } @@ -105,7 +109,8 @@ PointIndex AdFront3 :: AddPoint (const Point<3> & p, PointIndex globind) else { points.Append (FrontPoint3 (p, globind)); - return --points.End(); + // return --points.End(); + return *points.Range().end()-1; // return points.Size()-1+PointIndex::BASE; } } @@ -267,7 +272,7 @@ void AdFront3 :: CreateTrees () void AdFront3 :: GetIntersectingFaces (const Point<3> & pmin, const Point<3> & pmax, - Array & ifaces) const + NgArray & ifaces) const { facetree -> GetIntersecting (pmin, pmax, ifaces); } @@ -302,7 +307,8 @@ void AdFront3 :: RebuildInternalTables () int np = points.Size(); - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + for (PointIndex pi : points.Range()) points[pi].cluster = pi; NgProfiler::StopTimer (timer_a); @@ -343,21 +349,21 @@ void AdFront3 :: RebuildInternalTables () - BitArrayChar usecl(np); - usecl.Clear(); + Array usecl(np); + usecl = false; for (int i = 1; i <= faces.Size(); i++) { - usecl.Set (points[faces.Get(i).Face().PNum(1)].cluster); + usecl[points[faces.Get(i).Face().PNum(1)].cluster] = true; faces.Elem(i).cluster = points[faces.Get(i).Face().PNum(1)].cluster; } int cntcl = 0; for (int i = PointIndex::BASE; i < np+PointIndex::BASE; i++) - if (usecl.Test(i)) + if (usecl[i]) cntcl++; - Array clvol (np); + NgArray clvol (np); clvol = 0.0; for (int i = 1; i <= faces.Size(); i++) @@ -395,12 +401,13 @@ void AdFront3 :: RebuildInternalTables () if (clvol[i] < 0) negvol = 1; } - + if (negvol) { for (int i = 1; i <= faces.Size(); i++) faces.Elem(i).cluster = 1; - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + for (PointIndex pi : points.Range()) points[pi].cluster = 1; } @@ -485,10 +492,10 @@ int AdFront3 :: SelectBaseElement () int AdFront3 :: GetLocals (int fstind, - Array & locpoints, - Array & locfaces, // local index - Array & pindex, - Array & findex, + NgArray & locpoints, + NgArray & locfaces, // local index + NgArray & pindex, + NgArray & findex, INDEX_2_HASHTABLE & getconnectedpairs, float xh, float relh, @@ -509,11 +516,11 @@ int AdFront3 :: GetLocals (int fstind, PointIndex pstind; Point3d midp, p0; - // static Array invpindex; + // static NgArray invpindex; - Array locfaces2; //all local faces in radius xh - Array locfaces3; // all faces in outer radius relh - Array findex2; + NgArray locfaces2; //all local faces in radius xh + NgArray locfaces3; // all faces in outer radius relh + NgArray findex2; locfaces2.SetSize(0); locfaces3.SetSize(0); @@ -590,7 +597,7 @@ int AdFront3 :: GetLocals (int fstind, for (j = 1; j <= locfaces.Get(i).GetNP(); j++) { PointIndex pi = locfaces.Get(i).PNum(j); - invpindex[pi] = -1; + invpindex[pi] = PointIndex::INVALID; } for (i = 1; i <= locfaces.Size(); i++) @@ -598,7 +605,7 @@ int AdFront3 :: GetLocals (int fstind, for (j = 1; j <= locfaces.Get(i).GetNP(); j++) { PointIndex pi = locfaces.Get(i).PNum(j); - if (invpindex[pi] == -1) + if (!invpindex[pi].IsValid()) { pindex.Append (pi); locpoints.Append (points[pi].P()); @@ -657,19 +664,19 @@ int AdFront3 :: GetLocals (int fstind, // returns all points connected with fi void AdFront3 :: GetGroup (int fi, - Array & grouppoints, - Array & groupelements, - Array & pindex, - Array & findex) + NgArray & grouppoints, + NgArray & groupelements, + NgArray & pindex, + NgArray & findex) { - // static Array pingroup; + // static NgArray pingroup; int changed; pingroup.SetSize(points.Size()); pingroup = 0; for (int j = 1; j <= 3; j++) - pingroup.Elem (faces.Get(fi).Face().PNum(j)) = 1; + pingroup[faces.Get(fi).Face().PNum(j)] = 1; do { @@ -702,14 +709,14 @@ void AdFront3 :: GetGroup (int fi, int fused = 0; for (int j = 1; j <= 3; j++) - if (pingroup.Elem(face.PNum(j))) + if (pingroup[face.PNum(j)]) fused++; if (fused >= 2) for (int j = 1; j <= 3; j++) - if (!pingroup.Elem(face.PNum(j))) + if (!pingroup[face.PNum(j)]) { - pingroup.Elem(face.PNum(j)) = 1; + pingroup[face.PNum(j)] = 1; changed = 1; } } @@ -733,7 +740,7 @@ void AdFront3 :: GetGroup (int fi, { int fused = 0; for (int j = 1; j <= 3; j++) - if (pingroup.Get(faces.Get(i).Face().PNum(j))) + if (pingroup[faces.Get(i).Face().PNum(j)]) fused++; if (fused >= 2) @@ -753,7 +760,7 @@ void AdFront3 :: GetGroup (int fi, */ for (auto & e : groupelements) for (int j = 1; j <= 3; j++) - e.PNum(j) = invpindex.Get(e.PNum(j)); + e.PNum(j) = invpindex[e.PNum(j)]; } @@ -780,6 +787,7 @@ void AdFront3 :: SetStartFront (int /* baseelnp */) bool AdFront3 :: Inside (const Point<3> & p) const { + static Timer timer("AdFront3::Inside"); RegionTimer rt(timer); int cnt; Vec3d n, v1, v2; DenseMatrix a(3), ainv(3); @@ -833,7 +841,7 @@ bool AdFront3 :: Inside (const Point<3> & p) const int AdFront3 :: SameSide (const Point<3> & lp1, const Point<3> & lp2, - const Array * testfaces) const + const NgArray * testfaces) const { const Point<3> *line[2]; line[0] = &lp1; @@ -845,7 +853,7 @@ int AdFront3 :: SameSide (const Point<3> & lp1, const Point<3> & lp2, pmin.SetToMin (lp2); pmax.SetToMax (lp2); - ArrayMem aprif; + NgArrayMem aprif; aprif.SetSize(0); if (!testfaces) diff --git a/libsrc/meshing/adfront3.hpp b/libsrc/meshing/adfront3.hpp index 9b7f3818..5960ca5c 100644 --- a/libsrc/meshing/adfront3.hpp +++ b/libsrc/meshing/adfront3.hpp @@ -89,7 +89,7 @@ public: const PointIndex PNum (int i) const { return pnum[i-1]; } PointIndex & PNum (int i) { return pnum[i-1]; } const PointIndex PNumMod (int i) const { return pnum[(i-1)%np]; } - auto PNums() const { return FlatArray (np, &pnum[0]); } + auto PNums() const { return NgFlatArray (np, &pnum[0]); } void Delete () { deleted = true; for (PointIndex & p : pnum) p.Invalidate(); } bool IsDeleted () const { return deleted; } }; @@ -176,11 +176,11 @@ public: class AdFront3 { /// - Array points; + NgArray points; /// - Array faces; + NgArray faces; /// - Array delpointl; + NgArray delpointl; /// which points are connected to pi ? TABLE * connectedpairs; @@ -208,8 +208,8 @@ class AdFront3 int lasti; /// minimal selection-value of baseelements int minval; - Array invpindex; - Array pingroup; + NgArray invpindex; + NgArray pingroup; /// class BoxTree<3> * facetree; @@ -220,7 +220,7 @@ public: /// ~AdFront3 (); /// - void GetPoints (Array > & apoints) const; + void GetPoints (NgArray > & apoints) const; /// int GetNP() const { return points.Size(); } @@ -254,17 +254,17 @@ public: /// void GetIntersectingFaces (const Point<3> & pmin, const Point<3> & pmax, - Array & ifaces) const; + NgArray & ifaces) const; /// void GetFaceBoundingBox (int i, Box3d & box) const; /// int GetLocals (int baseelement, - Array & locpoints, - Array & locfaces, // local index - Array & pindex, - Array & findex, + NgArray & locpoints, + NgArray & locfaces, // local index + NgArray & pindex, + NgArray & findex, INDEX_2_HASHTABLE & connectedpairs, float xh, float relh, @@ -272,10 +272,10 @@ public: /// void GetGroup (int fi, - Array & grouppoints, - Array & groupelements, - Array & pindex, - Array & findex); + NgArray & grouppoints, + NgArray & groupelements, + NgArray & pindex, + NgArray & findex); /// void DeleteFace (INDEX fi); @@ -300,7 +300,7 @@ public: bool Inside (const Point<3> & p) const; /// both points on same side ? int SameSide (const Point<3> & lp1, const Point<3> & lp2, - const Array * testfaces = NULL) const; + const NgArray * testfaces = NULL) const; /// diff --git a/libsrc/meshing/basegeom.cpp b/libsrc/meshing/basegeom.cpp index e9b9aa5e..f6818eba 100644 --- a/libsrc/meshing/basegeom.cpp +++ b/libsrc/meshing/basegeom.cpp @@ -1,21 +1,1222 @@ +#include + #include #include "meshing.hpp" +#include namespace netgen { + struct PointTree + { + BoxTree<3> tree; - DLL_HEADER GeometryRegisterArray geometryregister; - //DLL_HEADER Array geometryregister; + PointTree( Box<3> bb ) : tree(bb) {} + + void Insert(Point<3> p, PointIndex n) + { + tree.Insert(p, p, n); + } + + PointIndex Find(Point<3> p) const + { + ArrayMem points; + tree.GetIntersecting(p, p, points); + if(points.Size()==0) + throw Exception("cannot find mapped point"); + return points[0]; + } + + double GetTolerance() { return tree.GetTolerance(); } + }; + + DLL_HEADER GeometryRegisterArray geometryregister; + //DLL_HEADER NgArray geometryregister; GeometryRegister :: ~GeometryRegister() { ; } + bool GeometryShape :: IsMappedShape( const GeometryShape & other_, const Transformation<3> & trafo, double tol ) const + { + throw Exception("GeometryShape::IsMappedShape not implemented for class " + Demangle(typeid(this).name())); + } + + bool GeometryVertex :: IsMappedShape( const GeometryShape & other_, const Transformation<3> & trafo, double tol ) const + { + const auto other_ptr = dynamic_cast(&other_); + if(!other_ptr) + return false; + + return Dist(trafo(GetPoint()), other_ptr->GetPoint()) < tol; + } + + bool GeometryEdge :: IsMappedShape( const GeometryShape & other_, const Transformation<3> & trafo, double tol ) const + { + const auto other_ptr = dynamic_cast(&other_); + if(!other_ptr) + return false; + auto & e = *other_ptr; + if (IsDegenerated(tol) || e.IsDegenerated(tol)) + return false; + + if(tol < Dist(trafo(GetCenter()), e.GetCenter())) + return false; + + auto v0 = trafo(GetStartVertex().GetPoint()); + auto v1 = trafo(GetEndVertex().GetPoint()); + auto w0 = e.GetStartVertex().GetPoint(); + auto w1 = e.GetEndVertex().GetPoint(); + + // have two closed edges, use midpoints to compare + if(Dist(v0,v1) < tol && Dist(w0,w1) < tol) + { + v1 = trafo(GetPoint(0.5)); + w1 = other_ptr->GetPoint(0.5); + } + + return( (Dist(v0, w0) < tol && Dist(v1, w1) < tol) || + (Dist(v0, w1) < tol && Dist(v1, w0) < tol) ); + } + + bool GeometryFace :: IsMappedShape( const GeometryShape & other_, const Transformation<3> & trafo, double tol ) const + { + const auto other_ptr = dynamic_cast(&other_); + if(!other_ptr) + return false; + auto & f = *other_ptr; + + if(tol < Dist(GetCenter(), f.GetCenter())) + return false; + + // simple check: check if there is a bijective mapping of mapped edges + auto & other_edges = f.edges; + if(edges.Size() != other_edges.Size()) + return false; + + auto nedges = edges.Size(); + Array is_mapped(nedges); + is_mapped = false; + + for(auto e : edges) + { + int found_mapping = 0; + for(auto other_e : other_edges) + if(e->IsMappedShape(*other_e, trafo, tol)) + found_mapping++; + if(found_mapping != 1) + return false; + } + + return true; + } + + bool GeometryFace :: IsConnectingCloseSurfaces() const + { + std::map verts; + for(const auto& edge : edges) + { + verts[&edge->GetStartVertex()] = false; + verts[&edge->GetEndVertex()] = false; + } + for(const auto& [v, is_mapped] : verts) + { + if(is_mapped) + continue; + for(const auto& v_ident : v->identifications) + { + const auto& other = v_ident.to == v ? v_ident.from : v_ident.to; + if(v_ident.type == Identifications::CLOSESURFACES && + verts.count(other)) + { + verts[v] = true; + verts[other] = true; + } + } + } + for(auto& [v, is_mapped] : verts) + if(!is_mapped) + return false; + return true; + } + + void GeometryFace :: RestrictHTrig(Mesh& mesh, + const PointGeomInfo& gi0, + const PointGeomInfo& gi1, + const PointGeomInfo& gi2, + const MeshingParameters& mparam, + int depth, double h) const + { + auto p0 = GetPoint(gi0); + auto p1 = GetPoint(gi1); + auto p2 = GetPoint(gi2); + auto longest = (p0-p1).Length(); + int cutedge = 2; + if(auto len = (p0-p2).Length(); len > longest) + { + longest = len; + cutedge = 1; + } + if(auto len = (p1-p2).Length(); len > longest) + { + longest = len; + cutedge = 0; + } + PointGeomInfo gi_mid; + gi_mid.u = (gi0.u + gi1.u + gi2.u)/3; + gi_mid.v = (gi0.v + gi1.v + gi2.v)/3; + + if(depth % 3 == 0) + { + double curvature = 0.; + curvature = max({curvature, GetCurvature(gi_mid), + GetCurvature(gi0), GetCurvature(gi1), + GetCurvature(gi2)}); + if(curvature < 1e-3) + return; + double kappa = curvature * mparam.curvaturesafety; + h = mparam.maxh * kappa < 1 ? mparam.maxh : 1./kappa; + if(h < 1e-4 * longest) + return; + } + + if(h < longest && depth < 10) + { + if(cutedge == 0) + { + PointGeomInfo gi_m; + gi_m.u = 0.5 * (gi1.u + gi2.u); + gi_m.v = 0.5 * (gi1.v + gi2.v); + RestrictHTrig(mesh, gi_m, gi2, gi0, mparam, depth+1, h); + RestrictHTrig(mesh, gi_m, gi0, gi1, mparam, depth+1, h); + } + else if(cutedge == 1) + { + PointGeomInfo gi_m; + gi_m.u = 0.5 * (gi0.u + gi2.u); + gi_m.v = 0.5 * (gi0.v + gi2.v); + RestrictHTrig(mesh, gi_m, gi1, gi2, mparam, depth+1, h); + RestrictHTrig(mesh, gi_m, gi0, gi1, mparam, depth+1, h); + } + else if(cutedge == 2) + { + PointGeomInfo gi_m; + gi_m.u = 0.5 * (gi0.u + gi1.u); + gi_m.v = 0.5 * (gi0.v + gi1.v); + RestrictHTrig(mesh, gi_m, gi1, gi2, mparam, depth+1, h); + RestrictHTrig(mesh, gi_m, gi2, gi0, mparam, depth+1, h); + } + } + else + { + auto pmid = GetPoint(gi_mid); + for(const auto& p : {p0, p1, p2, pmid}) + mesh.RestrictLocalH(p, h); + } + } + + struct Line + { + Point<3> p0, p1; + inline double Length() const { return (p1-p0).Length(); } + inline double Dist(const Line& other) const + { + Vec<3> n = p1-p0; + Vec<3> q = other.p1-other.p0; + double nq = n*q; + Point<3> p = p0 + 0.5*n; + double lambda = (p-other.p0)*n / (nq + 1e-10); + if (lambda >= 0 && lambda <= 1) + return (p-other.p0-lambda*q).Length(); + return 1e99; + } + }; + + void NetgenGeometry :: Clear() + { + vertices.SetSize0(); + edges.SetSize0(); + faces.SetSize0(); + solids.SetSize0(); + } + + void NetgenGeometry :: ProcessIdentifications() + { + for(auto i : Range(vertices)) + vertices[i]->nr = i; + for(auto i : Range(edges)) + edges[i]->nr = i; + for(auto i : Range(faces)) + faces[i]->nr = i; + for(auto i : Range(solids)) + solids[i]->nr = i; + + auto mirror_identifications = [&] ( auto & shapes ) + { + for(auto i : Range(shapes)) + { + auto &s = shapes[i]; + s->nr = i; + for(auto & ident : s->identifications) + if(s.get() == ident.from) + ident.to->identifications.Append(ident); + } + }; + + auto tol = 1e-8 * bounding_box.Diam(); + for(auto & f : faces) + for(auto & ident: f->identifications) + for(auto e : static_cast(ident.from)->edges) + for(auto e_other : static_cast(ident.to)->edges) + if(e->IsMappedShape(*e_other, ident.trafo, tol)) + e->identifications.Append( {e, e_other, ident.trafo, ident.type, ident.name} ); + + for(auto & e : edges) + for(auto & ident: e->identifications) + { + auto & from = static_cast(*ident.from); + auto & to = static_cast(*ident.to); + + GeometryVertex * pfrom[] = { &from.GetStartVertex(), &from.GetEndVertex() }; + GeometryVertex * pto[] = { &to.GetStartVertex(), &to.GetEndVertex() }; + + // swap points of other edge if necessary + Point<3> p_from0 = ident.trafo(from.GetStartVertex().GetPoint()); + Point<3> p_from1 = ident.trafo(from.GetEndVertex().GetPoint()); + Point<3> p_to0 = to.GetStartVertex().GetPoint(); + + if(Dist(p_from1, p_to0) < Dist(p_from0, p_to0)) + swap(pto[0], pto[1]); + + for(auto i : Range(2)) + pfrom[i]->identifications.Append( {pfrom[i], pto[i], ident.trafo, ident.type, ident.name} ); + } + + mirror_identifications(vertices); + mirror_identifications(edges); + mirror_identifications(faces); + + + auto find_primary = [&] (auto & shapes) + { + for(auto &s : shapes) + { + s->primary = s.get(); + s->primary_to_me = Transformation<3>{ Vec<3> {0,0,0} }; // init with identity + } + + bool changed = true; + + while(changed) { + changed = false; + for(auto &s : shapes) + { + for(auto & ident : s->identifications) + { + bool need_inverse = ident.from == s.get(); + auto other = need_inverse ? ident.to : ident.from; + if(other->nr < s->primary->nr) + { + auto trafo = ident.trafo; + if(need_inverse) + trafo = trafo.CalcInverse(); + s->primary = other; + s->primary_to_me.Combine(trafo, s->primary_to_me); + changed = true; + } + if(other->primary->nr < s->primary->nr) + { + auto trafo = ident.trafo; + if(need_inverse) + trafo = trafo.CalcInverse(); + s->primary = other->primary; + s->primary_to_me.Combine(trafo, other->primary_to_me); + changed = true; + } + } + } + } + }; + + find_primary(vertices); + find_primary(edges); + find_primary(faces); + } + + void NetgenGeometry :: Analyse(Mesh& mesh, + const MeshingParameters& mparam) const + { + static Timer t1("SetLocalMeshsize"); RegionTimer regt(t1); + mesh.SetGlobalH(mparam.maxh); + mesh.SetMinimalH(mparam.minh); + + mesh.SetLocalH(bounding_box.PMin(), bounding_box.PMax(), + mparam.grading); + + // only set meshsize for edges longer than this + double mincurvelength = 1e-3 * bounding_box.Diam(); + + if(mparam.uselocalh) + { + double eps = 1e-10 * bounding_box.Diam(); + const char* savetask = multithread.task; + multithread.task = "Analyse Edges"; + + // restrict meshsize on edges + for(auto i : Range(edges)) + { + multithread.percent = 100. * i/edges.Size(); + const auto & edge = edges[i]; + auto length = edge->GetLength(); + // skip very short edges + if(length < mincurvelength) + continue; + static constexpr int npts = 20; + // restrict mesh size based on edge length + for(auto i : Range(npts+1)) + mesh.RestrictLocalH(edge->GetPoint(double(i)/npts), length/mparam.segmentsperedge); + + // restrict mesh size based on edge curvature + double t = 0.; + auto p_old = edge->GetPoint(t); + while(t < 1.-eps) + { + t += edge->CalcStep(t, 1./mparam.curvaturesafety); + if(t < 1.) + { + auto p = edge->GetPoint(t); + auto dist = (p-p_old).Length(); + mesh.RestrictLocalH(p, dist); + p_old = p; + } + } + } + + multithread.task = "Analyse Faces"; + // restrict meshsize on faces + for(auto i : Range(faces)) + { + multithread.percent = 100. * i/faces.Size(); + const auto& face = faces[i]; + face->RestrictH(mesh, mparam); + } + + if(mparam.closeedgefac.has_value()) + { + multithread.task = "Analyse close edges"; + constexpr int sections = 100; + Array lines; + lines.SetAllocSize(sections*edges.Size()); + BoxTree<3> searchtree(bounding_box.PMin(), + bounding_box.PMax()); + for(const auto& edge : edges) + { + if(edge->GetLength() < eps) + continue; + double t = 0.; + auto p_old = edge->GetPoint(t); + auto t_old = edge->GetTangent(t); + t_old.Normalize(); + for(auto i : IntRange(1, sections+1)) + { + t = double(i)/sections; + auto p_new = edge->GetPoint(t); + auto t_new = edge->GetTangent(t); + t_new.Normalize(); + auto cosalpha = fabs(t_old * t_new); + if((i == sections) || (cosalpha < cos(10./180 * M_PI))) + { + auto index = lines.Append({p_old, p_new}); + searchtree.Insert(p_old, p_new, index); + p_old = p_new; + t_old = t_new; + } + } + } + Array linenums; + for(auto i : Range(lines)) + { + const auto& line = lines[i]; + if(line.Length() < eps) continue; + multithread.percent = 100.*i/lines.Size(); + Box<3> box; + box.Set(line.p0); + box.Add(line.p1); + // box.Increase(max2(mesh.GetH(line.p0), mesh.GetH(line.p1))); + box.Increase(line.Length()); + double mindist = 1e99; + linenums.SetSize0(); + searchtree.GetIntersecting(box.PMin(), box.PMax(), + linenums); + for(auto num : linenums) + { + if(i == num) continue; + const auto & other = lines[num]; + if((line.p0 - other.p0).Length2() < eps || + (line.p0 - other.p1).Length2() < eps || + (line.p1 - other.p0).Length2() < eps || + (line.p1 - other.p1).Length2() < eps) + continue; + mindist = min2(mindist, line.Dist(other)); + } + if(mindist == 1e99) continue; + mindist /= *mparam.closeedgefac + 1e-10; + if(mindist < 1e-3 * bounding_box.Diam()) + { + (*testout) << "extremely small local h: " << mindist + << " --> setting to " << 1e-3 * bounding_box.Diam() << endl; + (*testout) << "somewhere near " << line.p0 << " - " << line.p1 << endl +; + mindist = 1e-3 * bounding_box.Diam(); + } + mesh.RestrictLocalHLine(line.p0, line.p1, mindist); + } + } + multithread.task = savetask; + } + + for(const auto& mspnt : mparam.meshsize_points) + mesh.RestrictLocalH(mspnt.pnt, mspnt.h); + + mesh.LoadLocalMeshSize(mparam.meshsizefilename); + } + + void GeometryEdge :: Divide(const MeshingParameters & mparam, const Mesh & mesh, Array> & points, Array & params) + { + static Timer tdivedgesections("Divide edge sections"); + static Timer tdivide("Divide Edges"); + RegionTimer rt(tdivide); + // -------------------- DivideEdge ----------------- + tdivedgesections.Start(); + auto layer = properties.layer; + double safety = 0.5*(1.-mparam.grading); + + double lam = 0.0; + Point<3> p = GetPoint(0.0); + auto old_p = p; + Array hvalue, fine_params; + hvalue.Append(.0); + + while (lam<1. && hvalue.Size() < 20000) { + fine_params.Append(lam); + auto h = mesh.GetH(old_p, layer); + auto step = safety * h/GetTangent(lam).Length(); + lam += step; + lam = min2(lam, 1.0); + p = GetPoint(lam); + hvalue.Append((hvalue.Size()==0 ? 0.0 : hvalue.Last()) + 1./h * (p-old_p).Length()); + old_p = p; + } + + fine_params.Append(1.0); + + if(hvalue.Size()==20000 && lam<1.0) + cout << "Warning: Could not divide Edge" << endl; + + tdivedgesections.Stop(); + + auto n = hvalue.Size()-1; + int nsubedges = max2(1, int(floor(hvalue.Last()+0.5))); + points.SetSize(nsubedges-1); + params.SetSize(nsubedges+1); + + int i1 = 0; + for(auto i : Range(1,nsubedges)) + { + auto h_target = i*hvalue.Last()/nsubedges; + while(hvalue[i1] vert2meshpt; + for(auto & vert : vertices) + { + auto pi = mesh.AddPoint(vert->GetPoint(), vert->properties.layer); + tree.Insert(mesh[pi], pi); + vert2meshpt[vert->GetHash()] = pi; + mesh[pi].Singularity(vert->properties.hpref); + mesh[pi].SetType(FIXEDPOINT); + + Element0d el(pi, pi); + el.name = vert->properties.GetName(); + mesh.SetCD3Name(pi, el.name); + mesh.pointelements.Append (el); + } + + for(auto & vert : vertices) + for(auto & ident : vert->identifications) + identifications.Add(vert2meshpt[ident.from->GetHash()], + vert2meshpt[ident.to->GetHash()], + ident.name, + ident.type); + + size_t segnr = 0; + auto nedges = edges.Size(); + Array> all_pnums(nedges); + Array> all_params(nedges); + + for (auto edgenr : Range(edges)) + { + auto edge = edges[edgenr].get(); + PointIndex startp, endp; + // throws if points are not found + startp = vert2meshpt.at(edge->GetStartVertex().GetHash()); + endp = vert2meshpt.at(edge->GetEndVertex().GetHash()); + + // ignore collapsed edges + if(startp == endp && edge->GetLength() < 1e-10 * bounding_box.Diam()) + continue; + + // ----------- Add Points to mesh and create segments ----- + auto & pnums = all_pnums[edgenr]; + auto & params = all_params[edgenr]; + Array> edge_points; + Array edge_params; + + if(edge->primary == edge) + { + // check if start and end vertex are identified (if so, we only insert one segment and do z-refinement later) + bool is_identified_edge = false; + auto v0 = vertices[edge->GetStartVertex().nr].get(); + auto v1 = vertices[edge->GetEndVertex().nr].get(); + for(auto & ident : v0->identifications) + { + auto other = ident.from == v0 ? ident.to : ident.from; + if(other->nr == v1->nr && ident.type == Identifications::CLOSESURFACES) + { + is_identified_edge = true; + break; + } + } + + if(is_identified_edge) + { + params.SetSize(2); + params[0] = 0.; + params[1] = 1.; + } + else + { + edge->Divide(mparam, mesh, edge_points, params); + } + } + else + { + auto nr_primary = edge->primary->nr; + auto & pnums_primary = all_pnums[nr_primary]; + auto & params_primary = all_params[nr_primary]; + auto trafo = edge->primary_to_me; + + auto np = pnums_primary.Size(); + edge_points.SetSize(np-2); + edge_params.SetSize(np-2); + for(auto i : Range(np-2)) + { + edge_points[i] = trafo(mesh[pnums_primary[i+1]]); + EdgePointGeomInfo gi; + edge->ProjectPoint(edge_points[i], &gi); + edge_params[i] = gi.dist; + } + + params.SetSize(edge_params.Size()+2); + + for(auto i : Range(edge_params)) + params[i+1] = edge_params[i]; + + if(edge_params.Size()>1) + { + // Just projecting (code below) does not work for closed edges (startp == endp) + // In this case, there are at least 2 inner points which we use to check edge orientation + bool reversed = edge_params[1] < edge_params[0]; + if(reversed) + { + params[0] = 1.0; + params.Last() = 0.0; + } + else + { + params.Last() = 1.0; + params[0] = 0.0; + } + } + else + { + for(size_t i : std::vector{0UL, pnums_primary.Size()-1}) + { + auto p_mapped = trafo(mesh[pnums_primary[i]]); + EdgePointGeomInfo gi; + edge->ProjectPoint(p_mapped, &gi); + params[i] = gi.dist; + } + } + } + + pnums.SetSize(edge_points.Size() + 2); + + bool is_reversed = params.Last() < params[0]; + pnums[0] = is_reversed ? endp : startp; + pnums.Last() = is_reversed ? startp : endp; + + + for(auto i : Range(edge_points)) + { + auto pi = mesh.AddPoint(edge_points[i], edge->properties.layer); + tree.Insert(mesh[pi], pi); + pnums[i+1] = pi; + } + + for(auto i : Range(pnums.Size()-1)) + { + segnr++; + Segment seg; + seg[0] = pnums[i]; + seg[1] = pnums[i+1]; + seg.edgenr = edgenr+1; + seg.si = edgenr+1; + seg.epgeominfo[0].dist = params[i]; + seg.epgeominfo[1].dist = params[i+1]; + seg.epgeominfo[0].edgenr = edgenr; + seg.epgeominfo[1].edgenr = edgenr; + seg.singedge_left = edge->properties.hpref; + seg.singedge_right = edge->properties.hpref; + seg.domin = edge->domin+1; + seg.domout = edge->domout+1; + mesh.AddSegment(seg); + } + mesh.SetCD2Name(edgenr+1, edge->properties.GetName()); + } + + for (auto & edge : edges) + { + // identify points on edge + for(auto & ident : edge->identifications) + if(ident.from == edge.get()) + { + auto & pnums = all_pnums[edge->nr]; + // start and end vertex are already identified + for(auto pi : pnums.Range(1, pnums.Size()-1)) + { + auto pi_other = tree.Find(ident.trafo(mesh[pi])); + identifications.Add(pi, pi_other, ident.name, ident.type); + } + } + } + mesh.CalcSurfacesOfNode(); + multithread.task = savetask; + } + + bool NetgenGeometry :: MeshFace(Mesh& mesh, const MeshingParameters& mparam, + int k, FlatArray glob2loc) const + { + multithread.percent = 100. * k/faces.Size(); + const auto& face = *faces[k]; + auto bb = face.GetBoundingBox(); + bb.Increase(bb.Diam()/10); + Meshing2 meshing(*this, mparam, bb); + glob2loc = 0; + int cntp = 0; + + auto segments = face.GetBoundary(mesh); + for(auto& seg : segments) + { + for(auto j : Range(2)) + { + auto pi = seg[j]; + if(glob2loc[pi] == 0) + { + meshing.AddPoint(mesh[pi], pi); + cntp++; + glob2loc[pi] = cntp; + } + } + } + for(const auto& vert : GetFaceVertices(face)) + { + PointIndex pi = vert->nr + 1; + if(glob2loc[pi] == 0) + { + meshing.AddPoint(mesh[pi], pi); + cntp++; + glob2loc[pi] = cntp; + } + } + for(auto & seg : segments) + { + PointGeomInfo gi0, gi1; + gi0.trignum = gi1.trignum = k+1; + gi0.u = seg.epgeominfo[0].u; + gi0.v = seg.epgeominfo[0].v; + gi1.u = seg.epgeominfo[1].u; + gi1.v = seg.epgeominfo[1].v; + meshing.AddBoundaryElement(glob2loc[seg[0]], + glob2loc[seg[1]], + gi0, gi1); + } + + // TODO Set max area 2* area of face + + auto noldsurfels = mesh.GetNSE(); + + + static Timer t("GenerateMesh"); RegionTimer reg(t); + MESHING2_RESULT res = meshing.GenerateMesh(mesh, mparam, mparam.maxh, k+1, face.properties.layer); + + for(auto i : Range(noldsurfels, mesh.GetNSE())) + { + mesh.SurfaceElements()[i].SetIndex(k+1); + } + return res != MESHING2_OK; + } + + void NetgenGeometry :: MeshSurface(Mesh& mesh, + const MeshingParameters& mparam) const + { + static Timer t1("Surface Meshing"); RegionTimer regt(t1); + const char* savetask = multithread.task; + multithread.task = "Mesh Surface"; + mesh.ClearFaceDescriptors(); + + size_t n_failed_faces = 0; + Array glob2loc(mesh.GetNP()); + for(auto k : Range(faces)) + { + auto & face = *faces[k]; + FaceDescriptor fd(k+1, face.domin+1, face.domout+1, k+1); + if(face.properties.col) + fd.SetSurfColour(*face.properties.col); + mesh.AddFaceDescriptor(fd); + mesh.SetBCName(k, face.properties.GetName()); + if(face.primary == &face) + { + // check if this face connects two identified closesurfaces + auto & idents = mesh.GetIdentifications(); + std::set relevant_edges; + auto segments = face.GetBoundary(mesh); + for(const auto &s : segments) + relevant_edges.insert(s.edgenr-1); + + Array is_point_in_tree(mesh.Points().Size()); + is_point_in_tree = false; + PointTree tree( bounding_box ); + for(const auto &s : segments) + for(auto pi : s.PNums()) + if(!is_point_in_tree[pi]) + { + tree.Insert(mesh[pi], pi); + is_point_in_tree[pi] = true; + } + + Array mapped_edges(edges.Size()); + constexpr int UNINITIALIZED = -2; + constexpr int NOT_MAPPED = -1; + mapped_edges = UNINITIALIZED; + + Transformation<3> trafo; + + if(face.IsConnectingCloseSurfaces()) + { + Array, PointIndex> p2seg(mesh.Points().Size()); + for(int si : Range(segments)) + { + const auto & s = segments[si]; + p2seg[s[0]].Append(si); + p2seg[s[1]].Append(si); + } + for(const auto & s : segments) + { + auto edgenr = s.edgenr-1; + auto & edge = *edges[edgenr]; + ShapeIdentification *edge_mapping; + + // have edgenr first time, search for closesurface identification + + if(mapped_edges[edgenr] == UNINITIALIZED) + { + mapped_edges[edgenr] = NOT_MAPPED; + for(auto & edge_ident : edge.identifications) + { + if(edge_ident.type == Identifications::CLOSESURFACES && + edge_ident.from->nr == edgenr && + relevant_edges.count(edge_ident.to->nr) > 0 + ) + { + trafo = edge_ident.trafo; + mapped_edges[edgenr] = edge_ident.to->nr; + break; + } + } + } + + // this edge has a closesurface mapping to another -> make connecting quad + if(mapped_edges[edgenr] != NOT_MAPPED) + { + Element2d sel(4); + sel[0] = s[0]; + sel[1] = s[1]; + sel[2] = tree.Find(trafo(mesh[s[1]])); + sel[3] = tree.Find(trafo(mesh[s[0]])); + auto gis = sel.GeomInfo(); + for(auto i : Range(2)) + { + gis[i].u = s.epgeominfo[i].u; + gis[i].v = s.epgeominfo[i].v; + } + + // find mapped segment to set PointGeomInfo correctly + Segment s_other; + for(auto si_other : p2seg[sel[2]]) + { + s_other = segments[si_other]; + if(s_other[0] == sel[2] && s_other[1] == sel[3]) + break; + if(s_other[0] == sel[3] && s_other[1] == sel[2]) + break; + } + for(auto i : Range(2)) + { + auto i_other = sel[i+2] == s_other[i] ? i : 1-i; + gis[i+2].u = s_other.epgeominfo[i_other].u; + gis[i+2].v = s_other.epgeominfo[i_other].v; + } + + sel.SetIndex(face.nr+1); + mesh.AddSurfaceElement(sel); + } + } + } + else + if(MeshFace(mesh, mparam, k, glob2loc)) + n_failed_faces++; + } + } + + if(n_failed_faces) + { + cout << "WARNING! NOT ALL FACES HAVE BEEN MESHED" << endl; + cout << "SURFACE MESHING ERROR OCCURRED IN " << n_failed_faces << " FACES:" << endl; + return; + } + + if (mparam.perfstepsend >= MESHCONST_OPTSURFACE) + { + mesh.CalcSurfacesOfNode(); + OptimizeSurface(mesh, mparam); + } + + bool have_identifications = false; + for(auto & face : faces) + if(face->primary != face.get()) + { + have_identifications = true; + MapSurfaceMesh(mesh, *face); + } + + // identify points on faces + if(have_identifications) + { + mesh.CalcSurfacesOfNode(); + BitArray is_identified_face(faces.Size()); + is_identified_face = false; + for(auto & face : faces) + for(auto & ident : face->identifications) + { + is_identified_face.SetBit(ident.from->nr); + is_identified_face.SetBit(ident.to->nr); + } + + PointTree tree( bounding_box ); + Array pi_to_face(mesh.GetNP()); + pi_to_face = -1; + Array si_of_face; + Array> pi_of_face(faces.Size()); + for(auto & face : faces) + if(is_identified_face[face->nr]) + { + mesh.GetSurfaceElementsOfFace(face->nr+1, si_of_face); + for(auto si : si_of_face) + for(auto pi : mesh[si].PNums()) + { + if(mesh[pi].Type() == SURFACEPOINT && pi_to_face[pi]==-1) + { + pi_to_face[pi] = face->nr; + tree.Insert(mesh[pi], pi); + pi_of_face[face->nr].Append(pi); + } + } + } + + auto & mesh_ident = mesh.GetIdentifications(); + for(auto & face : faces) + for(auto & ident : face->identifications) + { + if(ident.from == face.get()) + for(auto pi : pi_of_face[face->nr]) + { + auto pi_other = tree.Find(ident.trafo(mesh[pi])); + mesh_ident.Add(pi, pi_other, ident.name, ident.type); + } + } + } + + mesh.CalcSurfacesOfNode(); + multithread.task = savetask; + } + + void NetgenGeometry :: MapSurfaceMesh( Mesh & mesh, const GeometryFace & dst ) const + { + static Timer timer("MapSurfaceMesh"); + RegionTimer rt(timer); + + const auto & src = dynamic_cast(*dst.primary); + auto trafo = dst.primary_to_me; + + PrintMessage(2, "Map face ", src.nr+1, " -> ", dst.nr+1); + + // point map from src to dst + auto np = mesh.Points().Size(); + Array pmap(np); + pmap = PointIndex::INVALID; + BitArray is_double_edge_point(np); + is_double_edge_point.Clear(); + + // first map points on edges (mapped points already in mesh, use search tree) + Array is_point_in_tree(mesh.Points().Size()); + is_point_in_tree = false; + PointTree tree( bounding_box ); + for (Segment & seg : src.GetBoundary(mesh)) + for(auto i : Range(2)) + { + auto pi = seg[i]; + if(!is_point_in_tree[pi]) + { + tree.Insert(trafo(mesh[pi]), pi); + is_point_in_tree[pi] = true; + } + } + + Array, 2>, PointIndex> uv_values(np); + for (Segment & seg : dst.GetBoundary(mesh)) + { + for(auto i : Range(2)) + { + auto pi = seg[i]; + if(!pmap[pi].IsValid()) + pmap[tree.Find(mesh[pi])] = pi; + + // store uv values (might be different values for same point in case of internal edges) + double u = seg.epgeominfo[i].u; + double v = seg.epgeominfo[i].v; + auto & vals = uv_values[pi]; + bool found = false; + for(const auto & [u1,v1] : vals) + if((u-u1)*(u-u1)+(v-v1)*(v-v1) < 1e-7) + found = true; + if(!found) + vals.Append({u,v}); + } + } + + xbool do_invert = maybe; + if(dst.identifications[0].type == Identifications::PERIODIC) + { + auto other = static_cast(dst.primary); + if(dst.domin != other->domout && dst.domout != other->domin) + do_invert = true; + } + + // now insert mapped surface elements + for(auto sei : mesh.SurfaceElements().Range()) + { + auto sel = mesh[sei]; + if(sel.GetIndex() != src.nr+1) + continue; + + if(do_invert.IsMaybe()) + { + auto n_src = src.GetNormal(mesh[sel[0]]); + auto n_dist = dst.GetNormal(trafo(mesh[sel[0]])); + Mat<3> normal_matrix; + CalcInverse(Trans(trafo.GetMatrix()), normal_matrix); + do_invert = n_src * (normal_matrix * n_dist) < 0.0; + } + auto sel_new = sel; + sel_new.SetIndex(dst.nr+1); + for(auto i : Range(sel.PNums())) + { + auto pi = sel[i]; + if(!pmap[pi].IsValid()) + { + pmap[pi] = mesh.AddPoint(trafo(mesh[pi]), 1, SURFACEPOINT); + } + sel_new[i] = pmap[pi]; + } + if(do_invert.IsTrue()) + sel_new.Invert(); + + for(auto i : Range(sel.PNums())) + { + auto pi = sel_new[i]; + if(uv_values.Range().Next() <= pi) + { + // new point (inner surface point) + PointGeomInfo gi; + dst.CalcPointGeomInfo(mesh[sel_new[i]], gi); + sel_new.GeomInfo()[i] = gi; + continue; + } + + const auto & uvs = uv_values[pi]; + if(uvs.Size() == 1) + { + // appears only once -> take uv values from edgepointgeominfo + const auto & [u,v] = uvs[0]; + PointGeomInfo gi; + gi.u = u; + gi.v = v; + sel_new.GeomInfo()[i] = gi; + } + else if(uvs.Size() > 1) + { + // multiple uv pairs -> project to close point and select closest uv pair + double eps = 1e-3; + auto p = Point<3>((1.0-eps)*Vec<3>(mesh[sel_new.PNumMod(i+1)]) + + eps/2*Vec<3>(mesh[sel_new.PNumMod(i+2)]) + + eps/2*Vec<3>(mesh[sel_new.PNumMod(i+3)])); + PointGeomInfo gi_p, gi; + dst.CalcPointGeomInfo(p, gi_p); + gi.trignum = gi_p.trignum; + double min_dist = numeric_limits::max(); + for(const auto & [u,v] : uvs) + { + double dist = (gi_p.u-u)*(gi_p.u-u) + (gi_p.v-v)*(gi_p.v-v); + if(dist < min_dist) + { + min_dist = dist; + gi.u = u; + gi.v = v; + } + } + sel_new.GeomInfo()[i] = gi; + } + else + throw Exception(string(__FILE__) + ":"+ToString(__LINE__) + " shouldn't come here"); + } + mesh.AddSurfaceElement(sel_new); + } + } + + void NetgenGeometry :: OptimizeSurface(Mesh& mesh, const MeshingParameters& mparam) const + { + const auto savetask = multithread.task; + multithread.task = "Optimizing surface"; + + static Timer timer_opt2d("Optimization 2D"); + RegionTimer reg(timer_opt2d); + auto meshopt = MeshOptimize2d(mesh); + for(auto i : Range(mparam.optsteps2d)) + for(auto k : Range(mesh.GetNFD())) + { + PrintMessage(3, "Optimization step ", i); + meshopt.SetFaceIndex(k+1); + int innerstep = 0; + for(auto optstep : mparam.optimize2d) + { + multithread.percent = 100. * (double(innerstep++)/mparam.optimize2d.size() + i)/mparam.optsteps2d; + switch(optstep) + { + case 's': + meshopt.EdgeSwapping(0); + break; + case 'S': + meshopt.EdgeSwapping(1); + break; + case 'm': + meshopt.ImproveMesh(mparam); + break; + case 'c': + meshopt.CombineImprove(); + break; + } + } + } + mesh.CalcSurfacesOfNode(); + mesh.Compress(); + multithread.task = savetask; + } + + void NetgenGeometry :: FinalizeMesh(Mesh& mesh) const + { + if(solids.Size()) + for (int i = 0; i < mesh.GetNDomains(); i++) + if (auto name = solids[i]->properties.name) + mesh.SetMaterial (i+1, *name); + + mesh.OrderElements(); + } shared_ptr GeometryRegisterArray :: LoadFromMeshFile (istream & ist) const { + if (!ist.good()) + return nullptr; + + string token; + ist >> token; + if(token == "TextOutArchive") + { + NetgenGeometry *geo = nullptr; + size_t string_length; + ist >> string_length; + string buffer(string_length+1, '\0'); + ist.read(&buffer[0], string_length); + auto ss = make_shared(buffer); + TextInArchive in(ss); + in & geo; + + return shared_ptr(geo); + } for (int i = 0; i < Size(); i++) { - NetgenGeometry * hgeom = (*this)[i]->LoadFromMeshFile (ist); + NetgenGeometry * hgeom = (*this)[i]->LoadFromMeshFile (ist, token); if (hgeom) return shared_ptr(hgeom); } @@ -25,28 +1226,63 @@ namespace netgen - int NetgenGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) + int NetgenGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mp) { - if (!mesh) return 1; + multithread.percent = 0; - if (mparam.perfstepsstart <= MESHCONST_MESHVOLUME) + // copy so that we don't change them outside + MeshingParameters mparam = mp; + if(restricted_h.Size()) + for(const auto& [pnt, maxh] : restricted_h) + mparam.meshsize_points.Append({pnt, maxh}); + + if(mparam.perfstepsstart <= MESHCONST_ANALYSE) { - multithread.task = "Volume meshing"; - - MESHING3_RESULT res = - MeshVolume (mparam, *mesh); - - if (res != MESHING3_OK) return 1; - - if (multithread.terminate) return 0; - - RemoveIllegalElements (*mesh); - if (multithread.terminate) return 0; - - MeshQuality3d (*mesh); + if(!mesh) + mesh = make_shared(); + mesh->geomtype = GetGeomType(); + Analyse(*mesh, mparam); + } + + if(multithread.terminate || mparam.perfstepsend <= MESHCONST_ANALYSE) + return 0; + + if(mparam.perfstepsstart <= MESHCONST_MESHEDGES) + FindEdges(*mesh, mparam); + + if(multithread.terminate || mparam.perfstepsend <= MESHCONST_MESHEDGES) + return 0; + + if (mparam.perfstepsstart <= MESHCONST_MESHSURFACE) + { + MeshSurface(*mesh, mparam); + } + + if (multithread.terminate || mparam.perfstepsend <= MESHCONST_OPTSURFACE) + return 0; + + if(dimension == 2) + { + FinalizeMesh(*mesh); + mesh->SetDimension(2); + return 0; + } + + if(mparam.perfstepsstart <= MESHCONST_MESHVOLUME) + { + multithread.task = "Volume meshing"; + + MESHING3_RESULT res = MeshVolume (mparam, *mesh); + + if (res != MESHING3_OK) return 1; + if (multithread.terminate) return 0; + + RemoveIllegalElements (*mesh); + if (multithread.terminate) return 0; + + MeshQuality3d (*mesh); } - if (multithread.terminate || mparam.perfstepsend <= MESHCONST_MESHVOLUME) return 0; @@ -54,22 +1290,15 @@ namespace netgen if (mparam.perfstepsstart <= MESHCONST_OPTVOLUME) { multithread.task = "Volume optimization"; - + OptimizeVolume (mparam, *mesh); if (multithread.terminate) return 0; } - + FinalizeMesh(*mesh); return 0; - } - - - const Refinement & NetgenGeometry :: GetRefinement () const - { - return *new Refinement;; } - - void NetgenGeometry :: Save (string filename) const + void NetgenGeometry :: Save (const filesystem::path & filename) const { throw NgException("Cannot save geometry - no geometry available"); } diff --git a/libsrc/meshing/basegeom.hpp b/libsrc/meshing/basegeom.hpp index ceb703f2..bb9a508c 100644 --- a/libsrc/meshing/basegeom.hpp +++ b/libsrc/meshing/basegeom.hpp @@ -7,25 +7,319 @@ /* Date: 23. Aug. 09 */ /**************************************************************************/ - struct Tcl_Interp; namespace netgen { + struct ShapeProperties + { + optional name; + optional> col; + double maxh = 1e99; + double hpref = 0; // number of hp refinement levels (will be multiplied by factor later) + int layer = 1; + optional quad_dominated; + void Merge(const ShapeProperties & prop2) + { + if (!name && prop2.name) name = prop2.name; + if (!col && prop2.col) col = prop2.col; + maxh = min2(maxh, prop2.maxh); + hpref = max2(hpref, prop2.hpref); + if(!quad_dominated.has_value()) quad_dominated = prop2.quad_dominated; + layer = max(layer, prop2.layer); + } + + string GetName() const { return name ? *name : "default"; } + Vec<4> GetColor() { return col ? *col : Vec<4>{0., 1., 0., 1.}; } + + void DoArchive(Archive& ar) + { + ar & name & col & maxh & hpref & layer; + } + }; + + class GeometryShape; + + struct ShapeIdentification + { + GeometryShape * from; + GeometryShape * to; + Transformation<3> trafo; + Identifications::ID_TYPE type; + string name = ""; + }; + + class DLL_HEADER GeometryShape + { + public: + int nr = -1; + int layer = 1; + ShapeProperties properties; + Array identifications; + GeometryShape * primary; + Transformation<3> primary_to_me; + + virtual ~GeometryShape() {} + virtual size_t GetHash() const = 0; + virtual bool IsMappedShape( const GeometryShape & other, const Transformation<3> & trafo, double tolerance ) const; + }; + + + class DLL_HEADER GeometryVertex : public GeometryShape + { + public: + virtual Point<3> GetPoint() const = 0; + virtual bool IsMappedShape( const GeometryShape & other, const Transformation<3> & trafo, double tolerance ) const override; + }; + + class DLL_HEADER GeometryEdge : public GeometryShape + { + protected: + GeometryVertex *start, *end; + public: + int domin=-1, domout=-1; + + GeometryEdge( GeometryVertex &start_, GeometryVertex &end_ ) + : start(&start_), end(&end_) + {} + + virtual const GeometryVertex& GetStartVertex() const { return *start; } + virtual const GeometryVertex& GetEndVertex() const { return *end; } + virtual GeometryVertex& GetStartVertex() { return *start; } + virtual GeometryVertex& GetEndVertex() { return *end; } + virtual double GetLength() const = 0; + virtual Point<3> GetCenter() const = 0; + virtual Point<3> GetPoint(double t) const = 0; + // Calculate parameter step respecting edges sag value + virtual double CalcStep(double t, double sag) const = 0; + virtual bool IsDegenerated(double tol = 1e-10) const { + return GetLength() < tol; + } + virtual void ProjectPoint(Point<3>& p, EdgePointGeomInfo* gi) const = 0; + virtual void PointBetween(const Point<3>& p1, + const Point<3>& p2, + double secpoint, + const EdgePointGeomInfo& gi1, + const EdgePointGeomInfo& gi2, + Point<3>& newp, + EdgePointGeomInfo& newgi) const + { + newp = p1 + secpoint * (p2-p1); + newgi = gi1; + ProjectPoint(newp, &newgi); + } + virtual Vec<3> GetTangent(double t) const = 0; + virtual bool IsMappedShape( const GeometryShape & other, const Transformation<3> & trafo, double tolerance ) const override; + virtual void Divide(const MeshingParameters & mparam, const Mesh & mesh, Array> & points, Array & params); + }; + + class DLL_HEADER GeometryFace : public GeometryShape + { + public: + Array edges; + int domin=-1, domout=-1; + + virtual Point<3> GetCenter() const = 0; + virtual size_t GetNBoundaries() const = 0; + virtual Array GetBoundary(const Mesh& mesh) const = 0; + + virtual PointGeomInfo Project(Point<3>& p) const = 0; + // Project point using geo info. Fast if point is close to + // parametrization in geo info. + virtual bool ProjectPointGI(Point<3>& p, PointGeomInfo& gi) const =0; + virtual bool CalcPointGeomInfo(const Point<3>& p, PointGeomInfo& gi) const + { + auto pnew = p; + gi = Project(pnew); + return (p-pnew).Length() < 1e-10 * GetBoundingBox().Diam() ; + } + virtual Point<3> GetPoint(const PointGeomInfo& gi) const = 0; + virtual void CalcEdgePointGI(const GeometryEdge& edge, + double t, + EdgePointGeomInfo& egi) const = 0; + virtual Box<3> GetBoundingBox() const = 0; + + // Get curvature in point from local coordinates in PointGeomInfo + virtual double GetCurvature(const PointGeomInfo& gi) const = 0; + + virtual void RestrictH(Mesh& mesh, const MeshingParameters& mparam) const = 0; + virtual Vec<3> GetNormal(const Point<3>& p, const PointGeomInfo* gi = nullptr) const = 0; + + virtual void PointBetween(const Point<3>& p1, + const Point<3>& p2, + double secpoint, + const PointGeomInfo& gi1, + const PointGeomInfo& gi2, + Point<3>& newp, + PointGeomInfo& newgi) const + { + newp = p1 + secpoint * (p2-p1); + newgi.trignum = gi1.trignum; + /* + newgi.u = 0.5 * (gi1.u + gi1.u); + newgi.v = 0.5 * (gi1.v + gi2.v); + */ + newgi.u = gi1.u + secpoint*(gi2.u - gi1.u); + newgi.v = gi1.v + secpoint*(gi2.v - gi1.v); + if(!ProjectPointGI(newp, newgi)) + newgi = Project(newp); + } + + virtual bool IsMappedShape( const GeometryShape & other, const Transformation<3> & trafo, double tolerance ) const override; + virtual bool IsConnectingCloseSurfaces() const; + + protected: + void RestrictHTrig(Mesh& mesh, + const PointGeomInfo& gi0, + const PointGeomInfo& gi1, + const PointGeomInfo& gi2, + const MeshingParameters& mparam, + int depth = 0, double h = 0.) const; + }; + + class DLL_HEADER GeometrySolid : public GeometryShape + { }; class DLL_HEADER NetgenGeometry { + unique_ptr ref; + protected: + Array> vertices; + Array> edges; + Array> faces; + Array> solids; + Array, double>> restricted_h; + Box<3> bounding_box; + int dimension = 3; + public: + + NetgenGeometry() + { + ref = make_unique(*this); + } virtual ~NetgenGeometry () { ; } + size_t GetNVertices() const { return vertices.Size(); } + size_t GetNEdges() const { return edges.Size(); } + size_t GetNFaces() const { return faces.Size(); } + + const GeometryFace & GetFace(int i) const { return *faces[i]; } + const GeometryEdge & GetEdge(int i) const { return *edges[i]; } + const GeometryVertex & GetVertex(int i) const { return *vertices[i]; } + + virtual Array GetFaceVertices(const GeometryFace& face) const { return Array{}; } + + void Clear(); + virtual int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam); - virtual const Refinement & GetRefinement () const; + void RestrictH(const Point<3>& pnt, double maxh) + { + restricted_h.Append({pnt, maxh}); + } - virtual void DoArchive(Archive&) + virtual const Refinement & GetRefinement () const + { + return *ref; + } + + virtual void DoArchive(Archive&) { throw NgException("DoArchive not implemented for " + Demangle(typeid(*this).name())); } - virtual void Save (string filename) const; + virtual Mesh::GEOM_TYPE GetGeomType() const { return Mesh::NO_GEOM; } + virtual void ProcessIdentifications(); + virtual void Analyse(Mesh& mesh, + const MeshingParameters& mparam) const; + virtual void FindEdges(Mesh& mesh, const MeshingParameters& mparam) const; + virtual void MeshSurface(Mesh& mesh, const MeshingParameters& mparam) const; + virtual bool MeshFace(Mesh& mesh, const MeshingParameters& mparam, + int nr, FlatArray glob2loc) const; + virtual void MapSurfaceMesh( Mesh & mesh, const GeometryFace & dst ) const; + virtual void OptimizeSurface(Mesh& mesh, const MeshingParameters& mparam) const; + + virtual void FinalizeMesh(Mesh& mesh) const; + + virtual PointGeomInfo ProjectPoint (int surfind, Point<3> & p) const + { + if(surfind <= faces.Size() && surfind > 0) + return faces[surfind-1]->Project(p); + return PointGeomInfo(); + } + + virtual void ProjectPointEdge (int surfind, int surfind2, Point<3> & p, EdgePointGeomInfo* gi = nullptr) const + { + if(gi && gi->edgenr < edges.Size()) + edges[gi->edgenr]->ProjectPoint(p, gi); + } + + virtual bool CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const + { + return faces[surfind-1]->CalcPointGeomInfo(p3, gi); + } + virtual bool ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const + { + if(surfind > 0 && surfind <= faces.Size()) + return faces[surfind-1]->ProjectPointGI(p, gi); + return false; + } + + virtual Vec<3> GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi = nullptr) const + { + if(surfind > 0 && surfind <= faces.Size()) + return faces[surfind-1]->GetNormal(p, gi); + else + return {0., 0., 0.}; + } + + 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 + { + if(faces.Size() >= surfi && surfi > 0) + { + faces[surfi-1]->PointBetween(p1, p2, secpoint, gi1, gi2, newp, newgi); + return; + } + newp = p1 + secpoint * (p2-p1); + } + + 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 + { + if(ap1.edgenr < edges.Size() && ap1.edgenr >= 0) + { + edges[ap1.edgenr]->PointBetween(p1, p2, secpoint, + ap1, ap2, newp, newgi); + return; + } + newp = p1+secpoint*(p2-p1); + } + + virtual Vec<3> GetTangent(const Point<3> & p, int surfi1, + int surfi2, + const EdgePointGeomInfo & egi) const + { + throw Exception("Base geometry get tangent called"); + } + + virtual size_t GetEdgeIndex(const GeometryEdge& edge) const + { + for(auto i : Range(edges)) + if(edge.GetHash() == edges[i]->GetHash()) + return i; + throw Exception("Couldn't find edge index"); + } + virtual void Save (const filesystem::path & filename) const; virtual void SaveToMeshFile (ostream & /* ost */) const { ; } }; @@ -37,14 +331,14 @@ namespace netgen { public: virtual ~GeometryRegister(); - virtual NetgenGeometry * Load (string filename) const = 0; - virtual NetgenGeometry * LoadFromMeshFile (istream & /* ist */) const { return NULL; } + virtual NetgenGeometry * Load (const filesystem::path & filename) const = 0; + virtual NetgenGeometry * LoadFromMeshFile (istream & /* ist */, string) const { return NULL; } virtual class VisualScene * GetVisualScene (const NetgenGeometry * /* geom */) const { return NULL; } virtual void SetParameters (Tcl_Interp * /* interp */) { ; } }; - class DLL_HEADER GeometryRegisterArray : public Array + class DLL_HEADER GeometryRegisterArray : public NgArray { public: virtual ~GeometryRegisterArray() @@ -56,7 +350,7 @@ namespace netgen virtual shared_ptr LoadFromMeshFile (istream & ist) const; }; - // extern DLL_HEADER Array geometryregister; + // extern DLL_HEADER NgArray geometryregister; extern DLL_HEADER GeometryRegisterArray geometryregister; } diff --git a/libsrc/meshing/bcfunctions.cpp b/libsrc/meshing/bcfunctions.cpp index 46317661..fd7441ad 100644 --- a/libsrc/meshing/bcfunctions.cpp +++ b/libsrc/meshing/bcfunctions.cpp @@ -32,13 +32,13 @@ namespace netgen colours match is defined as "eps" and is currently 2.5e-5 (for square of distance) */ - bool ColourMatch(Vec3d col1, Vec3d col2, double eps) + bool ColourMatch(Vec<4> col1, Vec<4> col2, double eps) { if(eps <= 0.0) eps = DEFAULT_EPS; bool colmatch = false; - if(Dist2(col1,col2) < eps) colmatch = true; + if((col1-col2).Length2() < eps) colmatch = true; return colmatch; } @@ -51,14 +51,14 @@ namespace netgen Function to create a list of all the unique colours available in a given mesh */ - void GetFaceColours(Mesh & mesh, Array & face_colours) + void GetFaceColours(Mesh & mesh, NgArray> & face_colours) { face_colours.SetSize(1); face_colours.Elem(1) = mesh.GetFaceDescriptor(1).SurfColour(); for(int i = 1; i <= mesh.GetNFD(); i++) { - Vec3d face_colour = mesh.GetFaceDescriptor(i).SurfColour(); + auto face_colour = mesh.GetFaceDescriptor(i).SurfColour(); bool col_found = false; for(int j = 1; j <= face_colours.Size(); j++) @@ -143,9 +143,9 @@ namespace netgen // Arrays to hold the specified RGB colour triplets as well // as the associated boundary condition number - Array bc_colours(numentries); - Array bc_num(numentries); - Array bc_used(numentries); + NgArray> bc_colours(numentries); + NgArray bc_num(numentries); + NgArray bc_used(numentries); // Actually read in the data from the file for(int i = 1; i <= numentries; i++) @@ -162,9 +162,9 @@ namespace netgen bc_num.Elem(i) = bcnum; bc_used.Elem(i) = false; - ocf >> bc_colours.Elem(i).X() - >> bc_colours.Elem(i).Y() - >> bc_colours.Elem(i).Z(); + ocf >> bc_colours.Elem(i)[0] + >> bc_colours.Elem(i)[1] + >> bc_colours.Elem(i)[2]; if(!ocf.good()) { @@ -175,12 +175,8 @@ namespace netgen // Bound checking of the values // The RGB values should be between 0.0 and 1.0 - if(bc_colours.Elem(bcnum).X() < 0.0) bc_colours.Elem(bcnum).X() = 0.0; - if(bc_colours.Elem(bcnum).X() > 1.0) bc_colours.Elem(bcnum).X() = 1.0; - if(bc_colours.Elem(bcnum).Y() < 0.0) bc_colours.Elem(bcnum).X() = 0.0; - if(bc_colours.Elem(bcnum).Y() > 1.0) bc_colours.Elem(bcnum).X() = 1.0; - if(bc_colours.Elem(bcnum).Z() < 0.0) bc_colours.Elem(bcnum).X() = 0.0; - if(bc_colours.Elem(bcnum).Z() > 1.0) bc_colours.Elem(bcnum).X() = 1.0; + for(auto i : Range(3)) + bc_colours.Elem(bcnum)[i] = max2(min2(bc_colours.Elem(bcnum)[i], 1.), 0.); } PrintMessage(3, "Successfully loaded Boundary Colour Profile file...."); @@ -198,7 +194,7 @@ namespace netgen PrintMessage(3, "Highest boundary number in list = ",max_bcnum); - Array all_colours; + NgArray> all_colours; // Extract all the colours to see how many there are GetFaceColours(mesh,all_colours); @@ -214,12 +210,9 @@ namespace netgen for(int face_index = 1; face_index <= nfd; face_index++) { - // Temporary container for individual face colours - Vec3d face_colour; - // Get the colour of the face being currently processed - face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); - if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B))) + auto face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); + if(!ColourMatch(face_colour,Vec<4>(DEFAULT_R,DEFAULT_G,DEFAULT_B, 1.0))) { // Boolean variable to check if the boundary condition was applied // or not... not applied would imply that the colour of the face @@ -257,7 +250,7 @@ namespace netgen } // User Information of the results of the operation - Vec3d ref_colour(0.0,1.0,0.0); + Vec<4> ref_colour(0.0,1.0,0.0,1.0); PrintMessage(3,"Colour based Boundary Condition Property details:"); for(int bc_index = 0; bc_index <= bc_num.Size(); bc_index++) { @@ -266,12 +259,12 @@ namespace netgen if(bc_index == 0) { PrintMessage(3, "BC Property: ",DEFAULT_BCNUM); - PrintMessage(3, " RGB Face Colour = ",ref_colour,"","\n"); + PrintMessage(3, " RGB Face Colour = ",Vec3d{ref_colour[0], ref_colour[1], ref_colour[2]},"","\n"); } else if(bc_used.Elem(bc_index)) { PrintMessage(3, "BC Property: ",bc_num.Elem(bc_index)); - PrintMessage(3, " RGB Face Colour = ",ref_colour,"","\n"); + PrintMessage(3, " RGB Face Colour = ",Vec3d{ref_colour[0], ref_colour[1], ref_colour[2]},"","\n"); } } } @@ -290,9 +283,9 @@ namespace netgen */ void AutoColourAlg_Sorted(Mesh & mesh) { - Array all_colours; - Array faces_sorted; - Array colours_sorted; + NgArray> all_colours; + NgArray faces_sorted; + NgArray colours_sorted; // Extract all the colours to see how many there are GetFaceColours(mesh,all_colours); @@ -301,7 +294,7 @@ namespace netgen // for automatically for(int i = 1; i <= all_colours.Size(); i++) { - if(ColourMatch(all_colours.Elem(i),Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B))) + if(ColourMatch(all_colours.Elem(i),Vec<4>(DEFAULT_R,DEFAULT_G,DEFAULT_B,1.0))) { all_colours.DeleteElement(i); break; @@ -322,7 +315,7 @@ namespace netgen colours_sorted.SetSize(all_colours.Size()+1); faces_sorted = 0; - // Slave Array 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 @@ -341,15 +334,13 @@ namespace netgen // Extract the number of surface elements having a given colour // And save this number into an array for later sorting for(int face_index = 1; face_index <= nfd; face_index++) - { - Array se_face; - + { + Array se_face; + mesh.GetSurfaceElementsOfFace(face_index, se_face); - Vec3d face_colour; - - face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); - if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B))) + auto face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); + if(!ColourMatch(face_colour,Vec<4>(DEFAULT_R,DEFAULT_G,DEFAULT_B,1.0))) { for(int i = 1; i <= all_colours.Size(); i++) { @@ -378,14 +369,12 @@ namespace netgen // Now actually assign the BC Property to the respective faces for(int face_index = 1; face_index <= nfd; face_index++) { - Vec3d face_colour; - - face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); - if(!ColourMatch(face_colour,Vec3d(DEFAULT_R,DEFAULT_G,DEFAULT_B))) + auto face_colour = mesh.GetFaceDescriptor(face_index).SurfColour(); + if(!ColourMatch(face_colour,Vec<4>(DEFAULT_R,DEFAULT_G,DEFAULT_B, 1.0))) { for(int i = 0; i < colours_sorted.Size(); i++) { - Vec3d ref_colour; + Vec<4> ref_colour; if(i != no_colour_index) ref_colour = all_colours.Elem(colours_sorted[i]); if(ColourMatch(face_colour, ref_colour)) @@ -403,7 +392,7 @@ namespace netgen } // User Information of the results of the operation - Vec3d ref_colour(0.0,1.0,0.0); + Vec<4> ref_colour(0.0,1.0,0.0,1.0); PrintMessage(3,"Colour based Boundary Condition Property details:"); for(int i = 0; i < faces_sorted.Size(); i++) { @@ -412,7 +401,7 @@ namespace netgen PrintMessage(3, "BC Property: ",i + DEFAULT_BCNUM); PrintMessage(3, " Nr. of Surface Elements = ", faces_sorted[i]); PrintMessage(3, " Colour Index = ", colours_sorted[i]); - PrintMessage(3, " RGB Face Colour = ",ref_colour,"","\n"); + PrintMessage(3, " RGB Face Colour = ",Vec3d{ref_colour[0], ref_colour[1], ref_colour[2]},"","\n"); } } diff --git a/libsrc/meshing/bcfunctions.hpp b/libsrc/meshing/bcfunctions.hpp index 074654ae..7e7f15ee 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 @@ -45,9 +45,9 @@ namespace netgen //extern void OCCAutoColourBcProps(Mesh & mesh, OCCGeometry & occgeometry, const char *occcolourfile); extern DLL_HEADER void AutoColourBcProps(Mesh & mesh, const char *bccolourfile); - extern DLL_HEADER void GetFaceColours(Mesh & mesh, Array & face_colours); + extern DLL_HEADER void GetFaceColours(Mesh & mesh, NgArray> & face_colours); - extern DLL_HEADER bool ColourMatch(Vec3d col1, Vec3d col2, double eps = 2.5e-05); + extern DLL_HEADER bool ColourMatch(Vec<4> col1, Vec<4> col2, double eps = 2.5e-05); } #endif diff --git a/libsrc/meshing/bisect.cpp b/libsrc/meshing/bisect.cpp index d73718be..fcabc286 100644 --- a/libsrc/meshing/bisect.cpp +++ b/libsrc/meshing/bisect.cpp @@ -13,10 +13,10 @@ namespace netgen class MarkedQuad; typedef Array T_MTETS; - typedef Array T_MPRISMS; - typedef Array T_MIDS; - typedef Array T_MTRIS; - typedef Array T_MQUADS; + typedef NgArray T_MPRISMS; + typedef NgArray T_MIDS; + typedef NgArray T_MTRIS; + typedef NgArray T_MQUADS; @@ -303,7 +303,7 @@ namespace netgen int BTSortEdges (const Mesh & mesh, - const Array< Array* > & idmaps, + const NgArray< NgArray* > & idmaps, INDEX_2_CLOSED_HASHTABLE & edgenumber) { PrintMessage(4,"sorting ... "); @@ -313,8 +313,8 @@ namespace netgen { // new, fast version - Array edges; - Array eclasses; + NgArray edges; + NgArray eclasses; int i, j, k; int cntedges = 0; @@ -585,7 +585,7 @@ namespace netgen // } // compute classlength: - Array edgelength(cntedges); + NgArray edgelength(cntedges); /* for (i = 1; i <= cntedges; i++) @@ -676,7 +676,7 @@ namespace netgen // sort edges: - Array sorted(cntedges); + NgArray sorted(cntedges); QuickSort (edgelength, sorted); @@ -974,7 +974,7 @@ namespace netgen bool BTDefineMarkedId(const Element2d & el, INDEX_2_CLOSED_HASHTABLE & edgenumber, - const Array & idmap, + const NgArray & idmap, MarkedIdentification & mi) { @@ -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; @@ -1116,8 +1120,8 @@ namespace netgen for (int j = 0; j < 3; j++) for (int k = j+1; k < 4; k++) { - const Point<3> & p1 = mesh.Point (mtets.Get(i).pnums[j]); - const Point<3> & p2 = mesh.Point (mtets.Get(i).pnums[k]); + const Point<3> & p1 = mesh.Point (mtets[i-1].pnums[j]); + const Point<3> & p2 = mesh.Point (mtets[i-1].pnums[k]); double hh = Dist2 (p1, p2); if (hh > h) h = hh; } @@ -1126,7 +1130,7 @@ namespace netgen double hshould = 1e10; for (int j = 0; j < 4; j++) { - double hi = hv (mtets.Get(i).pnums[j]-1); + double hi = hv (mtets[i-1].pnums[j]-1); if (hi < hshould) hshould = hi; } @@ -1141,11 +1145,11 @@ namespace netgen { if (h > hshould * hfac) { - mtets.Elem(i).marked = 1; + mtets[i-1].marked = 1; marked = 1; } else - mtets.Elem(i).marked = 0; + mtets[i-1].marked = 0; } } @@ -1392,7 +1396,7 @@ namespace netgen void BTBisectIdentification (const MarkedIdentification & oldid, - Array & newp, + NgArray & newp, MarkedIdentification & newid1, MarkedIdentification & newid2) { @@ -1626,10 +1630,10 @@ namespace netgen { int i,j,k; - Array< Array* > idmaps; + NgArray< NgArray* > idmaps; for(i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++) { - idmaps.Append(new Array); + idmaps.Append(new NgArray); mesh.GetIdentifications().GetMap(i,*idmaps.Last()); } @@ -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++) @@ -1847,7 +1851,7 @@ namespace netgen void ConnectToNodeRec (int node, int tonode, - const TABLE & conto, Array & connecttonode) + const TABLE & conto, NgArray & connecttonode) { // (*testout) << "connect " << node << " to " << tonode << endl; for (int i = 1; i <= conto.EntrySize(node); i++) @@ -1955,9 +1959,11 @@ namespace netgen void BisectTetsCopyMesh (Mesh & mesh, const NetgenGeometry *, BisectionOptions & opt, - const Array< Array* > & idmaps, + const NgArray< NgArray* > & idmaps, const string & refinfofile) { + if (mesh.GetDimension() < 2) + throw Exception ("Mesh bisection is available in 2D and 3D"); // mtets.SetName ("bisection, tets"); // mprisms.SetName ("bisection, prisms"); // mtris.SetName ("bisection, trigs"); @@ -2150,7 +2156,7 @@ namespace netgen } if(mesh.GetDimension() == 2) { - for (SegmentIndex j=1; j<=mesh.GetNSeg(); j++) + for (SegmentIndex j=0; j* > & idmaps) + const NgArray< NgArray* > & idmaps) { - Array< Array*,PointIndex::BASE > mtets_old(mesh.GetNP()); - Array< Array*,PointIndex::BASE > mprisms_old(mesh.GetNP()); - Array< Array*,PointIndex::BASE > mids_old(mesh.GetNP()); - Array< Array*,PointIndex::BASE > mtris_old(mesh.GetNP()); - Array< Array*,PointIndex::BASE > mquads_old(mesh.GetNP()); + NgArray< NgArray*,PointIndex::BASE > mtets_old(mesh.GetNP()); + NgArray< NgArray*,PointIndex::BASE > mprisms_old(mesh.GetNP()); + NgArray< NgArray*,PointIndex::BASE > mids_old(mesh.GetNP()); + NgArray< NgArray*,PointIndex::BASE > mtris_old(mesh.GetNP()); + NgArray< NgArray*,PointIndex::BASE > mquads_old(mesh.GetNP()); for(int i=PointIndex::BASE; i; + mtets_old[i] = new NgArray; for(int i=PointIndex::BASE; i; + mprisms_old[i] = new NgArray; for(int i=PointIndex::BASE; i; + mids_old[i] = new NgArray; for(int i=PointIndex::BASE; i; + mtris_old[i] = new NgArray; for(int i=PointIndex::BASE; i; + mquads_old[i] = new NgArray; for(int i=0; iAppend(mtets[i]); @@ -2454,11 +2460,11 @@ namespace netgen void UpdateEdgeMarks (Mesh & mesh, - const Array< Array* > & idmaps) - //const Array < Array* > & elements_before, - //const Array < Array* > & markedelts_num, - // const Array < Array* > & surfelements_before, - // const Array < Array* > & markedsurfelts_num) + const NgArray< NgArray* > & idmaps) + //const NgArray < NgArray* > & elements_before, + //const NgArray < NgArray* > & markedelts_num, + // const NgArray < NgArray* > & surfelements_before, + // const NgArray < NgArray* > & markedsurfelts_num) { /* T_MTETS mtets_old; mtets_old.Copy(mtets); @@ -2687,7 +2693,7 @@ namespace netgen void Refinement :: Bisect (Mesh & mesh, BisectionOptions & opt, - Array * quality_loss) const + NgArray * quality_loss) const { PrintMessage(1,"Mesh bisection"); PushStatus("Mesh bisection"); @@ -2706,6 +2712,8 @@ namespace netgen static int timer_bisecttet = NgProfiler::CreateTimer ("Bisect tets"); static int timer_bisecttrig = NgProfiler::CreateTimer ("Bisect trigs"); static int timer_bisectsegms = NgProfiler::CreateTimer ("Bisect segms"); + + NgProfiler::RegionTimer reg1 (timer); (*opt.tracer)("Bisect", false); @@ -2717,12 +2725,12 @@ namespace netgen LocalizeEdgePoints(mesh); delete loct; - Array< Array* > idmaps; + NgArray< NgArray* > idmaps; for(int i=1; i<=mesh.GetIdentifications().GetMaxNr(); i++) { if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC) { - idmaps.Append(new Array); + idmaps.Append(new NgArray); mesh.GetIdentifications().GetMap(i,*idmaps.Last(),true); } } @@ -2755,14 +2763,15 @@ namespace netgen inf.close(); } - - - if (mesh.mglevels == 1 || idmaps.Size() > 0) - BisectTetsCopyMesh(mesh, NULL, opt, idmaps, refelementinfofileread); - - mesh.ComputeNVertices(); - + + // if (mesh.mglevels == 1 || idmaps.Size() > 0) + if (mesh.level_nv.Size() == 0) // || idmaps.Size() ???? + { + BisectTetsCopyMesh(mesh, NULL, opt, idmaps, refelementinfofileread); + mesh.level_nv.Append (mesh.GetNV()); + } + int np = mesh.GetNV(); mesh.SetNP(np); @@ -2773,7 +2782,7 @@ namespace netgen // int initnp = np; // int maxsteps = 3; - mesh.mglevels++; + // mesh.mglevels++; /* if (opt.refinementfilename || opt.usemarkedelements) @@ -2805,7 +2814,7 @@ namespace netgen #ifndef SABINE //Nachbarelemente mit ordx,ordy,ordz - Array v_order (mesh.GetNP()); + NgArray v_order (mesh.GetNP()); v_order = 0; for (ElementIndex ei = 0; ei < ne; ei++) @@ -2856,7 +2865,7 @@ namespace netgen // new version { for(int i=1; i<=mtets.Size(); i++) - mtets.Elem(i).marked = 0; + mtets[i-1].marked = 0; for(int i=1; i<=mprisms.Size(); i++) mprisms.Elem(i).marked = 0; for(int i=1; i<=mtris.Size(); i++) @@ -2897,7 +2906,7 @@ namespace netgen while(inf && isint) { - mtets.Elem(atoi(st.c_str())).marked = 3; + mtets[atoi(st.c_str())-1].marked = 3; marked = 1; inf >> st; @@ -2989,7 +2998,7 @@ namespace netgen inf >> ch; if(!inf) throw NgException("something wrong with refinementinfo file (old format)"); - mtets.Elem(i).marked = (ch == '1'); + mtets[i-1].marked = (ch == '1'); } marked = 1; } @@ -3011,9 +3020,9 @@ namespace netgen mesh.VolumeElement(i).GetType() == TET10) { cnttet++; - mtets.Elem(cnttet).marked = - 3 * mesh.VolumeElement(i).TestRefinementFlag(); - if (mtets.Elem(cnttet).marked) + mtets[cnttet-1].marked = + (opt.onlyonce ? 3 : 1) * mesh.VolumeElement(i).TestRefinementFlag(); + if (mtets[cnttet-1].marked) cntm++; } else @@ -3030,9 +3039,9 @@ namespace netgen else for (int i = 1; i <= mtets.Size(); i++) { - mtets.Elem(i).marked = - 3 * mesh.VolumeElement(i).TestRefinementFlag(); - if (mtets.Elem(i).marked) + mtets[i-1].marked = + (opt.onlyonce ? 1 : 3) * mesh.VolumeElement(i).TestRefinementFlag(); + if (mtets[i-1].marked) cntm++; } @@ -3061,7 +3070,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++; @@ -3130,11 +3139,11 @@ namespace netgen PrintMessage(3,"refine p"); for (int i = 1; i <= mtets.Size(); i++) - mtets.Elem(i).incorder = mtets.Elem(i).marked ? 1 : 0; + mtets[i-1].incorder = mtets[i-1].marked ? 1 : 0; for (int i = 1; i <= mtets.Size(); i++) - if (mtets.Elem(i).incorder) - mtets.Elem(i).marked = 0; + if (mtets[i-1].incorder) + mtets[i-1].marked = 0; for (int i = 1; i <= mprisms.Size(); i++) @@ -3158,7 +3167,7 @@ namespace netgen if (opt.refine_hp) { PrintMessage(3,"refine hp"); - BitArray singv(np); + NgBitArray singv(np); singv.Clear(); if (mesh.GetDimension() == 3) @@ -3181,7 +3190,7 @@ namespace netgen else { // vertices with 2 different bnds - Array bndind(np); + NgArray bndind(np); bndind = 0; for (int i = 1; i <= mesh.GetNSeg(); i++) { @@ -3200,18 +3209,18 @@ namespace netgen for (int i = 1; i <= mtets.Size(); i++) - mtets.Elem(i).incorder = 1; + mtets[i-1].incorder = 1; for (int i = 1; i <= mtets.Size(); i++) { - if (!mtets.Elem(i).marked) - mtets.Elem(i).incorder = 0; + if (!mtets[i-1].marked) + mtets[i-1].incorder = 0; for (int j = 0; j < 4; j++) - if (singv.Test (mtets.Elem(i).pnums[j])) - mtets.Elem(i).incorder = 0; + if (singv.Test (mtets[i-1].pnums[j])) + mtets[i-1].incorder = 0; } for (int i = 1; i <= mtets.Size(); i++) - if (mtets.Elem(i).incorder) - mtets.Elem(i).marked = 0; + if (mtets[i-1].incorder) + mtets[i-1].marked = 0; for (int i = 1; i <= mprisms.Size(); i++) @@ -3261,21 +3270,21 @@ namespace netgen // refine volume elements NgProfiler::StartTimer (timer_bisecttet); (*opt.tracer)("bisecttet", false); - int nel = mtets.Size(); - for (int i = 1; i <= nel; i++) - if (mtets.Elem(i).marked) + size_t nel = mtets.Size(); + for (size_t i = 0; i < nel; i++) + if (mtets[i].marked) { - MarkedTet oldtet = mtets.Get(i); + MarkedTet oldtet = mtets[i]; INDEX_2 edge(oldtet.pnums[oldtet.tetedge1], oldtet.pnums[oldtet.tetedge2]); edge.Sort(); PointIndex newp; - if (cutedges.Used (edge)) - { - newp = cutedges.Get(edge); - } + if (auto optnewp = cutedges.GetIfUsed(edge)) + { + newp = *optnewp; + } else { Point<3> npt = Center (mesh.Point (edge.I1()), @@ -3287,10 +3296,9 @@ namespace netgen MarkedTet newtet1, newtet2; BTBisectTet (oldtet, newp, newtet1, newtet2); - mtets.Elem(i) = newtet1; + mtets[i] = newtet1; mtets.Append (newtet2); - - mesh.mlparentelement.Append (i); + mesh.mlparentelement.Append (i+1); } NgProfiler::StopTimer (timer_bisecttet); (*opt.tracer)("bisecttet", true); @@ -3347,11 +3355,11 @@ namespace netgen if (mids.Elem(i).marked) { MarkedIdentification oldid,newid1,newid2; - Array newp; + NgArray newp; oldid = mids.Get(i); - Array edges; + NgArray edges; edges.Append(INDEX_2(oldid.pnums[oldid.markededge], oldid.pnums[(oldid.markededge+1)%oldid.np])); edges.Append(INDEX_2(oldid.pnums[oldid.markededge + oldid.np], @@ -3411,9 +3419,17 @@ namespace netgen INDEX_2 edge(oldpi1, oldpi2); edge.Sort(); + int si = mesh.GetFaceDescriptor (oldtri.surfid).SurfNr(); + PointGeomInfo npgi; + PointGeomInfo gi1 = oldtri.pgeominfo[(oldtri.markededge+1)%3]; + PointGeomInfo gi2 = oldtri.pgeominfo[(oldtri.markededge+2)%3]; + if (cutedges.Used (edge)) { newp = cutedges.Get(edge); + npgi.u = 0.5*(gi1.u + gi2.u); + npgi.v = 0.5*(gi1.v + gi2.v); + geo.ProjectPointGI (si, mesh[newp], npgi); } else { @@ -3421,18 +3437,10 @@ namespace netgen mesh.Point (edge.I2())); newp = mesh.AddPoint (npt); cutedges.Set (edge, newp); + geo.PointBetween (mesh.Point (oldpi1), mesh.Point (oldpi2), + 0.5, si, gi1, gi2, mesh.Point (newp), npgi); } - int si = mesh.GetFaceDescriptor (oldtri.surfid).SurfNr(); - PointGeomInfo npgi; - - if (mesh[newp].Type() != EDGEPOINT) - PointBetween (mesh.Point (oldpi1), mesh.Point (oldpi2), - 0.5, si, - oldtri.pgeominfo[(oldtri.markededge+1)%3], - oldtri.pgeominfo[(oldtri.markededge+2)%3], - mesh.Point (newp), npgi); - BTBisectTri (oldtri, newp, npgi, newtri1, newtri2); mtris[i] = newtri1; @@ -3505,28 +3513,16 @@ namespace netgen PointGeomInfo npgi1, npgi2; int si = mesh.GetFaceDescriptor (oldquad.surfid).SurfNr(); - // geom->GetSurface(si)->Project (mesh.Point(newp1)); - // geom->GetSurface(si)->Project (mesh.Point(newp2)); - -// (*testout) -// cerr << "project point 1 " << newp1 << " old: " << mesh.Point(newp1); - PointBetween (mesh.Point (edge1.I1()), mesh.Point (edge1.I2()), - 0.5, si, - pgi11, - pgi12, - mesh.Point (newp1), npgi1); -// (*testout) -// cerr << " new: " << mesh.Point(newp1) << endl; - - -// cerr << "project point 2 " << newp2 << " old: " << mesh.Point(newp2); - PointBetween (mesh.Point (edge2.I1()), mesh.Point (edge2.I2()), - 0.5, si, - pgi21, - pgi22, - mesh.Point (newp2), npgi2); -// cerr << " new: " << mesh.Point(newp2) << endl; - + geo.PointBetween(mesh.Point (edge1.I1()), mesh.Point (edge1.I2()), + 0.5, si, + pgi11, + pgi12, + mesh.Point (newp1), npgi1); + geo.PointBetween (mesh.Point (edge2.I1()), mesh.Point (edge2.I2()), + 0.5, si, + pgi21, + pgi22, + mesh.Point (newp2), npgi2); BTBisectQuad (oldquad, newp1, npgi1, newp2, npgi2, newquad1, newquad2); @@ -3562,16 +3558,10 @@ namespace netgen EdgePointGeomInfo newepgi; - -// -// cerr << "move edgepoint " << newpi << " from " << mesh.Point(newpi); - PointBetween (mesh.Point (seg[0]), mesh.Point (seg[1]), - 0.5, seg.surfnr1, seg.surfnr2, - seg.epgeominfo[0], seg.epgeominfo[1], - mesh.Point (newpi), newepgi); -// cerr << " to " << mesh.Point (newpi) << endl; - - + geo.PointBetweenEdge(mesh.Point (seg[0]), mesh.Point (seg[1]), + 0.5, seg.surfnr1, seg.surfnr2, + seg.epgeominfo[0], seg.epgeominfo[1], + mesh.Point (newpi), newepgi); nseg1.epgeominfo[1] = newepgi; nseg2.epgeominfo[0] = newepgi; @@ -3617,13 +3607,13 @@ namespace netgen if (opt.refine_hp) { // - Array v_order (mesh.GetNP()); + NgArray v_order (mesh.GetNP()); v_order = 0; if (mesh.GetDimension() == 3) { for (int i = 1; i <= mtets.Size(); i++) - if (mtets.Elem(i).incorder) - mtets.Elem(i).order++; + if (mtets[i-1].incorder) + mtets[i-1].order++; for (int i = 0; i < mtets.Size(); i++) for (int j = 0; j < 4; j++) @@ -3807,7 +3797,8 @@ namespace netgen // write multilevel hierarchy to mesh: np = mesh.GetNP(); mesh.mlbetweennodes.SetSize(np); - if (mesh.mglevels <= 2) + // if (mesh.mglevels <= 2) + if (mesh.level_nv.Size() <= 1) { PrintMessage(4,"RESETTING mlbetweennodes"); for (int i = 1; i <= np; i++) @@ -3817,6 +3808,9 @@ namespace netgen } } + mesh.level_nv.Append (np); + + /* for (i = 1; i <= cutedges.GetNBags(); i++) for (j = 1; j <= cutedges.GetBagSize(i); j++) @@ -3828,9 +3822,12 @@ namespace netgen } */ - BitArray isnewpoint(np); + + NgBitArray isnewpoint(np); isnewpoint.Clear(); + { + static Timer t("update mlbetween"); RegionTimer reg(t); for (int i = 0; i < cutedges.Size(); i++) if (cutedges.UsedPos0(i)) { @@ -3840,8 +3837,7 @@ namespace netgen isnewpoint.Set(newpi); mesh.mlbetweennodes.Elem(newpi) = edge; } - - + } /* mesh.PrintMemInfo (cout); cout << "tets "; @@ -3905,19 +3901,20 @@ namespace netgen // mesh.BuildConnectedNodes(); - - + { + static Timer t("ComputeNV"); RegionTimer reg(t); mesh.ComputeNVertices(); - + } + (*opt.tracer)("call RebuildSurfElList", false); mesh.RebuildSurfaceElementLists(); (*opt.tracer)("call RebuildSurfElList", true); - // update identification tables + // update identification tables for (int i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++) { - Array identmap; + NgArray identmap; mesh.GetIdentifications().GetMap (i, identmap); @@ -3982,11 +3979,12 @@ namespace netgen } } - + // Check/Repair static bool repaired_once; - if(mesh.mglevels == 1) + // if(mesh.mglevels == 1) + if(mesh.level_nv.Size() == 1) repaired_once = false; //mesh.Save("before.vol"); @@ -3995,8 +3993,8 @@ namespace netgen NgProfiler::RegionTimer * regt(NULL); regt = new NgProfiler::RegionTimer(reptimer); - Array bad_elts; - Array pure_badness; + NgArray bad_elts; + NgArray pure_badness; if(do_repair || quality_loss != NULL) { @@ -4133,62 +4131,4 @@ namespace netgen refine_hp = 0; refine_p = 0; } - - - Refinement :: Refinement () - { - optimizer2d = NULL; - } - - Refinement :: ~Refinement () - { - ; - } - - - void Refinement :: 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 - { - newp = p1+secpoint*(p2-p1); - } - - void Refinement :: PointBetween (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 - { - //cout << "base class edge point between" << endl; - newp = p1+secpoint*(p2-p1); - } - - - Vec<3> Refinement :: GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & ap1) const - { - cerr << "Refinement::GetTangent not overloaded" << endl; - return Vec<3> (0,0,0); - } - - Vec<3> Refinement :: GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const - { - cerr << "Refinement::GetNormal not overloaded" << endl; - return Vec<3> (0,0,0); - } - - - void Refinement :: ProjectToSurface (Point<3> & p, int surfi) const - { - if (printmessage_importance>0) - cerr << "Refinement :: ProjectToSurface ERROR: no geometry set" << endl; - }; - - void Refinement :: ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const - { - cerr << "Refinement::ProjectToEdge not overloaded" << endl; - } } diff --git a/libsrc/meshing/bisect.hpp b/libsrc/meshing/bisect.hpp index 3fe0e85f..eef60d73 100644 --- a/libsrc/meshing/bisect.hpp +++ b/libsrc/meshing/bisect.hpp @@ -10,10 +10,11 @@ public: const char * femcode; int maxlevel; int usemarkedelements; - bool refine_hp; - bool refine_p; - TaskManager task_manager = &DummyTaskManager; - Tracer tracer = &DummyTracer; + bool refine_hp = false; + bool refine_p = false; + bool onlyonce = false; + NgTaskManager task_manager = &DummyTaskManager; + NgTracer tracer = &DummyTracer; DLL_HEADER BisectionOptions (); }; @@ -38,61 +39,22 @@ DLL_HEADER extern void ZRefinement (Mesh &, const class NetgenGeometry *, class DLL_HEADER Refinement { - MeshOptimize2d * optimizer2d; + const NetgenGeometry& geo; public: - Refinement (); - virtual ~Refinement (); + Refinement (const NetgenGeometry& ageo) : geo(ageo) {} + virtual ~Refinement () {} void Refine (Mesh & mesh) const; void Refine (Mesh & mesh); - void Bisect (Mesh & mesh, class BisectionOptions & opt, Array * quality_loss = NULL) const; + void Bisect (Mesh & mesh, class BisectionOptions & opt, NgArray * quality_loss = NULL) const; void MakeSecondOrder (Mesh & mesh) const; void MakeSecondOrder (Mesh & mesh); - 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; - - virtual void PointBetween (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; - - virtual Vec<3> GetTangent (const Point<3> & p, int surfi1, int surfi2, - const EdgePointGeomInfo & egi) const; - - virtual Vec<3> GetNormal (const Point<3> & p, int surfi1, - const PointGeomInfo & gi) const; - - - virtual void ProjectToSurface (Point<3> & p, int surfi) const; - - virtual void ProjectToSurface (Point<3> & p, int surfi, const PointGeomInfo & /* gi */) const - { - ProjectToSurface (p, surfi); - } - - virtual void ProjectToEdge (Point<3> & p, int surfi1, int surfi2, const EdgePointGeomInfo & egi) const; - - void ValidateSecondOrder (Mesh & mesh); void ValidateRefinedMesh (Mesh & mesh, - Array & parents); - - MeshOptimize2d * Get2dOptimizer(void) const - { - return optimizer2d; - } - void Set2dOptimizer(MeshOptimize2d * opti) - { - optimizer2d = opti; - } - + NgArray & parents); virtual void LocalizeEdgePoints(Mesh & /* mesh */) const {;} }; diff --git a/libsrc/meshing/boundarylayer.cpp b/libsrc/meshing/boundarylayer.cpp index ab424dff..72924f25 100644 --- a/libsrc/meshing/boundarylayer.cpp +++ b/libsrc/meshing/boundarylayer.cpp @@ -1,646 +1,1415 @@ #include #include "meshing.hpp" +#include "debugging.hpp" +#include "global.hpp" + +#include +#include namespace netgen { + // checks if a segment is intersecting a plane, spanned by three points, lam will be set s.t. p_intersect = seg[0] + lam * (seg[1]-seg[0]) + bool isIntersectingPlane ( const array, 2> & seg, const array, 3> & trig, double & lam) + { + auto n = Cross(trig[1]-trig[0], trig[2]-trig[0]); + auto v0n = (seg[0]-trig[0])*n; + auto v1n = (seg[1]-trig[0])*n; + if(v0n * v1n >= 0) + return false; - void InsertVirtualBoundaryLayer (Mesh & mesh) - { - cout << "Insert virt. b.l." << endl; + lam = -v0n/(v1n-v0n); + lam *= 0.9; + if(lam < -1e-8 || lam>1+1e-8) + return false; + return true; + } - int surfid; + bool isIntersectingPlane ( const array, 2> & seg, const ArrayMem, 4> & face, double & lam) + { + lam = 1.0; + bool intersect0 = isIntersectingPlane( seg, array, 3>{face[0], face[1], face[2]}, lam ); + if(face.Size()==3) + return intersect0; - cout << "Boundary Nr:"; - cin >> surfid; + double lam1 = 1.0; + bool intersect1 = isIntersectingPlane( seg, array, 3>{face[2], face[3], face[0]}, lam1 ); + lam = min(lam, lam1); + return intersect0 || intersect1; + } - int i; - int np = mesh.GetNP(); + bool isIntersectingTrig ( const array, 2> & seg, const array, 3> & trig, double & lam) + { + if(!isIntersectingPlane(seg, trig, lam)) + return false; - cout << "Old NP: " << mesh.GetNP() << endl; - cout << "Trigs: " << mesh.GetNSE() << endl; + auto p = seg[0] + lam/0.9*(seg[1]-seg[0]); - BitArray bndnodes(np); - Array mapto(np); - - bndnodes.Clear(); - for (i = 1; i <= mesh.GetNSeg(); i++) + auto n_trig = Cross(trig[1]-trig[0], trig[2]-trig[0]).Normalize(); + for(auto i : Range(3)) { - int snr = mesh.LineSegment(i).edgenr; - cout << "snr = " << snr << endl; - if (snr == surfid) - { - bndnodes.Set (mesh.LineSegment(i)[0]); - bndnodes.Set (mesh.LineSegment(i)[1]); - } - } - for (i = 1; i <= mesh.GetNSeg(); i++) - { - int snr = mesh.LineSegment(i).edgenr; - if (snr != surfid) - { - bndnodes.Clear (mesh.LineSegment(i)[0]); - bndnodes.Clear (mesh.LineSegment(i)[1]); - } - } + // check if p0 and p are on same side of segment p1-p2 + auto p0 = trig[i]; + auto p1 = trig[(i+1)%3]; + auto p2 = trig[(i+2)%3]; + auto n = Cross(p2-p1, n_trig); - for (i = 1; i <= np; i++) + auto v0 = (p2-p1).Normalize(); + auto v1 = (p0-p1).Normalize(); + auto inside_dir = (v1 - (v1*v0) * v0).Normalize(); + auto v2 = (p-p1).Normalize(); + if(inside_dir * v1 < 0) + inside_dir = -inside_dir; + + if( (inside_dir*v2) < 0 ) + return false; + } + return true; + }; + + bool isIntersectingFace( const array, 2> & seg, const ArrayMem, 4> & face, double & lam ) + { + lam = 1.0; + double lam0 = 1.0; + bool intersect0 = isIntersectingTrig( seg, {face[0], face[1], face[2]}, lam0 ); + if(intersect0) + lam = min(lam, lam0); + if(face.Size()==3) + return intersect0; + + double lam1 = 1.0; + bool intersect1 = isIntersectingTrig( seg, {face[2], face[3], face[0]}, lam1 ); + if(intersect1) + lam = min(lam, lam1); + return intersect0 || intersect1; + } + + array, 2> BoundaryLayerTool :: GetMappedSeg( PointIndex pi ) + { + return { mesh[pi], mesh[pi] + height*limits[pi]*growthvectors[pi] }; + } + + ArrayMem, 4> BoundaryLayerTool :: GetFace( SurfaceElementIndex sei ) + { + const auto & sel = mesh[sei]; + ArrayMem, 4> points(sel.GetNP()); + for(auto i : Range(sel.GetNP())) + points[i] = mesh[sel[i]]; + return points; + } + + ArrayMem, 4> BoundaryLayerTool :: GetMappedFace( SurfaceElementIndex sei ) + { + const auto & sel = mesh[sei]; + ArrayMem, 4> points(sel.GetNP()); + for(auto i : Range(sel.GetNP())) + points[i] = mesh[sel[i]] + height * limits[sel[i]]*growthvectors[sel[i]]; + return points; + } + + ArrayMem, 4> BoundaryLayerTool :: GetMappedFace( SurfaceElementIndex sei, int face ) + { + if(face == -1) return GetFace(sei); + if(face == -2) return GetMappedFace(sei); + const auto & sel = mesh[sei]; + auto np = sel.GetNP(); + auto pi0 = sel[face % np]; + auto pi1 = sel[(face+1) % np]; + ArrayMem, 4> points(4); + points[0] = points[3] = mesh[pi0]; + points[1] = points[2] = mesh[pi1]; + points[3] += height * limits[pi0]*growthvectors[pi0]; + points[2] += height * limits[pi1]*growthvectors[pi1]; + return points; + } + + Vec<3> BoundaryLayerTool :: getEdgeTangent(PointIndex pi, int edgenr) + { + Vec<3> tangent = 0.0; + ArrayMem pts; + for(auto segi : topo.GetVertexSegments(pi)) + { + auto & seg = mesh[segi]; + if(seg.edgenr != edgenr+1) + continue; + PointIndex other = seg[0]+seg[1]-pi; + if(!pts.Contains(other)) + pts.Append(other); + } + if(pts.Size() != 2) + throw Exception("Something went wrong in getEdgeTangent!"); + tangent = mesh[pts[1]] - mesh[pts[0]]; + return tangent.Normalize(); + } + + void BoundaryLayerTool :: LimitGrowthVectorLengths() + { + static Timer tall("BoundaryLayerTool::LimitGrowthVectorLengths"); RegionTimer rtall(tall); + + limits.SetSize(np); + limits = 1.0; + + auto smooth = [&] (size_t nsteps) { + for(auto i : Range(nsteps)) + for(const auto & sel : mesh.SurfaceElements()) + { + double min_limit = 999; + for(auto pi : sel.PNums()) + min_limit = min(min_limit, limits[pi]); + for(auto pi : sel.PNums()) + limits[pi] = min(limits[pi], 1.4*min_limit); + } + }; + + // check for self-intersection within new elements (prisms/hexes) + auto self_intersection = [&] () { + for(SurfaceElementIndex sei : mesh.SurfaceElements().Range()) + { + auto facei = mesh[sei].GetIndex(); + if(facei < nfd_old && !params.surfid.Contains(facei)) + continue; + + auto sel = mesh[sei]; + auto np = sel.GetNP(); + // check if a new edge intesects the plane of any opposing face + double lam; + for(auto i : Range(np)) + for(auto fi : Range(np-2)) + if(isIntersectingPlane(GetMappedSeg(sel[i]), GetMappedFace(sei, i+fi+1), lam)) + if(lam < 1.0) + limits[sel[i]] *= lam; + } + }; + + // first step: intersect with other surface elements that are boundary of domain the layer is grown into + // second (and subsequent) steps: intersect with other boundary layers, allow restriction by 20% in each step + auto changed_domains = domains; + if(!params.outside) + changed_domains.Invert(); + + bool limit_reached = true; + double lam_lower_limit = 1.0; + int step = 0; + while(limit_reached || step<2) + { + if(step>0) + lam_lower_limit *= 0.8; + limit_reached = false; + + // build search tree with all surface elements (bounding box of a surface element also covers the generated boundary layer) + Box<3> bbox(Box<3>::EMPTY_BOX); + for(auto pi : mesh.Points().Range()) { - if (bndnodes.Test(i)) - mapto.Elem(i) = mesh.AddPoint (mesh.Point (i)); - else - mapto.Elem(i) = 0; + bbox.Add(mesh[pi]); + bbox.Add(mesh[pi]+limits[pi]*height*growthvectors[pi]); + } + BoxTree<3> tree(bbox); + + for(auto sei : mesh.SurfaceElements().Range()) + { + const auto & sel = mesh[sei]; + Box<3> box(Box<3>::EMPTY_BOX); + const auto& fd = mesh.GetFaceDescriptor(sel.GetIndex()); + if(!changed_domains.Test(fd.DomainIn()) && + !changed_domains.Test(fd.DomainOut())) + continue; + for(auto pi : sel.PNums()) + box.Add(mesh[pi]); + // also add moved points to bounding box + if(params.surfid.Contains(sel.GetIndex())) + for(auto pi : sel.PNums()) + box.Add(mesh[pi]+limits[pi]*height*growthvectors[pi]); + tree.Insert(box, sei); } - for (i = 1; i <= mesh.GetNSE(); i++) - { - Element2d & el = mesh.SurfaceElement(i); - for (int j = 1; j <= el.GetNP(); j++) - if (mapto.Get(el.PNum(j))) - el.PNum(j) = mapto.Get(el.PNum(j)); - } - - - int nq = 0; - for (i = 1; i <= mesh.GetNSeg(); i++) - { - int snr = mesh.LineSegment(i).edgenr; - if (snr == surfid) - { - int p1 = mesh.LineSegment(i)[0]; - int p2 = mesh.LineSegment(i)[1]; - int p3 = mapto.Get (p1); - if (!p3) p3 = p1; - int p4 = mapto.Get (p2); - if (!p4) p4 = p2; - - Element2d el(QUAD); - el.PNum(1) = p1; - el.PNum(2) = p2; - el.PNum(3) = p3; - el.PNum(4) = p4; - el.SetIndex (2); - mesh.AddSurfaceElement (el); - nq++; - } - } - - cout << "New NP: " << mesh.GetNP() << endl; - cout << "Quads: " << nq << endl; - } - - - - - -/* - Philippose Rajan - 11 June 2009 - - Function to calculate the surface normal at a given - vertex of a surface element, with respect to that - surface element. - - 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; - - Vertex_A = Vertex + 1; - if(Vertex_A > el.GetNP()) Vertex_A = 1; - - Vertex_B = Vertex - 1; - if(Vertex_B <= 0) Vertex_B = el.GetNP(); - - 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)]; - - SurfaceNormal = Cross(Vect_A,Vect_B); - SurfaceNormal.Normalize(); - } - - - - - -/* - 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; - - - Array surfid (blp.surfid); - int prismlayers = blp.prismlayers; - double hfirst = blp.hfirst; - double growthfactor = blp.growthfactor; - Array 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--) + for(auto pi : mesh.Points().Range()) { - 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 - BitArray bndnodes(np+1); // big enough for 1-based array - - // Map of the old points to the new points - Array mapto(np); - - // Growth vectors for the prismatic layer based on - // the effective surface normal at a given point - Array 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 - BitArray 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; - Array pnt1_elems; - Array 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())) + if(mesh[pi].Type() == INNERPOINT) + continue; + if(growthvectors[pi].Length2() == 0.0) + continue; + Box<3> box(Box<3>::EMPTY_BOX); + auto seg = GetMappedSeg(pi); + box.Add(seg[0]); + box.Add(seg[1]); + double lam = 1.0; + tree.GetFirstIntersecting(box.PMin(), box.PMax(), [&](SurfaceElementIndex sei) { - for (int j = 1; j <= sel.GetNP(); j++) + const auto & sel = mesh[sei]; + if(sel.PNums().Contains(pi)) + return false; + auto face = GetMappedFace(sei, -2); + double lam_ = 999; + bool is_bl_sel = params.surfid.Contains(sel.GetIndex()); + + if(step==0) + { + if(isIntersectingFace(seg, face, lam_)) + { + if(is_bl_sel) // allow only half the distance if the opposing surface element has a boundary layer too + lam_ *= 0.5; + lam = min(lam, lam_); + } + } + // if the opposing surface element has a boundary layer, we need to additionally intersect with the new faces + if(step>0 && is_bl_sel) + { + for(auto facei : Range(-1, sel.GetNP())) + { + auto face = GetMappedFace(sei, facei); + if(isIntersectingFace(seg, face, lam_)) // && lam_ > other_limit) + { + lam = min(lam, lam_); + } + } + } + return false; + }); + if(lam<1) + { + if(lam0) + { + limit_reached = true; + lam = lam_lower_limit; + } + limits[pi] = min(limits[pi], lam); + } + } + step++; + } + + self_intersection(); + smooth(3); + + for(auto pi : Range(growthvectors)) + growthvectors[pi] *= limits[pi]; + + } + + + // depending on the geometry type, the mesh contains segments multiple times (once for each face) + bool HaveSingleSegments( const Mesh & mesh ) + { + auto& topo = mesh.GetTopology(); + NgArray surf_els; + + for(auto segi : Range(mesh.LineSegments())) + { + mesh.GetTopology().GetSegmentSurfaceElements(segi+1, surf_els); + if(surf_els.Size()<2) + continue; + + auto seg = mesh[segi]; + auto pi0 = min(seg[0], seg[1]); + auto pi1 = max(seg[0], seg[1]); + auto p0_segs = topo.GetVertexSegments(seg[0]); + + for(auto segi_other : p0_segs) + { + if(segi_other == segi) + continue; + + auto seg_other = mesh[segi_other]; + auto pi0_other = min(seg_other[0], seg_other[1]); + auto pi1_other = max(seg_other[0], seg_other[1]); + if( pi0_other == pi0 && pi1_other == pi1 ) + return false; + } + + // found segment with multiple adjacent surface elements but no other segments with same points -> have single segments + return true; + } + + return true; + } + + // duplicates segments (and sets seg.si accordingly) to have a unified data structure for all geometry types + Array BuildSegments( Mesh & mesh ) + { + Array segments; + auto& topo = mesh.GetTopology(); + + NgArray surf_els; + + for(auto segi : Range(mesh.LineSegments())) + { + auto seg = mesh[segi]; + mesh.GetTopology().GetSegmentSurfaceElements(segi+1, surf_els); + for(auto seli : surf_els) + { + const auto & sel = mesh[seli]; + seg.si = sel.GetIndex(); + + auto np = sel.GetNP(); + for(auto i : Range(np)) + { + if(sel[i] == seg[0]) { - // Check (Doublecheck) if the corresponding point has a - // copy available for remapping - if (mapto.Get(sel.PNum(j))) + if(sel[(i+1)%np] != seg[1]) + swap(seg[0], seg[1]); + break; + } + } + + segments.Append(seg); + } + } + return segments; + } + + void MergeAndAddSegments( Mesh & mesh, FlatArray new_segments) + { + INDEX_2_HASHTABLE already_added( 2*new_segments.Size() ); + + for(auto & seg : mesh.LineSegments()) + { + INDEX_2 i2 (seg[0], seg[1]); + i2.Sort(); + if(!already_added.Used(i2)) + already_added.Set(i2, true); + } + + for(auto & seg : new_segments) + { + INDEX_2 i2 (seg[0], seg[1]); + i2.Sort(); + + if(!already_added.Used(i2)) + { + mesh.AddSegment(seg); + already_added.Set(i2, true); + } + } + } + + void BoundaryLayerTool :: InterpolateSurfaceGrowthVectors() + { + static Timer tall("InterpolateSurfaceGrowthVectors"); RegionTimer rtall(tall); + static Timer tsmooth("InterpolateSurfaceGrowthVectors-Smoothing"); + auto np = mesh.GetNP(); + BitArray is_point_on_bl_surface(np+1); + is_point_on_bl_surface.Clear(); + BitArray is_point_on_other_surface(np+1); + is_point_on_other_surface.Clear(); + Array, PointIndex> normals(np); + for(auto pi : Range(growthvectors)) + normals[pi] = growthvectors[pi]; + + ParallelForRange( mesh.SurfaceElements().Range(), [&] ( auto myrange ) + { + for(SurfaceElementIndex sei : myrange) + { + auto facei = mesh[sei].GetIndex(); + if(facei < nfd_old && !params.surfid.Contains(facei)) + { + for(auto pi : mesh[sei].PNums()) + if(mesh[pi].Type() == SURFACEPOINT) + is_point_on_other_surface.SetBitAtomic(pi); + } + else + { + for(auto pi : mesh[sei].PNums()) + if(mesh[pi].Type() == SURFACEPOINT) + is_point_on_bl_surface.SetBitAtomic(pi); + } + } + }); + + Array points; + for(PointIndex pi : mesh.Points().Range()) + { + if(is_point_on_bl_surface[pi]) + { + points.Append(pi); + growthvectors[pi] = 0.0; + } + if(is_point_on_other_surface[pi]) + { + points.Append(pi); + } + } + + // smooth tangential part of growth vectors from edges to surface elements + RegionTimer rtsmooth(tsmooth); + for(auto i : Range(10)) + { + for(auto pi : points) + { + auto sels = p2sel[pi]; + Vec<3> new_gw = growthvectors[pi]; + int cnt = 1; + std::set suround; + suround.insert(pi); + auto normal = normals[pi]; + for(auto sei: sels) + { + const auto & sel = mesh[sei]; + for(auto pi1 : sel.PNums()) + if(suround.count(pi1)==0) + { + suround.insert(pi1); + auto gw_other = growthvectors[pi1]; + auto normal_other = getNormal(mesh[sei]); + auto tangent_part = gw_other - (gw_other*normal_other)*normal_other; + if(is_point_on_bl_surface[pi]) + new_gw += tangent_part; + else + new_gw += gw_other; + } + } + + growthvectors[pi] = 1.0/suround.size() * new_gw; + } + } + + for(auto pi : points) + growthvectors[pi] += normals[pi]; + } + + + BoundaryLayerTool::BoundaryLayerTool(Mesh & mesh_, const BoundaryLayerParameters & params_) + : mesh(mesh_), topo(mesh_.GetTopology()), params(params_) + { + static Timer timer("BoundaryLayerTool::ctor"); + RegionTimer regt(timer); + + //for(auto & seg : mesh.LineSegments()) + //seg.edgenr = seg.epgeominfo[1].edgenr; + + height = 0.0; + for (auto h : params.heights) + height += h; + + max_edge_nr = -1; + for(const auto& seg : mesh.LineSegments()) + if(seg.edgenr > max_edge_nr) + max_edge_nr = seg.edgenr; + + int ndom = mesh.GetNDomains(); + ndom_old = ndom; + + new_mat_nrs.SetSize(mesh.FaceDescriptors().Size() + 1); + new_mat_nrs = -1; + for(auto [bcname, matname] : params.new_mat) + { + mesh.SetMaterial(++ndom, matname); + regex pattern(bcname); + for(auto i : Range(1, mesh.GetNFD()+1)) + { + auto& fd = mesh.GetFaceDescriptor(i); + if(regex_match(fd.GetBCName(), pattern)) + new_mat_nrs[i] = ndom; + } + } + + domains = params.domains; + if(!params.outside) + domains.Invert(); + + topo.SetBuildVertex2Element(true); + mesh.UpdateTopology(); + + have_single_segments = HaveSingleSegments(mesh); + if(have_single_segments) + segments = BuildSegments(mesh); + else + segments = mesh.LineSegments(); + + np = mesh.GetNP(); + ne = mesh.GetNE(); + nse = mesh.GetNSE(); + nseg = segments.Size(); + + p2sel = mesh.CreatePoint2SurfaceElementTable(); + + nfd_old = mesh.GetNFD(); + moved_surfaces.SetSize(nfd_old+1); + moved_surfaces.Clear(); + si_map.SetSize(nfd_old+1); + for(auto i : Range(nfd_old+1)) + si_map[i] = i; + } + + void BoundaryLayerTool :: CreateNewFaceDescriptors() + { + surfacefacs.SetSize(nfd_old+1); + surfacefacs = 0.0; + // create new FaceDescriptors + for(auto i : Range(1, nfd_old+1)) + { + const auto& fd = mesh.GetFaceDescriptor(i); + string name = fd.GetBCName(); + if(params.surfid.Contains(i)) + { + if(auto isIn = domains.Test(fd.DomainIn()); isIn != domains.Test(fd.DomainOut())) + { + 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_nrs[i] : fd.DomainIn(), + isIn ? fd.DomainOut() : new_mat_nrs[i], -1); + new_fd.SetBCProperty(new_si); + mesh.AddFaceDescriptor(new_fd); + si_map[i] = new_si; + moved_surfaces.SetBit(i); + mesh.SetBCName(new_si-1, "mapped_" + name); + } + } + } + } + + void BoundaryLayerTool ::CreateFaceDescriptorsSides() + { + BitArray face_done(mesh.GetNFD()+1); + face_done.Clear(); + for(const auto& sel : mesh.SurfaceElements()) + { + auto facei = sel.GetIndex(); + if(face_done.Test(facei)) + continue; + bool point_moved = false; + bool point_fixed = false; + for(auto pi : sel.PNums()) + { + if(growthvectors[pi].Length() > 0) + point_moved = true; + else + point_fixed = true; + } + if(point_moved && !moved_surfaces.Test(facei)) + { + int new_si = mesh.GetNFD()+1; + const auto& fd = mesh.GetFaceDescriptor(facei); + auto isIn = domains.Test(fd.DomainIn()); + auto isOut = domains.Test(fd.DomainOut()); + int si = params.sides_keep_surfaceindex ? facei : -1; + // domin and domout can only be set later + FaceDescriptor new_fd(si, -1, + -1, si); + new_fd.SetBCProperty(new_si); + mesh.AddFaceDescriptor(new_fd); + si_map[facei] = new_si; + mesh.SetBCName(new_si-1, fd.GetBCName()); + face_done.SetBit(facei); + } + } + } + + void BoundaryLayerTool :: CalculateGrowthVectors() + { + growthvectors.SetSize(np); + growthvectors = 0.; + + for(auto pi : mesh.Points().Range()) + { + const auto & p = mesh[pi]; + if(p.Type() == INNERPOINT) + continue; + + std::map> normals; + + // calculate one normal vector per face (average with angles as weights for multiple surface elements within a face) + for(auto sei : p2sel[pi]) + { + const auto & sel = mesh[sei]; + auto facei = sel.GetIndex(); + if(!params.surfid.Contains(facei)) + continue; + + auto n = surfacefacs[sel.GetIndex()] * getNormal(sel); + + int itrig = sel.PNums().Pos(pi); + itrig += sel.GetNP(); + auto v0 = (mesh[sel.PNumMod(itrig+1)] - mesh[pi]).Normalize(); + auto v1 = (mesh[sel.PNumMod(itrig-1)] - mesh[pi]).Normalize(); + if(normals.count(facei)==0) + normals[facei] = {0.,0.,0.}; + normals[facei] += acos(v0*v1)*n; + } + + for(auto & [facei, n] : normals) + n *= 1.0/n.Length(); + + // combine normal vectors for each face to keep uniform distances + auto & np = growthvectors[pi]; + ArrayMem, 3> ns; + for (auto &[facei, n] : normals) { + ns.Append(n); + } + + ArrayMem, 3> removed; + // reduce to full rank of max 3 + while(true) + { + if(ns.Size() <= 1) + break; + if(ns.Size() == 2 && ns[0] * ns[1] < 1 - 1e-6) + break; + if (ns.Size() == 3) + { + DenseMatrix mat(3,3); + for(auto i : Range(3)) + for(auto j : Range(3)) + mat(i,j) = ns[i][j]; + if(fabs(mat.Det()) > 1e-6) + break; + } + int maxpos1; + int maxpos2; + double val = 0; + for (auto i : Range(ns)) + { + for (auto j : Range(i + 1, ns.Size())) + { + double ip = ns[i] * ns[j]; + if(ip > val) { - // Map the surface elements to the new points - sel.PNum(j) = mapto.Get(sel.PNum(j)); + val = ip; + maxpos1 = i; + maxpos2 = j; } } } - } - for (int i = 1; i <= ne; i++) - { - Element & el = mesh.VolumeElement(i); - if(el.GetIndex() != blp.bulk_matnr) + removed.Append(ns[maxpos1]); + removed.Append(ns[maxpos2]); + ns[maxpos1] = 0.5 * (ns[maxpos1] + ns[maxpos2]); + ns.DeleteElement(maxpos2); + } + + if(ns.Size() == 0) + continue; + if(ns.Size() == 1) + np = ns[0]; + else if(ns.Size() == 2) + { + np = ns[0]; + auto n = ns[1]; + 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); + } + else // ns.Size() == 3 + { + DenseMatrix mat(3,3); + for(auto i : Range(3)) + for(auto j : Range(3)) + mat(i, j) = ns[i] * ns[j]; + Vector rhs(3); + rhs = 1.; + Vector res(3); + DenseMatrix inv(3, ns.Size()); + CalcInverse(mat, inv); + inv.Mult(rhs, res); + for(auto i : Range(ns)) + np += res[i] * ns[i]; + } + for(auto& n : removed) + if(n * np < 0) + cout << "WARNING: Growth vector at point " << pi << " in opposite direction to face normal!" << endl << "Growthvector = " << np << ", face normal = " << n << endl; + } + } + + Array>, SegmentIndex> BoundaryLayerTool :: BuildSegMap() + { + // Bit array to keep track of segments already processed + BitArray segs_done(nseg+1); + 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(segments.Size()); + + // moved segments + is_edge_moved.SetSize(max_edge_nr+1); + is_edge_moved = false; + + // boundaries to project endings to + is_boundary_projected.SetSize(nfd_old+1); + is_boundary_projected.Clear(); + is_boundary_moved.SetSize(nfd_old+1); + is_boundary_moved.Clear(); + + for(auto si : Range(segments)) + { + if(segs_done[si]) continue; + const auto& segi = segments[si]; + if(!moved_surfaces.Test(segi.si)) continue; + segs_done.SetBit(si); + segmap[si].Append(make_pair(si, 0)); + moved_segs.Append(si); + is_edge_moved.SetBit(segi.edgenr); + for(auto sj : Range(segments)) + { + if(segs_done.Test(sj)) continue; + const auto& segj = segments[sj]; + if((segi[0] == segj[0] && segi[1] == segj[1]) || + (segi[0] == segj[1] && segi[1] == segj[0])) { - for (int j = 1; j <= el.GetNP(); j++) + segs_done.SetBit(sj); + int type; + if(moved_surfaces.Test(segj.si)) + type = 0; + else if(const auto& fd = mesh.GetFaceDescriptor(segj.si); domains.Test(fd.DomainIn()) && domains.Test(fd.DomainOut())) { - // 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)); - } + type = 2; + if(fd.DomainIn() == 0 || fd.DomainOut() == 0) + is_boundary_projected.SetBit(segj.si); } - } - } - - - - - // 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++) - { - if(bndnodes.Test(pi)) mesh.AddLockedPoint(pi); - } - - // 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++) - { - Array vertelems; - - if(bndnodes.Test(i)) - { - MeshPoint pointtomove; - - pointtomove = mesh.Point(i); - - if(layer == prismlayers) + 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 = 3; + is_boundary_moved.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 + is_boundary_projected.SetBit(segj.si); + } + segmap[si].Append(make_pair(sj, type)); + } + } + } + + return segmap; + } + + BitArray BoundaryLayerTool :: ProjectGrowthVectorsOnSurface() + { + BitArray in_surface_direction(nfd_old+1); + in_surface_direction.Clear(); + // project growthvector on surface for inner angles + if(params.grow_edges) + { + for(const auto& sel : mesh.SurfaceElements()) + if(is_boundary_projected.Test(sel.GetIndex())) + { + auto n = getNormal(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(); + auto tol = v1.Length() * 1e-12; + if((v1 * v3 > -tol) && (v2 * v3 > -tol)) + in_surface_direction.SetBit(sel.GetIndex()); + else + continue; + + if(!params.project_boundaries.Contains(sel.GetIndex())) + continue; + 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 : segments) + { + int count = 0; + for(const auto& seg2 : segments) + if(((seg[0] == seg2[0] && seg[1] == seg2[1]) || (seg[0] == seg2[1] && seg[1] == seg2[0])) && params.surfid.Contains(seg2.si)) + count++; + if(count == 1) + { + growthvectors[seg[0]] = {0., 0., 0.}; + growthvectors[seg[1]] = {0., 0., 0.}; + } + } + } + + return in_surface_direction; + } + + void BoundaryLayerTool :: InterpolateGrowthVectors() + { + // interpolate tangential component of growth vector along edge + for(auto edgenr : Range(max_edge_nr)) + { + // if(!is_edge_moved[edgenr+1]) continue; + + // build sorted list of edge + Array points; + // find first vertex on edge + double edge_len = 0.; + auto is_end_point = [&] (PointIndex pi) + { + // if(mesh[pi].Type() == FIXEDPOINT) + // return true; + // return false; + auto segs = topo.GetVertexSegments(pi); + auto first_edgenr = mesh[segs[0]].edgenr; + for(auto segi : segs) + if(mesh[segi].edgenr != first_edgenr) + return true; + return false; + }; + + bool any_grows = false; + + for(const auto& seg : segments) + { + if(seg.edgenr-1 == edgenr) + { + if(growthvectors[seg[0]].Length2() != 0 || + growthvectors[seg[1]].Length2() != 0) + any_grows = true; + if(points.Size() == 0 && is_end_point(seg[0])) + { + points.Append(seg[0]); + points.Append(seg[1]); + edge_len += (mesh[seg[1]] - mesh[seg[0]]).Length(); } } - } - mesh.Compress(); + } + + if(!any_grows) + continue; + + if(!points.Size()) + throw Exception("Could not find startpoint for edge " + ToString(edgenr)); + + while(true) + { + bool point_found = false; + for(auto si : topo.GetVertexSegments(points.Last())) + { + const auto& seg = mesh[si]; + if(seg.edgenr-1 != edgenr) + continue; + if(seg[0] == points.Last() && points[points.Size()-2] !=seg[1]) + { + edge_len += (mesh[points.Last()] - mesh[seg[1]]).Length(); + points.Append(seg[1]); + point_found = true; + break; + } + else if(seg[1] == points.Last() && + points[points.Size()-2] != seg[0]) + { + edge_len += (mesh[points.Last()] - mesh[seg[0]]).Length(); + points.Append(seg[0]); + point_found = true; + break; + } + } + if(is_end_point(points.Last())) + break; + if(!point_found) + { + throw Exception(string("Could not find connected list of line segments for edge ") + edgenr); + } + } + + if(growthvectors[points[0]].Length2() == 0 && + growthvectors[points.Last()].Length2() == 0) + continue; + + // tangential part of growth vectors + auto t1 = (mesh[points[1]]-mesh[points[0]]).Normalize(); + auto gt1 = growthvectors[points[0]] * t1 * t1; + auto t2 = (mesh[points.Last()]-mesh[points[points.Size()-2]]).Normalize(); + auto gt2 = growthvectors[points.Last()] * t2 * t2; + + if(!is_edge_moved[edgenr+1]) + { + if(growthvectors[points[0]] * (mesh[points[1]] - mesh[points[0]]) < 0) + gt1 = 0.; + if(growthvectors[points.Last()] * (mesh[points[points.Size()-2]] - mesh[points.Last()]) < 0) + gt2 = 0.; + } + + double len = 0.; + for(size_t i = 1; i < points.Size()-1; i++) + { + auto pi = points[i]; + len += (mesh[pi] - mesh[points[i-1]]).Length(); + auto t = getEdgeTangent(pi, edgenr); + auto lam = len/edge_len; + auto interpol = (1-lam) * (gt1 * t) * t + lam * (gt2 * t) * t; + growthvectors[pi] += interpol; + } + } + + InterpolateSurfaceGrowthVectors(); + } + + void BoundaryLayerTool :: InsertNewElements( FlatArray>, SegmentIndex> segmap, const BitArray & in_surface_direction ) + { + static Timer timer("BoundaryLayerTool::InsertNewElements"); RegionTimer rt(timer); + Array, PointIndex> mapto(np); + // insert new points + for (PointIndex pi = 1; pi <= np; pi++) + if (growthvectors[pi].Length2() != 0) + { + Point<3> p = mesh[pi]; + for(auto i : Range(params.heights)) + { + p += params.heights[i] * growthvectors[pi]; + mapto[pi].Append(mesh.AddPoint(p)); + } + } + + // add 2d quads on required surfaces + map, int> seg2edge; + if(params.grow_edges) + { + for(auto sei : moved_segs) + { + // copy here since we will add segments and this would + // invalidate a reference! + auto segi = segments[sei]; + for(auto [sej, type] : segmap[sei]) + { + auto segj = segments[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]; + new_segments.Append(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); + is_boundary_moved.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; + new_segments.Append(s0); + + for(auto i : Range(params.heights)) + { + Element2d sel(QUAD); + p3 = mapto[pp2][i]; + p4 = mapto[pp1][i]; + sel[0] = p1; + sel[1] = p2; + sel[2] = p3; + sel[3] = p4; + for(auto i : Range(4)) + { + sel.GeomInfo()[i].u = 0.0; + sel.GeomInfo()[i].v = 0.0; + } + sel.SetIndex(si_map[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; + new_segments.Append(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; + new_segments.Append(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; + new_segments.Append(s3); + } + } + } + } + + 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(moved_surfaces.Test(sel.GetIndex())) + { + Array points(sel.PNums()); + if(surfacefacs[sel.GetIndex()] > 0) Swap(points[0], points[2]); + for(auto j : Range(params.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_nrs[sel.GetIndex()]); + 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(is_boundary_moved.Test(sel.GetIndex())) + moveboundarypoint.SetBit(p); + } + } + } + if(is_boundary_moved.Test(sel.GetIndex())) + { + for(auto& p : mesh[si].PNums()) + if(mapto[p].Size()) + p = mapto[p].Last(); + } + } + + for(SegmentIndex sei = 0; sei < nseg; sei++) + { + auto& seg = segments[sei]; + if(is_boundary_moved.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(params.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(params.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(params.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(params.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!"); + } + } + } + + void BoundaryLayerTool :: SetDomInOut() + { + for(auto i : Range(1, nfd_old+1)) + if(moved_surfaces.Test(i)) + { + if(auto dom = mesh.GetFaceDescriptor(si_map[i]).DomainIn(); dom > ndom_old) + mesh.GetFaceDescriptor(i).SetDomainOut(dom); + else + mesh.GetFaceDescriptor(i).SetDomainIn(mesh.GetFaceDescriptor(si_map[i]).DomainOut()); + } + } + + void BoundaryLayerTool :: SetDomInOutSides() + { + BitArray done(mesh.GetNFD()+1); + done.Clear(); + for(auto sei : Range(mesh.SurfaceElements())) + { + auto& sel = mesh[sei]; + auto index = sel.GetIndex(); + if(done.Test(index)) + continue; + done.SetBit(index); + auto& fd = mesh.GetFaceDescriptor(index); + if(fd.DomainIn() != -1) + continue; + int e1, e2; + mesh.GetTopology().GetSurface2VolumeElement(sei+1, e1, e2); + if(e1 == 0) + fd.SetDomainIn(0); + else + fd.SetDomainIn(mesh.VolumeElement(e1).GetIndex()); + if(e2 == 0) + fd.SetDomainOut(0); + else + fd.SetDomainOut(mesh.VolumeElement(e2).GetIndex()); + } + } + + void BoundaryLayerTool :: AddSegments() + { + if(have_single_segments) + MergeAndAddSegments(mesh, new_segments); + else + { + for(auto & seg : new_segments) + mesh.AddSegment(seg); + } + } + + void BoundaryLayerTool :: FixVolumeElements() + { + static Timer timer("BoundaryLayerTool::FixVolumeElements"); RegionTimer rt(timer); + BitArray is_inner_point(mesh.GetNP()+1); + is_inner_point.Clear(); + + auto changed_domains = domains; + if(!params.outside) + changed_domains.Invert(); + + for(ElementIndex ei : Range(ne)) + if(changed_domains.Test(mesh[ei].GetIndex())) + for(auto pi : mesh[ei].PNums()) + if(mesh[pi].Type() == INNERPOINT) + is_inner_point.SetBit(pi); + + Array points; + for(auto pi : mesh.Points().Range()) + if(is_inner_point.Test(pi)) + points.Append(pi); + + auto p2el = mesh.CreatePoint2ElementTable(is_inner_point); + + // smooth growth vectors to shift additional element layers to the inside and fix flipped tets + for(auto step : Range(10)) + { + for(auto pi : points) + { + Vec<3> average_gw = 0.0; + auto & els = p2el[pi]; + size_t cnt = 0; + for(auto ei : els) + if(ei surfid; Array heights; - Array new_matnrs; - int prismlayers = 1; - int bulk_matnr = 1; - int new_matnr = 1; - double hfirst = 0.01; - double growthfactor = 1; - bool optimize = true; + map new_mat; + BitArray domains; + bool outside = false; // set the boundary layer on the outside + bool grow_edges = false; + bool limit_growth_vectors = true; + bool sides_keep_surfaceindex = 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{}); + +class BoundaryLayerTool +{ + public: + BoundaryLayerTool(Mesh & mesh_, const BoundaryLayerParameters & params_); + void Perform(); + + protected: + Mesh & mesh; + MeshTopology & topo; + BoundaryLayerParameters params; + Array, PointIndex> growthvectors; + Table p2sel; + + BitArray domains, is_edge_moved, is_boundary_projected, is_boundary_moved; + Array moved_segs; + int max_edge_nr, nfd_old, ndom_old; + Array new_mat_nrs; + BitArray moved_surfaces; + int np, nseg, nse, ne; + double height; + + bool have_single_segments; + Array segments, new_segments; + + Array surfacefacs; + Array si_map; + Array limits; + + // major steps called in Perform() + void CreateNewFaceDescriptors(); + void CreateFaceDescriptorsSides(); + void CalculateGrowthVectors(); + Array>, SegmentIndex> BuildSegMap(); + + BitArray ProjectGrowthVectorsOnSurface(); + void InterpolateSurfaceGrowthVectors(); + void InterpolateGrowthVectors(); + void LimitGrowthVectorLengths(); + + void InsertNewElements(FlatArray>, SegmentIndex> segmap, const BitArray & in_surface_direction); + void SetDomInOut(); + void SetDomInOutSides(); + void AddSegments(); + void FixVolumeElements(); + + // utility functions + array, 2> GetMappedSeg( PointIndex pi ); + ArrayMem, 4> GetFace( SurfaceElementIndex sei ); + ArrayMem, 4> GetMappedFace( SurfaceElementIndex sei ); + ArrayMem, 4> GetMappedFace( SurfaceElementIndex sei, int face ); + + Vec<3> getNormal(const Element2d & el) + { + auto v0 = mesh[el[0]]; + return Cross(mesh[el[1]]-v0, mesh[el[2]]-v0).Normalize(); + } + + Vec<3> getEdgeTangent(PointIndex pi, int edgenr); +}; #endif diff --git a/libsrc/meshing/boundarylayer2d.cpp b/libsrc/meshing/boundarylayer2d.cpp new file mode 100644 index 00000000..36f35513 --- /dev/null +++ b/libsrc/meshing/boundarylayer2d.cpp @@ -0,0 +1,755 @@ +#include +#include "meshing.hpp" +#include "meshing2.hpp" +#include "../geom2d/csg2d.hpp" + +namespace netgen +{ + void InsertVirtualBoundaryLayer (Mesh & mesh) + { + cout << "Insert virt. b.l." << endl; + + int surfid; + + cout << "Boundary Nr:"; + cin >> surfid; + + int i; + int np = mesh.GetNP(); + + cout << "Old NP: " << mesh.GetNP() << endl; + cout << "Trigs: " << mesh.GetNSE() << endl; + + NgBitArray bndnodes(np); + NgArray mapto(np); + + bndnodes.Clear(); + for (i = 1; i <= mesh.GetNSeg(); i++) + { + int snr = mesh.LineSegment(i).edgenr; + cout << "snr = " << snr << endl; + if (snr == surfid) + { + bndnodes.Set (mesh.LineSegment(i)[0]); + bndnodes.Set (mesh.LineSegment(i)[1]); + } + } + for (i = 1; i <= mesh.GetNSeg(); i++) + { + int snr = mesh.LineSegment(i).edgenr; + if (snr != surfid) + { + bndnodes.Clear (mesh.LineSegment(i)[0]); + bndnodes.Clear (mesh.LineSegment(i)[1]); + } + } + + for (i = 1; i <= np; i++) + { + if (bndnodes.Test(i)) + mapto.Elem(i) = mesh.AddPoint (mesh.Point (i)); + else + mapto.Elem(i) = 0; + } + + for (i = 1; i <= mesh.GetNSE(); i++) + { + Element2d & el = mesh.SurfaceElement(i); + for (int j = 1; j <= el.GetNP(); j++) + if (mapto.Get(el.PNum(j))) + el.PNum(j) = mapto.Get(el.PNum(j)); + } + + + int nq = 0; + for (i = 1; i <= mesh.GetNSeg(); i++) + { + int snr = mesh.LineSegment(i).edgenr; + if (snr == surfid) + { + int p1 = mesh.LineSegment(i)[0]; + int p2 = mesh.LineSegment(i)[1]; + int p3 = mapto.Get (p1); + if (!p3) p3 = p1; + int p4 = mapto.Get (p2); + if (!p4) p4 = p2; + + Element2d el(QUAD); + el.PNum(1) = p1; + el.PNum(2) = p2; + el.PNum(3) = p3; + el.PNum(4) = p4; + el.SetIndex (2); + mesh.AddSurfaceElement (el); + nq++; + } + } + + cout << "New NP: " << mesh.GetNP() << endl; + cout << "Quads: " << nq << endl; + } + + + 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.domin == domain || seg.domout == 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; + + auto p2sel = mesh.CreatePoint2SurfaceElementTable(); + PointGeomInfo gi; + gi.u = 0.0; + gi.v = 0.0; + gi.trignum = domain; + for(auto seg : mesh.LineSegments()) + { + if(seg.domin == domain || seg.domout == domain) + for (auto pi : {seg[0], seg[1]}) + if (compress[pi]==PointIndex{PointIndex::INVALID}) + { + meshing.AddPoint(mesh[pi], pi); + compress[pi] = cnt++; + } + if(seg.domin == domain) + meshing.AddBoundaryElement (compress[seg[0]], compress[seg[1]], gi, gi); + if(seg.domout == domain) + meshing.AddBoundaryElement (compress[seg[1]], compress[seg[0]], 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.CalcSurfacesOfNode(); + mesh.OrderElements(); + mesh.SetNextMajorTimeStamp(); + } + + int GenerateBoundaryLayer2 (Mesh & mesh, int domain, const Array & thicknesses, bool should_make_new_domain, const Array & boundaries) + { + mesh.GetTopology().SetBuildVertex2Element(true); + mesh.UpdateTopology(); + const auto & line_segments = mesh.LineSegments(); + SegmentIndex first_new_seg = mesh.LineSegments().Range().Next(); + + int np = mesh.GetNP(); + int nseg = line_segments.Size(); + 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 segments; + + // surface index map + Array si_map(mesh.GetNFD()+2); + si_map = -1; + + int fd_old = mesh.GetNFD(); + + int max_edge_nr = -1; + int max_domain = -1; + + for(const auto& seg : line_segments) + { + if(seg.epgeominfo[0].edgenr > max_edge_nr) + max_edge_nr = seg.epgeominfo[0].edgenr; + if(seg.domin > max_domain) + max_domain = seg.domin; + if(seg.domout > max_domain) + max_domain = seg.domout; + } + + 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(line_segments)) + { + const auto seg = line_segments[segi]; + if(active_boundaries.Test(seg.epgeominfo[0].edgenr) && (seg.domin==domain || seg.domout==domain)) + active_segments.SetBit(segi); + } + + { + FaceDescriptor new_fd(0, 0, 0, -1); + new_fd.SetBCProperty(new_domain); + int new_fd_index = mesh.AddFaceDescriptor(new_fd); + if(should_make_new_domain) + mesh.SetBCName(new_domain-1, "mapped_" + mesh.GetBCName(domain-1)); + } + + for(auto segi : Range(line_segments)) + { + if(segs_done[segi]) continue; + segs_done.SetBit(segi); + const auto& seg = line_segments[segi]; + if(seg.domin != domain && seg.domout != domain) continue; + if(!active_boundaries.Test(seg.epgeominfo[0].edgenr)) + continue; + moved_segs.Append(segi); + } + + // calculate growth vectors (average normal vectors of adjacent segments at each point) + for (auto si : moved_segs) + { + auto & seg = line_segments[si]; + + auto n = mesh[seg[1]] - mesh[seg[0]]; + n = {-n[1], n[0], 0}; + n.Normalize(); + + if(seg.domout == domain) + n = -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 = line_segments[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.domin != domain && seg0.domout != domain) || + (seg1.domin != domain && seg1.domout != domain) ) + 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 = line_segments[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 = line_segments[si]; + + bool swap = false; + auto & pm0 = mapto[seg[0]]; + auto & pm1 = mapto[seg[1]]; + + auto newindex = si_map[domain]; + + 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); + + 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(new_domain); + newel.GeomInfo() = PointGeomInfo{}; + + if(swap) + { + Swap(newel[0], newel[1]); + Swap(newel[2], newel[3]); + } + + for(auto i : Range(4)) + { + newel.GeomInfo()[i].u = 0.0; + newel.GeomInfo()[i].v = 0.0; + } + mesh.AddSurfaceElement(newel); + + } + // segment now adjacent to new 2d-domain! + if(line_segments[si].domin == domain) + line_segments[si].domin = new_domain; + if(line_segments[si].domout == domain) + line_segments[si].domout = new_domain; + } + + for(auto pi : Range(mapto)) + { + if(mapto[pi].Size() == 0) + continue; + auto pnew = mapto[pi].Last(); + for(auto old_sei : meshtopo.GetVertexSurfaceElements( pi )) + { + 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) + { + if(mesh[segi].domin == new_domain) + mesh[segi].domin = domain; + if(mesh[segi].domout == new_domain) + mesh[segi].domout = domain; + } + + mesh.Compress(); + mesh.CalcSurfacesOfNode(); + } + + return new_domain; + } + +} // namespace netgen diff --git a/libsrc/meshing/classifyhpel.hpp b/libsrc/meshing/classifyhpel.hpp index c1555371..51ebae59 100644 --- a/libsrc/meshing/classifyhpel.hpp +++ b/libsrc/meshing/classifyhpel.hpp @@ -1,6 +1,6 @@ HPREF_ELEMENT_TYPE ClassifyTet(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint) { int ep1(0), ep2(0), ep3(0), ep4(0), cp1(0), cp2(0), cp3(0), cp4(0), fp1, fp2, fp3, fp4; int isedge1(0), isedge2(0), isedge3(0), isedge4(0), isedge5(0), isedge6(0); @@ -423,8 +423,8 @@ HPREF_ELEMENT_TYPE ClassifyTet(HPRefElement & el, INDEX_2_HASHTABLE & edges HPREF_ELEMENT_TYPE ClassifyPrism(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint) { HPREF_ELEMENT_TYPE type = HP_NONE; @@ -660,11 +660,11 @@ HPREF_ELEMENT_TYPE ClassifyPrism(HPRefElement & el, INDEX_2_HASHTABLE & edg // #ifdef SABINE HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint, int dim, const FaceDescriptor & fd) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint, int dim, const FaceDescriptor & fd) { - HPREF_ELEMENT_TYPE type = HP_NONE; + HPREF_ELEMENT_TYPE type = HP_NONE; int pnums[3]; int p[3]; @@ -761,11 +761,10 @@ HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edge int ep1=p[eledges[k][0]-1]; int ep2=p[eledges[k][1]-1]; - INDEX_2 i2(el.PNum(ep1),el.PNum(ep2)); + INDEX_2 i2 = INDEX_2::Sort(el.PNum(ep1),el.PNum(ep2)); if(edges.Used(i2)) { - if(edgepoint_dom.Used(INDEX_2(fd.SurfNr(),pnums[ep1-1])) || edgepoint_dom.Used(INDEX_2(-1,pnums[ep1-1])) || edgepoint_dom.Used(INDEX_2(fd.SurfNr(),pnums[ep2-1])) || @@ -782,11 +781,13 @@ HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edge - for(int k=0;k<3;k++) - if(edgepoint.Test(pnums[k])) //edgepoint, but not member of sing_edge on trig -> cp + for (int k=0;k<3;k++) + if (edgepoint.Test(pnums[k]) && + (dim==3 || edgepoint_dom.Used(INDEX_2(fd.SurfNr(),pnums[k])) || edgepoint_dom.Used(INDEX_2(-1,pnums[k])))) + //edgepoint, but not member of sing_edge on trig -> cp { INDEX_2 i2a=INDEX_2::Sort(el.PNum(p[k]), el.PNum(p[(k+1)%3])); - INDEX_2 i2b=INDEX_2::Sort(el.PNum(p[k]), el.PNum(p[(k+2)%3])); + INDEX_2 i2b=INDEX_2::Sort(el.PNum(p[k]), el.PNum(p[(k+2)%3])); if(!edges.Used(i2a) && !edges.Used(i2b)) point_sing[p[k]-1] = 3; @@ -794,7 +795,7 @@ HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edge for(int k=0;k<3;k++) if(cornerpoint.Test(el.PNum(p[k]))) - point_sing[p[k]-1] = 3; + point_sing[p[k]-1] = 3; *testout << "point_sing = " << point_sing[0] << point_sing[1] << point_sing[2] << endl; @@ -875,8 +876,8 @@ HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edge } #ifdef HPREF_OLD HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint, int dim, const FaceDescriptor & fd) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint, int dim, const FaceDescriptor & fd) { HPREF_ELEMENT_TYPE type = HP_NONE; @@ -1136,8 +1137,8 @@ HPREF_ELEMENT_TYPE ClassifyTrig(HPRefElement & el, INDEX_2_HASHTABLE & edge } #endif HPREF_ELEMENT_TYPE ClassifyQuad(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint, int dim, const FaceDescriptor & fd) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint, int dim, const FaceDescriptor & fd) { HPREF_ELEMENT_TYPE type = HP_NONE; @@ -1486,8 +1487,8 @@ HPREF_ELEMENT_TYPE ClassifyQuad(HPRefElement & el, INDEX_2_HASHTABLE & edge HPREF_ELEMENT_TYPE ClassifyHex(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint) { HPREF_ELEMENT_TYPE type = HP_NONE; @@ -1586,8 +1587,8 @@ HPREF_ELEMENT_TYPE ClassifyHex(HPRefElement & el, INDEX_2_HASHTABLE & edges } HPREF_ELEMENT_TYPE ClassifySegm(HPRefElement & hpel, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint) { int cp1 = cornerpoint.Test (hpel[0]); @@ -1629,8 +1630,8 @@ HPREF_ELEMENT_TYPE ClassifySegm(HPRefElement & hpel, INDEX_2_HASHTABLE & ed HPREF_ELEMENT_TYPE ClassifyPyramid(HPRefElement & el, INDEX_2_HASHTABLE & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint) + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint) { HPREF_ELEMENT_TYPE type = HP_NONE; diff --git a/libsrc/meshing/clusters.cpp b/libsrc/meshing/clusters.cpp index 07db0247..4b544a20 100644 --- a/libsrc/meshing/clusters.cpp +++ b/libsrc/meshing/clusters.cpp @@ -15,19 +15,19 @@ namespace netgen { ; } - - void AnisotropicClusters :: Update(TaskManager tm, Tracer tracer) + + void AnisotropicClusters :: Update() { - static int timer = NgProfiler::CreateTimer ("clusters"); - static int timer1 = NgProfiler::CreateTimer ("clusters1"); - static int timer2 = NgProfiler::CreateTimer ("clusters2"); - static int timer3 = NgProfiler::CreateTimer ("clusters3"); - NgProfiler::RegionTimer reg (timer); + static Timer timer("clusters"); + // static int timer1 = NgProfiler::CreateTimer ("clusters1"); + // static int timer2 = NgProfiler::CreateTimer ("clusters2"); + // static int timer3 = NgProfiler::CreateTimer ("clusters3"); + RegionTimer reg (timer); const MeshTopology & top = mesh.GetTopology(); auto id = this->mesh.GetCommunicator().Rank(); - auto ntasks = this->mesh.GetCommunicator().Size(); + // auto ntasks = this->mesh.GetCommunicator().Size(); bool hasedges = top.HasEdges(); bool hasfaces = top.HasFaces(); @@ -46,13 +46,13 @@ namespace netgen cluster_reps.SetSize (nv+ned+nfa+ne); cluster_reps = -1; - Array llist (nv+ned+nfa+ne); + NgArray llist (nv+ned+nfa+ne); llist = 0; - Array nnums, ednums, fanums; + NgArray nnums, ednums, fanums; int changed; - NgProfiler::StartTimer(timer1); + // NgProfiler::StartTimer(timer1); /* @@ -81,39 +81,42 @@ namespace netgen cluster_reps.Elem(nnums[j]) = nnums[j]; } */ - ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) + ngcore::ParallelForRange + (mesh.VolumeElements().Range(), + [&] (auto myrange) { - Array nnums, ednums, fanums; - for (int i = begin+1; i <= end; i++) + NgArray nnums; // , ednums, fanums; + for (int i_ : myrange) { + int i = i_+1; const Element & el = mesh.VolumeElement(i); ELEMENT_TYPE typ = el.GetType(); - top.GetElementEdges (i, ednums); - top.GetElementFaces (i, fanums); + // top.GetElementEdges (i, ednums); + auto ednums = top.GetEdges (ElementIndex(i_)); + // top.GetElementFaces (i, fanums); + auto fanums = top.GetFaces (ElementIndex(i_)); int elnv = top.GetNVertices (typ); int elned = ednums.Size(); int elnfa = fanums.Size(); nnums.SetSize(elnv+elned+elnfa+1); - for (int j = 1; j <= elnv; j++) - nnums.Elem(j) = el.PNum(j)+1-PointIndex::BASE; - for (int j = 1; j <= elned; j++) - nnums.Elem(elnv+j) = nv+ednums.Elem(j); - for (int j = 1; j <= elnfa; j++) - nnums.Elem(elnv+elned+j) = nv+ned+fanums.Elem(j); - nnums.Elem(elnv+elned+elnfa+1) = nv+ned+nfa+i; - + for (int j = 0; j < elnv; j++) + nnums[j] = el[j]+1-PointIndex::BASE; + for (int j = 0; j < elned; j++) + nnums[elnv+j] = nv+ednums[j]+1; + for (int j = 0; j < elnfa; j++) + nnums[elnv+elned+j] = nv+ned+fanums[j]+1; + nnums[elnv+elned+elnfa] = nv+ned+nfa+i; + for (int j = 0; j < nnums.Size(); j++) cluster_reps.Elem(nnums[j]) = nnums[j]; } - }); + }, ngcore::TasksPerThread(4)); - NgProfiler::StopTimer(timer1); - NgProfiler::StartTimer(timer2); + // NgProfiler::StopTimer(timer1); + // NgProfiler::StartTimer(timer2); /* for (int i = 1; i <= nse; i++) { @@ -137,37 +140,41 @@ namespace netgen cluster_reps.Elem(nnums[j]) = nnums[j]; } */ - ParallelForRange - (tm, nse, - [&] (size_t begin, size_t end) + ngcore::ParallelForRange + (mesh.SurfaceElements().Range(), + [&] (auto myrange) { - ArrayMem nnums, ednums; - for (int i = begin+1; i <= end; i++) + NgArrayMem nnums; // , ednums; + for (int i_ : myrange) { + int i = i_+1; const Element2d & el = mesh.SurfaceElement(i); ELEMENT_TYPE typ = el.GetType(); - top.GetSurfaceElementEdges (i, ednums); + // top.GetSurfaceElementEdges (i, ednums); + auto ednums = top.GetEdges (SurfaceElementIndex(i_)); + // cout << "ednums = " << ednums << endl; + int fanum = top.GetSurfaceElementFace (i); int elnv = top.GetNVertices (typ); int elned = ednums.Size(); nnums.SetSize(elnv+elned+1); - for (int j = 1; j <= elnv; j++) - nnums.Elem(j) = el.PNum(j)+1-PointIndex::BASE; - for (int j = 1; j <= elned; j++) - nnums.Elem(elnv+j) = nv+ednums.Elem(j); - nnums.Elem(elnv+elned+1) = fanum; + for (int j = 0; j < elnv; j++) + nnums[j] = el[j]+1-PointIndex::BASE; + for (int j = 0; j < elned; j++) + nnums[elnv+j] = nv+ednums[j]+1; + nnums[elnv+elned] = fanum; for (int j = 0; j < nnums.Size(); j++) cluster_reps.Elem(nnums[j]) = nnums[j]; } - }); + }, ngcore::TasksPerThread(4)); - NgProfiler::StopTimer(timer2); - NgProfiler::StartTimer(timer3); + // NgProfiler::StopTimer(timer2); + // NgProfiler::StartTimer(timer3); static const int hex_cluster[] = @@ -215,7 +222,8 @@ namespace netgen do { - (*tracer) ("update cluster, identify", false); + static Timer t("update cluster, identify"); + RegionTimer rtr(t); cnt++; changed = 0; @@ -267,23 +275,24 @@ namespace netgen if (clustertab) { - top.GetElementEdges (i, ednums); - top.GetElementFaces (i, fanums); + // top.GetElementEdges (i, ednums); + // top.GetElementFaces (i, fanums); + auto ednums = top.GetEdges (ElementIndex(i-1)); + auto fanums = top.GetFaces (ElementIndex(i-1)); int elnv = top.GetNVertices (typ); int elned = ednums.Size(); int elnfa = fanums.Size(); nnums.SetSize(elnv+elned+elnfa+1); - for (int j = 1; j <= elnv; j++) - nnums.Elem(j) = el.PNum(j)+1-PointIndex::BASE; - for (int j = 1; j <= elned; j++) - nnums.Elem(elnv+j) = nv+ednums.Elem(j); - for (int j = 1; j <= elnfa; j++) - nnums.Elem(elnv+elned+j) = nv+ned+fanums.Elem(j); - nnums.Elem(elnv+elned+elnfa+1) = nv+ned+nfa+i; + for (int j = 0; j < elnv; j++) + nnums[j] = el[j]+1-PointIndex::BASE; + for (int j = 0; j < elned; j++) + nnums[elnv+j] = nv+ednums[j]+1; + for (int j = 0; j < elnfa; j++) + nnums[elnv+elned+j] = nv+ned+fanums[j]+1; + nnums[elnv+elned+elnfa] = nv+ned+nfa+i; - for (int j = 0; j < nnums.Size(); j++) for (int k = 0; k < j; k++) @@ -338,10 +347,9 @@ namespace netgen } */ } - (*tracer) ("update cluster, identify", true); } while (changed); - NgProfiler::StopTimer(timer3); + // NgProfiler::StopTimer(timer3); /* (*testout) << "cluster reps:" << endl; for (i = 1; i <= cluster_reps.Size(); i++) diff --git a/libsrc/meshing/clusters.hpp b/libsrc/meshing/clusters.hpp index 2cd701b4..21854f39 100644 --- a/libsrc/meshing/clusters.hpp +++ b/libsrc/meshing/clusters.hpp @@ -21,13 +21,13 @@ class AnisotropicClusters int nv, ned, nfa, ne; // connected nodes, nodes = vertices, edges, faces, elements - Array cluster_reps; + NgArray cluster_reps; public: AnisotropicClusters (const Mesh & amesh); ~AnisotropicClusters(); - void Update(TaskManager tm = &DummyTaskManager, Tracer trace = &DummyTracer); + void Update(); int GetVertexRepresentant (int vnr) const { return cluster_reps.Get(vnr); } diff --git a/libsrc/meshing/curvedelems.cpp b/libsrc/meshing/curvedelems.cpp index e694968c..50d3fa35 100644 --- a/libsrc/meshing/curvedelems.cpp +++ b/libsrc/meshing/curvedelems.cpp @@ -10,8 +10,7 @@ namespace netgen // bool rational = true; - - static void ComputeGaussRule (int n, Array & xi, Array & wi) + static void ComputeGaussRule (int n, NgArray & xi, NgArray & wi) { xi.SetSize (n); wi.SetSize (n); @@ -407,8 +406,17 @@ namespace netgen } - static Array> jacpols2; + static NgArray> jacpols2; + void CurvedElements::buildJacPols() + { + if (!jacpols2.Size()) + { + jacpols2.SetSize (100); + for (int i = 0; i < 100; i++) + jacpols2[i] = make_shared (100, i, 2); + } + } // compute face bubbles up to order n, 0 < y, y-x < 1, x+y < 1 template @@ -530,7 +538,7 @@ namespace netgen CurvedElements :: CurvedElements (const Mesh & amesh) - : mesh (amesh) + : mesh(amesh) { order = 1; rational = 0; @@ -540,32 +548,24 @@ namespace netgen CurvedElements :: ~CurvedElements() { - jacpols2.SetSize(0); } void CurvedElements :: BuildCurvedElements(const Refinement * ref, int aorder, bool arational) { - bool working = (ntasks == 1) || (id > 0); + auto & geo = *mesh.GetGeometry(); 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); - Array procs; -#else - // curve_comm = mesh.GetCommunicator(); #endif - int rank = curve_comm.Rank(); - int ntasks = curve_comm.Size(); + int ntasks = comm.Size(); + bool working = (ntasks == 1) || (comm.Rank() > 0); if (working) order = aorder; @@ -589,7 +589,7 @@ namespace netgen rational = arational; - Array edgenrs; + NgArray edgenrs; int nedges = top.GetNEdges(); int nfaces = top.GetNFaces(); @@ -622,7 +622,8 @@ namespace netgen if (mesh.GetDimension() == 3) for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++) { - top.GetEdges (i, edgenrs); + // top.GetEdges (i, edgenrs); + auto edgenrs = top.GetEdges (i); for (int j = 0; j < edgenrs.Size(); j++) edgeorder[edgenrs[j]] = aorder; faceorder[top.GetFace (i)] = aorder; @@ -639,43 +640,33 @@ namespace netgen #ifdef PARALLEL - TABLE send_orders(ntasks), recv_orders(ntasks); + // TABLE send_orders(ntasks), recv_orders(ntasks); + DynamicTable send_orders(ntasks), recv_orders(ntasks); 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); + comm.ExchangeTable (send_orders, recv_orders, MPI_TAG_CURVE); if (ntasks > 1 && working) { Array 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 @@ -715,50 +706,44 @@ namespace netgen return; } - Array xi, weight; + NgArray xi, weight; ComputeGaussRule (aorder+4, xi, weight); // on (0,1) - if (!jacpols2.Size()) - { - jacpols2.SetSize (100); - for (int i = 0; i < 100; i++) - jacpols2[i] = make_shared (100, i, 2); - } - + buildJacPols(); PrintMessage (3, "Curving edges"); if (mesh.GetDimension() == 3 || rational) { - Array surfnr(nedges); - Array gi0(nedges); - Array gi1(nedges); + static Timer tce("curve edges"); RegionTimer reg(tce); + NgArray surfnr(nedges); + NgArray gi0(nedges); + NgArray gi1(nedges); surfnr = -1; if (working) for (SurfaceElementIndex i = 0; i < mesh.GetNSE(); i++) { - top.GetEdges (i, edgenrs); + // top.GetEdges (i, edgenrs); + auto edgenrs = top.GetEdges(i); const Element2d & el = mesh[i]; const ELEMENT_EDGE * edges = MeshTopology::GetEdges0 (el.GetType()); 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); + } + } } @@ -766,51 +751,46 @@ namespace netgen if (ntasks > 1) { // distribute it ... - TABLE senddata(ntasks), recvdata(ntasks); + // TABLE senddata(ntasks), recvdata(ntasks); + DynamicTable 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); + comm.ExchangeTable (senddata, recvdata, MPI_TAG_CURVE); - Array cnt(ntasks); + 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 @@ -835,8 +815,8 @@ namespace netgen { Point<3> pm = Center (p1, p2); - Vec<3> n1 = ref -> GetNormal (p1, surfnr[e], gi0[e]); - Vec<3> n2 = ref -> GetNormal (p2, surfnr[e], gi1[e]); + Vec<3> n1 = geo.GetNormal (surfnr[e], p1, &gi0[e]); + Vec<3> n2 = geo.GetNormal (surfnr[e], p2, &gi1[e]); // p3 = pm + alpha1 n1 + alpha2 n2 @@ -873,7 +853,7 @@ namespace netgen Vec<3> v05 = 0.25 * Vec<3> (p1) + 0.5*w* Vec<3>(p3) + 0.25 * Vec<3> (p2); v05 /= 1 + (w-1) * 0.5; Point<3> p05 (v05), pp05(v05); - ref -> ProjectToSurface (pp05, surfnr[e], gi0[e]); + geo.ProjectPointGI(surfnr[e], pp05, gi0[e]); double d = Dist (pp05, p05); if (d < dold) @@ -908,16 +888,16 @@ namespace netgen if (swap) { p = p1 + xi[j] * (p2-p1); - ref -> PointBetween (p1, p2, xi[j], - surfnr[e], gi0[e], gi1[e], - pp, ppgi); + geo.PointBetween (p1, p2, xi[j], + surfnr[e], gi0[e], gi1[e], + pp, ppgi); } else { p = p2 + xi[j] * (p1-p2); - ref -> PointBetween (p2, p1, xi[j], - surfnr[e], gi1[e], gi0[e], - pp, ppgi); + geo.PointBetween (p2, p1, xi[j], + surfnr[e], gi1[e], gi0[e], + pp, ppgi); } Vec<3> dist = pp - p; @@ -944,12 +924,12 @@ namespace netgen } - Array use_edge(nedges); - Array edge_surfnr1(nedges); - Array edge_surfnr2(nedges); - Array swap_edge(nedges); - Array edge_gi0(nedges); - Array edge_gi1(nedges); + NgArray use_edge(nedges); + NgArray edge_surfnr1(nedges); + NgArray edge_surfnr2(nedges); + NgArray swap_edge(nedges); + NgArray edge_gi0(nedges); + NgArray edge_gi1(nedges); use_edge = 0; if (working) @@ -969,62 +949,60 @@ namespace netgen if (ntasks > 1) { // distribute it ... - TABLE senddata(ntasks), recvdata(ntasks); + // TABLE senddata(ntasks), recvdata(ntasks); + DynamicTable 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); - Array cnt(ntasks); + 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); + comm.ExchangeTable (senddata, recvdata, MPI_TAG_CURVE); + + 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 @@ -1050,10 +1028,10 @@ namespace netgen if (rational) { - Vec<3> tau1 = ref -> GetTangent (p1, edge_surfnr2[edgenr], edge_surfnr1[edgenr], - edge_gi0[edgenr]); - Vec<3> tau2 = ref -> GetTangent (p2, edge_surfnr2[edgenr], edge_surfnr1[edgenr], - edge_gi1[edgenr]); + Vec<3> tau1 = geo.GetTangent(p1, edge_surfnr2[edgenr], edge_surfnr1[edgenr], + edge_gi0[edgenr]); + Vec<3> tau2 = geo.GetTangent(p2, edge_surfnr2[edgenr], edge_surfnr1[edgenr], + edge_gi1[edgenr]); // p1 + alpha1 tau1 = p2 + alpha2 tau2; Mat<3,2> mat; @@ -1079,8 +1057,8 @@ namespace netgen Vec<3> v05 = 0.25 * Vec<3> (p1) + 0.5*w* Vec<3>(p3) + 0.25 * Vec<3> (p2); v05 /= 1 + (w-1) * 0.5; Point<3> p05 (v05), pp05(v05); - ref -> ProjectToEdge (pp05, edge_surfnr1[edgenr], edge_surfnr2[edgenr], - edge_gi0[edgenr]); + geo.ProjectPointEdge(edge_surfnr1[edgenr], edge_surfnr2[edgenr], pp05, + &edge_gi0[edgenr]); double d = Dist (pp05, p05); if (d < dold) @@ -1124,16 +1102,16 @@ namespace netgen if (swap) { p = p1 + xi[j] * (p2-p1); - ref -> PointBetween (p1, p2, xi[j], - edge_surfnr2[edgenr], edge_surfnr1[edgenr], - edge_gi0[edgenr], edge_gi1[edgenr], - pp, ppgi); + geo.PointBetweenEdge(p1, p2, xi[j], + edge_surfnr2[edgenr], edge_surfnr1[edgenr], + edge_gi0[edgenr], edge_gi1[edgenr], + pp, ppgi); } else { p = p2 + xi[j] * (p1-p2); - ref -> PointBetween (p2, p1, xi[j], - edge_surfnr2[edgenr], edge_surfnr1[edgenr], + geo.PointBetweenEdge(p2, p1, xi[j], + edge_surfnr2[edgenr], edge_surfnr1[edgenr], edge_gi1[edgenr], edge_gi0[edgenr], pp, ppgi); } @@ -1166,7 +1144,7 @@ namespace netgen PrintMessage (3, "Curving faces"); - Array surfnr(nfaces); + NgArray surfnr(nfaces); surfnr = -1; if (working) @@ -1175,36 +1153,33 @@ namespace netgen mesh.GetFaceDescriptor(mesh[i].GetIndex()).SurfNr(); #ifdef PARALLEL - TABLE send_surfnr(ntasks), recv_surfnr(ntasks); + // TABLE send_surfnr(ntasks), recv_surfnr(ntasks); + DynamicTable send_surfnr(ntasks), recv_surfnr(ntasks); 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); + comm.ExchangeTable (send_surfnr, recv_surfnr, MPI_TAG_CURVE); if (ntasks > 1 && working) { - Array cnt(ntasks); + 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 if (mesh.GetDimension() == 3 && working) - { + { + static Timer tcf("curve faces"); RegionTimer reg(tcf); for (int f = 0; f < nfaces; f++) { int facenr = f; @@ -1212,7 +1187,7 @@ namespace netgen // if (el.GetType() == TRIG && order >= 3) if (top.GetFaceType(facenr+1) == TRIG && order >= 3) { - ArrayMem verts(3); + NgArrayMem verts(3); top.GetFaceVertices (facenr+1, verts); int fnums[] = { 0, 1, 2 }; @@ -1235,8 +1210,8 @@ namespace netgen dmat = 0.0; int np = sqr(xi.Size()); - Array > xia(np); - Array > xa(np); + NgArray > xia(np); + NgArray > xa(np); for (int jx = 0, jj = 0; jx < xi.Size(); jx++) for (int jy = 0; jy < xi.Size(); jy++, jj++) @@ -1244,7 +1219,7 @@ namespace netgen // CalcMultiPointSurfaceTransformation (&xia, i, &xa, NULL); - Array edgenrs; + NgArray edgenrs; top.GetFaceEdges (facenr+1, edgenrs); for (int k = 0; k < edgenrs.Size(); k++) edgenrs[k]--; @@ -1291,7 +1266,21 @@ namespace netgen Point<3> pp = xa[jj]; // ref -> ProjectToSurface (pp, mesh.GetFaceDescriptor(el.GetIndex()).SurfNr()); - ref -> ProjectToSurface (pp, surfnr[facenr]); + /** + with MPI and an interior surface element between volume elements assigned to different + procs, only one of them has the surf-el + **/ + 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 + { geo.ProjectPoint(surfnr[facenr], pp); } Vec<3> dist = pp-xa[jj]; CalcTrigShape (order1, lami[fnums[1]]-lami[fnums[0]], @@ -1371,8 +1360,7 @@ namespace netgen #ifdef PARALLEL - curve_comm.Barrier(); - // MPI_Comm_free (&curve_comm); + comm.Barrier(); #endif } @@ -1405,7 +1393,7 @@ namespace netgen if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - info.edgenr = top.GetSegmentEdge (elnr+1)-1; + info.edgenr = top.GetEdge (elnr); info.ndof += edgeorder[info.edgenr]-1; } @@ -1417,7 +1405,7 @@ namespace netgen template void CurvedElements :: - CalcSegmentTransformation (T xi, SegmentIndex elnr, + CalcSegmentTransformation (const T & xi, SegmentIndex elnr, Point<3,T> * x, Vec<3,T> * dxdxi, bool * curved) { if (mesh.coarsemesh) @@ -1446,24 +1434,52 @@ namespace netgen // TVector shapes, dshapes; - // Array > coefs; + // NgArray > coefs; SegmentInfo info; info.elnr = elnr; info.order = order; info.ndof = info.nv = 2; + if (order == 1) + { + auto & seg = mesh.LineSegment(elnr); + if (seg.GetNP() == 2) + { + if (x) + *x = Point<3,T>(xi * Vec<3>(mesh[seg.PNums()[0]]) + (1-xi) * Vec<3>(mesh[seg.PNums()[1]])); + if (dxdxi) + *dxdxi = Vec<3>(mesh[seg.PNums()[0]])-Vec<3>(mesh[seg.PNums()[1]]); + } + else + { + if (x) + { + *x = Point<3,T>(2*(xi-1)*(xi-0.5) * Vec<3>(mesh[seg.PNums()[1]]) + + 4*xi*(1-xi) * Vec<3>(mesh[seg.PNums()[2]]) + + 2*xi*(xi-0.5) * Vec<3>(mesh[seg.PNums()[0]])); + } + if (dxdxi) + *dxdxi = (4*xi-1)*Vec<3>(mesh[seg.PNums()[0]]) + + (4*xi-3)*Vec<3>(mesh[seg.PNums()[1]]) + + (4-8*xi)*Vec<3>(mesh[seg.PNums()[2]]); + + } + return; + } + + if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - info.edgenr = top.GetSegmentEdge (elnr+1)-1; + info.edgenr = top.GetEdge (elnr); info.ndof += edgeorder[info.edgenr]-1; } - ArrayMem,100> coefs(info.ndof); - ArrayMem shapes_mem(info.ndof); + NgArrayMem,100> coefs(info.ndof); + NgArrayMem shapes_mem(info.ndof); TFlatVector shapes(info.ndof, &shapes_mem[0]); - ArrayMem dshapes_mem(info.ndof); + NgArrayMem dshapes_mem(info.ndof); TFlatVector dshapes(info.ndof, &dshapes_mem[0]); @@ -1575,7 +1591,7 @@ namespace netgen } void CurvedElements :: - GetCoefficients (SegmentInfo & info, Array > & coefs) const + GetCoefficients (SegmentInfo & info, NgArray > & coefs) const { const Segment & el = mesh[info.elnr]; @@ -1616,7 +1632,7 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; return mesh.coarsemesh->GetCurvedElements().IsSurfaceElementCurved (hpref_el.coarse_elnr); } @@ -1663,7 +1679,7 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; // xi umrechnen double lami[4]; @@ -1771,10 +1787,10 @@ namespace netgen } - ArrayMem,100> coefs(info.ndof); - ArrayMem shapes_mem(info.ndof); + NgArrayMem,100> coefs(info.ndof); + NgArrayMem shapes_mem(info.ndof); TFlatVector shapes(info.ndof, &shapes_mem[0]); - ArrayMem dshapes_mem(2*info.ndof); + NgArrayMem dshapes_mem(2*info.ndof); MatrixFixWidth<2> dshapes(info.ndof, &dshapes_mem[0]); @@ -1960,7 +1976,7 @@ namespace netgen template void CurvedElements :: - CalcElementDShapes (SurfaceElementInfo & info, const Point<2,T> xi, MatrixFixWidth<2,T> dshapes) const + CalcElementDShapes (SurfaceElementInfo & info, const Point<2,T> xi, MatrixFixWidth<2,T> & dshapes) const { const Element2d & el = mesh[info.elnr]; ELEMENT_TYPE type = el.GetType(); @@ -2168,7 +2184,7 @@ namespace netgen { -1, 1 } }; // double hshapes[20], hdshapes[20]; - ArrayMem hshapes(order+1), hdshapes(order+1); + NgArrayMem hshapes(order+1), hdshapes(order+1); int ii = 4; const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (QUAD); @@ -2239,6 +2255,20 @@ namespace netgen switch (el.GetType()) { + case TRIG6: + { + AutoDiff<2,T> lam3 = 1-x-y; + AutoDiff<2,T> lami[6] = { x * (2*x-1), y * (2*y-1), lam3 * (2*lam3-1), + 4 * y * lam3, 4 * x * lam3, 4 * x * y }; + for (int j = 0; j < 6; j++) + { + Point<3> p = mesh[el[j]]; + for (int k = 0; k < DIM_SPACE; k++) + mapped_x[k] += p(k) * lami[j]; + } + break; + } + case TRIG: { // if (info.order >= 2) return false; // not yet supported @@ -2345,7 +2375,7 @@ namespace netgen template void CurvedElements :: - GetCoefficients (SurfaceElementInfo & info, Array > & coefs) const + GetCoefficients (SurfaceElementInfo & info, NgArray > & coefs) const { const Element2d & el = mesh[info.elnr]; coefs.SetSize (info.ndof); @@ -2379,10 +2409,10 @@ namespace netgen template void CurvedElements :: - GetCoefficients<2> (SurfaceElementInfo & info, Array > & coefs) const; + GetCoefficients<2> (SurfaceElementInfo & info, NgArray > & coefs) const; template void CurvedElements :: - GetCoefficients<3> (SurfaceElementInfo & info, Array > & coefs) const; + GetCoefficients<3> (SurfaceElementInfo & info, NgArray > & coefs) const; @@ -2398,7 +2428,7 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; return mesh.coarsemesh->GetCurvedElements().IsElementCurved (hpref_el.coarse_elnr); } @@ -2433,19 +2463,12 @@ namespace netgen if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - - info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0); - for (int i = 0; i < info.nedges; i++) - info.edgenrs[i]--; - info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0); - for (int i = 0; i < info.nfaces; i++) - info.facenrs[i]--; - - for (int i = 0; i < info.nedges; i++) - info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]]; - for (int i = 0; i < info.nfaces; i++) - info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]]; + for (auto e : top.GetEdges(elnr)) + info.ndof += edgecoeffsindex[e+1] - edgecoeffsindex[e]; + + for (auto f : top.GetFaces(elnr)) + info.ndof += facecoeffsindex[f+1] - facecoeffsindex[f]; } return (info.ndof > info.nv); @@ -2457,7 +2480,7 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; return mesh.coarsemesh->GetCurvedElements().IsElementHighOrder (hpref_el.coarse_elnr); } @@ -2472,17 +2495,13 @@ namespace netgen if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - - info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0); - for (int i = 0; i < info.nedges; i++) info.edgenrs[i]--; - info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0); - for (int i = 0; i < info.nfaces; i++) info.facenrs[i]--; + for (auto e : top.GetEdges(elnr)) + if (edgecoeffsindex[e+1] > edgecoeffsindex[e]) return true; + + for (auto f : top.GetFaces(elnr)) + if (facecoeffsindex[f+1] > facecoeffsindex[f]) return true; - for (int i = 0; i < info.nedges; i++) - if (edgecoeffsindex[info.edgenrs[i]+1] > edgecoeffsindex[info.edgenrs[i]]) return true; - for (int i = 0; i < info.nfaces; i++) - if (facecoeffsindex[info.facenrs[i]+1] > facecoeffsindex[info.facenrs[i]]) return true; } return false; } @@ -2500,7 +2519,7 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; // xi umrechnen double lami[8]; @@ -2551,7 +2570,8 @@ namespace netgen if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - + + /* info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0); for (int i = 0; i < info.nedges; i++) info.edgenrs[i]--; @@ -2559,17 +2579,28 @@ namespace netgen info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0); for (int i = 0; i < info.nfaces; i++) info.facenrs[i]--; - + */ + info.SetEdges (top.GetEdges(elnr)); + info.SetFaces (top.GetFaces(elnr)); + + /* for (int i = 0; i < info.nedges; i++) info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]]; for (int i = 0; i < info.nfaces; i++) info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]]; + */ + + for (auto e : info.GetEdges()) + info.ndof += edgecoeffsindex[e+1] - edgecoeffsindex[e]; + + for (auto f : info.GetFaces()) + info.ndof += facecoeffsindex[f+1] - facecoeffsindex[f]; } } - ArrayMem mem(info.ndof); + NgArrayMem mem(info.ndof); TFlatVector shapes(info.ndof, &mem[0]); - ArrayMem dshapes_mem(info.ndof*3); + NgArrayMem dshapes_mem(info.ndof*3); MatrixFixWidth<3> dshapes(info.ndof, &dshapes_mem[0]); CalcElementShapes (info, xi, shapes); @@ -2922,7 +2953,7 @@ namespace netgen int ii = 8; const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (HEX); - for (int i = 0; i < 8; i++) + for (int i = 0; i < 12; i++) { int eorder = edgeorder[info.edgenrs[i]]; if (eorder >= 2) @@ -2991,7 +3022,7 @@ namespace netgen template void CurvedElements :: - CalcElementDShapes (ElementInfo & info, const Point<3,T> xi, MatrixFixWidth<3,T> dshapes) const + CalcElementDShapes (ElementInfo & info, const Point<3,T> xi, MatrixFixWidth<3,T> & dshapes) const { // static int timer = NgProfiler::CreateTimer ("calcelementdshapes"); @@ -3227,7 +3258,7 @@ namespace netgen vi1 = vi1 % 3; vi2 = vi2 % 3; - ArrayMem shapei_mem(order+1); + NgArrayMem shapei_mem(order+1); TFlatVector shapei(order+1, &shapei_mem[0]); CalcScaledEdgeShapeDxDt<3> (order, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &dshapes(ii,0) ); CalcScaledEdgeShape(order, lami[vi1]-lami[vi2], lami[vi1]+lami[vi2], &shapei(0) ); @@ -3310,8 +3341,8 @@ namespace netgen if(el[fav[1]] > el[fav[2]]) swap(fav[1],fav[2]); if(el[fav[0]] > el[fav[1]]) swap(fav[0],fav[1]); - ArrayMem dshapei_mem(ndf); - ArrayMem shapei_mem(ndf); + NgArrayMem dshapei_mem(ndf); + NgArrayMem shapei_mem(ndf); MatrixFixWidth<2,T> dshapei(ndf, &dshapei_mem[0]); TFlatVector shapei(ndf, &shapei_mem[0]); @@ -3451,7 +3482,7 @@ namespace netgen int vi1 = (edges[i][0]-1), vi2 = (edges[i][1]-1); if (el[vi1] > el[vi2]) swap (vi1, vi2); - ArrayMem shapei_mem(eorder+1); + NgArrayMem shapei_mem(eorder+1); TFlatVector shapei(eorder+1,&shapei_mem[0]); CalcScaledEdgeShapeDxDt<3> (eorder, sigma[vi1]-sigma[vi2], 1-z, &dshapes(ii,0) ); CalcScaledEdgeShape(eorder, sigma[vi1]-sigma[vi2], 1-z, &shapei(0) ); @@ -3612,12 +3643,11 @@ namespace netgen { -1, 1, 1 } }; - ArrayMem hshapes(order+1), hdshapes(order+1); + NgArrayMem hshapes(order+1), hdshapes(order+1); int ii = 8; const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (HEX); - - for (int i = 0; i < 8; i++) + for (int i = 0; i < 12; i++) { int eorder = edgeorder[info.edgenrs[i]]; if (eorder >= 2) @@ -3846,7 +3876,7 @@ namespace netgen // int ii = 8; const ELEMENT_EDGE * edges = MeshTopology::GetEdges1 (HEX); - for (int i = 0; i < 8; i++) + for (int i = 0; i < 12; i++) { int eorder = edgeorder[info.edgenrs[i]]; if (eorder >= 2) @@ -3945,9 +3975,9 @@ namespace netgen /* void CurvedElements :: - CalcMultiPointSegmentTransformation (Array * xi, SegmentIndex segnr, - Array > * x, - Array > * dxdxi) + CalcMultiPointSegmentTransformation (NgArray * xi, SegmentIndex segnr, + NgArray > * x, + NgArray > * dxdxi) { ; } @@ -4004,14 +4034,14 @@ namespace netgen SIMD * dxdxi, size_t sdxdxi); template void CurvedElements :: - CalcSegmentTransformation (double xi, SegmentIndex elnr, + CalcSegmentTransformation (const double & xi, SegmentIndex elnr, Point<3,double> * x, Vec<3,double> * dxdxi, bool * curved); void CurvedElements :: - CalcMultiPointSurfaceTransformation (Array< Point<2> > * xi, SurfaceElementIndex elnr, - Array< Point<3> > * x, - Array< Mat<3,2> > * dxdxi) + CalcMultiPointSurfaceTransformation (NgArray< Point<2> > * xi, SurfaceElementIndex elnr, + NgArray< Point<3> > * x, + NgArray< Mat<3,2> > * dxdxi) { double * px = (x) ? &(*x)[0](0) : NULL; double * pdxdxi = (dxdxi) ? &(*dxdxi)[0](0) : NULL; @@ -4035,13 +4065,13 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; // xi umrechnen T lami[4]; TFlatVector vlami(4, lami); - ArrayMem, 50> coarse_xi (npts); + NgArrayMem, 50> coarse_xi (npts); for (int pi = 0; pi < npts; pi++) { @@ -4195,13 +4225,13 @@ namespace netgen // THESE LAST LINES ARE COPIED FROM CurvedElements::CalcSurfaceTransformation - ArrayMem,100> coefs(info.ndof); + NgArrayMem,100> coefs(info.ndof); GetCoefficients (info, coefs); - ArrayMem shapes_mem(info.ndof); + NgArrayMem shapes_mem(info.ndof); TFlatVector shapes(info.ndof, &shapes_mem[0]); - ArrayMem dshapes_mem(info.ndof*2); + NgArrayMem dshapes_mem(info.ndof*2); MatrixFixWidth<2,T> dshapes(info.ndof,&shapes_mem[0]); @@ -4322,9 +4352,9 @@ namespace netgen void CurvedElements :: - CalcMultiPointElementTransformation (Array< Point<3> > * xi, ElementIndex elnr, - Array< Point<3> > * x, - Array< Mat<3,3> > * dxdxi) + CalcMultiPointElementTransformation (NgArray< Point<3> > * xi, ElementIndex elnr, + NgArray< Point<3> > * x, + NgArray< Mat<3,3> > * dxdxi) { double * px = (x) ? &(*x)[0](0) : NULL; double * pdxdxi = (dxdxi) ? &(*dxdxi)[0](0) : NULL; @@ -4347,7 +4377,7 @@ namespace netgen FlatVector vlami(8, lami); - ArrayMem, 50> coarse_xi (xi->Size()); + NgArrayMem, 50> coarse_xi (xi->Size()); for (int pi = 0; pi < xi->Size(); pi++) { @@ -4427,7 +4457,7 @@ namespace netgen // info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr]; } - Array > coefs(info.ndof); + NgArray > coefs(info.ndof); GetCoefficients (info, &coefs[0]); if (x) { @@ -4499,14 +4529,14 @@ namespace netgen if (mesh.coarsemesh) { const HPRefElement & hpref_el = - (*mesh.hpelements) [mesh[elnr].hp_elnr]; + (*mesh.hpelements) [mesh[elnr].GetHpElnr()]; // xi umrechnen T lami[8]; TFlatVector vlami(8, &lami[0]); - ArrayMem coarse_xi (3*n); + NgArrayMem coarse_xi (3*n); for (int pi = 0; pi < n; pi++) { @@ -4585,20 +4615,15 @@ namespace netgen if (info.order > 1) { const MeshTopology & top = mesh.GetTopology(); - - info.nedges = top.GetElementEdges (elnr+1, info.edgenrs, 0); - for (int i = 0; i < info.nedges; i++) - info.edgenrs[i]--; - info.nfaces = top.GetElementFaces (elnr+1, info.facenrs, 0); - for (int i = 0; i < info.nfaces; i++) - info.facenrs[i]--; + info.SetEdges (top.GetEdges(elnr)); + info.SetFaces (top.GetFaces(elnr)); - for (int i = 0; i < info.nedges; i++) - info.ndof += edgecoeffsindex[info.edgenrs[i]+1] - edgecoeffsindex[info.edgenrs[i]]; - for (int i = 0; i < info.nfaces; i++) - info.ndof += facecoeffsindex[info.facenrs[i]+1] - facecoeffsindex[info.facenrs[i]]; - // info.ndof += facecoeffsindex[info.facenr+1] - facecoeffsindex[info.facenr]; + for (auto e : info.GetEdges()) + info.ndof += edgecoeffsindex[e+1] - edgecoeffsindex[e]; + + for (auto f : info.GetFaces()) + info.ndof += facecoeffsindex[f+1] - facecoeffsindex[f]; } // NgProfiler::StopTimer (timer2); @@ -4624,12 +4649,12 @@ namespace netgen } if (ok) return; - ArrayMem,100> coefs(info.ndof); - ArrayMem shapes_mem(info.ndof); + NgArrayMem,100> coefs(info.ndof); + NgArrayMem shapes_mem(info.ndof); TFlatVector shapes(info.ndof, &shapes_mem[0]); - ArrayMem dshapes_mem(3*info.ndof); + NgArrayMem dshapes_mem(3*info.ndof); MatrixFixWidth<3,T> dshapes(info.ndof, &dshapes_mem[0]); // NgProfiler::StopTimer (timer3); diff --git a/libsrc/meshing/curvedelems.hpp b/libsrc/meshing/curvedelems.hpp index 29c911aa..59fbb86d 100644 --- a/libsrc/meshing/curvedelems.hpp +++ b/libsrc/meshing/curvedelems.hpp @@ -18,21 +18,22 @@ class CurvedElements { const Mesh & mesh; - Array edgeorder; - Array faceorder; + NgArray edgeorder; + NgArray faceorder; - Array edgecoeffsindex; - Array facecoeffsindex; + NgArray edgecoeffsindex; + NgArray facecoeffsindex; - Array< Vec<3> > edgecoeffs; - Array< Vec<3> > facecoeffs; + NgArray< Vec<3> > edgecoeffs; + NgArray< Vec<3> > facecoeffs; - Array< double > edgeweight; // for rational 2nd order splines + NgArray< double > edgeweight; // for rational 2nd order splines int order; bool rational; bool ishighorder; + void buildJacPols(); public: DLL_HEADER CurvedElements (const Mesh & amesh); @@ -48,16 +49,18 @@ public: int GetOrder () { return order; } - virtual void DoArchive(Archive& ar) + void DoArchive(Archive& ar) { + if(ar.Input()) + buildJacPols(); ar & edgeorder & faceorder & edgecoeffsindex & facecoeffsindex & edgecoeffs & facecoeffs & edgeweight & order & rational & ishighorder; } - bool IsSegmentCurved (SegmentIndex segnr) const; - bool IsSurfaceElementCurved (SurfaceElementIndex sei) const; - bool IsElementCurved (ElementIndex ei) const; - bool IsElementHighOrder (ElementIndex ei) const; + DLL_HEADER bool IsSegmentCurved (SegmentIndex segnr) const; + DLL_HEADER bool IsSurfaceElementCurved (SurfaceElementIndex sei) const; + DLL_HEADER bool IsElementCurved (ElementIndex ei) const; + DLL_HEADER bool IsElementHighOrder (ElementIndex ei) const; void CalcSegmentTransformation (double xi, SegmentIndex segnr, @@ -121,9 +124,9 @@ public: /* - void CalcMultiPointSegmentTransformation (Array * xi, SegmentIndex segnr, - Array > * x, - Array > * dxdxi); + void CalcMultiPointSegmentTransformation (NgArray * xi, SegmentIndex segnr, + NgArray > * x, + NgArray > * dxdxi); */ template @@ -132,9 +135,9 @@ public: T * x, size_t sx, T * dxdxi, size_t sdxdxi); - void CalcMultiPointSurfaceTransformation (Array< Point<2> > * xi, SurfaceElementIndex elnr, - Array< Point<3> > * x, - Array< Mat<3,2> > * dxdxi); + DLL_HEADER void CalcMultiPointSurfaceTransformation (NgArray< Point<2> > * xi, SurfaceElementIndex elnr, + NgArray< Point<3> > * x, + NgArray< Mat<3,2> > * dxdxi); template void CalcMultiPointSurfaceTransformation (SurfaceElementIndex elnr, int n, @@ -142,9 +145,9 @@ public: T * x, size_t sx, T * dxdxi, size_t sdxdxi); - void CalcMultiPointElementTransformation (Array< Point<3> > * xi, ElementIndex elnr, - Array< Point<3> > * x, - Array< Mat<3,3> > * dxdxi); + DLL_HEADER void CalcMultiPointElementTransformation (NgArray< Point<3> > * xi, ElementIndex elnr, + NgArray< Point<3> > * x, + NgArray< Mat<3,3> > * dxdxi); template void CalcMultiPointElementTransformation (ElementIndex elnr, int n, @@ -158,13 +161,13 @@ public: private: template - void CalcSegmentTransformation (T xi, SegmentIndex segnr, + DLL_HEADER void CalcSegmentTransformation (const T & xi, SegmentIndex segnr, Point<3,T> * x = NULL, Vec<3,T> * dxdxi = NULL, bool * curved = NULL); - void CalcSurfaceTransformation (Point<2> xi, SurfaceElementIndex elnr, + DLL_HEADER void CalcSurfaceTransformation (Point<2> xi, SurfaceElementIndex elnr, Point<3> * x = NULL, Mat<3,2> * dxdxi = NULL, bool * curved = NULL); - void CalcElementTransformation (Point<3> xi, ElementIndex elnr, + DLL_HEADER void CalcElementTransformation (Point<3> xi, ElementIndex elnr, Point<3> * x = NULL, Mat<3,3> * dxdxi = NULL, // bool * curved = NULL, void * buffer = NULL, bool valid = 0); @@ -185,7 +188,7 @@ private: template void CalcElementShapes (SegmentInfo & elnr, T xi, TFlatVector shapes) const; - void GetCoefficients (SegmentInfo & elnr, Array > & coefs) const; + void GetCoefficients (SegmentInfo & elnr, NgArray > & coefs) const; template void CalcElementDShapes (SegmentInfo & elnr, T xi, TFlatVector dshapes) const; @@ -203,13 +206,33 @@ private: int facenrs[6]; Mat<3> hdxdxi; Vec<3> hcoefs[10]; // enough for second order tets + + void SetEdges (FlatArray edges) + { + nedges = edges.Size(); + for (int i = 0; i < edges.Size(); i++) + edgenrs[i] = edges[i]; + } + + auto GetEdges() const + { return FlatArray(nedges, edgenrs); } + + void SetFaces (FlatArray faces) + { + nfaces = faces.Size(); + for (int i = 0; i < faces.Size(); i++) + facenrs[i] = faces[i]; + } + + auto GetFaces() const + { return FlatArray(nfaces, facenrs); } }; template void CalcElementShapes (ElementInfo & info, Point<3,T> xi, TFlatVector shapes) const; void GetCoefficients (ElementInfo & info, Vec<3> * coefs) const; template - void CalcElementDShapes (ElementInfo & info, const Point<3,T> xi, MatrixFixWidth<3,T> dshapes) const; + void CalcElementDShapes (ElementInfo & info, const Point<3,T> xi, MatrixFixWidth<3,T> & dshapes) const; template bool EvaluateMapping (ElementInfo & info, const Point<3,T> xi, Point<3,T> & x, Mat<3,3,T> & jac) const; @@ -221,16 +244,16 @@ private: int order; int nv; int ndof; - ArrayMem edgenrs; + NgArrayMem edgenrs; int facenr; }; template void CalcElementShapes (SurfaceElementInfo & elinfo, const Point<2,T> xi, TFlatVector shapes) const; template - void GetCoefficients (SurfaceElementInfo & elinfo, Array > & coefs) const; + void GetCoefficients (SurfaceElementInfo & elinfo, NgArray > & coefs) const; template - void CalcElementDShapes (SurfaceElementInfo & elinfo, const Point<2,T> xi, MatrixFixWidth<2,T> dshapes) const; + void CalcElementDShapes (SurfaceElementInfo & elinfo, const Point<2,T> xi, MatrixFixWidth<2,T> & dshapes) const; template bool EvaluateMapping (SurfaceElementInfo & info, const Point<2,T> xi, Point & x, Mat & jac) const; diff --git a/libsrc/meshing/debugging.cpp b/libsrc/meshing/debugging.cpp new file mode 100644 index 00000000..949bb45c --- /dev/null +++ b/libsrc/meshing/debugging.cpp @@ -0,0 +1,100 @@ +#include + +namespace netgen +{ + unique_ptr GetOpenElements( const Mesh & m, int dom = 0 ) + { + static Timer t("GetOpenElements"); RegionTimer rt(t); + auto mesh = make_unique(); + *mesh = m; + + Array interesting_points(mesh->GetNP()); + interesting_points = false; + + mesh->FindOpenElements(dom); + NgArray openelements; + openelements = mesh->OpenElements(); + + for (auto & el : openelements) + for (auto i : el.PNums()) + interesting_points[i] = true; + + for (auto & el : mesh->VolumeElements()) + { + int num_interesting_points = 0; + + for (auto pi : el.PNums()) + if(interesting_points[pi]) + num_interesting_points++; + + if(num_interesting_points==0) + el.Delete(); + el.SetIndex(num_interesting_points); + } + + mesh->SetMaterial(1, "1_point"); + mesh->SetMaterial(2, "2_points"); + mesh->SetMaterial(3, "3_points"); + mesh->SetMaterial(4, "4_points"); + mesh->Compress(); + + mesh->ClearSurfaceElements(); + + for (auto & el : openelements) + mesh->AddSurfaceElement( el ); + + return mesh; + } + + unique_ptr FilterMesh( const Mesh & m, FlatArray points, FlatArray sels, FlatArray els ) + { + static Timer t("GetOpenElements"); RegionTimer rt(t); + auto mesh_ptr = make_unique(); + auto & mesh = *mesh_ptr; + mesh = m; + + Array keep_point(mesh.GetNP()); + Array keep_sel(mesh.GetNSE()); + Array keep_el(mesh.GetNE()); + mesh.LineSegments().DeleteAll(); + + keep_point = false; + for(auto pi : points) + keep_point[pi] = true; + + auto set_keep = [&] (auto & input, auto & keep_array, auto & els) + { + keep_array = false; + for(auto ind : input) + keep_array[ind] = true; + + for(auto ind : Range(els)) + { + bool & keep = keep_array[ind]; + if(keep) continue; + + for(auto pi : mesh[ind].PNums()) + keep |= keep_point[pi]; + + if(!keep) + mesh[ind].Delete(); + } + + for(auto i = 0; i GetOpenElements( const Mesh & m, int dom = 0 ); + + unique_ptr FilterMesh( const Mesh & m, FlatArray points, FlatArray sels = Array{}, FlatArray els = Array{} ); + + + +} diff --git a/libsrc/meshing/delaunay.cpp b/libsrc/meshing/delaunay.cpp index c51598b7..349a021e 100644 --- a/libsrc/meshing/delaunay.cpp +++ b/libsrc/meshing/delaunay.cpp @@ -1,10 +1,10 @@ #include #include "meshing.hpp" - - namespace netgen { + // typedef BoxTree<3> DTREE; + typedef DelaunayTree<3> DTREE; static const int deltetfaces[][3] = { { 1, 2, 3 }, @@ -16,16 +16,10 @@ namespace netgen class DelaunayTet { PointIndex pnums[4]; - int nb[4]; + int nb[4] = {0,0,0,0}; public: - DelaunayTet () { ; } - - DelaunayTet (const DelaunayTet & el) - { - for (int i = 0; i < 4; i++) - pnums[i] = el[i]; - } + DelaunayTet () = default; DelaunayTet (const Element & el) { @@ -81,12 +75,12 @@ namespace netgen INDEX_3_CLOSED_HASHTABLE faces; // - Array & tets; + NgArray & tets; public: // estimated number of points - MeshNB (Array & atets, int np) + MeshNB (NgArray & atets, int np) : faces(200), tets(atets) { ; } @@ -155,7 +149,7 @@ namespace netgen */ class SphereList { - Array links; + NgArray links; public: SphereList () { ; } @@ -178,17 +172,31 @@ namespace netgen links.Elem (toi) = eli; } - void GetList (int eli, Array & linked) const; + void GetList (int eli, NgArray & linked) const; + + template + void IterateList (int eli, TFUNC func) + { + int pi = eli; + do + { + func(pi); + pi = links.Get(pi); + } + while (pi != eli); + } + }; - void SphereList :: GetList (int eli, Array & linked) const + void SphereList :: GetList (int eli, NgArray & linked) const { linked.SetSize (0); int pi = eli; do { +#ifdef DELAUNAY_DEBUG if (pi <= 0 || pi > links.Size()) { cerr << "link, error " << endl; @@ -200,7 +208,7 @@ namespace netgen cerr << "links have loop" << endl; exit(1); } - +#endif linked.Append (pi); pi = links.Get(pi); } @@ -212,27 +220,41 @@ namespace netgen void AddDelaunayPoint (PointIndex newpi, const Point3d & newp, - Array & tempels, + NgArray & tempels, Mesh & mesh, - BoxTree<3> & tettree, + DTREE & tettree, MeshNB & meshnb, - Array > & centers, Array & radi2, - Array & connected, Array & treesearch, - Array & freelist, SphereList & list, - IndexSet & insphere, IndexSet & closesphere) + NgArray > & centers, NgArray & radi2, + NgArray & connected, NgArray & treesearch, + NgArray & freelist, SphereList & list, + IndexSet & insphere, IndexSet & closesphere, Array & newels) { - // static Timer t("Meshing3::AddDelaunayPoint"); RegionTimer reg(t); + static Timer t("Meshing3::AddDelaunayPoint", NoTracing, NoTiming); RegionTimer reg(t); + static Timer tsearch("addpoint, search", NoTracing, NoTiming); + static Timer tfind("addpoint, find all tets", NoTracing, NoTiming); + static Timer tnewtets("addpoint, build new tets", NoTracing, NoTiming); + static Timer tinsert("addpoint, insert", NoTracing, NoTiming); + /* find any sphere, such that newp is contained in */ - DelaunayTet el; + // DelaunayTet el; int cfelind = -1; const Point<3> * pp[4]; Point<3> pc; Point3d tpmin, tpmax; - tettree.GetIntersecting (newp, newp, treesearch); + + + /* + // stop search if intersecting point is close enough to center + tettree.GetFirstIntersecting (newp, newp, treesearch, [&](const auto pi) + { + double quot = Dist2 (centers.Get(pi), newp); + return (quot < 0.917632 * radi2.Get(pi)); + } ); + // tettree.GetIntersecting (newp, newp, treesearch); double quot,minquot(1e20); @@ -249,7 +271,32 @@ namespace netgen break; } } + */ + tsearch.Start(); + double minquot{1e20}; + tettree.GetFirstIntersecting + (newp, newp, [&](const auto pi) + { + double rad2 = radi2.Get(pi); + double d2 = Dist2 (centers.Get(pi), newp); // / radi2.Get(pi); + if (d2 >= rad2) return false; + + // if (d2 < 0.917632 * rad2) + if (d2 < 0.99 * rad2) + { + cfelind = pi; + return true; + } + + if (cfelind == -1 || d2 < 0.99*minquot*rad2) + { + minquot = d2/rad2; + cfelind = pi; + } + return false; + } ); + tsearch.Stop(); if (cfelind == -1) { @@ -257,6 +304,7 @@ namespace netgen return; } + tfind.Start(); /* insphere: point is in sphere -> delete element closesphere: point is close to sphere -> considered for same center @@ -272,12 +320,12 @@ namespace netgen insphere.Add (cfelind); - int changed = 1; + bool changed = true; int nstarti = 1, starti; while (changed) { - changed = 0; + changed = false; starti = nstarti; nstarti = insphere.GetArray().Size()+1; @@ -293,18 +341,16 @@ namespace netgen // add connected spheres to insphere - list for (int j = starti; j < nstarti; j++) { - list.GetList (insphere.GetArray().Get(j), connected); - for (int k = 0; k < connected.Size(); k++) - { - int celind = connected[k]; - - if (tempels.Get(celind)[0] != -1 && - !insphere.IsIn (celind)) - { - changed = 1; - insphere.Add (celind); - } - } + list.IterateList (insphere.GetArray().Get(j), + [&](int celind) + { + if (tempels.Get(celind)[0] != PointIndex(PointIndex::INVALID) && + !insphere.IsIn (celind)) + { + changed = true; + insphere.Add (celind); + } + }); } // check neighbour-tets @@ -316,56 +362,44 @@ namespace netgen if (nbind && !insphere.IsIn (nbind) ) { - //changed - //int prec = testout->precision(); - //testout->precision(12); - //(*testout) << "val1 " << Dist2 (centers.Get(nbind), newp) - // << " val2 " << radi2.Get(nbind) * (1+1e-8) - // << " val3 " << radi2.Get(nbind) - // << " val1 / val3 " << Dist2 (centers.Get(nbind), newp)/radi2.Get(nbind) << endl; - //testout->precision(prec); - if (Dist2 (centers.Get(nbind), newp) - < radi2.Get(nbind) * (1+1e-8) ) + double d2 = Dist2 (centers.Get(nbind), newp); + if (d2 < radi2.Get(nbind) * (1+1e-8) ) closesphere.Add (nbind); - if (Dist2 (centers.Get(nbind), newp) - < radi2.Get(nbind) * (1 + 1e-12)) + if (d2 < radi2.Get(nbind) * (1 + 1e-12)) { // point is in sphere -> remove tet insphere.Add (nbind); - changed = 1; + changed = true; } else { INDEX_3 i3 = tempels.Get(helind).GetFace (k); + const Point<3> & p1 = mesh[PointIndex (i3.I1())]; + const Point<3> & p2 = mesh[PointIndex (i3.I2())]; + const Point<3> & p3 = mesh[PointIndex (i3.I3())]; - const Point<3> & p1 = mesh.Point ( PointIndex (i3.I1()) ); - const Point<3> & p2 = mesh.Point ( PointIndex (i3.I2()) ); - const Point<3> & p3 = mesh.Point ( PointIndex (i3.I3()) ); + Vec<3> n = Cross (p2-p1, p3-p1); + n /= n.Length(); - Vec<3> v1 = p2-p1; - Vec<3> v2 = p3-p1; - Vec<3> n = Cross (v1, v2); - n /= n.Length(); - - if (n * Vec3d (p1, mesh.Point (tempels.Get(helind)[k])) > 0) - n *= -1; - - double dist = n * Vec3d (p1, newp); + double dist = n * (newp-p1); + double scal = n * (mesh.Point (tempels.Get(helind)[k])-p1); + if (scal > 0) dist *= -1; if (dist > -1e-10) // 1e-10 { insphere.Add (nbind); - changed = 1; + changed = true; } } } } } // while (changed) - // Array newels; - Array newels; - + tfind.Stop(); + tnewtets.Start(); + newels.SetSize(0); + Element2d face(TRIG); for (int celind : insphere.GetArray()) @@ -385,11 +419,12 @@ namespace netgen newels.Append (newel); +#ifdef DEBUG_DELAUNAY Vec<3> v1 = mesh[face[1]] - mesh[face[0]]; Vec<3> v2 = mesh[face[2]] - mesh[face[0]]; Vec<3> n = Cross (v1, v2); - n.Normalize(); + n.Normalize(); if (n * Vec3d(mesh.Point (face[0]), mesh.Point (tempels.Get(celind)[k])) > 0) @@ -407,6 +442,7 @@ namespace netgen << mesh.Point (face[1]) << " " << mesh.Point (face[2]) << endl; } +#endif } } @@ -418,14 +454,12 @@ namespace netgen list.DeleteElement (celind); for (int k = 0; k < 4; k++) - tempels.Elem(celind)[k] = -1; + tempels.Elem(celind)[k] = PointIndex::INVALID; tettree.DeleteElement (celind); freelist.Append (celind); } - - bool hasclose = false; for (int ind : closesphere.GetArray()) { @@ -434,9 +468,13 @@ namespace netgen hasclose = true; } + /* for (int j = 1; j <= newels.Size(); j++) { const auto & newel = newels.Get(j); + */ + for (const auto & newel : newels) + { int nelind; if (!freelist.Size()) @@ -460,6 +498,7 @@ namespace netgen if (CalcSphereCenter (&pp[0], pc) ) { +#ifdef DEBUG_DELAUNAY PrintSysError ("Delaunay: New tet is flat"); (*testout) << "new tet is flat" << endl; @@ -469,16 +508,22 @@ namespace netgen for (int k = 0; k < 4; k++) (*testout) << *pp[k-1] << " "; (*testout) << endl; +#endif + ; } double r2 = Dist2 (*pp[0], pc); if (hasclose) + /* for (int k = 1; k <= closesphere.GetArray().Size(); k++) { int csameind = closesphere.GetArray().Get(k); + */ + for (int csameind : closesphere.GetArray()) + { if (!insphere.IsIn(csameind) && fabs (r2 - radi2.Get(csameind)) < 1e-10 && - Dist (pc, centers.Get(csameind)) < 1e-10) + Dist2 (pc, centers.Get(csameind)) < 1e-20) { pc = centers.Get(csameind); r2 = radi2.Get(csameind); @@ -507,8 +552,11 @@ namespace netgen tpmax.SetToMax (*pp[k]); } tpmax = tpmax + 0.01 * (tpmax - tpmin); + tinsert.Start(); tettree.Insert (tpmin, tpmax, nelind); + tinsert.Stop(); } + tnewtets.Stop(); } @@ -517,13 +565,13 @@ namespace netgen void Delaunay1 (Mesh & mesh, const MeshingParameters & mp, AdFront3 * adfront, - Array & tempels, + NgArray & tempels, int oldnp, DelaunayTet & startel, Point3d & pmin, Point3d & pmax) { static Timer t("Meshing3::Delaunay1"); RegionTimer reg(t); - Array> centers; - Array radi2; + NgArray> centers; + NgArray radi2; Box<3> bbox(Box<3>::EMPTY_BOX); @@ -561,22 +609,22 @@ namespace netgen startel[3] = mesh.AddPoint (cp4); // flag points to use for Delaunay: - BitArrayChar usep(np); - usep.Clear(); + Array usep(np); + usep = false; for (auto & face : adfront->Faces()) for (PointIndex pi : face.Face().PNums()) - usep.Set (pi); + usep[pi] = true; for (size_t i = oldnp + PointIndex::BASE; i < np + PointIndex::BASE; i++) - usep.Set (i); + usep[i] = true; for (PointIndex pi : mesh.LockedPoints()) - usep.Set (pi); + usep[pi] = true; - Array freelist; + NgArray freelist; int cntp = 0; @@ -586,13 +634,13 @@ namespace netgen pmin2 = pmin2 + 0.1 * (pmin2 - pmax2); pmax2 = pmax2 + 0.1 * (pmax2 - pmin2); - BoxTree<3> tettree(pmin2, pmax2); + DTREE tettree(pmin2, pmax2); tempels.Append (startel); meshnb.Add (1); list.AddElement (1); - Array connected, treesearch; + NgArray connected, treesearch; Box<3> tbox(Box<3>::EMPTY_BOX); for (size_t k = 0; k < 4; k++) @@ -618,8 +666,10 @@ namespace netgen IndexSet closesphere(mesh.GetNP()); // "random" reordering of points (speeds a factor 3 - 5 !!!) - Array mixed(np); - int prims[] = { 11, 13, 17, 19, 23, 29, 31, 37 }; + NgArray mixed(np); + // int prims[] = { 11, 13, 17, 19, 23, 29, 31, 37 }; + // int prims[] = { 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 }; + int prims[] = { 211, 223, 227, 229, 233, 239, 241, 251, 257, 263 }; int prim; { @@ -628,10 +678,13 @@ namespace netgen prim = prims[i]; } - for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End()-4; pi++) + // for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End()-4; pi++) + for (PointIndex pi : mesh.Points().Range().Modify(0, -4)) mixed[pi] = PointIndex ( (prim * pi) % np + PointIndex::BASE ); - for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End()-4; pi++) + Array newels; + // for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End()-4; pi++) + for (PointIndex pi : mesh.Points().Range().Modify(0, -4)) { if (pi % 1000 == 0) { @@ -647,7 +700,7 @@ namespace netgen PointIndex newpi = mixed[pi]; - if (!usep.Test(newpi)) + if (!usep[newpi]) continue; cntp++; @@ -656,7 +709,7 @@ namespace netgen AddDelaunayPoint (newpi, newp, tempels, mesh, tettree, meshnb, centers, radi2, - connected, treesearch, freelist, list, insphere, closesphere); + connected, treesearch, freelist, list, insphere, closesphere, newels); } @@ -668,6 +721,8 @@ namespace netgen PrintMessage (3, "Points: ", cntp); PrintMessage (3, "Elements: ", tempels.Size()); + PrintMessage (3, "Tree data entries per element: ", 1.0*tettree.N*tettree.GetNLeaves() / tempels.Size()); + PrintMessage (3, "Tree nodes per element: ", 1.0*tettree.GetNNodes() / tempels.Size()); // (*mycout) << cntp << " / " << tempels.Size() << " points/elements" << endl; /* @@ -681,130 +736,13 @@ namespace netgen } - - - - - void Meshing3 :: Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp) + void DelaunayRemoveDegenerated( const Mesh::T_POINTS & points, NgArray & tempels, int np ) { - static Timer t("Meshing3::Delaunay"); RegionTimer reg(t); - - int np, ne; + static Timer tdegenerated("Delaunay - remove degenerated"); RegionTimer rt(tdegenerated); - PrintMessage (1, "Delaunay meshing"); - PrintMessage (3, "number of points: ", mesh.GetNP()); - PushStatus ("Delaunay meshing"); - - - Array tempels; - Point3d pmin, pmax; - - DelaunayTet startel; - - int oldnp = mesh.GetNP(); - if (mp.blockfill) - { - BlockFillLocalH (mesh, mp); - PrintMessage (3, "number of points: ", mesh.GetNP()); - } - - np = mesh.GetNP(); - - Delaunay1 (mesh, mp, adfront, tempels, oldnp, startel, pmin, pmax); - - { - // improve delaunay - mesh by swapping !!!! - - Mesh tempmesh; - - for (auto & meshpoint : mesh.Points()) - tempmesh.AddPoint (meshpoint); - - for (auto & tempel : tempels) - { - Element el(4); - for (int j = 0; j < 4; j++) - el[j] = tempel[j]; - - el.SetIndex (1); - - const Point<3> & lp1 = mesh.Point (el[0]); - const Point<3> & lp2 = mesh.Point (el[1]); - const Point<3> & lp3 = mesh.Point (el[2]); - const Point<3> & lp4 = mesh.Point (el[3]); - Vec<3> v1 = lp2-lp1; - Vec<3> v2 = lp3-lp1; - Vec<3> v3 = lp4-lp1; - - Vec<3> n = Cross (v1, v2); - double vol = n * v3; - if (vol > 0) swap (el[2], el[3]); - - tempmesh.AddVolumeElement (el); - } - - - MeshQuality3d (tempmesh); - - tempmesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0)); - tempmesh.AddFaceDescriptor (FaceDescriptor (2, 1, 0, 0)); - - - - for (int i = 1; i <= mesh.GetNOpenElements(); i++) - { - Element2d sel = mesh.OpenElement(i); - sel.SetIndex(1); - tempmesh.AddSurfaceElement (sel); - swap (sel[1], sel[2]); - tempmesh.AddSurfaceElement (sel); - } - - - for (int i = 1; i <= 4; i++) - { - Element2d self(TRIG); - self.SetIndex (1); - startel.GetFace (i-1, self); - tempmesh.AddSurfaceElement (self); - } - - - // for (i = mesh.GetNP() - 3; i <= mesh.GetNP(); i++) - // tempmesh.AddLockedPoint (i); - for (auto pi : tempmesh.Points().Range()) - tempmesh.AddLockedPoint (pi); - - // tempmesh.PrintMemInfo(cout); - // tempmesh.Save ("tempmesh.vol"); - - for (int i = 1; i <= 4; i++) - { - tempmesh.FindOpenElements (); - - PrintMessage (5, "Num open: ", tempmesh.GetNOpenElements()); - tempmesh.CalcSurfacesOfNode (); - - tempmesh.FreeOpenElementsEnvironment (1); - - MeshOptimize3d meshopt(mp); - // tempmesh.CalcSurfacesOfNode(); - meshopt.SwapImprove(tempmesh, OPT_CONFORM); - } - - MeshQuality3d (tempmesh); - - tempels.SetSize(0); - for (auto & el : tempmesh.VolumeElements()) - tempels.Append (el); - } - - - - // remove degenerated - - BitArray badnode(mesh.GetNP()); + NgBitArray badnode(points.Size()); badnode.Clear(); + int ndeg = 0; for (int i = 1; i <= tempels.Size(); i++) { @@ -812,10 +750,10 @@ namespace netgen for (int j = 0; j < 4; j++) el[j] = tempels.Elem(i)[j]; // Element & el = tempels.Elem(i); - const Point3d & lp1 = mesh.Point (el[0]); - const Point3d & lp2 = mesh.Point (el[1]); - const Point3d & lp3 = mesh.Point (el[2]); - const Point3d & lp4 = mesh.Point (el[3]); + const Point3d & lp1 = points[el[0]]; + const Point3d & lp2 = points[el[1]]; + const Point3d & lp3 = points[el[2]]; + const Point3d & lp4 = points[el[3]]; Vec3d v1(lp1, lp2); Vec3d v2(lp1, lp3); Vec3d v3(lp1, lp4); @@ -839,7 +777,7 @@ namespace netgen Swap (el[2], el[3]); } - ne = tempels.Size(); + auto ne = tempels.Size(); for (int i = ne; i >= 1; i--) { const DelaunayTet & el = tempels.Get(i); @@ -852,166 +790,177 @@ namespace netgen PrintMessage (3, ndeg, " degenerated elements removed"); + } + + // Remove flat tets containing two adjacent surface trigs + void DelaunayRemoveTwoTriaTets( const Mesh & mesh, NgArray & tempels, NgArray & openels ) + { + static Timer topenel("Delaunay - find openel"); RegionTimer rt(topenel); // find surface triangles which are no face of any tet + BitArray bnd_points( mesh.GetNP() + PointIndex::BASE ); + bnd_points.Clear(); - INDEX_3_HASHTABLE openeltab(mesh.GetNOpenElements()+3); - Array openels; + for (int i = 1; i <= mesh.GetNOpenElements(); i++) + { + const Element2d & tri = mesh.OpenElement(i); + bnd_points.SetBit(tri[0]); + bnd_points.SetBit(tri[1]); + bnd_points.SetBit(tri[2]); + } + + auto ne = tempels.Size(); + Array tets_with_3_bnd_points(ne); + atomic cnt = 0; + + // table of tets with >= 2 boundary points, store in extra array tets with >=3 boundary points + auto p2el = ngcore::CreateSortedTable( ne, + [&](auto & table, int ei) + { + const auto & el = tempels[ei]; + int num_bnd_points = 0; + + for( auto i : Range(4) ) + if(bnd_points[el[i]]) + num_bnd_points++; + + if(num_bnd_points>1) + { + table.Add (el[0], ei); + table.Add (el[1], ei); + table.Add (el[2], ei); + table.Add (el[3], ei); + } + + // table creator is running this code 2 times, only store tets on last run + if(table.GetMode()==3 && num_bnd_points>2) + tets_with_3_bnd_points[cnt++] = ei; + }, mesh.GetNP()); + + tets_with_3_bnd_points.SetSize(cnt); + + static Timer t1("Build face table"); t1.Start(); + ngcore::ClosedHashTable< ngcore::INT<3>, int > face_table( 4*cnt + 3 ); + for(auto ei : tets_with_3_bnd_points) + for(auto j : Range(4)) + { + auto i3_ = tempels[ei].GetFace (j); + ngcore::INT<3> i3 = {i3_[0], i3_[1], i3_[2]}; + if(bnd_points[i3[0]] && bnd_points[i3[1]] && bnd_points[i3[2]]) + { + i3.Sort(); + face_table.Set( i3, true ); + } + } + t1.Stop(); + + static Timer t2("check faces"); t2.Start(); + + openels.SetSize(0); for (int i = 1; i <= mesh.GetNOpenElements(); i++) { const Element2d & tri = mesh.OpenElement(i); - INDEX_3 i3(tri[0], tri[1], tri[2]); + ngcore::INT<3> i3(tri[0], tri[1], tri[2]); i3.Sort(); - openeltab.Set (i3, i); + if(!face_table.Used(i3)) + openels.Append(i); } - for (int i = 1; i <= tempels.Size(); i++) - { - for (int j = 0; j < 4; j++) - { - INDEX_3 i3 = tempels.Get(i).GetFace (j); - i3.Sort(); - if (openeltab.Used(i3)) - openeltab.Set (i3, 0); - } - } - - // and store them in openels - for (int i = 1; i <= openeltab.GetNBags(); i++) - for (int j = 1; j <= openeltab.GetBagSize(i); j++) - { - INDEX_3 i3; - int fnr; - openeltab.GetData (i, j, i3, fnr); - if (fnr) - openels.Append (fnr); - } + t2.Stop(); + auto p2sel = ngcore::CreateSortedTable( Range(openels.Size()), + [&](auto & table, int i) + { + auto openel_i = openels[i]; + const Element2d & tri = mesh.OpenElement(openel_i); + table.Add(tri[0], openel_i); + table.Add(tri[1], openel_i); + table.Add(tri[2], openel_i); + }, mesh.GetNP()); + ngcore::BitArray badnode(mesh.GetNP()+PointIndex::BASE); + badnode.Clear(); + ngcore::ParallelForRange(openels.Size(), [&] (auto myrange) { + for (auto i_ : myrange) + { + auto i = openels[i_]; + const Element2d & tri = mesh.OpenElement(i); + for( auto edge : Range(3) ) + { + auto pi0 = tri[edge]; + auto pi1 = tri[(edge+1)%3]; + if(pi0>pi1) + Swap(pi0, pi1); - // find open triangle with close edge (from halfening of surface squares) - - INDEX_2_HASHTABLE twotrias(mesh.GetNOpenElements()+5); - // for (i = 1; i <= mesh.GetNOpenElements(); i++) - for (int ii = 1; ii <= openels.Size(); ii++) - { - int i = openels.Get(ii); - const Element2d & el = mesh.OpenElement(i); - for (int j = 1; j <= 3; j++) - { - INDEX_2 hi2 (el.PNumMod (j), el.PNumMod(j+1)); - hi2.Sort(); - if (twotrias.Used(hi2)) - { - INDEX_2 hi3; - hi3 = twotrias.Get (hi2); - hi3.I2() = el.PNumMod (j+2); - twotrias.Set (hi2, hi3); - } - else - { - INDEX_2 hi3(el.PNumMod (j+2), 0); - twotrias.Set (hi2, hi3); - } - } - } + // find other trig with edge pi0, pi1 + int i_other = -1; + for(auto ii : p2sel[pi0]) + { + if(ii==i) + continue; + auto & tri_other = mesh.OpenElement(ii); + if(tri_other[0]==pi1 || tri_other[1]==pi1 || tri_other[2]==pi1) + { + i_other = ii; + break; + } + } - INDEX_2_HASHTABLE tetedges(tempels.Size() + 5); - for (int i = 1; i <= tempels.Size(); i++) - { - const DelaunayTet & el = tempels.Get(i); - INDEX_2 i2; - for (int j = 1; j <= 6; j++) - { - switch (j) - { - case 1: i2.I1()=el[0]; i2.I2()=el[1]; break; - case 2: i2.I1()=el[0]; i2.I2()=el[2]; break; - case 3: i2.I1()=el[0]; i2.I2()=el[3]; break; - case 4: i2.I1()=el[1]; i2.I2()=el[2]; break; - case 5: i2.I1()=el[1]; i2.I2()=el[3]; break; - case 6: i2.I1()=el[2]; i2.I2()=el[3]; break; - default: i2.I1()=i2.I2()=0; break; - } - i2.Sort(); - tetedges.Set (i2, 1); - } - } - // cout << "tetedges:"; - // tetedges.PrintMemInfo (cout); + if(i_other>i) + { + auto & tri_other = mesh.OpenElement(i_other); + PointIndex pi2 = tri[(edge+2)%3]; + PointIndex pi3 = tri_other[0]+tri_other[1]+tri_other[2] - pi0 - pi1; + if(pi2>pi3) + Swap(pi2, pi3); + // search for tet with edge pi2-pi3 + for(auto ei : p2el[pi2]) + { + auto & el = tempels[ei]; - for (INDEX_2_HASHTABLE::Iterator it = twotrias.Begin(); - it != twotrias.End(); it++) - { - INDEX_2 hi2, hi3; - twotrias.GetData (it, hi2, hi3); - hi3.Sort(); - if (tetedges.Used (hi3)) - { - const Point3d & p1 = mesh.Point ( PointIndex (hi2.I1())); - const Point3d & p2 = mesh.Point ( PointIndex (hi2.I2())); - const Point3d & p3 = mesh.Point ( PointIndex (hi3.I1())); - const Point3d & p4 = mesh.Point ( PointIndex (hi3.I2())); - Vec3d v1(p1, p2); - Vec3d v2(p1, p3); - Vec3d v3(p1, p4); - Vec3d n = Cross (v1, v2); - double vol = n * v3; - - double h = v1.Length() + v2.Length() + v3.Length(); - if (fabs (vol) < 1e-4 * (h * h * h)) // old: 1e-12 - { - badnode.Set(hi3.I1()); - badnode.Set(hi3.I2()); - } - } - } + if(el[0]==pi3 || el[1]==pi3 || el[2]==pi3 || el[3]==pi3) + { + const Point3d & p1 = mesh[pi0]; + const Point3d & p2 = mesh[pi1]; + const Point3d & p3 = mesh[pi2]; + const Point3d & p4 = mesh[pi3]; + Vec3d v1(p1, p2); + Vec3d v2(p1, p3); + Vec3d v3(p1, p4); + Vec3d n = Cross (v1, v2); + double vol = n * v3; - /* - for (i = 1; i <= twotrias.GetNBags(); i++) - for (j = 1; j <= twotrias.GetBagSize (i); j++) - { - INDEX_2 hi2, hi3; - twotrias.GetData (i, j, hi2, hi3); - hi3.Sort(); - if (tetedges.Used (hi3)) - { - const Point3d & p1 = mesh.Point (hi2.I1()); - const Point3d & p2 = mesh.Point (hi2.I2()); - const Point3d & p3 = mesh.Point (hi3.I1()); - const Point3d & p4 = mesh.Point (hi3.I2()); - Vec3d v1(p1, p2); - Vec3d v2(p1, p3); - Vec3d v3(p1, p4); - Vec3d n = Cross (v1, v2); - double vol = n * v3; - - double h = v1.Length() + v2.Length() + v3.Length(); - if (fabs (vol) < 1e-4 * (h * h * h)) // old: 1e-12 - { - badnode.Set(hi3.I1()); - badnode.Set(hi3.I2()); - } - } - } - */ + double h = v1.Length() + v2.Length() + v3.Length(); + if (fabs (vol) < 1e-4 * (h * h * h)) // old: 1e-12 + { + badnode.SetBitAtomic(pi2); + badnode.SetBitAtomic(pi3); + } + break; + } + } + } + } + } + }); - ne = tempels.Size(); for (int i = ne; i >= 1; i--) { const DelaunayTet & el = tempels.Get(i); - if (badnode.Test(el[0]) || - badnode.Test(el[1]) || - badnode.Test(el[2]) || - badnode.Test(el[3]) ) + if (badnode[el[0]] || + badnode[el[1]] || + badnode[el[2]] || + badnode[el[3]] ) tempels.DeleteElement(i); } + } - - + void DelaunayRemoveIntersecting( const Mesh & mesh, NgArray & tempels, NgArray & openels, Point3d pmin, Point3d pmax ) + { + static Timer trem_intersect("Delaunay - remove intersecting"); RegionTimer rt(trem_intersect); // find intersecting: PrintMessage (3, "Remove intersecting"); @@ -1044,7 +993,7 @@ namespace netgen } } - Array neartrias; + NgArray neartrias; for (int i = 1; i <= tempels.Size(); i++) { const Point<3> *pp[4]; @@ -1126,8 +1075,11 @@ namespace netgen } } } - + } + void DelaunayRemoveOuter( const Mesh & mesh, NgArray & tempels, AdFront3 * adfront ) + { + static Timer trem_outer("Delaunay - remove outer"); RegionTimer rt(trem_outer); PrintMessage (3, "Remove outer"); @@ -1317,11 +1269,11 @@ namespace netgen PrintMessage (5, "tables filled"); - ne = tempels.Size(); - BitArray inner(ne), outer(ne); + auto ne = tempels.Size(); + NgBitArray inner(ne), outer(ne); inner.Clear(); outer.Clear(); - Array elstack; + NgArray elstack; /* int starti = 0; @@ -1338,15 +1290,17 @@ namespace netgen } */ + int lowest_undefined_el = 1; while (1) { int inside; bool done = 1; int i; - for (i = 1; i <= ne; i++) + for (i = lowest_undefined_el; i <= ne; i++) if (!inner.Test(i) && !outer.Test(i)) { + lowest_undefined_el = i+1; done = 0; break; } @@ -1571,6 +1525,137 @@ namespace netgen // mesh.points.SetSize(mesh.points.Size()-4); + PrintMessage (5, "outer removed"); + + } + + + + void Meshing3 :: Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp) + { + static Timer t("Meshing3::Delaunay"); RegionTimer reg(t); + + PrintMessage (1, "Delaunay meshing"); + PrintMessage (3, "number of points: ", mesh.GetNP()); + // PushStatus ("Delaunay meshing"); + + + NgArray tempels; + Point3d pmin, pmax; + + DelaunayTet startel; + + int oldnp = mesh.GetNP(); + if (mp.blockfill) + { + BlockFillLocalH (mesh, mp); + PrintMessage (3, "number of points: ", mesh.GetNP()); + } + + int np = mesh.GetNP(); + + Delaunay1 (mesh, mp, adfront, tempels, oldnp, startel, pmin, pmax); + + { + // improve delaunay - mesh by swapping !!!! + + Mesh tempmesh; + tempmesh.GetMemoryTracer().SetName("delaunay-tempmesh"); + + for (auto & meshpoint : mesh.Points()) + tempmesh.AddPoint (meshpoint); + + for (auto & tempel : tempels) + { + Element el(4); + for (int j = 0; j < 4; j++) + el[j] = tempel[j]; + + el.SetIndex (1); + + const Point<3> & lp1 = mesh.Point (el[0]); + const Point<3> & lp2 = mesh.Point (el[1]); + const Point<3> & lp3 = mesh.Point (el[2]); + const Point<3> & lp4 = mesh.Point (el[3]); + Vec<3> v1 = lp2-lp1; + Vec<3> v2 = lp3-lp1; + Vec<3> v3 = lp4-lp1; + + Vec<3> n = Cross (v1, v2); + double vol = n * v3; + if (vol > 0) swap (el[2], el[3]); + + tempmesh.AddVolumeElement (el); + } + + tempels.DeleteAll(); + + MeshQuality3d (tempmesh); + + tempmesh.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0)); + tempmesh.AddFaceDescriptor (FaceDescriptor (2, 1, 0, 0)); + + + + for (int i = 1; i <= mesh.GetNOpenElements(); i++) + { + Element2d sel = mesh.OpenElement(i); + sel.SetIndex(1); + tempmesh.AddSurfaceElement (sel); + swap (sel[1], sel[2]); + tempmesh.AddSurfaceElement (sel); + } + + + for (int i = 1; i <= 4; i++) + { + Element2d self(TRIG); + self.SetIndex (1); + startel.GetFace (i-1, self); + tempmesh.AddSurfaceElement (self); + } + + + // for (i = mesh.GetNP() - 3; i <= mesh.GetNP(); i++) + // tempmesh.AddLockedPoint (i); + for (auto pi : tempmesh.Points().Range()) + tempmesh.AddLockedPoint (pi); + + // tempmesh.PrintMemInfo(cout); + // tempmesh.Save ("tempmesh.vol"); + + { + MeshOptimize3d meshopt(mp); + tempmesh.Compress(); + tempmesh.FindOpenElements (); + RegionTaskManager rtm(mp.parallel_meshing ? mp.nthreads : 0); + for (auto i : Range(10)) + { + PrintMessage (5, "Num open: ", tempmesh.GetNOpenElements()); + + if(i%5==0) + tempmesh.FreeOpenElementsEnvironment (1); + + meshopt.SwapImprove(tempmesh, OPT_CONFORM); + } + tempmesh.Compress(); + } + + MeshQuality3d (tempmesh); + + tempels.SetSize(tempmesh.GetNE()); + tempels.SetSize(0); + for (auto & el : tempmesh.VolumeElements()) + tempels.Append (el); + } + + DelaunayRemoveDegenerated(mesh.Points(), tempels, np); + + NgArray openels; + DelaunayRemoveTwoTriaTets(mesh, tempels, openels); + DelaunayRemoveIntersecting(mesh, tempels, openels, pmin, pmax); + DelaunayRemoveOuter(mesh, tempels, adfront); + for (int i = 0; i < tempels.Size(); i++) { Element el(4); @@ -1579,11 +1664,9 @@ namespace netgen mesh.AddVolumeElement (el); } - PrintMessage (5, "outer removed"); - mesh.FindOpenElements(domainnr); mesh.Compress(); - PopStatus (); + // PopStatus (); } } diff --git a/libsrc/meshing/delaunay2d.cpp b/libsrc/meshing/delaunay2d.cpp index f2de6216..db6bcc71 100644 --- a/libsrc/meshing/delaunay2d.cpp +++ b/libsrc/meshing/delaunay2d.cpp @@ -1,54 +1,349 @@ #include -#include "meshing.hpp" +#include "delaunay2d.hpp" + +#include -// not yet working .... namespace netgen { - - - - class DelaunayTrig + void DelaunayTrig::CalcCenter (FlatArray, PointIndex> points) { - PointIndex pnums[3]; - Point<3> c; - double r; - double rad2; - public: - DelaunayTrig () { ; } - DelaunayTrig (int p1, int p2, int p3) - { pnums[0] = p1; pnums[1] = p2; pnums[2] = p3; } + Point<2> p1 = points[pnums[0]]; + Point<2> p2 = points[pnums[1]]; + Point<2> p3 = points[pnums[2]]; - PointIndex & operator[] (int j) { return pnums[j]; } - const PointIndex & operator[] (int j) const { return pnums[j]; } + Vec<2> v1 = p2-p1; + Vec<2> v2 = p3-p1; - void CalcCenter (Mesh & mesh) + // 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); + } + + int DelaunayMesh::GetNeighbour( 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) return -1; + auto i2 = edge_to_trig.GetData(pos); + return i2[0] == eli ? i2[1] : i2[0]; + } + + void DelaunayMesh::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); + } + } + + void DelaunayMesh::UnsetNeighbours( int eli ) + { + for(int edge : Range(3)) { - 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; - Mat<2,2> mat, inv; - mat(0,0) = v1*v1; - mat(0,1) = v1*v2; - mat(1,0) = v2*v1; - mat(1,1) = v2*v2; - Vec<2> rhs, sol; - rhs(0) = 0.5 * v1*v1; - rhs(1) = 0.5 * v2*v2; - CalcInverse (mat, inv); - sol = inv * rhs; - - c = p1 + sol(0) * v1 + sol(1) * v2; - rad2 = Dist2(c, p1); - r = sqrt(rad2); + 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); + auto i2 = edge_to_trig.GetData(pos); + + if(i2[0]==eli) + i2[0] = i2[1]; + i2[1] = -1; + + edge_to_trig.SetData (pos, i2); + } + } + + + void DelaunayMesh::AppendTrig( int pi0, int pi1, int pi2 ) + { + DelaunayTrig el; + el[0] = pi0; + el[1] = pi1; + el[2] = pi2; + + el.CalcCenter(points); + + trigs.Append(el); + int ti = trigs.Size()-1; + tree->Insert(el.BoundingBox(), ti); + + for(int i : Range(3)) + SetNeighbour(ti, i); + } + + DelaunayMesh::DelaunayMesh( Array, PointIndex> & points_, Box<2> box ) + : points(points_) + { + 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 = points.Append (p0); + auto pi1 = points.Append (p1); + auto pi2 = points.Append (p2); + AppendTrig(pi0, pi1, pi2); + } + + void DelaunayMesh::CalcIntersecting( PointIndex pi_new ) + { + static Timer t("CalcIntersecting"); RegionTimer reg(t); + + Point<2> newp = points[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; + } + } } - Point<3> 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)); } - }; + if(definitive_overlapping_trig==-1) + { + // GetMesh(pi_new)->Save("error.vol.gz"); + throw Exception("point not in any circle "+ ToString(pi_new)); + } + + Array trigs_to_visit; + trigs_to_visit.Append(definitive_overlapping_trig); + intersecting.Append(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(); + + auto & trig = trigs[ti]; + trig.visited_pi = pi_new; + + for(auto ei : Range(3)) + { + auto nb = GetNeighbour(ti, ei); + if(nb==-1) + continue; + + const auto & trig_nb = trigs[nb]; + if (trig_nb.visited_pi == pi_new) + continue; + + 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 = points[PointIndex (trig[(ei+1)%3])]; + const Point<2> p1 = points[PointIndex (trig[(ei+2)%3])]; + const Point<2> p2 = points[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); + } + } + } + + void DelaunayMesh::CalcWeights( PointIndex pi_new, std::map & weights ) + { + double eps = tree->GetTolerance(); + weights.clear(); + double sum = 0.0; + auto p = points[pi_new]; + auto pi_last = *points.Range().end()-3; + for(auto edge : edges) + { + auto v0 = points[edge[0]] - p; + auto v1 = points[edge[1]] - p; + v0.Normalize(); + v1.Normalize(); + double angle = acos(v0*v1); + for(PointIndex pi : {edge[0], edge[1]}) + { + if(pi>=pi_last) + continue; + double weight = angle/(eps+Dist(p, points[pi])); + sum += weight; + weights[pi] += weight; + } + } + double isum = 1.0/sum; + for(auto & [pi, weight] : weights) + weight *= isum; + } + + void DelaunayMesh::AddPoint( PointIndex pi_new) + { + static Timer t("AddPoint"); RegionTimer reg(t); + + CalcIntersecting(pi_new); + + 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); + } + + unique_ptr DelaunayMesh::GetMesh(PointIndex pi_new) + { + auto mesh = make_unique(); + Mesh & m = *mesh; + m.AddFaceDescriptor (FaceDescriptor (1, 1, 0, 0)); + for(auto pi : points.Range()) + m.AddPoint(P3(points[pi])); + + for (DelaunayTrig & trig : trigs) + { + if (trig[0] < 0) continue; + + Vec<3> n = Cross (P3(points[trig[1]])-P3(points[trig[0]]), + P3(points[trig[2]])-P3(points[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.Compress(); + m.AddPoint(P3(points[pi_new])); + return mesh; + } ostream & operator<< (ostream & ost, DelaunayTrig trig) { @@ -59,34 +354,32 @@ 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"); - Array > npoints; + NgArray > npoints; // adfront -> CreateTrees(); Box<3> bbox ( Box<3>::EMPTY_BOX ); double maxh = 0; - for (int i = 0; i < adfront->GetNFL(); i++) + for (int i = 0; i < adfront.GetNFL(); i++) { - const FrontLine & line = adfront->GetLine (i); + const FrontLine & line = adfront.GetLine (i); - const Point<3> & p1 = adfront->GetPoint(line.L().I1()); - const Point<3> & p2 = adfront->GetPoint(line.L().I2()); + const Point<3> & p1 = adfront.GetPoint(line.L().I1()); + const Point<3> & p2 = adfront.GetPoint(line.L().I2()); maxh = max (maxh, Dist (p1, p2)); @@ -95,15 +388,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,14 +403,20 @@ namespace netgen bool changed; do { - mesh.LocalHFunction().ClearFlags(); - - for (int i = 0; i < adfront->GetNFL(); i++) + 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); + const FrontLine & line = adfront.GetLine(i); - Box<3> bbox (adfront->GetPoint (line.L().I1())); - bbox.Add (adfront->GetPoint (line.L().I2())); + Box<3> bbox (adfront.GetPoint (line.L().I1())); + bbox.Add (adfront.GetPoint (line.L().I2())); double filld = filldist * bbox.Diam(); @@ -128,9 +424,9 @@ namespace netgen mesh.LocalHFunction().CutBoundary (bbox); } - + tcut.Stop(); - mesh.LocalHFunction().FindInnerBoxes (adfront, NULL); + mesh.LocalHFunction().FindInnerBoxes (&adfront, NULL); npoints.SetSize(0); mesh.LocalHFunction().GetInnerPoints (npoints); @@ -147,29 +443,44 @@ 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)); - if (!adfront->Inside(p2d)) + Point<2> p2d (npoints[hi](0), npoints[hi](1)); + if (!adfront.Inside(p2d)) { cout << "add outside point" << endl; (*testout) << "outside" << endl; @@ -179,52 +490,60 @@ namespace netgen } } - NgProfiler::StopTimer (timer3); - NgProfiler::StartTimer (timer4); + timer3.Stop(); + timer4.Start(); // find outer points loch2.ClearFlags(); - for (int i = 0; i < adfront->GetNFL(); i++) + for (int i = 0; i < adfront.GetNFL(); i++) { - const FrontLine & line = adfront->GetLine(i); + const FrontLine & line = adfront.GetLine(i); - Box<3> bbox (adfront->GetPoint (line.L().I1())); - bbox.Add (adfront->GetPoint (line.L().I2())); + Box<3> bbox (adfront.GetPoint (line.L().I1())); + bbox.Add (adfront.GetPoint (line.L().I2())); loch2.SetH (bbox.Center(), bbox.Diam()); } - for (int i = 0; i < adfront->GetNFL(); i++) + for (int i = 0; i < adfront.GetNFL(); i++) { - const FrontLine & line = adfront->GetLine(i); + const FrontLine & line = adfront.GetLine(i); - Box<3> bbox (adfront->GetPoint (line.L().I1())); - bbox.Add (adfront->GetPoint (line.L().I2())); + Box<3> bbox (adfront.GetPoint (line.L().I1())); + bbox.Add (adfront.GetPoint (line.L().I2())); bbox.Increase (filldist * bbox.Diam()); loch2.CutBoundary (bbox); } - loch2.FindInnerBoxes (adfront, NULL); + loch2.FindInnerBoxes (&adfront, NULL); // 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))) { PointIndex gpnum = mesh.AddPoint (npoints.Get(i)); - adfront->AddPoint (npoints.Get(i), gpnum); + 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 +551,353 @@ 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 + t1.Start(); + // Bounding box for starting trig in delaunay + Box<2> bbox (Box<2>::EMPTY_BOX); - - // face bounding box: - Box<3> bbox (Box<3>::EMPTY_BOX); - - for (int i = 0; i < adfront->GetNFL(); i++) + 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]))); + const FrontLine & line = adfront.GetLine(i); + 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); - - Array tempels; - startel.CalcCenter (mesh); - - tempels.Append (startel); - searchtree.Insert(startel.BoundingBox(), 0); - - Array closeels; - Array intersecting; - Array edges; - - - - - // reorder points - Array 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()); + + Array, PointIndex> temp_points; + for (PointIndex pi : addpoints) + { + icompress[pi] = tempmesh.AddPoint(mesh[pi]); + compress.Append(pi); + temp_points.Append(P2(mesh[pi])); + } + + for (PointIndex pi : Range(first_point_blockfill, last_point_blockfill)) + { + icompress[pi] = tempmesh.AddPoint(mesh[pi]); + compress.Append(pi); + temp_points.Append(P2(mesh[pi])); + } + t3.Stop(); + // DelaunayMesh adds surrounding trig (don't add the last 3 points to delaunay AGAIN! + auto points_range = temp_points.Range(); + + DelaunayMesh dmesh(temp_points, 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 : points_range) + dmesh.AddPoint(pi); + + auto first_new_point = points_range.Next(); + tempmesh.AddPoint(P3(temp_points[first_new_point])); + tempmesh.AddPoint(P3(temp_points[first_new_point+1])); + tempmesh.AddPoint(P3(temp_points[first_new_point+2])); + + 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 unknown 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/delaunay2d.hpp b/libsrc/meshing/delaunay2d.hpp new file mode 100644 index 00000000..40f3b000 --- /dev/null +++ b/libsrc/meshing/delaunay2d.hpp @@ -0,0 +1,74 @@ +#include "meshing.hpp" + +namespace netgen +{ + using ngcore::INT; + + 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<2> c; + + public: + double r; + double rad2; + DelaunayTrig () = default; + DelaunayTrig (int p1, int p2, int 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 (FlatArray, PointIndex> points); + + Point<2> Center() const { return c; } + double Radius2() const { return rad2; } + 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; + Array, PointIndex> & points; + + Array closeels; + Array intersecting; + Array> edges; + + int GetNeighbour( int eli, int edge ); + + void SetNeighbour( int eli, int edge ); + + void UnsetNeighbours( int eli ); + + void AppendTrig( int pi0, int pi1, int pi2 ); + + public: + DelaunayMesh( Array, PointIndex> & points_, Box<2> box ); + + void CalcIntersecting( PointIndex pi_new ); + void CalcWeights( PointIndex pi_new, std::map & weights ); + void AddPoint( PointIndex pi_new ); + Array & GetElements() { return trigs; } + unique_ptr GetMesh(PointIndex pi_new); // for debugging purposes + }; + +} // namespace netgen diff --git a/libsrc/meshing/fieldlines.cpp b/libsrc/meshing/fieldlines.cpp new file mode 100644 index 00000000..84cf0a65 --- /dev/null +++ b/libsrc/meshing/fieldlines.cpp @@ -0,0 +1,385 @@ +#include + +#include +#include +#include +#include + +#include "fieldlines.hpp" + +namespace netgen +{ + RKStepper :: ~RKStepper() + { + delete a; + } + + RKStepper :: RKStepper(int type) : a(NULL), tolerance(1e100) + { + notrestarted = 0; + + if (type == 0) // explicit Euler + { + c.SetSize(1); c[0] = 0; + b.SetSize(1); b[0] = 1; + steps = order = 1; + } + else if (type == 1) // Euler-Cauchy + { + c.SetSize(2); c[0] = 0; c[1] = 0.5; + b.SetSize(2); b[0] = 0; b[1] = 1; + NgArray size(2); + size[0] = 0; size[1] = 1; + a = new TABLE(size); + a->Set(2,1,0.5); // Set, Get: 1-based! + steps = order = 2; + } + else if (type == 2) // Simpson + { + c.SetSize(3); c[0] = 0; c[1] = 1; c[2] = 0.5; + b.SetSize(3); b[0] = b[1] = 1./6.; b[2] = 2./3.; + NgArray size(3); + size[0] = 0; size[1] = 1; size[2] = 2; + a = new TABLE(size); + a->Set(2,1,1); + a->Set(3,1,0.25); a->Set(3,2,0.25); + steps = order = 3; + } + else if (type == 3) // classical Runge-Kutta + { + c.SetSize(4); c[0] = 0; c[1] = c[2] = 0.5; c[3] = 1; + b.SetSize(4); b[0] = b[3] = 1./6.; b[1] = b[2] = 1./3.; + NgArray size(4); + size[0] = 0; size[1] = 1; size[2] = 2; size[3] = 3; + a = new TABLE(size); + a->Set(2,1,0.5); + a->Set(3,1,0); a->Set(3,2,0.5); + a->Set(4,1,0); a->Set(4,2,0); a->Set(4,3,1); + steps = order = 4; + } + + K.SetSize(steps); + } + + void RKStepper :: StartNextValCalc(const Point<3> & astartval, const double astartt, const double ah, const bool aadaptive) + { + //cout << "Starting RK-Step with h=" << ah << endl; + + stepcount = 0; + h = ah; + startt = astartt; + startval = astartval; + adaptive = aadaptive; + adrun = 0; + } + + bool RKStepper :: GetNextData(Point<3> & val, double & t, double & ah) + { + bool finished = false; + + if(stepcount <= steps && stepcount>0) + { + t = startt + c[stepcount-1]*h; + val = startval; + for(int i=0; iGet(stepcount,i+1) * K[i]; + } + + + if(stepcount == steps) + { + val = startval; + for(int i=0; i valh2 = val; + val = valh2 + 1./(pow(2.,order)-1.) * (valh2 - valh); + auto errvec = val - valh; + + double err = errvec.Length(); + + double fac = 0.7 * pow(tolerance/err,1./(order+1.)); + if(fac > 1.3) fac = 1.3; + + if(fac < 1 || notrestarted >= 2) + ah = 2.*h * fac; + + if(err < tolerance) + { + finished = true; + notrestarted++; + //(*testout) << "finished RK-Step, new h=" << ah << " tolerance " << tolerance << " err " << err << endl; + } + else + { + //ah *= 0.9; + notrestarted = 0; + //(*testout) << "restarting h " << 2.*h << " ah " << ah << " tolerance " << tolerance << " err " << err << endl; + StartNextValCalc(startval_bak,startt_bak, ah, adaptive); + } + } + } + else + { + t = startt + h; + finished = true; + } + + } + + if(stepcount == 0) + { + t = startt + c[stepcount]*h; + val = startval; + for(int i=0; iGet(stepcount,i) * K[i]; + } + + return finished; + } + + + bool RKStepper :: FeedNextF(const Vec<3> & f) + { + K[stepcount] = f; + stepcount++; + return true; + } + + + + void FieldLineCalc :: GenerateFieldLines(Array> & potential_startpoints, const int numlines) + { + + + Array> line_points; + Array line_values; + Array drawelems; + Array dirstart; + pstart.SetSize0(); + pend.SetSize0(); + values.SetSize0(); + + double crit = 1.0; + + if(randomized) + { + double sum = 0; + double lami[3]; + Vec<3> v; + + for(int i=0; i= numlines) break; + + Calc(potential_startpoints[i],line_points,line_values,drawelems,dirstart); + + bool usable = false; + + for(int j=1; j 0) ? rel_length : 0.5; + maxlength *= 2.*rad; + + thickness = (rel_thickness > 0) ? rel_thickness : 0.0015; + thickness *= 2.*rad; + + double auxtolerance = (rel_tolerance > 0) ? rel_tolerance : 1.5e-3; + auxtolerance *= 2.*rad; + + stepper.SetTolerance(auxtolerance); + + direction = adirection; + + + maxpoints = amaxpoints; + + if(direction == 0) + { + maxlength *= 0.5; + maxpoints /= 2; + } + + + critical_value = -1; + + randomized = false; + + } + + + FieldLineCalc :: ~FieldLineCalc() {;} + + + void FieldLineCalc :: Calc(const Point<3> & startpoint, Array> & points, Array & vals, Array & drawelems, Array & dirstart) + { + Vec<3> v = 0.0; + double startlami[3] = {0.0, 0.0, 0.0}; + + points.SetSize(0); + vals.SetSize(0); + drawelems.SetSize(0); + + dirstart.SetSize(0); + dirstart.Append(0); + + + int startelnr = mesh.GetElementOfPoint(startpoint,startlami,true) - 1; + (*testout) << "p = " << startpoint << "; elnr = " << startelnr << endl; + if (startelnr == -1) + return; + + mesh.SetPointSearchStartElement(startelnr); + + Vec<3> startv; + bool startdraw = func(startelnr, startlami, startv); + + double startval = startv.Length(); + + if(critical_value > 0 && fabs(startval) < critical_value) + return; + + //cout << "p = " << startpoint << "; elnr = " << startelnr << endl; + + + + for(int dir = 1; dir >= -1; dir -= 2) + { + if(dir*direction < 0) continue; + + points.Append(startpoint); + vals.Append(startval); + drawelems.Append(startdraw); + + double h = 0.001*rad/startval; // otherwise no nice lines; should be made accessible from outside + + v = startv; + if(dir == -1) v *= -1.; + + int elnr = startelnr; + double lami[3] = { startlami[0], startlami[1], startlami[2]}; + + + for(double length = 0; length < maxlength; length += h*vals.Last()) + { + if(v.Length() < 1e-12*rad) + { + (*testout) << "Current fieldlinecalculation came to a stillstand at " << points.Last() << endl; + break; + } + + double dummyt; + stepper.StartNextValCalc(points.Last(),dummyt,h,true); + stepper.FeedNextF(v); + bool drawelem = false; + + Point<3> newp; + while(!stepper.GetNextData(newp,dummyt,h) && elnr != -1) + { + elnr = mesh.GetElementOfPoint(newp,lami,true) - 1; + if(elnr != -1) + { + mesh.SetPointSearchStartElement(elnr); + drawelem = func(elnr, lami, v); + if(dir == -1) v *= -1.; + stepper.FeedNextF(v); + } + } + + if (elnr == -1) + { + //cout << "direction " < 1) + (*testout) << "Points in current fieldline: " << points.Size() << ", current position: " << newp << endl; + + if(maxpoints > 0 && points.Size() >= maxpoints) + { + break; + } + + //cout << "length " << length << " h " << h << " vals.Last() " << vals.Last() << " maxlength " << maxlength << endl; + } + dirstart.Append(points.Size()); + } + } + +} diff --git a/libsrc/meshing/fieldlines.hpp b/libsrc/meshing/fieldlines.hpp new file mode 100644 index 00000000..06b1f482 --- /dev/null +++ b/libsrc/meshing/fieldlines.hpp @@ -0,0 +1,103 @@ +#ifndef FIELDLINES_HPP_INCLUDED +#define FIELDLINES_HPP_INCLUDED + +namespace netgen +{ + +class RKStepper +{ +private: + Array c,b; + TABLE *a; + int steps; + int order; + + double tolerance; + + Array> K; + + int stepcount; + + double h; + double startt; + double startt_bak; + Point<3> startval; + Point<3> startval_bak; + + bool adaptive; + int adrun; + Point<3> valh; + + int notrestarted; + +public: + + DLL_HEADER ~RKStepper(); + + RKStepper(int type = 0); + + void SetTolerance(const double tol){tolerance = tol;} + + void StartNextValCalc(const Point<3> & astartval, const double astartt, const double ah, const bool aadaptive = false); + + bool GetNextData(Point<3> & val, double & t, double & ah); + + bool FeedNextF(const Vec<3> & f); +}; + + + + +class FieldLineCalc +{ +private: + const Mesh & mesh; + + typedef std::function &)> VectorFunction; + + const VectorFunction & func; + RKStepper stepper; + + Array values; + Array> pstart, pend; + + double maxlength; + + int maxpoints; + + int direction; + + Point3d pmin, pmax; + double rad; + + double critical_value; + + bool randomized; + + double thickness; + +public: + DLL_HEADER FieldLineCalc(const Mesh & amesh, const VectorFunction & afunc, + const double rel_length, const int amaxpoints = -1, + const double rel_thickness = -1, const double rel_tolerance = -1, const int rk_type = 0, const int adirection = 0); + + DLL_HEADER ~FieldLineCalc(); + + void SetCriticalValue(const double val) { critical_value = val; } + + void Randomized(void) { randomized = true; } + void NotRandomized(void) { randomized = false; } + + DLL_HEADER void Calc(const Point<3> & startpoint, Array> & points, Array & vals, Array & drawelems, Array & dirstart); + + DLL_HEADER void GenerateFieldLines(Array> & potential_startpoints, const int numlines); + + const auto & GetPStart() const { return pstart; } + const auto & GetPEnd() const { return pend; } + const auto & GetValues() const { return values; } + const auto GetThickness() const { return thickness; } +}; + +} // namespace netgen + +#endif // VSFIELDLINES_HPP_INCLUDED diff --git a/libsrc/meshing/findip.hpp b/libsrc/meshing/findip.hpp index f5d8e424..71de05f9 100644 --- a/libsrc/meshing/findip.hpp +++ b/libsrc/meshing/findip.hpp @@ -2,8 +2,8 @@ -inline void Minimize (const Array & a, - const Array & c, +inline void Minimize (const NgArray & a, + const NgArray & c, int * act, Vec<3> & x, double & f, int * sol) @@ -75,8 +75,8 @@ inline int FindInnerPoint (POINTArray & points, static int timer = NgProfiler::CreateTimer ("FindInnerPoint"); NgProfiler::RegionTimer reg (timer); - Array a; - Array c; + NgArray a; + NgArray c; Mat<3> m, inv; Vec<3> rs, x = 0.0, center; double f; @@ -90,9 +90,9 @@ inline int FindInnerPoint (POINTArray & points, for (int i = 0; i < nf; i++) { - Point3d p1 = points.Get(faces[i][0]); - a[i] = Cross (points.Get(faces[i][1]) - p1, - points.Get(faces[i][2]) - p1); + Point3d p1 = points[faces[i][0]]; + a[i] = Cross (points[faces[i][1]] - p1, + points[faces[i][2]] - p1); a[i] /= a[i].Length(); c[i] = - (a[i].X() * p1.X() + a[i].Y() * p1.Y() + a[i].Z() * p1.Z()); } @@ -107,7 +107,7 @@ inline int FindInnerPoint (POINTArray & points, center = 0; for (int i = 0; i < faces.Size(); i++) for (int j = 0; j < 3; j++) - center += Vec<3> (points.Get(faces[i][j])); + center += Vec<3> (points[faces[i][j]]); center /= (3*faces.Size()); @@ -118,10 +118,10 @@ inline int FindInnerPoint (POINTArray & points, { // const Element2d & el = faces[i]; // (*testout) << "el[" << i << "] = " << el << endl; - for (int j = 1; j <= 3; j++) + for (int j : Range(3)) { - double hi = Dist (points.Get(faces[i].PNumMod(j)), - points.Get(faces[i].PNumMod(j+1))); + double hi = Dist (points[faces[i][j%3]], + points[faces[i][(j+1)%3]]); if (hi > hmax) hmax = hi; } } diff --git a/libsrc/meshing/findip2.hpp b/libsrc/meshing/findip2.hpp index 95385534..62009b25 100644 --- a/libsrc/meshing/findip2.hpp +++ b/libsrc/meshing/findip2.hpp @@ -8,8 +8,8 @@ inline int FindInnerPoint2 (POINTArray & points, static int timer = NgProfiler::CreateTimer ("FindInnerPoint2"); NgProfiler::RegionTimer reg (timer); - Array a; - Array c; + NgArray a; + NgArray c; Mat<3> m, inv; Vec<3> rs, x, pmin; diff --git a/libsrc/meshing/geomsearch.cpp b/libsrc/meshing/geomsearch.cpp index 5a2192ea..dde4d5ac 100644 --- a/libsrc/meshing/geomsearch.cpp +++ b/libsrc/meshing/geomsearch.cpp @@ -19,7 +19,7 @@ namespace netgen } } - void GeomSearch3d :: Init (Array *pointsi, Array *facesi) + void GeomSearch3d :: Init (NgArray *pointsi, NgArray *facesi) { points = pointsi; faces = facesi; @@ -120,7 +120,7 @@ namespace netgen for (k = 1; k <= size.i3; k++) { INDEX ind=i+(j-1)*size.i1+(k-1)*size.i2*size.i1; - hashtable.Elem(ind) = new Array (); + hashtable.Elem(ind) = new NgArray (); } } } @@ -175,7 +175,7 @@ namespace netgen } } - void GeomSearch3d :: GetLocals(Array & locfaces, Array & findex, + void GeomSearch3d :: GetLocals(NgArray & locfaces, NgArray & findex, INDEX fstind, const Point3d& p0, double xh) { hashcount++; @@ -212,7 +212,7 @@ namespace netgen INDEX ind=ix+(iy-1)*size.i1+(iz-1)*size.i2*size.i1; //go through all elements in one hash area - const Array & area = *hashtable.Elem(ind); + const NgArray & area = *hashtable.Elem(ind); for (k = 1; k <= area.Size(); k++) { cnt2++; diff --git a/libsrc/meshing/geomsearch.hpp b/libsrc/meshing/geomsearch.hpp index d483c823..5adba567 100644 --- a/libsrc/meshing/geomsearch.hpp +++ b/libsrc/meshing/geomsearch.hpp @@ -22,7 +22,7 @@ public: virtual ~GeomSearch3d(); /// - void Init (Array *pointsi, Array *facesi); + void Init (NgArray *pointsi, NgArray *facesi); ///get elements max extension void ElemMaxExt(Point3d& minp, Point3d& maxp, const MiniElement2d& elem); @@ -41,15 +41,15 @@ public: void AddElem(const MiniElement2d& elem, INDEX elemnum); ///GetLocal faces in sphere with radius xh and middlepoint p - void GetLocals(Array & locfaces, Array & findex, + void GetLocals(NgArray & locfaces, NgArray & findex, INDEX fstind, const Point3d& p0, double xh); private: - Array *faces; // Pointers to Arrays in Adfront - Array *points; + NgArray *faces; // Pointers to Arrays in Adfront + NgArray *points; - Array *> hashtable; + NgArray *> hashtable; Point3d minext; //extension of Hashdomain Point3d maxext; diff --git a/libsrc/meshing/global.cpp b/libsrc/meshing/global.cpp index 18092496..9bbce766 100644 --- a/libsrc/meshing/global.cpp +++ b/libsrc/meshing/global.cpp @@ -1,5 +1,6 @@ #include #include "meshing.hpp" +#include namespace netgen @@ -18,10 +19,11 @@ namespace netgen // testout -> clear(ios::failbit); // ostream * testout = &cout; - ostream * testout = new ostream(0); // NetgenOutStream * testout = new NetgenOutStream; + const string netgen_version = NETGEN_VERSION; + ostream * mycout = &cout; ostream * myerr = &cerr; @@ -65,21 +67,19 @@ namespace netgen (*testout) << "Error !!! " << ch << endl << flush; } - static clock_t starttimea; + static double starttimea; void ResetTime () { - starttimea = clock(); + starttimea = WallTime(); } double GetTime () { - return double(clock() - starttimea) / CLOCKS_PER_SEC; + return WallTime() - starttimea; } - Array tets_in_qualclass; - mutex tcl_todo_mutex; int h_argc = 0; diff --git a/libsrc/meshing/global.hpp b/libsrc/meshing/global.hpp index b1da9344..e19c094b 100644 --- a/libsrc/meshing/global.hpp +++ b/libsrc/meshing/global.hpp @@ -27,8 +27,6 @@ namespace netgen // extern DLL_HEADER MeshingParameters mparam; - DLL_HEADER extern Array tets_in_qualclass; - DLL_HEADER extern mutex tcl_todo_mutex; class DLL_HEADER multithreadt diff --git a/libsrc/meshing/hexarls.cpp b/libsrc/meshing/hexarls.cpp deleted file mode 100644 index f2e9e7c6..00000000 --- a/libsrc/meshing/hexarls.cpp +++ /dev/null @@ -1,209 +0,0 @@ -namespace netgen -{ -const char * hexrules[] = { -"rule \"Hexa left-right-top\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"flags t;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 } ;\n",\ -"(1, 1, 0) { 1 } ;\n",\ -"(0, 1, 0) { 1 } ;\n",\ -"(0, 0, 1) { 1 } ;\n",\ -"(1, 0, 1) { 1 } ;\n",\ -"(1, 1, 1) { 1 } ;\n",\ -"(0, 1, 1) { 1 } ;\n",\ -"\n",\ -"mapfaces\n",\ -"(4, 3, 2, 1) del;\n",\ -"(3, 7, 6, 2) del;\n",\ -"(7, 8, 5, 6) del;\n",\ -"(8, 4, 1, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 6, 2, 1);\n",\ -"(7, 8, 4, 3);\n",\ -"\n",\ -"elements\n",\ -"(4, 3, 2, 1, 8, 7, 6, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.3 P1, 0.3 P2, 0.3 P5, 0.3 P6, -0.05 P3, -0.05 P4, -0.05 P7, -0.05 P8 };\n",\ -"{ 0.3 P3, 0.3 P4, 0.3 P7, 0.3 P8, -0.05 P1, -0.05 P2, -0.05 P5, -0.05 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.25 P1, 0.25 P2, 0.25 P5, 0.25 P6, -0.0 P3, -0.0 P4, -0.0 P7, -0.0 P8 };\n",\ -"{ 0.25 P3, 0.25 P4, 0.25 P7, 0.25 P8, -0.0 P1, -0.0 P1, -0.0 P5, -0.0 P6 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Hexa left-right-top (10)\"\n",\ -"\n",\ -"quality 10\n",\ -"\n",\ -"flags t;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 } ;\n",\ -"(1, 1, 0) { 1 } ;\n",\ -"(0, 1, 0) { 1 } ;\n",\ -"(0, 0, 1) { 1 } ;\n",\ -"(1, 0, 1) { 1 } ;\n",\ -"(1, 1, 1) { 1 } ;\n",\ -"(0, 1, 1) { 1 } ;\n",\ -"\n",\ -"mapfaces\n",\ -"(4, 3, 2, 1) del;\n",\ -"(3, 7, 6, 2) del;\n",\ -"(7, 8, 5, 6) del;\n",\ -"(8, 4, 1, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 6, 2, 1);\n",\ -"(7, 8, 4, 3);\n",\ -"\n",\ -"elements\n",\ -"(4, 3, 2, 1, 8, 7, 6, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.251 P1, 0.251 P2, 0.251 P5, 0.251 P6, -0.05 P3, -0.001 P4, -0.001 P7, -0.001 P8 };\n",\ -"{ 0.251 P3, 0.251 P4, 0.251 P7, 0.251 P8, -0.05 P1, -0.001 P2, -0.001 P5, -0.001 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.25 P1, 0.25 P2, 0.25 P5, 0.25 P6, -0.0 P3, -0.0 P4, -0.0 P7, -0.0 P8 };\n",\ -"{ 0.25 P3, 0.25 P4, 0.25 P7, 0.25 P8, -0.0 P1, -0.0 P1, -0.0 P5, -0.0 P6 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Hexa left-right-top-front\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"flags t;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 } ;\n",\ -"(1, 1, 0) { 1 } ;\n",\ -"(0, 1, 0) { 1 } ;\n",\ -"(0, 0, 1) { 1 } ;\n",\ -"(1, 0, 1) { 1 } ;\n",\ -"(1, 1, 1) { 1 } ;\n",\ -"(0, 1, 1) { 1 } ;\n",\ -"\n",\ -"mapfaces\n",\ -"(4, 3, 2, 1) del;\n",\ -"(3, 7, 6, 2) del;\n",\ -"(7, 8, 5, 6) del;\n",\ -"(8, 4, 1, 5) del;\n",\ -"(1, 2, 6, 5) del;\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(7, 8, 4, 3);\n",\ -"\n",\ -"elements\n",\ -"(4, 3, 2, 1, 8, 7, 6, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.3 P3, 0.3 P4, 0.3 P7, 0.3 P8, -0.05 P1, -0.05 P2, -0.05 P5, -0.05 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 0.25 P3, 0.25 P4, 0.25 P7, 0.25 P8, -0.0 P1, -0.0 P1, -0.0 P5, -0.0 P6 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Hexa fill\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"flags t;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 } ;\n",\ -"(1, 1, 0) { 1 } ;\n",\ -"(0, 1, 0) { 1 } ;\n",\ -"(0, 0, 1) { 1 } ;\n",\ -"(1, 0, 1) { 1 } ;\n",\ -"(1, 1, 1) { 1 } ;\n",\ -"(0, 1, 1) { 1 } ;\n",\ -"\n",\ -"mapfaces\n",\ -"(4, 3, 2, 1) del;\n",\ -"(3, 7, 6, 2) del;\n",\ -"(7, 8, 5, 6) del;\n",\ -"(8, 4, 1, 5) del;\n",\ -"(1, 2, 6, 5) del;\n",\ -"(3, 4, 8, 7) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"\n",\ -"elements\n",\ -"(4, 3, 2, 1, 8, 7, 6, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P3 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/hprefinement.cpp b/libsrc/meshing/hprefinement.cpp index d2752ba8..d6819c44 100644 --- a/libsrc/meshing/hprefinement.cpp +++ b/libsrc/meshing/hprefinement.cpp @@ -33,7 +33,7 @@ namespace netgen } HPRefElement :: HPRefElement(Element & el) : - np(el.GetNV()), index(el.GetIndex()), levelx(0), levely(0), levelz(0), type(HP_NONE), domin(-1), domout(-1) //domin,out for segements + type(HP_NONE), index(el.GetIndex()), levelx(0), levely(0), levelz(0), np(el.GetNV()), domin(-1), domout(-1) //domin,out for segments { //Reset(); for (int i=0; i & edges, INDEX_2_HASHTABLE & edgepoiclt_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint, int & levels, int & act_ref); + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint, int & levels, int & act_ref); - bool ClassifyHPElements (Mesh & mesh, Array & elements, int & act_ref, int & levels); + bool ClassifyHPElements (Mesh & mesh, NgArray & elements, int & act_ref, int & levels); - void InitHPElements(Mesh & mesh, Array & elements) + void InitHPElements(Mesh & mesh, NgArray & elements) { for(ElementIndex i = 0; i < mesh.GetNE(); i++) { @@ -600,11 +600,15 @@ namespace netgen HPRefElement hpel(mesh[i]); hpel.coarse_elnr = i; hpel.type = HP_SEGM; - hpel.index = seg.edgenr + 10000*seg.si; + // hpel.index = seg.edgenr + 10000*seg.si; + hpel.index = seg.edgenr; + hpel.si = seg.si; + /* if(seg.edgenr >= 10000) { throw NgException("assumption that seg.edgenr < 10000 is wrong"); } + */ elements.Append(hpel); } } @@ -612,7 +616,7 @@ namespace netgen /* ******************************* DoRefinement *************************************** */ - void DoRefinement (Mesh & mesh, Array & elements, + void DoRefinement (Mesh & mesh, NgArray & elements, Refinement * ref, double fac1) { elements.SetAllocSize (5 * elements.Size()); @@ -622,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; @@ -868,7 +872,7 @@ namespace netgen /* ************************** DoRefineDummies ******************************** */ - void DoRefineDummies (Mesh & mesh, Array & elements, + void DoRefineDummies (Mesh & mesh, NgArray & elements, Refinement * ref) { int oldelsize = elements.Size(); @@ -925,6 +929,7 @@ namespace netgen for (int k = 0; k < 8; k++) newel.pnums[k] = newpnums[hprs->newels[j][k]-1]; newel.index = el.index; + newel.si = el.si; newel.coarse_elnr = el.coarse_elnr; newel.levelx = newel.levely = newel.levelz = newlevel; @@ -947,7 +952,7 @@ namespace netgen - void SubdivideDegeneratedHexes (Mesh & mesh, Array & elements, double fac1) + void SubdivideDegeneratedHexes (Mesh & mesh, NgArray & elements, double fac1) { int oldne = elements.Size(); for (int i = 0; i < oldne; i++) @@ -988,7 +993,7 @@ namespace netgen for (int j = 0; j < 6; j++) { - Array pts; + NgArray pts; for (int k = 0; k < 4; k++) { bool same = 0; @@ -1086,7 +1091,7 @@ namespace netgen } - void CalcStatistics (Array & elements) + void CalcStatistics (NgArray & elements) { return; #ifdef ABC @@ -1238,9 +1243,9 @@ namespace netgen - void ReorderPoints (Mesh & mesh, Array & hpelements) + void ReorderPoints (Mesh & mesh, NgArray & hpelements) { - Array map (mesh.GetNP()); + NgArray map (mesh.GetNP()); for (int i = 1; i <= mesh.GetNP(); i++) map[i] = i; @@ -1278,10 +1283,10 @@ 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"); - Array hpts(mesh.GetNP()); + NgArray hpts(mesh.GetNP()); for (int i = 1; i <= mesh.GetNP(); i++) hpts[map[i]] = mesh.Point(i); @@ -1308,7 +1313,7 @@ namespace netgen // NgLock mem_lock (mem_mutex,1); - mesh.coarsemesh = new Mesh; + mesh.coarsemesh = make_unique(); *mesh.coarsemesh = mesh; // #ifdef CURVEDELEMS_NEW @@ -1317,14 +1322,14 @@ namespace netgen // #endif - delete mesh.hpelements; - mesh.hpelements = new Array; + // delete mesh.hpelements; + mesh.hpelements = make_unique>(); - Array & hpelements = *mesh.hpelements; + NgArray & hpelements = *mesh.hpelements; InitHPElements(mesh,hpelements); - Array nplevel; + NgArray nplevel; nplevel.Append (mesh.GetNP()); int act_ref=1; @@ -1333,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); @@ -1361,8 +1366,10 @@ namespace netgen seg[0] = hpel.pnums[0]; seg[1] = hpel.pnums[1]; // NOTE: only for less than 10000 elements (HACK) !!! - seg.edgenr = hpel.index % 10000; - seg.si = hpel.index / 10000; + // seg.edgenr = hpel.index % 10000; + // seg.si = hpel.index / 10000; + seg.edgenr = hpel.index; + seg.si = hpel.si; /* seg.epgeominfo[0].dist = hpel.param[0][0]; // he: war hpel.param[0][0] @@ -1396,10 +1403,12 @@ namespace netgen Element2d el(hpel.np); for(int j=0;j 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) { for(ElementIndex i=0;i & edges, INDEX_2_HASHTABLE & edgepoint_dom, - BitArray & cornerpoint, BitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, - INDEX_2_HASHTABLE & surf_edges, Array & facepoint, int & levels, int & act_ref) -{ + NgBitArray & cornerpoint, NgBitArray & edgepoint, INDEX_3_HASHTABLE & faces, INDEX_2_HASHTABLE & face_edges, + INDEX_2_HASHTABLE & surf_edges, NgArray & facepoint, int & levels, int & act_ref) +{ bool sing = 0; if (mesh.GetDimension() == 3) { /* // check, if point has as least 3 different surfs: - Array surfonpoint(mesh.GetNP()); + NgArray surfonpoint(mesh.GetNP()); surfonpoint = INDEX_3(0,0,0); for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) @@ -1626,7 +1637,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS } // if 2 adjacent edges of an element are singular, the - // commen point must be a singular point + // common point must be a singular point for (int i = 1; i <= mesh.GetNE(); i++) { const Element & el = mesh.VolumeElement(i); @@ -1710,7 +1721,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS // 2D case // check, if point has as least 3 different surfs: - Array surfonpoint(mesh.GetNP()); + NgArray surfonpoint(mesh.GetNP()); for (int i = 1; i <= mesh.GetNP(); i++) surfonpoint.Elem(i) = INDEX_3(0,0,0); @@ -1722,7 +1733,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS if (seg.singedge_left * levels >= act_ref) { - INDEX_2 i2 (mesh.LineSegment(i)[0], + INDEX_2 i2 = INDEX_2::Sort(mesh.LineSegment(i)[0], mesh.LineSegment(i)[1]); edges.Set(i2,1); edgepoint.Set(i2.I1()); @@ -1738,7 +1749,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS if (seg.singedge_right * levels >= act_ref) { - INDEX_2 i2 (mesh.LineSegment(i)[1], + INDEX_2 i2 = INDEX_2::Sort(mesh.LineSegment(i)[1], mesh.LineSegment(i)[0]); edges.Set (i2, 1); edgepoint.Set(i2.I1()); @@ -1802,21 +1813,21 @@ 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); } - bool ClassifyHPElements (Mesh & mesh, Array & elements, int & act_ref, int & levels) + bool ClassifyHPElements (Mesh & mesh, NgArray & elements, int & act_ref, int & levels) { INDEX_2_HASHTABLE edges(mesh.GetNSeg()+1); - BitArray edgepoint(mesh.GetNP()); + NgBitArray edgepoint(mesh.GetNP()); INDEX_2_HASHTABLE edgepoint_dom(mesh.GetNSeg()+1); edgepoint.Clear(); - BitArray cornerpoint(mesh.GetNP()); + NgBitArray cornerpoint(mesh.GetNP()); cornerpoint.Clear(); // value = nr > 0 ... refine elements in domain nr @@ -1824,7 +1835,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS INDEX_3_HASHTABLE faces(mesh.GetNSE()+1); INDEX_2_HASHTABLE face_edges(mesh.GetNSE()+1); INDEX_2_HASHTABLE surf_edges(mesh.GetNSE()+1); - Array facepoint(mesh.GetNP()); + NgArray facepoint(mesh.GetNP()); bool sing = CheckSingularities(mesh, edges, edgepoint_dom, cornerpoint, edgepoint, faces, face_edges, @@ -1833,7 +1844,7 @@ bool CheckSingularities(Mesh & mesh, INDEX_2_HASHTABLE & edges, INDEX_2_HAS if(sing==0) return(sing); int cnt_undef = 0, cnt_nonimplement = 0; - Array misses(10000); + NgArray misses(10000); misses = 0; (*testout) << "edgepoint_dom = " << endl << edgepoint_dom << endl; @@ -1949,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/hprefinement.hpp b/libsrc/meshing/hprefinement.hpp index 8ccf5069..0affd82b 100644 --- a/libsrc/meshing/hprefinement.hpp +++ b/libsrc/meshing/hprefinement.hpp @@ -291,6 +291,7 @@ public: PointIndex pnums[8]; double param[8][3]; int index; + int si; int levelx; int levely; int levelz; diff --git a/libsrc/meshing/improve2.cpp b/libsrc/meshing/improve2.cpp index 97fcb764..139eddb4 100644 --- a/libsrc/meshing/improve2.cpp +++ b/libsrc/meshing/improve2.cpp @@ -3,417 +3,360 @@ #include "meshing.hpp" #include -#ifndef SMALLLIB -//#ifndef NOTCL -//#include -//#endif -#endif namespace netgen { - class Neighbour - { - int nr[3]; - int orient[3]; - - public: - Neighbour () { ; } - - void SetNr (int side, int anr) { nr[side] = anr; } - int GetNr (int side) { return nr[side]; } - - void SetOrientation (int side, int aorient) { orient[side] = aorient; } - int GetOrientation (int side) { return orient[side]; } - - - - /* - void SetNr1 (int side, int anr) { nr[side-1] = anr; } - int GetNr1 (int side) { return nr[side-1]; } - - void SetOrientation1 (int side, int aorient) { orient[side-1] = aorient; } - int GetOrientation1 (int side) { return orient[side-1]; } - */ - }; - - - - class trionedge { public: - int tnr; + SurfaceElementIndex tnr; int sidenr; trionedge () { tnr = 0; sidenr = 0; } - trionedge (int atnr, int asidenr) + trionedge (SurfaceElementIndex atnr, int asidenr) { tnr = atnr; sidenr = asidenr; } }; + // check if element is quad with at least one surface point -> relevant for optimization + // (quads with 4 edge points are not optimized and can be ignored) + bool checkMixedElement(const Mesh & mesh, FlatArray seia) + { + bool mixed = false; + ParallelForRange( Range(seia), [&] (auto myrange) NETGEN_LAMBDA_INLINE + { + for (auto i : myrange) + { + const auto & sel = mesh[seia[i]]; + if(sel.GetNP() == 3) + continue; + + for(auto pi : Range(sel.GetNP())) + if(mesh[sel[pi]].Type() == SURFACEPOINT) + mixed = true; + } + }); + return mixed; + } + + bool MeshOptimize2d :: EdgeSwapping (const int usemetric, + Array &neighbors, + Array &swapped, + const SurfaceElementIndex t1, const int o1, + const int t, + Array &pdef, + const bool check_only) + { + bool should; + bool do_swap = false; + + SurfaceElementIndex t2 = neighbors[t1].GetNr (o1); + int o2 = neighbors[t1].GetOrientation (o1); + + if (t2 == -1) return false; + if (swapped[t1] || swapped[t2]) return false; + if (mesh[t2].IsDeleted()) return false; + + const int faceindex = mesh[t1].GetIndex(); + const int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr(); + + PointIndex pi1 = mesh[t1].PNumMod(o1+1+1); + PointIndex pi2 = mesh[t1].PNumMod(o1+1+2); + PointIndex pi3 = mesh[t1].PNumMod(o1+1); + PointIndex pi4 = mesh[t2].PNumMod(o2+1); + + PointGeomInfo gi1 = mesh[t1].GeomInfoPiMod(o1+1+1); + PointGeomInfo gi2 = mesh[t1].GeomInfoPiMod(o1+1+2); + PointGeomInfo gi3 = mesh[t1].GeomInfoPiMod(o1+1); + PointGeomInfo gi4 = mesh[t2].GeomInfoPiMod(o2+1); + + bool allowswap = true; + + Vec<3> auxvec1 = mesh[pi3]-mesh[pi4]; + Vec<3> auxvec2 = mesh[pi1]-mesh[pi4]; + + allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4; + + if(!allowswap) + return false; + + // normal of new + Vec<3> nv1 = Cross (auxvec1, auxvec2); + + auxvec1 = mesh.Point(pi4)-mesh.Point(pi3); + auxvec2 = mesh.Point(pi2)-mesh.Point(pi3); + allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4; + + + if(!allowswap) + return false; + + Vec<3> nv2 = Cross (auxvec1, auxvec2); + + + // normals of original + Vec<3> nv3 = Cross (mesh[pi1]-mesh[pi4], mesh[pi2]-mesh[pi4]); + Vec<3> nv4 = Cross (mesh[pi2]-mesh[pi3], mesh[pi1]-mesh[pi3]); + + nv3 *= -1; + nv4 *= -1; + nv3.Normalize(); + nv4.Normalize(); + + nv1.Normalize(); + nv2.Normalize(); + + auto nvp3 = geo.GetNormal (surfnr, mesh.Point(pi3), &gi3); + + nvp3.Normalize(); + + auto nvp4 = geo.GetNormal (surfnr, mesh.Point(pi4), &gi4); + + nvp4.Normalize(); + + + + double critval = cos (M_PI / 6); // 30 degree + allowswap = allowswap && + (nv1 * nvp3 > critval) && + (nv1 * nvp4 > critval) && + (nv2 * nvp3 > critval) && + (nv2 * nvp4 > critval) && + (nvp3 * nv3 > critval) && + (nvp4 * nv4 > critval); + + + double horder = Dist (mesh[pi1], mesh[pi2]); + + if ( // nv1 * nv2 >= 0 && + nv1.Length() > 1e-3 * horder * horder && + nv2.Length() > 1e-3 * horder * horder && + allowswap ) + { + if (!usemetric) + { + int e = pdef[pi1] + pdef[pi2] - pdef[pi3] - pdef[pi4]; + double d = + Dist2 (mesh[pi1], mesh[pi2]) - + Dist2 (mesh[pi3], mesh[pi4]); + + should = e >= t && (e > 2 || d > 0); + } + else + { + double loch = 0.25*(mesh.GetH(pi1) + mesh.GetH(pi2) + mesh.GetH(pi3) + mesh.GetH(pi4)); + should = + CalcTriangleBadness (mesh[pi4], mesh[pi3], mesh[pi1], metricweight, loch) + + CalcTriangleBadness (mesh[pi3], mesh[pi4], mesh[pi2], metricweight, loch) < + CalcTriangleBadness (mesh[pi1], mesh[pi2], mesh[pi3], metricweight, loch) + + CalcTriangleBadness (mesh[pi2], mesh[pi1], mesh[pi4], metricweight, loch); + } + + if (allowswap) + { + Element2d sw1 (pi4, pi3, pi1); + Element2d sw2 (pi3, pi4, pi2); + + int legal1 = + mesh.LegalTrig (mesh[t1]) + + mesh.LegalTrig (mesh[t2]); + int legal2 = + mesh.LegalTrig (sw1) + mesh.LegalTrig (sw2); + + if (legal1 < legal2) should = true; + if (legal2 < legal1) should = false; + } + + do_swap = should; + if (should && !check_only) + { + // do swapping ! + + mesh[t1] = { { pi1, gi1 }, { pi4, gi4 }, { pi3, gi3 } }; + mesh[t2] = { { pi2, gi2 }, { pi3, gi3 }, { pi4, gi4 } }; + + pdef[pi1]--; + pdef[pi2]--; + pdef[pi3]++; + pdef[pi4]++; + + swapped[t1] = true; + swapped[t2] = true; + } + } + return do_swap; + } - void MeshOptimize2d :: EdgeSwapping (Mesh & mesh, int usemetric) + void MeshOptimize2d :: EdgeSwapping (int usemetric) { - if (!faceindex) - { - if (usemetric) - PrintMessage (3, "Edgeswapping, metric"); - else - PrintMessage (3, "Edgeswapping, topological"); - - for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) - { - EdgeSwapping (mesh, usemetric); - - if (multithread.terminate) - throw NgException ("Meshing stopped"); - } - - faceindex = 0; - mesh.CalcSurfacesOfNode(); - return; - } - - - static int timer = NgProfiler::CreateTimer ("EdgeSwapping 2D"); - NgProfiler::RegionTimer reg1 (timer); - - static int timerstart = NgProfiler::CreateTimer ("EdgeSwapping 2D start"); - NgProfiler::StartTimer (timerstart); + static Timer timer("EdgeSwapping (2D)"); RegionTimer reg(timer); + static Timer timer_nb("EdgeSwapping-Find neighbors"); + if (usemetric) + PrintMessage (3, "Edgeswapping, metric"); + else + PrintMessage (3, "Edgeswapping, topological"); + static Timer timerstart("EdgeSwapping 2D start"); + timerstart.Start(); Array seia; mesh.GetSurfaceElementsOfFace (faceindex, seia); - for (int i = 0; i < seia.Size(); i++) - if (mesh[seia[i]].GetNP() != 3) - { - GenericImprove (mesh); - return; - } - - int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr(); + if(checkMixedElement(mesh, seia)) + { + timerstart.Stop(); + return GenericImprove(); + } Array neighbors(mesh.GetNSE()); - INDEX_2_HASHTABLE other(seia.Size() + 2); + auto elements_on_node = mesh.CreatePoint2SurfaceElementTable(faceindex); + Array swapped(mesh.GetNSE()); + Array pdef(mesh.GetNP()); + Array pangle(mesh.GetNP()); - Array swapped(mesh.GetNSE()); - Array pdef(mesh.GetNP()); - Array pangle(mesh.GetNP()); - - - // int e; - // double d; - // Vec3d nv1, nv2; - - // double loch(-1); static const double minangle[] = { 0, 1.481, 2.565, 3.627, 4.683, 5.736, 7, 9 }; - for (int i = 0; i < seia.Size(); i++) + if(faceindex == 0) { - const Element2d & sel = mesh[seia[i]]; - for (int j = 0; j < 3; j++) - pangle[sel[j]] = 0.0; + ParallelFor( Range(pangle), [&] (auto i) NETGEN_LAMBDA_INLINE + { + pangle[i] = 0.0; + }); } - // pangle = 0; - - for (int i = 0; i < seia.Size(); i++) + else { - const Element2d & sel = mesh[seia[i]]; - for (int j = 0; j < 3; j++) - { - POINTTYPE typ = mesh[sel[j]].Type(); - if (typ == FIXEDPOINT || typ == EDGEPOINT) - { - pangle[sel[j]] += - Angle (mesh[sel[(j+1)%3]] - mesh[sel[j]], - mesh[sel[(j+2)%3]] - mesh[sel[j]]); - } - } + ParallelFor( Range(seia), [&] (auto i) NETGEN_LAMBDA_INLINE + { + const Element2d & sel = mesh[seia[i]]; + for (int j = 0; j < 3; j++) + pangle[sel[j]] = 0.0; + }); } - // for (PointIndex pi = PointIndex::BASE; - // pi < mesh.GetNP()+PointIndex::BASE; pi++) + ParallelFor( Range(seia), [&] (auto i) NETGEN_LAMBDA_INLINE + { + const Element2d & sel = mesh[seia[i]]; + for (int j = 0; j < 3; j++) + { + POINTTYPE typ = mesh[sel[j]].Type(); + if (typ == FIXEDPOINT || typ == EDGEPOINT) + { + AtomicAdd(pangle[sel[j]], + Angle (mesh[sel[(j+1)%3]] - mesh[sel[j]], + mesh[sel[(j+2)%3]] - mesh[sel[j]])); + } + } + }); + + ParallelFor( Range(seia), [&] (auto i) NETGEN_LAMBDA_INLINE + { + const Element2d & sel = mesh[seia[i]]; + for (int j = 0; j < 3; j++) + { + PointIndex pi = sel[j]; + if (mesh[pi].Type() == INNERPOINT || mesh[pi].Type() == SURFACEPOINT) + pdef[pi] = -6; + else + for (int j = 0; j < 8; j++) + if (pangle[pi] >= minangle[j]) + pdef[pi] = -1-j; + } + }); + + ParallelFor( Range(seia), [this, &pdef, &neighbors, &seia, &elements_on_node] (auto i) NETGEN_LAMBDA_INLINE + { + auto sei = seia[i]; + for (PointIndex pi : mesh[sei].template PNums<3>()) + AsAtomic(pdef[pi])++; + for (int j = 0; j < 3; j++) + { + neighbors[sei].SetNr (j, -1); + neighbors[sei].SetOrientation (j, 0); + } + + const auto sel = mesh[sei]; + auto index = sel.GetIndex(); + for (int j = 0; j < 3; j++) + { + PointIndex pi1 = sel.PNumMod(j+2); + PointIndex pi2 = sel.PNumMod(j+3); + if(mesh.IsSegment(pi1, pi2)) + continue; + + for (auto sei_other : elements_on_node[pi1]) + { + if(sei_other==sei) continue; + if(mesh[sei_other].GetIndex()!=index) continue; + const auto & other = mesh[sei_other]; + int pi1_other = -1; + int pi2_other = -1; + bool common_edge = false; + for (int k = 0; k < 3; k++) + { + if(other[k] == pi1) + pi1_other = k; + if(other[k] == pi2) + { + pi2_other = k; + common_edge = true; + } + } + + if(common_edge) + { + neighbors[sei].SetNr (j, sei_other); + neighbors[sei].SetOrientation (j, 3-pi1_other-pi2_other); + } + } + } + }); + + + for (SurfaceElementIndex sei : seia) + swapped[sei] = false; - // pdef = 0; - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & sel = mesh[seia[i]]; - for (int j = 0; j < 3; j++) - { - PointIndex pi = sel[j]; - if (mesh[pi].Type() == INNERPOINT || mesh[pi].Type() == SURFACEPOINT) - pdef[pi] = -6; - else - for (int j = 0; j < 8; j++) - if (pangle[pi] >= minangle[j]) - pdef[pi] = -1-j; - } - } - - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & sel = mesh[seia[i]]; - for (int j = 0; j < 3; j++) - pdef[sel[j]]++; - } - - for (int i = 0; i < seia.Size(); i++) - { - for (int j = 0; j < 3; j++) - { - neighbors[seia[i]].SetNr (j, -1); - neighbors[seia[i]].SetOrientation (j, 0); - } - } - - /* - Array normals(mesh.GetNP()); - for (i = 1; i <= mesh.GetNSE(); i++) - { - Element2d & hel = mesh.SurfaceElement(i); - if (hel.GetIndex() == faceindex) - for (k = 1; k <= 3; k++) - { - int pi = hel.PNum(k); - SelectSurfaceOfPoint (mesh.Point(pi), hel.GeomInfoPi(k)); - int surfi = mesh.GetFaceDescriptor(faceindex).SurfNr(); - GetNormalVector (surfi, mesh.Point(pi), normals.Elem(pi)); - normals.Elem(pi) /= normals.Elem(pi).Length(); - } - } - */ - - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & sel = mesh[seia[i]]; - - for (int j = 0; j < 3; j++) - { - PointIndex pi1 = sel.PNumMod(j+2); - PointIndex pi2 = sel.PNumMod(j+3); - - // double loch = mesh.GetH(mesh[pi1]); - - INDEX_2 edge(pi1, pi2); - edge.Sort(); - - if (mesh.IsSegment (pi1, pi2)) - continue; - - /* - if (segments.Used (edge)) - continue; - */ - INDEX_2 ii2 (pi1, pi2); - if (other.Used (ii2)) - { - // INDEX_2 i2s(ii2); - // i2s.Sort(); - - int i2 = other.Get(ii2).tnr; - int j2 = other.Get(ii2).sidenr; - - neighbors[seia[i]].SetNr (j, i2); - neighbors[seia[i]].SetOrientation (j, j2); - neighbors[i2].SetNr (j2, seia[i]); - neighbors[i2].SetOrientation (j2, j); - } - else - { - other.Set (INDEX_2 (pi2, pi1), trionedge (seia[i], j)); - } - } - } - - for (int i = 0; i < seia.Size(); i++) - swapped[seia[i]] = 0; - - NgProfiler::StopTimer (timerstart); + timerstart.Stop(); + Array> improvement_candidates(3*seia.Size()); + atomic cnt(0); + int t = 4; - int done = 0; + bool done = false; while (!done && t >= 2) { - for (int i = 0; i < seia.Size(); i++) - { - SurfaceElementIndex t1 = seia[i]; + cnt = 0; + ParallelFor( Range(seia), [&] (auto i) NETGEN_LAMBDA_INLINE + { + SurfaceElementIndex t1 = seia[i]; - if (mesh[t1].IsDeleted()) - continue; + if (mesh[t1].IsDeleted()) + return; - if (mesh[t1].GetIndex() != faceindex) - continue; + if (swapped[t1]) + return; - if (multithread.terminate) - throw NgException ("Meshing stopped"); + if(mesh[t1].GetNP() != 3) + return; - for (int o1 = 0; o1 < 3; o1++) - { - bool should; + if (multithread.terminate) + throw NgException ("Meshing stopped"); + for (int o1 = 0; o1 < 3; o1++) + if(EdgeSwapping(usemetric, neighbors, swapped, t1, o1, t, pdef, true)) + improvement_candidates[cnt++]= std::make_pair(t1,o1); + }); - SurfaceElementIndex t2 = neighbors[t1].GetNr (o1); - int o2 = neighbors[t1].GetOrientation (o1); + auto elements_with_improvement = improvement_candidates.Range(cnt.load()); + QuickSort(elements_with_improvement); - if (t2 == -1) continue; - if (swapped[t1] || swapped[t2]) continue; - - - PointIndex pi1 = mesh[t1].PNumMod(o1+1+1); - PointIndex pi2 = mesh[t1].PNumMod(o1+1+2); - PointIndex pi3 = mesh[t1].PNumMod(o1+1); - PointIndex pi4 = mesh[t2].PNumMod(o2+1); - - PointGeomInfo gi1 = mesh[t1].GeomInfoPiMod(o1+1+1); - PointGeomInfo gi2 = mesh[t1].GeomInfoPiMod(o1+1+2); - PointGeomInfo gi3 = mesh[t1].GeomInfoPiMod(o1+1); - PointGeomInfo gi4 = mesh[t2].GeomInfoPiMod(o2+1); - - bool allowswap = true; - - Vec<3> auxvec1 = mesh[pi3]-mesh[pi4]; - Vec<3> auxvec2 = mesh[pi1]-mesh[pi4]; - - allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4; - - if(!allowswap) - continue; - - // normal of new - Vec<3> nv1 = Cross (auxvec1, auxvec2); - - auxvec1 = mesh.Point(pi4)-mesh.Point(pi3); - auxvec2 = mesh.Point(pi2)-mesh.Point(pi3); - allowswap = allowswap && fabs(1.-(auxvec1*auxvec2)/(auxvec1.Length()*auxvec2.Length())) > 1e-4; - - - if(!allowswap) - continue; - - Vec<3> nv2 = Cross (auxvec1, auxvec2); - - - // normals of original - Vec<3> nv3 = Cross (mesh[pi1]-mesh[pi4], mesh[pi2]-mesh[pi4]); - Vec<3> nv4 = Cross (mesh[pi2]-mesh[pi3], mesh[pi1]-mesh[pi3]); - - nv3 *= -1; - nv4 *= -1; - nv3.Normalize(); - nv4.Normalize(); - - nv1.Normalize(); - nv2.Normalize(); - - Vec<3> nvp3, nvp4; - SelectSurfaceOfPoint (mesh.Point(pi3), gi3); - GetNormalVector (surfnr, mesh.Point(pi3), gi3, nvp3); - - nvp3.Normalize(); - - SelectSurfaceOfPoint (mesh.Point(pi4), gi4); - GetNormalVector (surfnr, mesh.Point(pi4), gi4, nvp4); - - nvp4.Normalize(); - - - - double critval = cos (M_PI / 6); // 30 degree - allowswap = allowswap && - (nv1 * nvp3 > critval) && - (nv1 * nvp4 > critval) && - (nv2 * nvp3 > critval) && - (nv2 * nvp4 > critval) && - (nvp3 * nv3 > critval) && - (nvp4 * nv4 > critval); - - - double horder = Dist (mesh.Point(pi1), mesh.Point(pi2)); - - if ( // nv1 * nv2 >= 0 && - nv1.Length() > 1e-3 * horder * horder && - nv2.Length() > 1e-3 * horder * horder && - allowswap ) - { - if (!usemetric) - { - int e = pdef[pi1] + pdef[pi2] - pdef[pi3] - pdef[pi4]; - double d = - Dist2 (mesh.Point(pi1), mesh.Point(pi2)) - - Dist2 (mesh.Point(pi3), mesh.Point(pi4)); - - should = e >= t && (e > 2 || d > 0); - } - else - { - double loch = mesh.GetH(mesh[pi1]); - should = - CalcTriangleBadness (mesh.Point(pi4), mesh.Point(pi3), mesh.Point(pi1), - metricweight, loch) + - CalcTriangleBadness (mesh.Point(pi3), mesh.Point(pi4), mesh.Point(pi2), - metricweight, loch) < - CalcTriangleBadness (mesh.Point(pi1), mesh.Point(pi2), mesh.Point(pi3), - metricweight, loch) + - CalcTriangleBadness (mesh.Point(pi2), mesh.Point(pi1), mesh.Point(pi4), - metricweight, loch); - - } - - if (allowswap) - { - Element2d sw1 (pi4, pi3, pi1); - Element2d sw2 (pi3, pi4, pi2); - - int legal1 = - mesh.LegalTrig (mesh.SurfaceElement (t1)) + - mesh.LegalTrig (mesh.SurfaceElement (t2)); - int legal2 = - mesh.LegalTrig (sw1) + mesh.LegalTrig (sw2); - - if (legal1 < legal2) should = 1; - if (legal2 < legal1) should = 0; - } - - if (should) - { - // do swapping ! - - done = 1; - - mesh[t1].PNum(1) = pi1; - mesh[t1].PNum(2) = pi4; - mesh[t1].PNum(3) = pi3; - - mesh[t2].PNum(1) = pi2; - mesh[t2].PNum(2) = pi3; - mesh[t2].PNum(3) = pi4; - - mesh[t1].GeomInfoPi(1) = gi1; - mesh[t1].GeomInfoPi(2) = gi4; - mesh[t1].GeomInfoPi(3) = gi3; - - mesh[t2].GeomInfoPi(1) = gi2; - mesh[t2].GeomInfoPi(2) = gi3; - mesh[t2].GeomInfoPi(3) = gi4; - - pdef[pi1]--; - pdef[pi2]--; - pdef[pi3]++; - pdef[pi4]++; - - swapped[t1] = 1; - swapped[t2] = 1; - } - } - } - } + for (auto [t1,o1] : elements_with_improvement) + done |= EdgeSwapping(usemetric, neighbors, swapped, t1, o1, t, pdef, false); t--; } @@ -423,384 +366,456 @@ namespace netgen - - - - - void MeshOptimize2d :: CombineImprove (Mesh & mesh) + double CombineImproveEdge( Mesh & mesh, + const Table & elementsonnode, + Array, PointIndex> & normals, + Array & fixed, + PointIndex pi1, PointIndex pi2, + double metricweight, + bool check_only = true) { - if (!faceindex) + Vec<3> nv; + ArrayMem hasonepi, hasbothpi; + + if (!pi1.IsValid() || !pi2.IsValid()) + return 0.0; + + bool debugflag = 0; + + if (debugflag) { - PrintMessage (3, "Combine improve"); - - for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) - { - CombineImprove (mesh); - - if (multithread.terminate) - throw NgException ("Meshing stopped"); - } - faceindex = 0; - return; + (*testout) << "Combineimprove " + << "pi1 = " << pi1 << " pi2 = " << pi2 << endl; } + if (fixed[pi2]) + return 0.0; - static int timer = NgProfiler::CreateTimer ("Combineimprove 2D"); - NgProfiler::RegionTimer reg (timer); + double loch = 0.5*(mesh.GetH(pi1) + mesh.GetH(pi2)); + int faceindex = -1; - static int timerstart = NgProfiler::CreateTimer ("Combineimprove 2D start"); - NgProfiler::StartTimer (timerstart); + for (SurfaceElementIndex sei2 : elementsonnode[pi1]) + { + const Element2d & el2 = mesh[sei2]; + + if (el2.IsDeleted()) continue; + + if (el2[0] == pi2 || el2[1] == pi2 || el2[2] == pi2) + { + faceindex = el2.GetIndex(); + hasbothpi.Append (sei2); + } + } + + if(hasbothpi.Size()==0) + return 0.0; - static int timerstart1 = NgProfiler::CreateTimer ("Combineimprove 2D start1"); - NgProfiler::StartTimer (timerstart1); + nv = normals[pi2]; + for (SurfaceElementIndex sei2 : elementsonnode[pi2]) + { + const Element2d & el2 = mesh[sei2]; + if (el2.IsDeleted()) continue; + if (!el2.PNums<3>().Contains (pi1)) + hasonepi.Append (sei2); + } - // int i, j, k, l; - // PointIndex pi; - // SurfaceElementIndex sei; + double bad1 = 0; + int illegal1 = 0, illegal2 = 0; + /* + for (SurfaceElementIndex sei : hasonepi) + { + const Element2d & el = mesh[sei]; + bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], + nv, -1, loch); + illegal1 += 1-mesh.LegalTrig(el); + } + */ + for (const Element2d & el : mesh.SurfaceElements()[hasonepi]) + { + bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], + nv, metricweight, loch); + illegal1 += 1-mesh.LegalTrig(el); + } + + for (int k = 0; k < hasbothpi.Size(); k++) + { + const Element2d & el = mesh[hasbothpi[k]]; + bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], + nv, metricweight, loch); + illegal1 += 1-mesh.LegalTrig(el); + } + + double bad2 = 0; + for (int k = 0; k < hasonepi.Size(); k++) + { + Element2d el = mesh[hasonepi[k]]; + for (auto i : Range(3)) + if(el[i]==pi2) + el[i] = pi1; + + double err = + CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], + nv, metricweight, loch); + bad2 += err; + + Vec<3> hnv = Cross (Vec3d (mesh[el[0]], + mesh[el[1]]), + Vec3d (mesh[el[0]], + mesh[el[2]])); + if (hnv * nv < 0) + bad2 += 1e10; + + for (int l = 0; l < 3; l++) + { + auto normal = normals[el[l]]; + if(mesh[el[l]].Type() != SURFACEPOINT) + { + // point possibly on edge -> multiple normal vectors (for each surface), need to calculate it to be sure + const int surfnr = mesh.GetFaceDescriptor (el.GetIndex()).SurfNr(); + normal = mesh.GetGeometry()->GetNormal (surfnr, mesh[el[l]], &el.GeomInfo()[l]); + } + if ( ( normal * nv) < 0.5) + bad2 += 1e10; + } + + illegal2 += 1-mesh.LegalTrig(el); + } + + if (debugflag) + { + (*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl; + } + + bool should = (illegal2<=illegal1 && bad2 < bad1 && bad2 < 1e4); + if(illegal2 < illegal1) + { + should = true; + bad1 += 1e4; + } + + double d_badness = should * (bad2-bad1); + + if(check_only) + return d_badness; + + if (should) + { + /* + (*testout) << "combine !" << endl; + (*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl; + (*testout) << "illegal1 = " << illegal1 << ", illegal2 = " << illegal2 << endl; + (*testout) << "loch = " << loch << endl; + */ + + PointGeomInfo gi; + // bool gi_set(false); + + /* + Element2d *el1p(NULL); + int l = 0; + while(mesh[elementsonnode[pi1][l]].IsDeleted() && lGetNP(); l++) + if ((*el1p)[l] == pi1) + { + gi = el1p->GeomInfoPi (l+1); + // gi_set = true; + } + */ + for (auto sei : hasbothpi) + { + const Element2d & el1p = mesh[sei]; + if (el1p.IsDeleted()) continue; + if(el1p.GetIndex() != faceindex) continue; + + for (int l = 0; l < el1p.GetNP(); l++) + if (el1p[l] == pi1) + gi = el1p.GeomInfo()[l]; + break; + } + // (*testout) << "Connect point " << pi2 << " to " << pi1 << "\n"; + // for (int k = 0; k < elementsonnode[pi2].Size(); k++) + for (SurfaceElementIndex sei2 : elementsonnode[pi2]) + { + Element2d & el = mesh[sei2]; + if (el.IsDeleted()) continue; + if (el.PNums().Contains(pi1)) continue; + + for (auto l : Range(el.GetNP())) + { + if (el[l] == pi2) + { + el[l] = pi1; + el.GeomInfo()[l] = gi; + } + + fixed[el[l]] = true; + } + } + + for (auto sei : hasbothpi) + mesh[sei].Delete(); + + } + return d_badness; + } + + void MeshOptimize2d :: CombineImprove () + { + SplitImprove(); + PrintMessage (3, "Combine improve"); + + if (multithread.terminate) + throw NgException ("Meshing stopped"); + + static Timer timer ("Combineimprove 2D"); + RegionTimer reg (timer); + + static Timer timerstart ("Combineimprove 2D start"); + timerstart.Start(); + + + static Timer timerstart1 ("Combineimprove 2D start1"); + timerstart1.Start(); + + Array seia; mesh.GetSurfaceElementsOfFace (faceindex, seia); - - for (int i = 0; i < seia.Size(); i++) - if (mesh[seia[i]].GetNP() != 3) - return; - - - - int surfnr = 0; - if (faceindex) - surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr(); - - - // PointIndex pi1, pi2; - // MeshPoint p1, p2, pnew; - double bad1, bad2; - Vec<3> nv; + if(checkMixedElement(mesh, seia)) + { + timerstart1.Stop(); + timerstart.Stop(); + return; + } int np = mesh.GetNP(); - //int nse = mesh.GetNSE(); - TABLE elementsonnode(np); - Array hasonepi, hasbothpi; + auto elementsonnode = mesh.CreatePoint2SurfaceElementTable(faceindex); - for (int i = 0; i < seia.Size(); i++) + int ntasks = ngcore::TaskManager::GetMaxThreads(); + Array> edges; + + BuildEdgeList( mesh, elementsonnode, edges ); + + Array fixed(np); + ParallelFor( fixed.Range(), [&] (auto i) NETGEN_LAMBDA_INLINE + { fixed[i] = mesh[i].Type() != SURFACEPOINT; }); + + timerstart1.Stop(); + + ParallelFor( mesh.LockedPoints().Range(), [&] (auto i) NETGEN_LAMBDA_INLINE + { + fixed[mesh.LockedPoints()[i]] = true; + }); + + + Array,PointIndex> normals(np); + + ParallelFor( mesh.Points().Range(), [&] (auto pi) NETGEN_LAMBDA_INLINE + { + if (elementsonnode[pi].Size()) + { + Element2d & hel = mesh[elementsonnode[pi][0]]; + for (int k = 0; k < 3; k++) + if (hel[k] == pi) + { + const int faceindex = hel.GetIndex(); + const int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr(); + normals[pi] = geo.GetNormal (surfnr, mesh[pi], &hel.GeomInfoPi(k+1)); + break; + } + } + }, TasksPerThread(4)); + + timerstart.Stop(); + + // Find edges with improvement + Array> candidate_edges(edges.Size()); + std::atomic improvement_counter(0); + + ParallelFor( Range(edges), [&] (auto i) NETGEN_LAMBDA_INLINE { - Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - elementsonnode.Add (el[j], seia[i]); - } + auto [pi1, pi2] = edges[i]; + double d_badness = CombineImproveEdge(mesh, elementsonnode, normals, fixed, pi1, pi2, metricweight, true); + if(d_badness < 0.0) + candidate_edges[improvement_counter++] = make_tuple(d_badness, i); + d_badness = CombineImproveEdge(mesh, elementsonnode, normals, fixed, pi2, pi1, metricweight, true); + if(d_badness < 0.0) + candidate_edges[improvement_counter++] = make_tuple(d_badness, -i); + }, TasksPerThread(4)); - Array fixed(np); - fixed = false; + auto edges_with_improvement = candidate_edges.Part(0, improvement_counter.load()); + QuickSort(edges_with_improvement); - NgProfiler::StopTimer (timerstart1); - - /* - for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++) + for(auto [d_badness, ei] : edges_with_improvement) { - INDEX_2 i2(mesh[si][0], mesh[si][1]); - fixed[i2.I1()] = true; - fixed[i2.I2()] = true; - } - */ - - for (int i = 0; i < seia.Size(); i++) - { - Element2d & sel = mesh[seia[i]]; - for (int j = 0; j < sel.GetNP(); j++) - { - PointIndex pi1 = sel.PNumMod(j+2); - PointIndex pi2 = sel.PNumMod(j+3); - if (mesh.IsSegment (pi1, pi2)) - { - fixed[pi1] = true; - fixed[pi2] = true; - } - } - } - - - - for(int i = 0; i < mesh.LockedPoints().Size(); i++) - fixed[mesh.LockedPoints()[i]] = true; - - - - Array,PointIndex::BASE> normals(np); - - for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End(); pi++) - { - if (elementsonnode[pi].Size()) - { - Element2d & hel = mesh[elementsonnode[pi][0]]; - for (int k = 0; k < 3; k++) - if (hel[k] == pi) - { - SelectSurfaceOfPoint (mesh[pi], hel.GeomInfoPi(k+1)); - GetNormalVector (surfnr, mesh[pi], hel.GeomInfoPi(k+1), normals[pi]); - break; - } - } - } - - NgProfiler::StopTimer (timerstart); - - for (int i = 0; i < seia.Size(); i++) - { - SurfaceElementIndex sei = seia[i]; - Element2d & elem = mesh[sei]; - if (elem.IsDeleted()) continue; - - for (int j = 0; j < 3; j++) - { - PointIndex pi1 = elem[j]; - PointIndex pi2 = elem[(j+1) % 3]; - - if (pi1 < PointIndex::BASE || - pi2 < PointIndex::BASE) - continue; - - /* - INDEX_2 i2(pi1, pi2); - i2.Sort(); - if (segmentht.Used(i2)) - continue; - */ - - bool debugflag = 0; - - if (debugflag) - { - (*testout) << "Combineimprove, face = " << faceindex - << "pi1 = " << pi1 << " pi2 = " << pi2 << endl; - } - - /* - // save version: - if (fixed.Get(pi1) || fixed.Get(pi2)) - continue; - if (pi2 < pi1) swap (pi1, pi2); - */ - - // more general - if (fixed[pi2]) - Swap (pi1, pi2); - - if (fixed[pi2]) - continue; - - double loch = mesh.GetH (mesh[pi1]); - - INDEX_2 si2 (pi1, pi2); - si2.Sort(); - - /* - if (edgetested.Used (si2)) - continue; - edgetested.Set (si2, 1); - */ - - hasonepi.SetSize(0); - hasbothpi.SetSize(0); - - for (int k = 0; k < elementsonnode[pi1].Size(); k++) - { - const Element2d & el2 = mesh[elementsonnode[pi1][k]]; - - if (el2.IsDeleted()) continue; - - if (el2[0] == pi2 || el2[1] == pi2 || el2[2] == pi2) - { - hasbothpi.Append (elementsonnode[pi1][k]); - nv = Cross (Vec3d (mesh[el2[0]], mesh[el2[1]]), - Vec3d (mesh[el2[0]], mesh[el2[2]])); - } - else - { - hasonepi.Append (elementsonnode[pi1][k]); - } - } - - - Element2d & hel = mesh[hasbothpi[0]]; - for (int k = 0; k < 3; k++) - if (hel[k] == pi1) - { - SelectSurfaceOfPoint (mesh[pi1], - hel.GeomInfoPi(k+1)); - GetNormalVector (surfnr, mesh[pi1], hel.GeomInfoPi(k+1), nv); - break; - } - - // nv = normals.Get(pi1); - - - - for (int k = 0; k < elementsonnode[pi2].Size(); k++) - { - const Element2d & el2 = mesh[elementsonnode[pi2][k]]; - if (el2.IsDeleted()) continue; - - if (el2[0] == pi1 || el2[1] == pi1 || el2[2] == pi1) - ; - else - hasonepi.Append (elementsonnode[pi2][k]); - } - - bad1 = 0; - int illegal1 = 0, illegal2 = 0; - for (int k = 0; k < hasonepi.Size(); k++) - { - const Element2d & el = mesh[hasonepi[k]]; - bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], - nv, -1, loch); - illegal1 += 1-mesh.LegalTrig(el); - } - - for (int k = 0; k < hasbothpi.Size(); k++) - { - const Element2d & el = mesh[hasbothpi[k]]; - bad1 += CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], - nv, -1, loch); - illegal1 += 1-mesh.LegalTrig(el); - } - bad1 /= (hasonepi.Size()+hasbothpi.Size()); - - MeshPoint p1 = mesh[pi1]; - MeshPoint p2 = mesh[pi2]; - - MeshPoint pnew = p1; - mesh[pi1] = pnew; - mesh[pi2] = pnew; - - bad2 = 0; - for (int k = 0; k < hasonepi.Size(); k++) - { - Element2d & el = mesh[hasonepi[k]]; - double err = - CalcTriangleBadness (mesh[el[0]], mesh[el[1]], mesh[el[2]], - nv, -1, loch); - bad2 += err; - - Vec<3> hnv = Cross (Vec3d (mesh[el[0]], - mesh[el[1]]), - Vec3d (mesh[el[0]], - mesh[el[2]])); - if (hnv * nv < 0) - bad2 += 1e10; - - for (int l = 0; l < 3; l++) - if ( (normals[el[l]] * nv) < 0.5) - bad2 += 1e10; - - illegal2 += 1-mesh.LegalTrig(el); - } - bad2 /= hasonepi.Size(); - - mesh[pi1] = p1; - mesh[pi2] = p2; - - - if (debugflag) - { - (*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl; - } - - - bool should = (bad2 < bad1 && bad2 < 1e4); - if (bad2 < 1e4) - { - if (illegal1 > illegal2) should = 1; - if (illegal2 > illegal1) should = 0; - } - - - if (should) - { - /* - (*testout) << "combine !" << endl; - (*testout) << "bad1 = " << bad1 << ", bad2 = " << bad2 << endl; - (*testout) << "illegal1 = " << illegal1 << ", illegal2 = " << illegal2 << endl; - (*testout) << "loch = " << loch << endl; - */ - - mesh[pi1] = pnew; - PointGeomInfo gi; - // bool gi_set(false); - - - Element2d *el1p(NULL); - int l = 0; - while(mesh[elementsonnode[pi1][l]].IsDeleted() && lGetNP(); l++) - if ((*el1p)[l] == pi1) - { - gi = el1p->GeomInfoPi (l+1); - // gi_set = true; - } - - // (*testout) << "Connect point " << pi2 << " to " << pi1 << "\n"; - for (int k = 0; k < elementsonnode[pi2].Size(); k++) - { - Element2d & el = mesh[elementsonnode[pi2][k]]; - if(el.IsDeleted()) continue; - elementsonnode.Add (pi1, elementsonnode[pi2][k]); - - bool haspi1 = 0; - for (l = 0; l < el.GetNP(); l++) - if (el[l] == pi1) - haspi1 = 1; - if (haspi1) continue; - - for (int l = 0; l < el.GetNP(); l++) - { - if (el[l] == pi2) - { - el[l] = pi1; - el.GeomInfoPi (l+1) = gi; - } - - fixed[el[l]] = true; - } - } - - /* - for (k = 0; k < hasbothpi.Size(); k++) - { - cout << mesh[hasbothpi[k]] << endl; - for (l = 0; l < 3; l++) - cout << mesh[mesh[hasbothpi[k]][l]] << " "; - cout << endl; - } - */ - - for (int k = 0; k < hasbothpi.Size(); k++) - { - mesh[hasbothpi[k]].Delete(); - /* - for (l = 0; l < 4; l++) - mesh[hasbothpi[k]][l] = PointIndex::BASE-1; - */ - } - - } - } + auto [pi1, pi2] = edges[ei < 0 ? -ei : ei]; + if(ei<0) + Swap(pi1,pi2); + CombineImproveEdge(mesh, elementsonnode, normals, fixed, pi1, pi2, metricweight, false); } // mesh.Compress(); mesh.SetNextTimeStamp(); } + void MeshOptimize2d :: SplitImprove() + { + if (!faceindex) + { + PrintMessage (3, "Split improve"); + + mesh.CalcSurfacesOfNode(); // TODO: needed? + for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) + { + SplitImprove(); + + if (multithread.terminate) + throw NgException ("Meshing stopped"); + } + + faceindex = 0; + mesh.Compress(); // TODO: needed? + return; + } + + Array elements; + mesh.GetSurfaceElementsOfFace (faceindex, elements); + + // return if we have quads in this surface + for (auto & ei : elements) + if (mesh[ei].GetNP() != 3) + return; + + // maps from edges to adjacent trigs + INDEX_2_HASHTABLE> els_on_edge(2*elements.Size() + 2); + + // build els_on_edge table + for (SurfaceElementIndex sei : elements) + { + const Element2d & sel = mesh[sei]; + + for (int j = 0; j < 3; j++) + { + PointIndex pi1 = sel.PNumMod(j+2); + PointIndex pi2 = sel.PNumMod(j+3); + + if (mesh.IsSegment (pi1, pi2)) continue; + + INDEX_2 ii2 (pi1, pi2); + ii2.Sort(); + if (els_on_edge.Used (ii2)) + { + auto els = els_on_edge.Get(ii2); + get<1>(els) = sei; + els_on_edge.Set(ii2, els); + } + else + { + els_on_edge.Set (ii2, make_tuple(sei, sei)); + } + } + } + + // split edges of illegal trigs + for (SurfaceElementIndex sei : elements) + { + Element2d & sel = mesh[sei]; + + if (sel.IsDeleted()) continue; + + // TODO: split also bad trigs, nut just illegal ones + if (mesh.LegalTrig(sel)) continue; + + // find longest edge + INDEX_2 edge; + double edge_len = 0; + PointIndex pi1, pi2, pi3, pi4; + PointGeomInfo gi1, gi2, gi3, gi4; + for(auto j : Range(1,4)) + { + auto test_pi1 = sel.PNumMod(j); + auto test_pi2 = sel.PNumMod(j+1); + if (mesh.IsSegment(test_pi1, test_pi2)) + continue; + auto len = (mesh[test_pi2]-mesh[test_pi1]).Length(); + if(len > edge_len) + { + edge = {test_pi1, test_pi2}; + edge.Sort(); + edge_len = len; + pi1 = test_pi1; + pi2 = test_pi2; + pi3 = sel.PNumMod(j+2); + gi1 = sel.GeomInfoPiMod(j); + gi2 = sel.GeomInfoPiMod(j+1); + gi3 = sel.GeomInfoPiMod(j+2); + } + } + if(!edge_len) + throw Exception("Couldn't find edge to split, something is wrong"); + // get neighbor element + auto els = els_on_edge.Get(edge); + SurfaceElementIndex other_i = get<0>(els); + if(other_i==sei) other_i = get<1>(els); + auto & other = mesh[other_i]; + + // find opposite point of neighbor element + for (int j = 0; j < 3; j++) + if(other[j]!=pi1 && other[j]!=pi2) + { + pi4 = other[j]; + gi4 = other.GeomInfoPi(j); + break; + } + + // split edge pi1,pi2 + Point<3> p5; + PointIndex pi5; + PointGeomInfo gi5; + + geo.PointBetween(mesh[pi1], mesh[pi2], 0.5, + faceindex, + gi1, gi2, p5, gi5); + + pi5 = mesh.AddPoint(p5); + + Element2d e1(3); + e1.SetIndex(faceindex); + e1={ {pi1,gi1}, {pi5,gi5}, {pi3,gi3} }; + mesh.AddSurfaceElement( e1 ); + + Element2d e2(3); + e2.SetIndex(faceindex); + e2 ={ {pi5,gi5}, {pi2,gi2}, {pi3,gi3} }; + mesh.AddSurfaceElement( e2 ); + + Element2d e3(3); + e3.SetIndex(faceindex); + e3 ={ {pi1,gi1}, {pi4,gi4}, {pi5,gi5} }; + mesh.AddSurfaceElement( e3 ); + + Element2d e4(3); + e4.SetIndex(faceindex); + e4 ={ {pi4,gi4}, {pi2,gi2}, {pi5,gi5} }; + mesh.AddSurfaceElement( e4 ); + + sel.Delete(); + other.Delete(); + } + + mesh.SetNextTimeStamp(); + } void MeshOptimize2d :: CheckMeshApproximation (Mesh & mesh) { @@ -812,7 +827,7 @@ namespace netgen int surfnr; Vec3d n, ng; - Array ngs(3); + NgArray ngs(3); (*mycout) << "Check Surface Approximation" << endl; (*testout) << "Check Surface Approximation" << endl; diff --git a/libsrc/meshing/improve2.hpp b/libsrc/meshing/improve2.hpp index 9c3c1be1..444062a2 100644 --- a/libsrc/meshing/improve2.hpp +++ b/libsrc/meshing/improve2.hpp @@ -1,31 +1,125 @@ #ifndef FILE_IMPROVE2 #define FILE_IMPROVE2 +inline void AppendEdges( const Element2d & elem, PointIndex pi, Array> & edges ) +{ + for (int j = 0; j < 3; j++) + { + PointIndex pi0 = elem[j]; + PointIndex pi1 = elem[(j+1)%3]; + if (pi1 < pi0) Swap(pi0, pi1); + if(pi0==pi) + edges.Append(std::make_tuple(pi0, pi1)); + } +} +inline void AppendEdges( const Element & elem, PointIndex pi, Array> & edges ) +{ + static constexpr int tetedges[6][2] = + { { 0, 1 }, { 0, 2 }, { 0, 3 }, + { 1, 2 }, { 1, 3 }, { 2, 3 } }; + + if(elem.Flags().fixed) + return; + for (int j = 0; j < 6; j++) + { + PointIndex pi0 = elem[tetedges[j][0]]; + PointIndex pi1 = elem[tetedges[j][1]]; + if (pi1 < pi0) Swap(pi0, pi1); + if(pi0==pi) + edges.Append(std::make_tuple(pi0, pi1)); + } +} + +template +void BuildEdgeList( const Mesh & mesh, const Table & elementsonnode, Array> & edges ) +{ + static_assert(is_same_v||is_same_v, "Invalid type for TINDEX"); + static Timer tbuild_edges("Build edges"); RegionTimer reg(tbuild_edges); + + int ntasks = 4*ngcore::TaskManager::GetMaxThreads(); + Array>> task_edges(ntasks); + + ParallelFor(IntRange(ntasks), [&] (int ti) + { + auto myrange = mesh.Points().Range().Split(ti, ntasks); + ArrayMem, 100> local_edges; + for (auto pi : myrange) + { + local_edges.SetSize(0); + + for(auto ei : elementsonnode[pi]) + { + const auto & elem = mesh[ei]; + if (elem.IsDeleted()) continue; + + AppendEdges(elem, pi, local_edges); + } + QuickSort(local_edges); + + auto edge_prev = std::make_tuple(-1,-1); + + for(auto edge : local_edges) + if(edge != edge_prev) + { + task_edges[ti].Append(edge); + edge_prev = edge; + } + } + }, ntasks); + + int num_edges = 0; + for (auto & edg : task_edges) + num_edges += edg.Size(); + edges.SetAllocSize(num_edges); + for (auto & edg : task_edges) + edges.Append(edg); +} + + +class Neighbour +{ + int nr[3]; + int orient[3]; + +public: + Neighbour () { ; } + + void SetNr (int side, int anr) { nr[side] = anr; } + int GetNr (int side) { return nr[side]; } + + void SetOrientation (int side, int aorient) { orient[side] = aorient; } + int GetOrientation (int side) { return orient[side]; } +}; /// class MeshOptimize2d { - int faceindex; - int improveedges; - double metricweight; - int writestatus; - + int faceindex = 0; + int improveedges = 0; + double metricweight = 0.; + int writestatus = 1; + Mesh& mesh; + const NetgenGeometry& geo; public: /// - MeshOptimize2d (); + MeshOptimize2d(Mesh& amesh) : mesh(amesh), geo(*mesh.GetGeometry()) + {} virtual ~MeshOptimize2d() { ; } /// - void ImproveMesh (Mesh & mesh2d, const MeshingParameters & mp); - void ImproveMeshJacobian (Mesh & mesh2d, const MeshingParameters & mp); - void ImproveVolumeMesh (Mesh & mesh); - void ProjectBoundaryPoints(Array & surfaceindex, - const Array* > & from, Array* > & dest); + DLL_HEADER void ImproveMesh (const MeshingParameters & mp); + DLL_HEADER void ImproveMeshJacobian (const MeshingParameters & mp); + DLL_HEADER void ImproveVolumeMesh (); + DLL_HEADER void ProjectBoundaryPoints(NgArray & surfaceindex, + const NgArray* > & from, NgArray* > & dest); - void EdgeSwapping (Mesh & mesh, int usemetric); - void CombineImprove (Mesh & mesh); + DLL_HEADER bool EdgeSwapping (const int usemetric, Array &neighbors, Array &swapped, + const SurfaceElementIndex t1, const int edge, const int t, Array &pdef, const bool check_only=false); + DLL_HEADER void EdgeSwapping (int usemetric); + DLL_HEADER void CombineImprove (); + DLL_HEADER void SplitImprove (); - void GenericImprove (Mesh & mesh); + DLL_HEADER void GenericImprove (); void SetFaceIndex (int fi) { faceindex = fi; } @@ -34,31 +128,9 @@ public: void SetWriteStatus (int ws) { writestatus = ws; } - - /// - virtual void SelectSurfaceOfPoint (const Point<3> & p, - const PointGeomInfo & gi); - /// - virtual void ProjectPoint (INDEX /* surfind */, Point<3> & /* p */) const { }; - - /// project point, use gi as initial value, and compute new gi - virtual int ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const - { ProjectPoint (surfind, p); return CalcPointGeomInfo (surfind, gi, p); } - - /// - virtual void ProjectPoint2 (INDEX /* surfind */, INDEX /* surfind2 */, Point<3> & /* p */) const { }; - /// liefert zu einem 3d-Punkt die geominfo (Dreieck) und liefert 1, wenn erfolgreich, /// 0, wenn nicht (Punkt ausserhalb von chart) - virtual int CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & /*p3*/) const - { gi.trignum = 1; return 1;}; - - virtual int CalcPointGeomInfo(int /* surfind */, PointGeomInfo& gi, const Point<3> & p3) const - { return CalcPointGeomInfo (gi, p3); } - /// - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & gi, Vec<3> & n) const; - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const; void CheckMeshApproximation (Mesh & mesh); diff --git a/libsrc/meshing/improve2gen.cpp b/libsrc/meshing/improve2gen.cpp index fab74c66..02fa5fd6 100644 --- a/libsrc/meshing/improve2gen.cpp +++ b/libsrc/meshing/improve2gen.cpp @@ -9,27 +9,29 @@ namespace netgen class ImprovementRule { public: - Array oldels; - Array newels; - Array deledges; - Array incelsonnode; - Array reused; + NgArray oldels; + NgArray newels; + NgArray deledges; + NgArray incelsonnode; + NgArray reused; int bonus; int onp; }; - void MeshOptimize2d :: GenericImprove (Mesh & mesh) + void MeshOptimize2d :: GenericImprove () { + static Timer timer("MeshOptimize2d::GenericImprove"); RegionTimer reg(timer); if (!faceindex) { if (writestatus) PrintMessage (3, "Generic Improve"); for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) - GenericImprove (mesh); + GenericImprove (); faceindex = 0; + return; } // int j, k, l, ri; @@ -50,11 +52,11 @@ namespace netgen bool ok; int olddef, newdef; - Array rules; - Array elmap; - Array elrot; - Array pmap; - Array pgi; + NgArray rules; + NgArray elmap; + NgArray elrot; + NgArray pmap; + NgArray pgi; int surfnr = mesh.GetFaceDescriptor (faceindex).SurfNr(); @@ -195,8 +197,8 @@ namespace netgen - Array mapped(rules.Size()); - Array used(rules.Size()); + NgArray mapped(rules.Size()); + NgArray used(rules.Size()); used = 0; mapped = 0; @@ -208,18 +210,23 @@ namespace netgen rule.incelsonnode.SetSize (rule.onp); rule.reused.SetSize (rule.onp); - for (int j = 1; j <= rule.onp; j++) + for (int j = 0; j < rule.onp; j++) { - rule.incelsonnode.Elem(j) = 0; - rule.reused.Elem(j) = 0; + rule.incelsonnode[j] = 0; + rule.reused[j] = 0; } + /* for (int j = 1; j <= rule.oldels.Size(); j++) { const Element2d & el = rule.oldels.Elem(j); for (int k = 1; k <= el.GetNP(); k++) rule.incelsonnode.Elem(el.PNum(k))--; } + */ + for (const Element2d & el : rule.oldels) + for (PointIndex pi : el.PNums()) + rule.incelsonnode[pi-PointIndex::BASE]--; for (int j = 1; j <= rule.newels.Size(); j++) { @@ -236,7 +243,7 @@ namespace netgen TABLE elonnode(np); - Array nelonnode(np); + NgArray nelonnode(np); TABLE nbels(ne); nelonnode = -4; @@ -296,7 +303,7 @@ namespace netgen if (mesh[sei].IsDeleted()) continue; elmap[0] = sei; - FlatArray neighbours = nbels[sei]; + NgFlatArray neighbours = nbels[sei]; for (elrot[0] = 0; elrot[0] < mesh[sei].GetNP(); elrot[0]++) { @@ -390,13 +397,11 @@ namespace netgen // calc metric badness double bad1 = 0, bad2 = 0; - Vec<3> n; - - SelectSurfaceOfPoint (mesh.Point(pmap.Get(1)), pgi.Get(1)); - GetNormalVector (surfnr, mesh.Point(pmap.Get(1)), pgi.Elem(1), n); + // SelectSurfaceOfPoint (mesh.Point(pmap.Get(1)), pgi.Get(1)); + auto n = geo.GetNormal(surfnr, mesh.Point(pmap.Get(1)), &pgi.Elem(1)); - for (int j = 1; j <= rule.oldels.Size(); j++) - bad1 += mesh.SurfaceElement(elmap.Get(j)).CalcJacobianBadness (mesh.Points(), n); + for (int j = 0; j < rule.oldels.Size(); j++) + bad1 += mesh[elmap[j]].CalcJacobianBadness (mesh.Points(), n); // check new element: for (int j = 1; j <= rule.newels.Size(); j++) @@ -430,7 +435,7 @@ namespace netgen } for (int j = 0; j < rule.oldels.Size(); j++) - mesh.DeleteSurfaceElement ( elmap[j] ); + mesh.Delete (elmap[j]); for (int j = 1; j <= pmap.Size(); j++) nelonnode[pmap.Get(j)] += rule.incelsonnode.Get(j); diff --git a/libsrc/meshing/improve3.cpp b/libsrc/meshing/improve3.cpp index 544592b3..b9ee7585 100644 --- a/libsrc/meshing/improve3.cpp +++ b/libsrc/meshing/improve3.cpp @@ -1,4 +1,8 @@ #include +#include + +#include +#include #include "meshing.hpp" @@ -10,6 +14,128 @@ 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) + { + if (elem.GetType() != TET) return 0; + + MeshPoint* p[] = {&points[elem[0]], &points[elem[1]], &points[elem[2]], &points[elem[3]]}; + + for (auto i : Range(4)) + if(elem[i]==pi1 || elem[i]==pi2) p[i] = &pnew; + + 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 @@ -17,225 +143,227 @@ namespace netgen Connect inner point to boundary point, if one point is inner point. */ +double MeshOptimize3d :: CombineImproveEdge (Mesh & mesh, + const MeshingParameters & mp, + Table & elements_of_point, + Array & elerrs, + PointIndex pi0, PointIndex pi1, + FlatArray is_point_removed, + bool check_only) +{ + if (pi1 < pi0) Swap (pi0, pi1); + if(is_point_removed[pi0] || is_point_removed[pi1]) return false; + MeshPoint p0 = mesh[pi0]; + MeshPoint p1 = mesh[pi1]; + + if (p1.Type() != INNERPOINT) + return false; + + ArrayMem has_one_point; + ArrayMem has_both_points; + + for (auto ei : elements_of_point[pi0] ) + { + 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) + { + if(!has_both_points.Contains(ei)) + has_both_points.Append (ei); + } + else + { + if(!has_one_point.Contains(ei)) + has_one_point.Append (ei); + } + } + + for (auto ei : elements_of_point[pi1] ) + { + Element & elem = mesh[ei]; + if (elem.IsDeleted()) return false; + + if (elem[0] == pi0 || elem[1] == pi0 || elem[2] == pi0 || elem[3] == pi0) + { + ; + } + else + { + if(!has_one_point.Contains(ei)) + has_one_point.Append (ei); + } + } + + double badness_old = 0.0; + for (auto ei : has_one_point) + badness_old += elerrs[ei]; + for (auto ei : has_both_points) + badness_old += elerrs[ei]; + + MeshPoint pnew = p0; + if (p0.Type() == INNERPOINT) + pnew = Center (p0, p1); + + ArrayMem one_point_badness(has_one_point.Size()); + + double badness_new = 0; + for (auto i : Range(has_one_point)) + { + const Element & elem = mesh[has_one_point[i]]; + double badness = CalcBadReplacePoints (mesh.Points(), mp, elem, 0, pi0, pi1, pnew); + badness_new += badness; + one_point_badness[i] = badness; + } + + // Check if changed tets are topologically legal + if (p0.Type() != INNERPOINT) + { + for (auto ei : has_one_point) + { + Element elem = mesh[ei]; + int l; + for (int l = 0; l < 4; l++) + if (elem[l] == pi1) + { + elem[l] = pi0; + break; + } + + elem.Flags().illegal_valid = 0; + if (!mesh.LegalTet(elem)) + badness_new += 1e4; + } + } + + double d_badness = badness_new / has_one_point.Size() - badness_old / (has_one_point.Size()+has_both_points.Size()); + + // Do the actual combine operation + if (d_badness < 0.0 && !check_only) + { + is_point_removed[pi1] = true; + mesh[pi0] = pnew; + + for (auto ei : elements_of_point[pi1]) + { + Element & elem = mesh[ei]; + if (elem.IsDeleted()) continue; + + for (int l = 0; l < elem.GetNP(); l++) + if (elem[l] == pi1) + elem[l] = pi0; + + elem.Flags().illegal_valid = 0; + if (!mesh.LegalTet (elem)) + (*testout) << "illegal tet " << ei << endl; + } + + for (auto i : Range(has_one_point)) + elerrs[has_one_point[i]] = one_point_badness[i]; + + for (auto ei : has_both_points) + { + mesh[ei].Flags().illegal_valid = 0; + mesh[ei].Delete(); + } + } + + return d_badness; +} void MeshOptimize3d :: CombineImprove (Mesh & mesh, OPTIMIZEGOAL goal) { static Timer t("MeshOptimize3d::CombineImprove"); RegionTimer reg(t); + static Timer topt("Optimize"); + static Timer tsearch("Search"); + static Timer tbuild_elements_table("Build elements table"); + static Timer tbad("CalcBad"); + + mesh.BuildBoundaryEdges(false); int np = mesh.GetNP(); int ne = mesh.GetNE(); + int ntasks = 4*ngcore::TaskManager::GetNumThreads(); - TABLE elementsonnode(np); - Array hasonepi, hasbothpi; - - Array oneperr; Array elerrs (ne); + Array is_point_removed (np); + is_point_removed = false; PrintMessage (3, "CombineImprove"); (*testout) << "Start CombineImprove" << "\n"; // mesh.CalcSurfacesOfNode (); const char * savetask = multithread.task; - multithread.task = "Combine Improve"; + multithread.task = "Optimize Volume: Combine Improve"; - double totalbad = 0; - for (ElementIndex ei = 0; ei < ne; ei++) + tbad.Start(); + double totalbad = 0.0; + ParallelForRange(Range(ne), [&] (auto myrange) + { + double totalbad_local = 0.0; + for (ElementIndex ei : myrange) { - if(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex()) + if(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh[ei].GetIndex()) continue; double elerr = CalcBad (mesh.Points(), mesh[ei], 0); - totalbad += elerr; + totalbad_local += elerr; elerrs[ei] = elerr; } + AtomicAdd(totalbad, totalbad_local); + }, ntasks); + tbad.Stop(); if (goal == OPT_QUALITY) { - totalbad = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); + totalbad = mesh.CalcTotalBad (mp); (*testout) << "Total badness = " << totalbad << endl; - PrintMessage (5, "Total badness = ", totalbad); } - for (ElementIndex ei = 0; ei < ne; ei++) - if (!mesh[ei].IsDeleted()) - for (int j = 0; j < mesh[ei].GetNP(); j++) - elementsonnode.Add (mesh[ei][j], ei); - - INDEX_2_HASHTABLE edgetested (np+1); + auto elementsonnode = mesh.CreatePoint2ElementTable(nullopt, mp.only3D_domain_nr); - int cnt = 0; + Array> edges; + BuildEdgeList(mesh, elementsonnode, edges); - for (ElementIndex ei = 0; ei < ne; ei++) + // Find edges with improvement + Array> combine_candidate_edges(edges.Size()); + std::atomic improvement_counter(0); + + tsearch.Start(); + ParallelForRange(Range(edges), [&] (auto myrange) + { + for(auto i : myrange) { - if(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex()) - continue; - if (multithread.terminate) - break; - - multithread.percent = 100.0 * (ei+1) / ne; - - if (mesh.ElementType(ei) == FIXEDELEMENT) - continue; - - for (int j = 0; j < 6; j++) - { - Element & elemi = mesh[ei]; - if (elemi.IsDeleted()) continue; - - static const int tetedges[6][2] = - { { 0, 1 }, { 0, 2 }, { 0, 3 }, - { 1, 2 }, { 1, 3 }, { 2, 3 } }; - - PointIndex pi1 = elemi[tetedges[j][0]]; - PointIndex pi2 = elemi[tetedges[j][1]]; - - if (pi2 < pi1) Swap (pi1, pi2); - - INDEX_2 si2 (pi1, pi2); - si2.Sort(); - - if (edgetested.Used (si2)) continue; - edgetested.Set (si2, 1); - - - // hasonepoint.SetSize(0); - // hasbothpoints.SetSize(0); - hasonepi.SetSize(0); - hasbothpi.SetSize(0); - - FlatArray row1 = elementsonnode[pi1]; - for (int k = 0; k < row1.Size(); k++) - { - Element & elem = mesh[row1[k]]; - if (elem.IsDeleted()) continue; - - if (elem[0] == pi2 || elem[1] == pi2 || - elem[2] == pi2 || elem[3] == pi2) - { - hasbothpi.Append (row1[k]); - } - else - { - hasonepi.Append (row1[k]); - } - } - - FlatArray row2 = elementsonnode[pi2]; - for (int k = 0; k < row2.Size(); k++) - { - Element & elem = mesh[row2[k]]; - if (elem.IsDeleted()) continue; - - if (elem[0] == pi1 || elem[1] == pi1 || - elem[2] == pi1 || elem[3] == pi1) - ; - else - { - hasonepi.Append (row2[k]); - } - } - - double bad1 = 0; - for (int k = 0; k < hasonepi.Size(); k++) - bad1 += elerrs[hasonepi[k]]; - for (int k = 0; k < hasbothpi.Size(); k++) - bad1 += elerrs[hasbothpi[k]]; - - MeshPoint p1 = mesh[pi1]; - MeshPoint p2 = mesh[pi2]; - - - // if (mesh.PointType(pi2) != INNERPOINT) - if (p2.Type() != INNERPOINT) - continue; - - MeshPoint pnew; - // if (mesh.PointType(pi1) != INNERPOINT) - if (p1.Type() != INNERPOINT) - pnew = p1; - else - pnew = Center (p1, p2); - - mesh[pi1] = pnew; - mesh[pi2] = pnew; - - oneperr.SetSize (hasonepi.Size()); - - double bad2 = 0; - for (int k = 0; k < hasonepi.Size(); k++) - { - const Element & elem = mesh[hasonepi[k]]; - double err = CalcBad (mesh.Points(), elem, 0); - // CalcTetBadness (mesh[elem[0]], mesh[elem[1]], - // mesh[elem[2]], mesh[elem[3]], 0, mparam); - bad2 += err; - oneperr[k] = err; - } - - mesh[pi1] = p1; - mesh[pi2] = p2; - - // if (mesh.PointType(pi1) != INNERPOINT) - if (p1.Type() != INNERPOINT) - { - for (int k = 0; k < hasonepi.Size(); k++) - { - Element & elem = mesh[hasonepi[k]]; - int l; - for (l = 0; l < 4; l++) - if (elem[l] == pi2) - { - elem[l] = pi1; - break; - } - - elem.flags.illegal_valid = 0; - if (!mesh.LegalTet(elem)) - bad2 += 1e4; - - if (l < 4) - { - elem.flags.illegal_valid = 0; - elem[l] = pi2; - } - } - } - - if (bad2 / hasonepi.Size() < - bad1 / (hasonepi.Size()+hasbothpi.Size())) - { - mesh[pi1] = pnew; - cnt++; - - FlatArray row = elementsonnode[pi2]; - for (int k = 0; k < row.Size(); k++) - { - Element & elem = mesh[row[k]]; - if (elem.IsDeleted()) continue; - - elementsonnode.Add (pi1, row[k]); - for (int l = 0; l < elem.GetNP(); l++) - if (elem[l] == pi2) - elem[l] = pi1; - - elem.flags.illegal_valid = 0; - if (!mesh.LegalTet (elem)) - (*testout) << "illegal tet " << elementsonnode[pi2][k] << endl; - } - - for (int k = 0; k < hasonepi.Size(); k++) - elerrs[hasonepi[k]] = oneperr[k]; - - for (int k = 0; k < hasbothpi.Size(); k++) - { - mesh[hasbothpi[k]].flags.illegal_valid = 0; - mesh[hasbothpi[k]].Delete(); - } - } - } + auto [p0,p1] = edges[i]; + double d_badness = CombineImproveEdge (mesh, mp, elementsonnode, elerrs, p0, p1, is_point_removed, true); + if(d_badness<0.0) + { + int index = improvement_counter++; + combine_candidate_edges[index] = make_tuple(d_badness, i); + } } + }, ntasks); + tsearch.Stop(); + + auto edges_with_improvement = combine_candidate_edges.Part(0, improvement_counter.load()); + + QuickSort(edges_with_improvement); + PrintMessage(5, edges.Size(), " edges"); + PrintMessage(5, edges_with_improvement.Size(), " edges with improvement"); + + // Apply actual optimizations + topt.Start(); + int cnt = 0; + for(auto [d_badness, ei] : edges_with_improvement) + { + auto [p0,p1] = edges[ei]; + if (CombineImproveEdge (mesh, mp, elementsonnode, elerrs, p0, p1, is_point_removed, false) < 0.0) + cnt++; + } + topt.Stop(); mesh.Compress(); mesh.MarkIllegalElements(); @@ -243,14 +371,9 @@ void MeshOptimize3d :: CombineImprove (Mesh & mesh, PrintMessage (5, cnt, " elements combined"); (*testout) << "CombineImprove done" << "\n"; - totalbad = 0; - for (ElementIndex ei = 0; ei < mesh.GetNE(); ei++) - if(!(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex())) - totalbad += CalcBad (mesh.Points(), mesh[ei], 0); - if (goal == OPT_QUALITY) { - totalbad = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); + totalbad = mesh.CalcTotalBad (mp); (*testout) << "Total badness = " << totalbad << endl; int cntill = 0; @@ -266,294 +389,262 @@ void MeshOptimize3d :: CombineImprove (Mesh & mesh, +double MeshOptimize3d :: SplitImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, Table & elementsonnode, Array &elerrs, NgArray &locfaces, double badmax, PointIndex pi1, PointIndex pi2, PointIndex ptmp, bool check_only) +{ + double d_badness = 0.0; + int cnt = 0; + ArrayMem hasbothpoints; + + if (mesh.BoundaryEdge (pi1, pi2)) return 0.0; + + for (ElementIndex ei : elementsonnode[pi1]) + { + Element & el = mesh[ei]; + + if(el.IsDeleted()) return 0.0; + if (mesh[ei].GetType() != TET) return 0.0; + + bool has1 = el.PNums().Contains(pi1); + bool has2 = el.PNums().Contains(pi2); + + if (has1 && has2) + if (!hasbothpoints.Contains (ei)) + hasbothpoints.Append (ei); + } + + if(mp.only3D_domain_nr) + for(auto ei : hasbothpoints) + if(mp.only3D_domain_nr != mesh[ei].GetIndex()) + return 0.0; + + if (goal == OPT_LEGAL) + { + bool all_tets_legal = true; + for(auto ei : hasbothpoints) + if( !mesh.LegalTet (mesh[ei]) || elerrs[ei] > 1e3) + all_tets_legal = false; + if(all_tets_legal) + return 0.0; + } + + double bad1 = 0.0; + double bad1_max = 0.0; + for (ElementIndex ei : hasbothpoints) + { + double bad = elerrs[ei]; + bad1 += bad; + bad1_max = max(bad1_max, bad); + } + + if(bad1_max < 100.0) + return 0.0; + + bool puretet = 1; + for (ElementIndex ei : hasbothpoints) + if (mesh[ei].GetType() != TET) + puretet = 0; + if (!puretet) return 0.0; + + Point3d p1 = mesh[pi1]; + Point3d p2 = mesh[pi2]; + + locfaces.SetSize(0); + for (ElementIndex ei : hasbothpoints) + { + const Element & el = mesh[ei]; + + for (int l = 0; l < 4; l++) + if (el[l] == pi1 || el[l] == pi2) + { + INDEX_3 i3; + Element2d face(TRIG); + el.GetFace (l+1, face); + for (int kk = 1; kk <= 3; kk++) + i3.I(kk) = face.PNum(kk); + locfaces.Append (i3); + } + } + + PointFunction1 pf (mesh.Points(), locfaces, mp, -1); + OptiParameters par; + par.maxit_linsearch = 50; + par.maxit_bfgs = 20; + + Point3d pnew = Center (p1, p2); + Vector px(3); + px(0) = pnew.X(); + px(1) = pnew.Y(); + px(2) = pnew.Z(); + + if (bad1_max > 0.1 * badmax) + { + int pok = pf.Func (px) < 1e10; + if (!pok) + pok = FindInnerPoint (mesh.Points(), locfaces, pnew); + + if(pok) + { + px(0) = pnew.X(); + px(1) = pnew.Y(); + px(2) = pnew.Z(); + BFGS (px, pf, par); + pnew.X() = px(0); + pnew.Y() = px(1); + pnew.Z() = px(2); + } + } + + double bad2 = pf.Func (px); + + mesh[ptmp] = Point<3>(pnew); + + for (int k = 0; k < hasbothpoints.Size(); k++) + { + Element & oldel = mesh[hasbothpoints[k]]; + Element newel1 = oldel; + Element newel2 = oldel; + + oldel.Flags().illegal_valid = 0; + newel1.Flags().illegal_valid = 0; + newel2.Flags().illegal_valid = 0; + + for (int l = 0; l < 4; l++) + { + if (newel1[l] == pi2) newel1[l] = ptmp; + if (newel2[l] == pi1) newel2[l] = ptmp; + } + + if (!mesh.LegalTet (oldel)) bad1 += 1e6; + if (!mesh.LegalTet (newel1)) bad2 += 1e6; + if (!mesh.LegalTet (newel2)) bad2 += 1e6; + } + + d_badness = bad2-bad1; + if(check_only) + return d_badness; + + if (d_badness<0.0) + { + cnt++; + + PointIndex pinew = mesh.AddPoint (pnew); + + for (ElementIndex ei : hasbothpoints) + { + Element & oldel = mesh[ei]; + Element newel1 = oldel; + Element newel2 = oldel; + + oldel.Flags().illegal_valid = 0; + oldel.Delete(); + + newel1.Flags().illegal_valid = 0; + newel2.Flags().illegal_valid = 0; + + for (int l = 0; l < 4; l++) + { + if (newel1[l] == pi2) newel1[l] = pinew; + if (newel2[l] == pi1) newel2[l] = pinew; + } + + mesh.AddVolumeElement (newel1); + mesh.AddVolumeElement (newel2); + } + } + return d_badness; +} -/* - Mesh improvement by edge splitting. - If mesh quality is improved by inserting a node into an inner edge, - the edge is split into two parts. -*/ void MeshOptimize3d :: SplitImprove (Mesh & mesh, OPTIMIZEGOAL goal) { static Timer t("MeshOptimize3d::SplitImprove"); RegionTimer reg(t); - static Timer tloop("MeshOptimize3d::SplitImprove loop"); - - double bad1, bad2, badmax, badlimit; - int cnt = 0; + static Timer topt("Optimize"); + static Timer tsearch("Search"); + int np = mesh.GetNP(); int ne = mesh.GetNE(); + double bad = 0.0; + double badmax = 0.0; - TABLE elementsonnode(np); - Array hasbothpoints; - - BitArray origpoint(np+1), boundp(np+1); // big enough for 0 and 1-based - origpoint.Set(); + auto elementsonnode = mesh.CreatePoint2ElementTable(nullopt, mp.only3D_domain_nr); Array elerrs(ne); - BitArray illegaltet(ne); - illegaltet.Clear(); const char * savetask = multithread.task; - multithread.task = "Split Improve"; + multithread.task = "Optimize Volume: Split Improve"; PrintMessage (3, "SplitImprove"); (*testout) << "start SplitImprove" << "\n"; + mesh.BuildBoundaryEdges(false); - Array locfaces; - - INDEX_2_HASHTABLE edgetested (np); - - bad1 = 0; - badmax = 0; - for (ElementIndex ei = 0; ei < ne; ei++) + ParallelFor( mesh.VolumeElements().Range(), [&] (ElementIndex ei) NETGEN_LAMBDA_INLINE { if(mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex()) - continue; - + return; + elerrs[ei] = CalcBad (mesh.Points(), mesh[ei], 0); - bad1 += elerrs[ei]; - if (elerrs[ei] > badmax) badmax = elerrs[ei]; - } + bad += elerrs[ei]; + AtomicMax(badmax, elerrs[ei]); + }); - PrintMessage (5, "badmax = ", badmax); - badlimit = 0.5 * badmax; - - boundp.Clear(); - for (auto & el : mesh.SurfaceElements()) - for (PointIndex pi : el.PNums()) - boundp.Set (pi); - if (goal == OPT_QUALITY) { - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); - (*testout) << "Total badness = " << bad1 << endl; + bad = mesh.CalcTotalBad (mp); + (*testout) << "Total badness = " << bad << endl; } - for (ElementIndex ei : mesh.VolumeElements().Range()) - for (PointIndex pi : mesh[ei].PNums()) - elementsonnode.Add (pi, ei); + Array> edges; + BuildEdgeList(mesh, elementsonnode, edges); - mesh.MarkIllegalElements(); - if (goal == OPT_QUALITY || goal == OPT_LEGAL) + // Find edges with improvement + Array> candidate_edges(edges.Size()); + std::atomic improvement_counter(0); + auto ptmp = mesh.AddPoint( {0,0,0} ); + + tsearch.Start(); + ParallelForRange(Range(edges), [&] (auto myrange) + { + NgArray locfaces; + + for(auto i : myrange) { - int cntill = 0; - for (ElementIndex ei = 0; ei < ne; ei++) - { - // if (!LegalTet (volelements.Get(i))) - if (mesh[ei].flags.illegal) - { - cntill++; - illegaltet.Set (ei); - } - } + auto [p0,p1] = edges[i]; + double d_badness = SplitImproveEdge (mesh, goal, elementsonnode, elerrs, locfaces, badmax, p0, p1, ptmp, true); + if(d_badness<0.0) + { + int index = improvement_counter++; + candidate_edges[index] = make_tuple(d_badness, i); + } } + }, ngcore::TasksPerThread(4)); + tsearch.Stop(); - tloop.Start(); - for (ElementIndex ei : mesh.VolumeElements().Range()) - { - Element & elem = mesh[ei]; - - if(mp.only3D_domain_nr && mp.only3D_domain_nr != elem.GetIndex()) - continue; - if (multithread.terminate) - break; + auto edges_with_improvement = candidate_edges.Part(0, improvement_counter.load()); - multithread.percent = 100.0 * (ei+1) / ne; - - bool ltestmode = 0; - - if (elerrs[ei] < badlimit && !illegaltet.Test(ei)) continue; - - if ((goal == OPT_LEGAL) && - !illegaltet.Test(ei) && - CalcBad (mesh.Points(), elem, 0) < 1e3) - continue; - - if (ltestmode) - { - (*testout) << "test el " << ei << endl; - for (int j = 0; j < 4; j++) - (*testout) << elem[j] << " "; - (*testout) << endl; - } - - for (int j = 0; j < 6; j++) - { - static const int tetedges[6][2] = - { { 0, 1 }, { 0, 2 }, { 0, 3 }, - { 1, 2 }, { 1, 3 }, { 2, 3 } }; - - PointIndex pi1 = elem[tetedges[j][0]]; - PointIndex pi2 = elem[tetedges[j][1]]; - - if (pi2 < pi1) Swap (pi1, pi2); - if (pi2 >= elementsonnode.Size()+PointIndex::BASE) continue; // old number of points - - if (!origpoint.Test(pi1) || !origpoint.Test(pi2)) - continue; - - INDEX_2 i2(pi1, pi2); - i2.Sort(); - - if (mesh.BoundaryEdge (pi1, pi2)) continue; - - if (edgetested.Used (i2) && !illegaltet.Test(ei)) continue; - edgetested.Set (i2, 1); - - hasbothpoints.SetSize (0); - /* - for (int k = 1; k <= elementsonnode.EntrySize(pi1); k++) - { - ElementIndex elnr = elementsonnode.Get(pi1, k); - */ - for (ElementIndex ei : elementsonnode[pi1]) - { - Element & el = mesh[ei]; - bool has1 = el.PNums().Contains(pi1); - bool has2 = el.PNums().Contains(pi2); - - if (has1 && has2) - if (!hasbothpoints.Contains (ei)) - hasbothpoints.Append (ei); - } - - bad1 = 0; - - for (ElementIndex ei : hasbothpoints) - bad1 += CalcBad (mesh.Points(), mesh[ei], 0); - - bool puretet = 1; - for (ElementIndex ei : hasbothpoints) - if (mesh[ei].GetType() != TET) - puretet = 0; - if (!puretet) continue; - - Point3d p1 = mesh[pi1]; - Point3d p2 = mesh[pi2]; - - /* - pnew = Center (p1, p2); - - points.Elem(pi1) = pnew; - bad2 = 0; - for (k = 1; k <= hasbothpoints.Size(); k++) - bad2 += CalcBad (points, - volelements.Get(hasbothpoints.Get(k)), 0); - - points.Elem(pi1) = p1; - points.Elem(pi2) = pnew; - - for (k = 1; k <= hasbothpoints.Size(); k++) - bad2 += CalcBad (points, - volelements.Get(hasbothpoints.Get(k)), 0); - points.Elem(pi2) = p2; - */ - - locfaces.SetSize (0); - for (ElementIndex ei : hasbothpoints) - { - const Element & el = mesh[ei]; - - for (int l = 0; l < 4; l++) - if (el[l] == pi1 || el[l] == pi2) - { - INDEX_3 i3; - Element2d face(TRIG); - el.GetFace (l+1, face); - for (int kk = 1; kk <= 3; kk++) - i3.I(kk) = face.PNum(kk); - locfaces.Append (i3); - } - } - - PointFunction1 pf (mesh.Points(), locfaces, mp, -1); - OptiParameters par; - par.maxit_linsearch = 50; - par.maxit_bfgs = 20; - - Point3d pnew = Center (p1, p2); - Vector px(3); - px(0) = pnew.X(); - px(1) = pnew.Y(); - px(2) = pnew.Z(); - - if (elerrs[ei] > 0.1 * badmax) - BFGS (px, pf, par); - - bad2 = pf.Func (px); - - pnew.X() = px(0); - pnew.Y() = px(1); - pnew.Z() = px(2); - - PointIndex hpinew = mesh.AddPoint (pnew); - // ptyps.Append (INNERPOINT); - - for (int k = 0; k < hasbothpoints.Size(); k++) - { - Element & oldel = mesh[hasbothpoints[k]]; - Element newel1 = oldel; - Element newel2 = oldel; - - oldel.flags.illegal_valid = 0; - newel1.flags.illegal_valid = 0; - newel2.flags.illegal_valid = 0; - - for (int l = 0; l < 4; l++) - { - if (newel1[l] == pi2) newel1[l] = hpinew; - if (newel2[l] == pi1) newel2[l] = hpinew; - } - - if (!mesh.LegalTet (oldel)) bad1 += 1e6; - if (!mesh.LegalTet (newel1)) bad2 += 1e6; - if (!mesh.LegalTet (newel2)) bad2 += 1e6; - } - - // mesh.PointTypes().DeleteLast(); - mesh.Points().DeleteLast(); - - if (bad2 < bad1) - /* (bad1 > 1e4 && boundp.Test(pi1) && boundp.Test(pi2)) */ - { - cnt++; - - PointIndex pinew = mesh.AddPoint (pnew); - - for (ElementIndex ei : hasbothpoints) - { - Element & oldel = mesh[ei]; - Element newel = oldel; - - newel.flags.illegal_valid = 0; - oldel.flags.illegal_valid = 0; - - for (int l = 0; l < 4; l++) - { - origpoint.Clear (oldel[l]); - - if (oldel[l] == pi2) oldel[l] = pinew; - if (newel[l] == pi1) newel[l] = pinew; - } - mesh.AddVolumeElement (newel); - } - - j = 10; // end j-loop - } - } - } - tloop.Stop(); + QuickSort(edges_with_improvement); + PrintMessage(5, edges.Size(), " edges"); + PrintMessage(5, edges_with_improvement.Size(), " edges with improvement"); + // Apply actual optimizations + topt.Start(); + int cnt = 0; + NgArray locfaces; + for(auto [d_badness, ei] : edges_with_improvement) + { + auto [p0,p1] = edges[ei]; + if (SplitImproveEdge (mesh, goal, elementsonnode, elerrs, locfaces, badmax, p0, p1, ptmp, false) < 0.0) + cnt++; + } + topt.Stop(); mesh.Compress(); PrintMessage (5, cnt, " splits performed"); - (*testout) << "Splitt - Improve done" << "\n"; if (goal == OPT_QUALITY) { - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); - (*testout) << "Total badness = " << bad1 << endl; + bad = mesh.CalcTotalBad (mp); + (*testout) << "Total badness = " << bad << endl; int cntill = 0; ne = mesh.GetNE(); @@ -567,40 +658,679 @@ void MeshOptimize3d :: SplitImprove (Mesh & mesh, } - - - -void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal, - const BitArray * working_elements) +double MeshOptimize3d :: SwapImproveEdge (Mesh & mesh, OPTIMIZEGOAL goal, + const NgBitArray * working_elements, + Table & elementsonnode, + INDEX_3_HASHTABLE & faces, + PointIndex pi1, PointIndex pi2, bool check_only) { - static Timer t("MeshOptimize3d::SwapImprove"); RegionTimer reg(t); - static Timer tloop("MeshOptimize3d::SwapImprove loop"); - - PointIndex pi3(0), pi4(0), pi5(0), pi6(0); - int cnt = 0; + PointIndex pi3(PointIndex::INVALID), pi4(PointIndex::INVALID), + pi5(PointIndex::INVALID), pi6(PointIndex::INVALID); + + double bad1, bad2, bad3; Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET); Element el1(TET), el2(TET), el3(TET), el4(TET); Element el1b(TET), el2b(TET), el3b(TET), el4b(TET); - - double bad1, bad2, bad3; + ArrayMem hasbothpoints; + + double d_badness = 0.0; + if (pi2 < pi1) Swap (pi1, pi2); + + if (mesh.BoundaryEdge (pi1, pi2)) return 0.0; + + + hasbothpoints.SetSize (0); + for (ElementIndex elnr : elementsonnode[pi1]) + { + bool has1 = 0, has2 = 0; + const Element & elem = mesh[elnr]; + + if (elem.IsDeleted()) return 0.0; + + for (int l = 0; l < elem.GetNP(); l++) + { + if (elem[l] == pi1) has1 = 1; + if (elem[l] == pi2) has2 = 1; + } + + if (has1 && has2) + { // only once + if (hasbothpoints.Contains (elnr)) + has1 = false; + + if (has1) + { + hasbothpoints.Append (elnr); + } + } + } + + bool have_bad_element = false; + + for (ElementIndex ei : hasbothpoints) + { + if (mesh[ei].GetType () != TET) + return 0.0; + + if (mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex()) + return 0.0; + + + if ((mesh.ElementType(ei)) == FIXEDELEMENT) + return 0.0; + + if(working_elements && + ei < working_elements->Size() && + !working_elements->Test(ei)) + return 0.0; + + if (mesh[ei].IsDeleted()) + return 0.0; + + if ((goal == OPT_LEGAL) && + mesh.LegalTet (mesh[ei]) && + 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(); + + if ( nsuround == 3 ) + { + Element & elem = mesh[hasbothpoints[0]]; + for (int l = 0; l < 4; l++) + if (elem[l] != pi1 && elem[l] != pi2) + { + pi4 = pi3; + pi3 = elem[l]; + } + + el31[0] = pi1; + el31[1] = pi2; + el31[2] = pi3; + el31[3] = pi4; + el31.SetIndex (mattyp); + + if (WrongOrientation (mesh.Points(), el31)) + { + Swap (pi3, pi4); + el31[2] = pi3; + el31[3] = pi4; + } + + pi5.Invalidate(); + for (int k = 0; k < 3; k++) // JS, 201212 + { + const Element & elemk = mesh[hasbothpoints[k]]; + bool has1 = false; + for (int l = 0; l < 4; l++) + if (elemk[l] == pi4) + has1 = true; + if (has1) + { + for (int l = 0; l < 4; l++) + if (elemk[l] != pi1 && elemk[l] != pi2 && elemk[l] != pi4) + pi5 = elemk[l]; + } + } + + if (!pi5.IsValid()) + throw NgException("Illegal state observed in SwapImprove"); + + + el32[0] = pi1; + el32[1] = pi2; + el32[2] = pi4; + el32[3] = pi5; + el32.SetIndex (mattyp); + + el33[0] = pi1; + el33[1] = pi2; + el33[2] = pi5; + el33[3] = pi3; + el33.SetIndex (mattyp); + + bad1 = CalcBad (mesh.Points(), el31, 0) + + CalcBad (mesh.Points(), el32, 0) + + CalcBad (mesh.Points(), el33, 0); + + el31.Flags().illegal_valid = 0; + el32.Flags().illegal_valid = 0; + el33.Flags().illegal_valid = 0; + + if (!mesh.LegalTet(el31) || + !mesh.LegalTet(el32) || + !mesh.LegalTet(el33)) + bad1 += 1e4; + + el21[0] = pi3; + el21[1] = pi4; + el21[2] = pi5; + el21[3] = pi2; + el21.SetIndex (mattyp); + + el22[0] = pi5; + el22[1] = pi4; + el22[2] = pi3; + el22[3] = pi1; + el22.SetIndex (mattyp); + + bad2 = CalcBad (mesh.Points(), el21, 0) + + CalcBad (mesh.Points(), el22, 0); + + el21.Flags().illegal_valid = 0; + el22.Flags().illegal_valid = 0; + + if (!mesh.LegalTet(el21) || + !mesh.LegalTet(el22)) + bad2 += 1e4; + + + if (goal == OPT_CONFORM && NotTooBad(bad1, bad2)) + { + INDEX_3 face(pi3, pi4, pi5); + face.Sort(); + if (faces.Used(face)) + { + // (*testout) << "3->2 swap, could improve conformity, bad1 = " << bad1 + // << ", bad2 = " << bad2 << endl; + bad2 = bad1 + IMPROVEMENT_CONFORMING_EDGE; + } + } + + if (bad2 < bad1) + { + // (*mycout) << "3->2 " << flush; + // (*testout) << "3->2 conversion" << endl; + d_badness = bad2-bad1; + if(check_only) + return d_badness; + + + /* + (*testout) << "3->2 swap, old els = " << endl + << mesh[hasbothpoints[0]] << endl + << mesh[hasbothpoints[1]] << endl + << mesh[hasbothpoints[2]] << endl + << "new els = " << endl + << el21 << endl + << el22 << endl; + */ + + mesh[hasbothpoints[0]].Delete(); + mesh[hasbothpoints[1]].Delete(); + mesh[hasbothpoints[2]].Delete(); + + el21.Flags().illegal_valid = 0; + el22.Flags().illegal_valid = 0; + mesh.AddVolumeElement(el21); + mesh.AddVolumeElement(el22); + } + } + + if (nsuround == 4) + { + const Element & elem1 = mesh[hasbothpoints[0]]; + for (int l = 0; l < 4; l++) + if (elem1[l] != pi1 && elem1[l] != pi2) + { + pi4 = pi3; + pi3 = elem1[l]; + } + + el1[0] = pi1; el1[1] = pi2; + el1[2] = pi3; el1[3] = pi4; + el1.SetIndex (mattyp); + + if (WrongOrientation (mesh.Points(), el1)) + { + Swap (pi3, pi4); + el1[2] = pi3; + el1[3] = pi4; + } + + pi5.Invalidate(); + for (int k = 0; k < 4; k++) + { + const Element & elem = mesh[hasbothpoints[k]]; + bool has1 = elem.PNums().Contains(pi4); + if (has1) + { + for (int l = 0; l < 4; l++) + if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi4) + pi5 = elem[l]; + } + } + + pi6.Invalidate(); + for (int k = 0; k < 4; k++) + { + const Element & elem = mesh[hasbothpoints[k]]; + bool has1 = elem.PNums().Contains(pi3); + if (has1) + { + for (int l = 0; l < 4; l++) + if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi3) + pi6 = elem[l]; + } + } + + el1[0] = pi1; el1[1] = pi2; + el1[2] = pi3; el1[3] = pi4; + el1.SetIndex (mattyp); + + el2[0] = pi1; el2[1] = pi2; + el2[2] = pi4; el2[3] = pi5; + el2.SetIndex (mattyp); + + el3[0] = pi1; el3[1] = pi2; + el3[2] = pi5; el3[3] = pi6; + el3.SetIndex (mattyp); + + el4[0] = pi1; el4[1] = pi2; + el4[2] = pi6; el4[3] = pi3; + el4.SetIndex (mattyp); + + bad1 = CalcBad (mesh.Points(), el1, 0) + + CalcBad (mesh.Points(), el2, 0) + + CalcBad (mesh.Points(), el3, 0) + + CalcBad (mesh.Points(), el4, 0); + + + el1.Flags().illegal_valid = 0; + el2.Flags().illegal_valid = 0; + el3.Flags().illegal_valid = 0; + el4.Flags().illegal_valid = 0; + + + if (goal != OPT_CONFORM) + { + if (!mesh.LegalTet(el1) || + !mesh.LegalTet(el2) || + !mesh.LegalTet(el3) || + !mesh.LegalTet(el4)) + bad1 += 1e4; + } + + el1[0] = pi3; el1[1] = pi5; + el1[2] = pi2; el1[3] = pi4; + el1.SetIndex (mattyp); + + el2[0] = pi3; el2[1] = pi5; + el2[2] = pi4; el2[3] = pi1; + el2.SetIndex (mattyp); + + el3[0] = pi3; el3[1] = pi5; + el3[2] = pi1; el3[3] = pi6; + el3.SetIndex (mattyp); + + el4[0] = pi3; el4[1] = pi5; + el4[2] = pi6; el4[3] = pi2; + el4.SetIndex (mattyp); + + bad2 = CalcBad (mesh.Points(), el1, 0) + + CalcBad (mesh.Points(), el2, 0) + + CalcBad (mesh.Points(), el3, 0) + + CalcBad (mesh.Points(), el4, 0); + + el1.Flags().illegal_valid = 0; + el2.Flags().illegal_valid = 0; + el3.Flags().illegal_valid = 0; + el4.Flags().illegal_valid = 0; + + if (goal != OPT_CONFORM) + { + if (!mesh.LegalTet(el1) || + !mesh.LegalTet(el2) || + !mesh.LegalTet(el3) || + !mesh.LegalTet(el4)) + bad2 += 1e4; + } + + + el1b[0] = pi4; el1b[1] = pi6; + el1b[2] = pi3; el1b[3] = pi2; + el1b.SetIndex (mattyp); + + el2b[0] = pi4; el2b[1] = pi6; + el2b[2] = pi2; el2b[3] = pi5; + el2b.SetIndex (mattyp); + + el3b[0] = pi4; el3b[1] = pi6; + el3b[2] = pi5; el3b[3] = pi1; + el3b.SetIndex (mattyp); + + el4b[0] = pi4; el4b[1] = pi6; + el4b[2] = pi1; el4b[3] = pi3; + el4b.SetIndex (mattyp); + + bad3 = CalcBad (mesh.Points(), el1b, 0) + + CalcBad (mesh.Points(), el2b, 0) + + CalcBad (mesh.Points(), el3b, 0) + + CalcBad (mesh.Points(), el4b, 0); + + el1b.Flags().illegal_valid = 0; + el2b.Flags().illegal_valid = 0; + el3b.Flags().illegal_valid = 0; + el4b.Flags().illegal_valid = 0; + + if (goal != OPT_CONFORM) + { + if (!mesh.LegalTet(el1b) || + !mesh.LegalTet(el2b) || + !mesh.LegalTet(el3b) || + !mesh.LegalTet(el4b)) + bad3 += 1e4; + } + + bool swap2, swap3; + + 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); + d_badness = swap2 ? bad2-bad1 : bad3-bad1; + } + + if(check_only) + return d_badness; + + if (swap2) + { + for (auto i : IntRange(4)) + mesh[hasbothpoints[i]].Delete(); + + el1.Flags().illegal_valid = 0; + el2.Flags().illegal_valid = 0; + el3.Flags().illegal_valid = 0; + el4.Flags().illegal_valid = 0; + mesh.AddVolumeElement (el1); + mesh.AddVolumeElement (el2); + mesh.AddVolumeElement (el3); + mesh.AddVolumeElement (el4); + } + else if (swap3) + { + for (auto i : IntRange(4)) + mesh[hasbothpoints[i]].Delete(); + + el1b.Flags().illegal_valid = 0; + el2b.Flags().illegal_valid = 0; + el3b.Flags().illegal_valid = 0; + el4b.Flags().illegal_valid = 0; + mesh.AddVolumeElement (el1b); + mesh.AddVolumeElement (el2b); + mesh.AddVolumeElement (el3b); + mesh.AddVolumeElement (el4b); + } + } + + // if (goal == OPT_QUALITY) + if (nsuround >= 5) + { + Element hel(TET); + + NgArrayMem suroundpts(nsuround); + NgArrayMem tetused(nsuround); + + Element & elem = mesh[hasbothpoints[0]]; + + for (int l = 0; l < 4; l++) + if (elem[l] != pi1 && elem[l] != pi2) + { + pi4 = pi3; + pi3 = elem[l]; + } + + hel[0] = pi1; + hel[1] = pi2; + hel[2] = pi3; + hel[3] = pi4; + hel.SetIndex (mattyp); + + if (WrongOrientation (mesh.Points(), hel)) + { + Swap (pi3, pi4); + hel[2] = pi3; + hel[3] = pi4; + } + + + // suroundpts.SetSize (nsuround); + suroundpts = PointIndex::INVALID; + suroundpts[0] = pi3; + suroundpts[1] = pi4; + + tetused = false; + tetused[0] = true; + + for (int l = 2; l < nsuround; l++) + { + PointIndex oldpi = suroundpts[l-1]; + PointIndex newpi; + newpi.Invalidate(); + + for (int k = 0; k < nsuround && !newpi.IsValid(); k++) + if (!tetused[k]) + { + const Element & nel = mesh[hasbothpoints[k]]; + for (int k2 = 0; k2 < 4 && !newpi.IsValid(); k2++) + if (nel[k2] == oldpi) + { + newpi = + nel[0] + nel[1] + nel[2] + nel[3] + - pi1 - pi2 - oldpi; + + tetused[k] = true; + suroundpts[l] = newpi; + } + } + } + + + bad1 = 0; + for (int k = 0; k < nsuround; k++) + { + hel[0] = pi1; + hel[1] = pi2; + hel[2] = suroundpts[k]; + hel[3] = suroundpts[(k+1) % nsuround]; + hel.SetIndex (mattyp); + + bad1 += CalcBad (mesh.Points(), hel, 0); + } + + // (*testout) << "nsuround = " << nsuround << " bad1 = " << bad1 << endl; + + + int bestl = -1; + int confface = -1; + int confedge = -1; + double badopt = bad1; + + for (int l = 0; l < nsuround; l++) + { + bad2 = 0; + + for (int k = l+1; k <= nsuround + l - 2; k++) + { + hel[0] = suroundpts[l]; + hel[1] = suroundpts[k % nsuround]; + hel[2] = suroundpts[(k+1) % nsuround]; + hel[3] = pi2; + + bad2 += CalcBad (mesh.Points(), hel, 0); + hel.Flags().illegal_valid = 0; + if (!mesh.LegalTet(hel)) bad2 += 1e4; + + hel[2] = suroundpts[k % nsuround]; + hel[1] = suroundpts[(k+1) % nsuround]; + hel[3] = pi1; + + bad2 += CalcBad (mesh.Points(), hel, 0); + + hel.Flags().illegal_valid = 0; + if (!mesh.LegalTet(hel)) bad2 += 1e4; + } + // (*testout) << "bad2," << l << " = " << bad2 << endl; + + if ( bad2 < badopt ) + { + bestl = l; + badopt = bad2; + } + + + if (goal == OPT_CONFORM) + { + bool nottoobad = NotTooBad(bad1, bad2); + + for (int k = l+1; k <= nsuround + l - 2; k++) + { + INDEX_3 hi3(suroundpts[l], + suroundpts[k % nsuround], + suroundpts[(k+1) % nsuround]); + hi3.Sort(); + if (faces.Used(hi3)) + { + // (*testout) << "could improve face conformity, bad1 = " << bad1 + // << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl; + if (nottoobad) + confface = l; + } + } + + for (int k = l+2; k <= nsuround+l-2; k++) + { + if (mesh.BoundaryEdge (suroundpts[l], + suroundpts[k % nsuround])) + { + /* + *testout << "could improve edge conformity, bad1 = " << bad1 + << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl; + */ + if (nottoobad) + confedge = l; + } + } + } + } + + if (confedge != -1) + bestl = confedge; + if (confface != -1) + bestl = confface; + + if(confface != -1 || confedge != -1) + badopt = bad1 + IMPROVEMENT_CONFORMING_EDGE; + + if (bestl != -1) + { + // (*mycout) << nsuround << "->" << 2 * (nsuround-2) << " " << flush; + d_badness = badopt-bad1; + if(check_only) + return d_badness; + + for (int k = bestl+1; k <= nsuround + bestl - 2; k++) + { + int k1; + + hel[0] = suroundpts[bestl]; + hel[1] = suroundpts[k % nsuround]; + hel[2] = suroundpts[(k+1) % nsuround]; + hel[3] = pi2; + hel.Flags().illegal_valid = 0; + + /* + (*testout) << nsuround << "-swap, new el,top = " + << hel << endl; + */ + mesh.AddVolumeElement (hel); + + hel[2] = suroundpts[k % nsuround]; + hel[1] = suroundpts[(k+1) % nsuround]; + hel[3] = pi1; + + /* + (*testout) << nsuround << "-swap, new el,bot = " + << hel << endl; + */ + + mesh.AddVolumeElement (hel); + } + + for (int k = 0; k < nsuround; k++) + { + Element & rel = mesh[hasbothpoints[k]]; + /* + (*testout) << nsuround << "-swap, old el = " + << rel << endl; + */ + rel.Delete(); + for (int k1 = 0; k1 < 4; k1++) + rel[k1].Invalidate(); + } + } + } + return d_badness; +} + +void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal, + const NgBitArray * working_elements) +{ + static Timer t("MeshOptimize3d::SwapImprove"); RegionTimer reg(t); + static Timer tloop("MeshOptimize3d::SwapImprove loop"); + + int cnt = 0; int np = mesh.GetNP(); int ne = mesh.GetNE(); - - // contains at least all elements at node - TABLE elementsonnode(np); - Array hasbothpoints; + mesh.BuildBoundaryEdges(false); + BitArray free_points(mesh.GetNP()+PointIndex::BASE); + free_points.Clear(); + + ParallelForRange(mesh.VolumeElements().Range(), [&] (auto myrange) + { + for (ElementIndex eli : myrange) + { + const auto & el = mesh[eli]; + if(el.Flags().fixed) + continue; + + if(mp.only3D_domain_nr && mp.only3D_domain_nr != el.GetIndex()) + continue; + + for (auto pi : el.PNums()) + if(!free_points[pi]) + free_points.SetBitAtomic(pi); + } + }); + + auto elementsonnode = mesh.CreatePoint2ElementTable(free_points, mp.only3D_domain_nr ); + + NgArray hasbothpoints; PrintMessage (3, "SwapImprove "); (*testout) << "\n" << "Start SwapImprove" << endl; const char * savetask = multithread.task; - multithread.task = "Swap Improve"; - - // mesh.CalcSurfacesOfNode (); - + multithread.task = "Optimize Volume: Swap Improve"; + INDEX_3_HASHTABLE faces(mesh.GetNOpenElements()/3 + 2); if (goal == OPT_CONFORM) { @@ -609,835 +1339,84 @@ void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal, const Element2d & hel = mesh.OpenElement(i); INDEX_3 face(hel[0], hel[1], hel[2]); face.Sort(); - faces.Set (face, 1); + faces.Set (face, i); } } - + // Calculate total badness if (goal == OPT_QUALITY) { - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); + double bad1 = mesh.CalcTotalBad (mp); (*testout) << "Total badness = " << bad1 << endl; } - - // find elements on node - for (ElementIndex ei = 0; ei < ne; ei++) - for (PointIndex pi : mesh[ei].PNums()) - elementsonnode.Add (pi, ei); - /* - for (int j = 0; j < mesh[ei].GetNP(); j++) - elementsonnode.Add (mesh[ei][j], ei); - */ + Array> edges; + BuildEdgeList(mesh, elementsonnode, edges); - // INDEX_2_HASHTABLE edgeused(2 * ne + 5); - INDEX_2_CLOSED_HASHTABLE edgeused(12 * ne + 5); + Array> candidate_edges(edges.Size()); + std::atomic improvement_counter(0); tloop.Start(); - for (ElementIndex ei = 0; ei < ne; ei++) + + auto num_elements_before = mesh.VolumeElements().Range().Next(); + + ParallelForRange(Range(edges), [&] (auto myrange) + { + for(auto i : myrange) { if (multithread.terminate) - break; - - if (mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(ei).GetIndex()) - continue; - - multithread.percent = 100.0 * (ei+1) / ne; - - if ((mesh.ElementType(ei)) == FIXEDELEMENT) - continue; - - if(working_elements && - ei < working_elements->Size() && - !working_elements->Test(ei)) - continue; - - if (mesh[ei].IsDeleted()) - continue; - - if ((goal == OPT_LEGAL) && - mesh.LegalTet (mesh[ei]) && - CalcBad (mesh.Points(), mesh[ei], 0) < 1e3) - continue; - - // int onlybedges = 1; - - for (int j = 0; j < 6; j++) - { - // loop over edges - - const Element & elemi = mesh[ei]; - if (elemi.IsDeleted()) continue; - - int mattyp = elemi.GetIndex(); - - static const int tetedges[6][2] = - { { 0, 1 }, { 0, 2 }, { 0, 3 }, - { 1, 2 }, { 1, 3 }, { 2, 3 } }; - - PointIndex pi1 = elemi[tetedges[j][0]]; - PointIndex pi2 = elemi[tetedges[j][1]]; - - if (pi2 < pi1) Swap (pi1, pi2); - - if (mesh.BoundaryEdge (pi1, pi2)) continue; - - - INDEX_2 i2 (pi1, pi2); - i2.Sort(); - if (edgeused.Used(i2)) continue; - edgeused.Set (i2, 1); - - hasbothpoints.SetSize (0); - for (int k = 0; k < elementsonnode[pi1].Size(); k++) - { - bool has1 = 0, has2 = 0; - ElementIndex elnr = elementsonnode[pi1][k]; - const Element & elem = mesh[elnr]; - - if (elem.IsDeleted()) continue; - - for (int l = 0; l < elem.GetNP(); l++) - { - if (elem[l] == pi1) has1 = 1; - if (elem[l] == pi2) has2 = 1; - } - - if (has1 && has2) - { // only once - if (hasbothpoints.Contains (elnr)) - has1 = false; - - if (has1) - { - hasbothpoints.Append (elnr); - } - } - } - - bool puretet = true; - for (ElementIndex ei : hasbothpoints) - if (mesh[ei].GetType () != TET) - puretet = false; - - if (!puretet) continue; - - int nsuround = hasbothpoints.Size(); - - if ( nsuround == 3 ) - { - Element & elem = mesh[hasbothpoints[0]]; - for (int l = 0; l < 4; l++) - if (elem[l] != pi1 && elem[l] != pi2) - { - pi4 = pi3; - pi3 = elem[l]; - } - - el31[0] = pi1; - el31[1] = pi2; - el31[2] = pi3; - el31[3] = pi4; - el31.SetIndex (mattyp); - - if (WrongOrientation (mesh.Points(), el31)) - { - Swap (pi3, pi4); - el31[2] = pi3; - el31[3] = pi4; - } - - pi5 = 0; - for (int k = 0; k < 3; k++) // JS, 201212 - { - const Element & elemk = mesh[hasbothpoints[k]]; - bool has1 = false; - for (int l = 0; l < 4; l++) - if (elemk[l] == pi4) - has1 = true; - if (has1) - { - for (int l = 0; l < 4; l++) - if (elemk[l] != pi1 && elemk[l] != pi2 && elemk[l] != pi4) - pi5 = elemk[l]; - } - } - - if (!pi5.IsValid()) - throw NgException("Illegal state observed in SwapImprove"); - - - el32[0] = pi1; - el32[1] = pi2; - el32[2] = pi4; - el32[3] = pi5; - el32.SetIndex (mattyp); - - el33[0] = pi1; - el33[1] = pi2; - el33[2] = pi5; - el33[3] = pi3; - el33.SetIndex (mattyp); - - elementsonnode.Add (pi4, hasbothpoints[1]); - elementsonnode.Add (pi3, hasbothpoints[2]); - - bad1 = CalcBad (mesh.Points(), el31, 0) + - CalcBad (mesh.Points(), el32, 0) + - CalcBad (mesh.Points(), el33, 0); - - el31.flags.illegal_valid = 0; - el32.flags.illegal_valid = 0; - el33.flags.illegal_valid = 0; - - if (!mesh.LegalTet(el31) || - !mesh.LegalTet(el32) || - !mesh.LegalTet(el33)) - bad1 += 1e4; - - el21[0] = pi3; - el21[1] = pi4; - el21[2] = pi5; - el21[3] = pi2; - el21.SetIndex (mattyp); - - el22[0] = pi5; - el22[1] = pi4; - el22[2] = pi3; - el22[3] = pi1; - el22.SetIndex (mattyp); - - bad2 = CalcBad (mesh.Points(), el21, 0) + - CalcBad (mesh.Points(), el22, 0); - - el21.flags.illegal_valid = 0; - el22.flags.illegal_valid = 0; - - if (!mesh.LegalTet(el21) || - !mesh.LegalTet(el22)) - bad2 += 1e4; - - - if (goal == OPT_CONFORM && bad2 < 1e4) - { - INDEX_3 face(pi3, pi4, pi5); - face.Sort(); - if (faces.Used(face)) - { - // (*testout) << "3->2 swap, could improve conformity, bad1 = " << bad1 - // << ", bad2 = " << bad2 << endl; - if (bad2 < 1e4) - bad1 = 2 * bad2; - } - /* - else - { - INDEX_2 hi1(pi3, pi4); - hi1.Sort(); - INDEX_2 hi2(pi3, pi5); - hi2.Sort(); - INDEX_2 hi3(pi4, pi5); - hi3.Sort(); - - if (boundaryedges->Used (hi1) || - boundaryedges->Used (hi2) || - boundaryedges->Used (hi3) ) - bad1 = 2 * bad2; - } - */ - } - - if (bad2 < bad1) - { - // (*mycout) << "3->2 " << flush; - // (*testout) << "3->2 conversion" << endl; - cnt++; - - - /* - (*testout) << "3->2 swap, old els = " << endl - << mesh[hasbothpoints[0]] << endl - << mesh[hasbothpoints[1]] << endl - << mesh[hasbothpoints[2]] << endl - << "new els = " << endl - << el21 << endl - << el22 << endl; - */ - - el21.flags.illegal_valid = 0; - el22.flags.illegal_valid = 0; - mesh[hasbothpoints[0]] = el21; - mesh[hasbothpoints[1]] = el22; - for (int l = 0; l < 4; l++) - mesh[hasbothpoints[2]][l].Invalidate(); - mesh[hasbothpoints[2]].Delete(); - - for (int k = 0; k < 2; k++) - for (int l = 0; l < 4; l++) - elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]); - } - } - - if (nsuround == 4) - { - const Element & elem1 = mesh[hasbothpoints[0]]; - for (int l = 0; l < 4; l++) - if (elem1[l] != pi1 && elem1[l] != pi2) - { - pi4 = pi3; - pi3 = elem1[l]; - } - - el1[0] = pi1; el1[1] = pi2; - el1[2] = pi3; el1[3] = pi4; - el1.SetIndex (mattyp); - - if (WrongOrientation (mesh.Points(), el1)) - { - Swap (pi3, pi4); - el1[2] = pi3; - el1[3] = pi4; - } - - pi5.Invalidate(); - for (int k = 0; k < 4; k++) - { - const Element & elem = mesh[hasbothpoints[k]]; - bool has1 = elem.PNums().Contains(pi4); - if (has1) - { - for (int l = 0; l < 4; l++) - if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi4) - pi5 = elem[l]; - } - } - - pi6.Invalidate(); - for (int k = 0; k < 4; k++) - { - const Element & elem = mesh[hasbothpoints[k]]; - bool has1 = elem.PNums().Contains(pi3); - if (has1) - { - for (int l = 0; l < 4; l++) - if (elem[l] != pi1 && elem[l] != pi2 && elem[l] != pi3) - pi6 = elem[l]; - } - } - - - /* - INDEX_2 i22(pi3, pi5); - i22.Sort(); - INDEX_2 i23(pi4, pi6); - i23.Sort(); - */ - - el1[0] = pi1; el1[1] = pi2; - el1[2] = pi3; el1[3] = pi4; - el1.SetIndex (mattyp); - - el2[0] = pi1; el2[1] = pi2; - el2[2] = pi4; el2[3] = pi5; - el2.SetIndex (mattyp); - - el3[0] = pi1; el3[1] = pi2; - el3[2] = pi5; el3[3] = pi6; - el3.SetIndex (mattyp); - - el4[0] = pi1; el4[1] = pi2; - el4[2] = pi6; el4[3] = pi3; - el4.SetIndex (mattyp); - - // elementsonnode.Add (pi4, hasbothpoints.Elem(2)); - // elementsonnode.Add (pi3, hasbothpoints.Elem(3)); - - bad1 = CalcBad (mesh.Points(), el1, 0) + - CalcBad (mesh.Points(), el2, 0) + - CalcBad (mesh.Points(), el3, 0) + - CalcBad (mesh.Points(), el4, 0); - - - el1.flags.illegal_valid = 0; - el2.flags.illegal_valid = 0; - el3.flags.illegal_valid = 0; - el4.flags.illegal_valid = 0; - - - if (goal != OPT_CONFORM) - { - if (!mesh.LegalTet(el1) || - !mesh.LegalTet(el2) || - !mesh.LegalTet(el3) || - !mesh.LegalTet(el4)) - bad1 += 1e4; - } - - el1[0] = pi3; el1[1] = pi5; - el1[2] = pi2; el1[3] = pi4; - el1.SetIndex (mattyp); - - el2[0] = pi3; el2[1] = pi5; - el2[2] = pi4; el2[3] = pi1; - el2.SetIndex (mattyp); - - el3[0] = pi3; el3[1] = pi5; - el3[2] = pi1; el3[3] = pi6; - el3.SetIndex (mattyp); - - el4[0] = pi3; el4[1] = pi5; - el4[2] = pi6; el4[3] = pi2; - el4.SetIndex (mattyp); - - bad2 = CalcBad (mesh.Points(), el1, 0) + - CalcBad (mesh.Points(), el2, 0) + - CalcBad (mesh.Points(), el3, 0) + - CalcBad (mesh.Points(), el4, 0); - - el1.flags.illegal_valid = 0; - el2.flags.illegal_valid = 0; - el3.flags.illegal_valid = 0; - el4.flags.illegal_valid = 0; - - if (goal != OPT_CONFORM) - { - if (!mesh.LegalTet(el1) || - !mesh.LegalTet(el2) || - !mesh.LegalTet(el3) || - !mesh.LegalTet(el4)) - bad2 += 1e4; - } - - - el1b[0] = pi4; el1b[1] = pi6; - el1b[2] = pi3; el1b[3] = pi2; - el1b.SetIndex (mattyp); - - el2b[0] = pi4; el2b[1] = pi6; - el2b[2] = pi2; el2b[3] = pi5; - el2b.SetIndex (mattyp); - - el3b[0] = pi4; el3b[1] = pi6; - el3b[2] = pi5; el3b[3] = pi1; - el3b.SetIndex (mattyp); - - el4b[0] = pi4; el4b[1] = pi6; - el4b[2] = pi1; el4b[3] = pi3; - el4b.SetIndex (mattyp); - - bad3 = CalcBad (mesh.Points(), el1b, 0) + - CalcBad (mesh.Points(), el2b, 0) + - CalcBad (mesh.Points(), el3b, 0) + - CalcBad (mesh.Points(), el4b, 0); - - el1b.flags.illegal_valid = 0; - el2b.flags.illegal_valid = 0; - el3b.flags.illegal_valid = 0; - el4b.flags.illegal_valid = 0; - - if (goal != OPT_CONFORM) - { - if (!mesh.LegalTet(el1b) || - !mesh.LegalTet(el2b) || - !mesh.LegalTet(el3b) || - !mesh.LegalTet(el4b)) - bad3 += 1e4; - } - - - /* - int swap2 = (bad2 < bad1) && (bad2 < bad3); - int swap3 = !swap2 && (bad3 < bad1); - - if ( ((bad2 < 10 * bad1) || - (bad2 < 1e6)) && mesh.BoundaryEdge (pi3, pi5)) - swap2 = 1; - else if ( ((bad3 < 10 * bad1) || - (bad3 < 1e6)) && mesh.BoundaryEdge (pi4, pi6)) - { - swap3 = 1; - swap2 = 0; - } - */ - bool swap2, swap3; - - if (goal != OPT_CONFORM) - { - 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); - } - - - if (swap2 || swap3) - { - // (*mycout) << "4->4 " << flush; - cnt++; - // (*testout) << "4->4 conversion" << "\n"; - /* - (*testout) << "bad1 = " << bad1 - << " bad2 = " << bad2 - << " bad3 = " << bad3 << "\n"; - - (*testout) << "Points: " << pi1 << " " << pi2 << " " << pi3 - << " " << pi4 << " " << pi5 << " " << pi6 << "\n"; - (*testout) << "Elements: " - << hasbothpoints.Get(1) << " " - << hasbothpoints.Get(2) << " " - << hasbothpoints.Get(3) << " " - << hasbothpoints.Get(4) << " " << "\n"; - */ - - /* - { - int i1, j1; - for (i1 = 1; i1 <= 4; i1++) - { - for (j1 = 1; j1 <= 4; j1++) - (*testout) << volelements.Get(hasbothpoints.Get(i1)).PNum(j1) - << " "; - (*testout) << "\n"; - } - } - */ - } - - - if (swap2) - { - // (*mycout) << "bad1 = " << bad1 << " bad2 = " << bad2 << "\n"; - - - /* - (*testout) << "4->4 swap A, old els = " << endl - << mesh[hasbothpoints[0]] << endl - << mesh[hasbothpoints[1]] << endl - << mesh[hasbothpoints[2]] << endl - << mesh[hasbothpoints[3]] << endl - << "new els = " << endl - << el1 << endl - << el2 << endl - << el3 << endl - << el4 << endl; - */ - - - - el1.flags.illegal_valid = 0; - el2.flags.illegal_valid = 0; - el3.flags.illegal_valid = 0; - el4.flags.illegal_valid = 0; - - mesh[hasbothpoints[0]] = el1; - mesh[hasbothpoints[1]] = el2; - mesh[hasbothpoints[2]] = el3; - mesh[hasbothpoints[3]] = el4; - - for (int k = 0; k < 4; k++) - for (int l = 0; l < 4; l++) - elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]); - } - else if (swap3) - { - // (*mycout) << "bad1 = " << bad1 << " bad3 = " << bad3 << "\n"; - el1b.flags.illegal_valid = 0; - el2b.flags.illegal_valid = 0; - el3b.flags.illegal_valid = 0; - el4b.flags.illegal_valid = 0; - - - /* - (*testout) << "4->4 swap A, old els = " << endl - << mesh[hasbothpoints[0]] << endl - << mesh[hasbothpoints[1]] << endl - << mesh[hasbothpoints[2]] << endl - << mesh[hasbothpoints[3]] << endl - << "new els = " << endl - << el1b << endl - << el2b << endl - << el3b << endl - << el4b << endl; - */ - - - mesh[hasbothpoints[0]] = el1b; - mesh[hasbothpoints[1]] = el2b; - mesh[hasbothpoints[2]] = el3b; - mesh[hasbothpoints[3]] = el4b; - - for (int k = 0; k < 4; k++) - for (int l = 0; l < 4; l++) - elementsonnode.Add (mesh[hasbothpoints[k]][l], hasbothpoints[k]); - } - } - - if (nsuround >= 5) - { - Element hel(TET); - - ArrayMem suroundpts(nsuround); - ArrayMem tetused(nsuround); - - Element & elem = mesh[hasbothpoints[0]]; - - for (int l = 0; l < 4; l++) - if (elem[l] != pi1 && elem[l] != pi2) - { - pi4 = pi3; - pi3 = elem[l]; - } - - hel[0] = pi1; - hel[1] = pi2; - hel[2] = pi3; - hel[3] = pi4; - hel.SetIndex (mattyp); - - if (WrongOrientation (mesh.Points(), hel)) - { - Swap (pi3, pi4); - hel[2] = pi3; - hel[3] = pi4; - } - - - // suroundpts.SetSize (nsuround); - suroundpts = -17; - suroundpts[0] = pi3; - suroundpts[1] = pi4; - - tetused = false; - tetused[0] = true; - - for (int l = 2; l < nsuround; l++) - { - PointIndex oldpi = suroundpts[l-1]; - PointIndex newpi; - newpi.Invalidate(); - - for (int k = 0; k < nsuround && !newpi.IsValid(); k++) - if (!tetused[k]) - { - const Element & nel = mesh[hasbothpoints[k]]; - for (int k2 = 0; k2 < 4 && !newpi.IsValid(); k2++) - if (nel[k2] == oldpi) - { - newpi = - nel[0] + nel[1] + nel[2] + nel[3] - - pi1 - pi2 - oldpi; - - tetused[k] = true; - suroundpts[l] = newpi; - } - } - } - - - bad1 = 0; - for (int k = 0; k < nsuround; k++) - { - hel[0] = pi1; - hel[1] = pi2; - hel[2] = suroundpts[k]; - hel[3] = suroundpts[(k+1) % nsuround]; - hel.SetIndex (mattyp); - - bad1 += CalcBad (mesh.Points(), hel, 0); - } - - // (*testout) << "nsuround = " << nsuround << " bad1 = " << bad1 << endl; - - - int bestl = -1; - int confface = -1; - int confedge = -1; - double badopt = bad1; - - for (int l = 0; l < nsuround; l++) - { - bad2 = 0; - - for (int k = l+1; k <= nsuround + l - 2; k++) - { - hel[0] = suroundpts[l]; - hel[1] = suroundpts[k % nsuround]; - hel[2] = suroundpts[(k+1) % nsuround]; - hel[3] = pi2; - - bad2 += CalcBad (mesh.Points(), hel, 0); - hel.flags.illegal_valid = 0; - if (!mesh.LegalTet(hel)) bad2 += 1e4; - - hel[2] = suroundpts[k % nsuround]; - hel[1] = suroundpts[(k+1) % nsuround]; - hel[3] = pi1; - - bad2 += CalcBad (mesh.Points(), hel, 0); - - hel.flags.illegal_valid = 0; - if (!mesh.LegalTet(hel)) bad2 += 1e4; - } - // (*testout) << "bad2," << l << " = " << bad2 << endl; - - if ( bad2 < badopt ) - { - bestl = l; - badopt = bad2; - } - - - if (goal == OPT_CONFORM) - // (bad2 <= 100 * bad1 || bad2 <= 1e6)) - { - bool nottoobad = - (bad2 <= bad1) || - (bad2 <= 100 * bad1 && bad2 <= 1e18) || - (bad2 <= 1e8); - - for (int k = l+1; k <= nsuround + l - 2; k++) - { - INDEX_3 hi3(suroundpts[l], - suroundpts[k % nsuround], - suroundpts[(k+1) % nsuround]); - hi3.Sort(); - if (faces.Used(hi3)) - { - // (*testout) << "could improve face conformity, bad1 = " << bad1 - // << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl; - if (nottoobad) - confface = l; - } - } - - for (int k = l+2; k <= nsuround+l-2; k++) - { - if (mesh.BoundaryEdge (suroundpts[l], - suroundpts[k % nsuround])) - { - /* - *testout << "could improve edge conformity, bad1 = " << bad1 - << ", bad 2 = " << bad2 << ", nottoobad = " << nottoobad << endl; - */ - if (nottoobad) - confedge = l; - } - } - } - } - - if (confedge != -1) - bestl = confedge; - if (confface != -1) - bestl = confface; - - if (bestl != -1) - { - // (*mycout) << nsuround << "->" << 2 * (nsuround-2) << " " << flush; - cnt++; - - for (int k = bestl+1; k <= nsuround + bestl - 2; k++) - { - int k1; - - hel[0] = suroundpts[bestl]; - hel[1] = suroundpts[k % nsuround]; - hel[2] = suroundpts[(k+1) % nsuround]; - hel[3] = pi2; - hel.flags.illegal_valid = 0; - - /* - (*testout) << nsuround << "-swap, new el,top = " - << hel << endl; - */ - mesh.AddVolumeElement (hel); - - for (k1 = 0; k1 < 4; k1++) - elementsonnode.Add (hel[k1], mesh.GetNE()-1); - - - hel[2] = suroundpts[k % nsuround]; - hel[1] = suroundpts[(k+1) % nsuround]; - hel[3] = pi1; - - /* - (*testout) << nsuround << "-swap, new el,bot = " - << hel << endl; - */ - - mesh.AddVolumeElement (hel); - - for (k1 = 0; k1 < 4; k1++) - elementsonnode.Add (hel[k1], mesh.GetNE()-1); - } - - for (int k = 0; k < nsuround; k++) - { - Element & rel = mesh[hasbothpoints[k]]; - /* - (*testout) << nsuround << "-swap, old el = " - << rel << endl; - */ - rel.Delete(); - for (int k1 = 0; k1 < 4; k1++) - rel[k1].Invalidate(); - } - } - } - } - - /* - if (onlybedges) - { - (*testout) << "bad tet: " - << volelements.Get(i)[0] - << volelements.Get(i)[1] - << volelements.Get(i)[2] - << volelements.Get(i)[3] << "\n"; - - if (!mesh.LegalTet (volelements.Get(i))) - cerr << "Illegal tet" << "\n"; - } - */ + break; + + auto [pi0, pi1] = edges[i]; + double d_badness = SwapImproveEdge (mesh, goal, working_elements, elementsonnode, faces, pi0, pi1, true); + if(d_badness<0.0) + { + int index = improvement_counter++; + candidate_edges[index] = make_tuple(d_badness, i); + } } - // (*mycout) << endl; + }, TasksPerThread (4)); + + auto edges_with_improvement = candidate_edges.Part(0, improvement_counter.load()); + QuickSort(edges_with_improvement); + + for(auto [d_badness, ei] : edges_with_improvement) + { + auto [pi0,pi1] = edges[ei]; + if(SwapImproveEdge (mesh, goal, working_elements, elementsonnode, faces, pi0, pi1, false) < 0.0) + cnt++; + } + tloop.Stop(); - /* - cout << "edgeused: "; - edgeused.PrintMemInfo(cout); - */ + PrintMessage (5, cnt, " swaps performed"); + if(goal == OPT_CONFORM) + { + // Remove open elements that were closed by new tets + auto & open_els = mesh.OpenElements(); + for (auto & el : mesh.VolumeElements().Range( num_elements_before, mesh.VolumeElements().Range().Next() )) + { + for (auto i : Range(1,5)) + { + Element2d sel; + el.GetFace(i, sel); + INDEX_3 face(sel[0], sel[1], sel[2]); + face.Sort(); + if(faces.Used(face)) + open_els[faces.Get(face)-1].Delete(); + } + } + for(int i=open_els.Size()-1; i>=0; i--) + if(open_els[i].IsDeleted()) + open_els.Delete(i); - + mesh.DeleteBoundaryEdges(); + } mesh.Compress (); - /* - if (goal == OPT_QUALITY) - { - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); - // (*testout) << "Total badness = " << bad1 << endl; - } - */ - - /* - for (i = 1; i <= GetNE(); i++) - if (volelements.Get(i)[0]) - if (!mesh.LegalTet (volelements.Get(i))) - { - cout << "detected illegal tet, 2" << endl; - (*testout) << "detected illegal tet1: " << i << endl; - } - */ - multithread.task = savetask; } @@ -1447,11 +1426,11 @@ void MeshOptimize3d :: SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal, void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, - const BitArray * working_elements, - const Array< Array* > * idmaps) + const NgBitArray * working_elements, + const NgArray< NgArray* > * idmaps) { - Array< Array* > locidmaps; - const Array< Array* > * used_idmaps; + NgArray< NgArray* > locidmaps; + const NgArray< NgArray* > * used_idmaps; if(idmaps) used_idmaps = idmaps; @@ -1463,7 +1442,7 @@ void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, { if(mesh.GetIdentifications().GetType(i) == Identifications::PERIODIC) { - locidmaps.Append(new Array); + locidmaps.Append(new NgArray); mesh.GetIdentifications().GetMap(i,*locidmaps.Last(),true); } } @@ -1490,8 +1469,8 @@ void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, TABLE surfaceelementsonnode(np); TABLE surfaceindicesonnode(np); - Array hasbothpoints; - Array hasbothpointsother; + NgArray hasbothpoints; + NgArray hasbothpointsother; PrintMessage (3, "SwapImproveSurface "); (*testout) << "\n" << "Start SwapImproveSurface" << endl; @@ -1817,7 +1796,7 @@ void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, int nsuround = hasbothpoints.Size(); int nsuroundother = hasbothpointsother.Size(); - Array < int > outerpoints(nsuround+1); + NgArray < int > outerpoints(nsuround+1); outerpoints[0] = sp1; for(int i=0; i outerpointsother; + NgArray < int > outerpointsother; if(nsuroundother > 0) { @@ -1988,8 +1967,8 @@ void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, startpointsother = outerpointsother.Size(); - Array < Array < Element* > * > newelts(startpoints); - Array < Array < Element* > * > neweltsother(startpointsother); + NgArray < NgArray < Element* > * > newelts(startpoints); + NgArray < NgArray < Element* > * > neweltsother(startpointsother); double minbad = 1e50, minbadother = 1e50, currbad; int minpos = -1, minposother = -1; @@ -1998,7 +1977,7 @@ void MeshOptimize3d :: SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal, for(int i=0; i(2*(nsuround-1)); + newelts[i] = new NgArray (2*(nsuround-1)); for(int jj=0; jj face_index; + NgArray face_index; for(int k = 0; k(2*(nsuroundother)); + neweltsother[i] = new NgArray (2*(nsuroundother)); for(int jj=0; jj 3 conversion */ +double MeshOptimize3d :: SwapImprove2 ( Mesh & mesh, OPTIMIZEGOAL goal, ElementIndex eli1, int face, + Table & elementsonnode, + TABLE & belementsonnode, bool check_only ) +{ + PointIndex pi1, pi2, pi3, pi4, pi5; + Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET); + int j = face; + double bad1, bad2; + double d_badness = 0.0; + + Element & elem = mesh[eli1]; + if (elem.IsDeleted()) return 0.0; + + int mattyp = elem.GetIndex(); + + switch (j) + { + case 0: + pi1 = elem.PNum(1); pi2 = elem.PNum(2); + pi3 = elem.PNum(3); pi4 = elem.PNum(4); + break; + case 1: + pi1 = elem.PNum(1); pi2 = elem.PNum(4); + pi3 = elem.PNum(2); pi4 = elem.PNum(3); + break; + case 2: + pi1 = elem.PNum(1); pi2 = elem.PNum(3); + pi3 = elem.PNum(4); pi4 = elem.PNum(2); + break; + case 3: + pi1 = elem.PNum(2); pi2 = elem.PNum(4); + pi3 = elem.PNum(3); pi4 = elem.PNum(1); + break; + } + + + bool bface = 0; + for (int k = 0; k < belementsonnode[pi1].Size(); k++) + { + const Element2d & bel = + mesh[belementsonnode[pi1][k]]; + + bool bface1 = 1; + for (int l = 0; l < 3; l++) + if (bel[l] != pi1 && bel[l] != pi2 && bel[l] != pi3) + { + bface1 = 0; + break; + } + + if (bface1) + { + bface = 1; + break; + } + } + + if (bface) return 0.0; + + + 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]; + + if ( eli1 != eli2 ) + { + Element & elem2 = mesh[eli2]; + if (elem2.GetType() != TET) + continue; + + int comnodes=0; + for (int l = 1; l <= 4; l++) + if (elem2.PNum(l) == pi1 || elem2.PNum(l) == pi2 || + elem2.PNum(l) == pi3) + { + comnodes++; + } + else + { + pi5 = elem2.PNum(l); + } + + if (comnodes == 3) + { + bad1 = CalcBad (mesh.Points(), elem, 0) + + CalcBad (mesh.Points(), elem2, 0); + + if (!mesh.LegalTet(elem) || + !mesh.LegalTet(elem2)) + bad1 += 1e4; + + + el31.PNum(1) = pi1; + el31.PNum(2) = pi2; + el31.PNum(3) = pi5; + el31.PNum(4) = pi4; + el31.SetIndex (mattyp); + + el32.PNum(1) = pi2; + el32.PNum(2) = pi3; + el32.PNum(3) = pi5; + el32.PNum(4) = pi4; + el32.SetIndex (mattyp); + + el33.PNum(1) = pi3; + el33.PNum(2) = pi1; + el33.PNum(3) = pi5; + el33.PNum(4) = pi4; + el33.SetIndex (mattyp); + + bad2 = CalcBad (mesh.Points(), el31, 0) + + CalcBad (mesh.Points(), el32, 0) + + CalcBad (mesh.Points(), el33, 0); + + + el31.Flags().illegal_valid = 0; + el32.Flags().illegal_valid = 0; + el33.Flags().illegal_valid = 0; + + if (!mesh.LegalTet(el31) || + !mesh.LegalTet(el32) || + !mesh.LegalTet(el33)) + bad2 += 1e4; + + + d_badness = bad2 - bad1; + + if ( ((bad2 < 1e6) || (bad2 < 10 * bad1)) && + mesh.BoundaryEdge (pi4, pi5)) + d_badness = -1e4; + + if(check_only) + return d_badness; + + if (d_badness<0.0) + { + el31.Flags().illegal_valid = 0; + el32.Flags().illegal_valid = 0; + el33.Flags().illegal_valid = 0; + + mesh[eli1].Delete(); + mesh[eli2].Delete(); + mesh.AddVolumeElement (el31); + mesh.AddVolumeElement (el32); + mesh.AddVolumeElement (el33); + } + return d_badness; + } + } + } + return d_badness; +} + +/* + 2 -> 3 conversion +*/ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) { static Timer t("MeshOptimize3d::SwapImprove2"); RegionTimer reg(t); - - PointIndex pi1(0), pi2(0), pi3(0), pi4(0), pi5(0); - Element el21(TET), el22(TET), el31(TET), el32(TET), el33(TET); + + if (goal == OPT_CONFORM) return; + + mesh.BuildBoundaryEdges(false); int cnt = 0; double bad1, bad2; @@ -2323,258 +2472,281 @@ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) int ne = mesh.GetNE(); int nse = mesh.GetNSE(); - if (goal == OPT_CONFORM) return; - // contains at least all elements at node - TABLE elementsonnode(np); TABLE belementsonnode(np); PrintMessage (3, "SwapImprove2 "); (*testout) << "\n" << "Start SwapImprove2" << "\n"; - // TestOk(); - - /* - CalcSurfacesOfNode (); - for (i = 1; i <= GetNE(); i++) - if (volelements.Get(i)[0]) - if (!mesh.LegalTet (volelements.Get(i))) - { - cout << "detected illegal tet, 1" << endl; - (*testout) << "detected illegal tet1: " << i << endl; - } - */ - - - // Calculate total badness - - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); + bad1 = mesh.CalcTotalBad (mp); (*testout) << "Total badness = " << bad1 << endl; - // cout << "tot bad = " << bad1 << endl; // find elements on node - for (ElementIndex ei = 0; ei < ne; ei++) - for (int j = 0; j < mesh[ei].GetNP(); j++) - elementsonnode.Add (mesh[ei][j], ei); - + auto elementsonnode = mesh.CreatePoint2ElementTable(nullopt, mp.only3D_domain_nr); + // todo: respect mp.only3D_domain_nr + for (SurfaceElementIndex sei = 0; sei < nse; sei++) for (int j = 0; j < 3; j++) belementsonnode.Add (mesh[sei][j], sei); - for (ElementIndex eli1 = 0; eli1 < ne; eli1++) + int num_threads = ngcore::TaskManager::GetNumThreads(); + + Array> faces_with_improvement; + Array>> faces_with_improvement_threadlocal(num_threads); + + ParallelForRange( Range(ne), [&]( auto myrange ) + { + int tid = ngcore::TaskManager::GetThreadId(); + auto & my_faces_with_improvement = faces_with_improvement_threadlocal[tid]; + for (ElementIndex eli1 : myrange) + { + if (multithread.terminate) + break; + + if (mesh.ElementType (eli1) == FIXEDELEMENT) + continue; + + if (mesh[eli1].GetType() != TET) + continue; + + if ((goal == OPT_LEGAL) && + mesh.LegalTet (mesh[eli1]) && + CalcBad (mesh.Points(), mesh[eli1], 0) < 1e3) + continue; + + if(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(eli1).GetIndex()) + continue; + + for (int j = 0; j < 4; j++) + { + double d_badness = SwapImprove2( mesh, goal, eli1, j, elementsonnode, belementsonnode, true); + if(d_badness<0.0) + my_faces_with_improvement.Append( std::make_tuple(d_badness, eli1, j) ); + } + } + }); + + for (auto & a : faces_with_improvement_threadlocal) + faces_with_improvement.Append(a); + + QuickSort(faces_with_improvement); + + for (auto [dummy, eli,j] : faces_with_improvement) { - if (multithread.terminate) - break; - - if (mesh.ElementType (eli1) == FIXEDELEMENT) - continue; - - if (mesh[eli1].GetType() != TET) - continue; - - if ((goal == OPT_LEGAL) && - mesh.LegalTet (mesh[eli1]) && - CalcBad (mesh.Points(), mesh[eli1], 0) < 1e3) - continue; - - if(mesh.GetDimension()==3 && mp.only3D_domain_nr && mp.only3D_domain_nr != mesh.VolumeElement(eli1).GetIndex()) - continue; - - // cout << "eli = " << eli1 << endl; - // (*testout) << "swapimp2, eli = " << eli1 << "; el = " << mesh[eli1] << endl; - - for (int j = 0; j < 4; j++) - { - // loop over faces - - Element & elem = mesh[eli1]; - // if (elem[0] < PointIndex::BASE) continue; - if (elem.IsDeleted()) continue; - - int mattyp = elem.GetIndex(); - - switch (j) - { - case 0: - pi1 = elem.PNum(1); pi2 = elem.PNum(2); - pi3 = elem.PNum(3); pi4 = elem.PNum(4); - break; - case 1: - pi1 = elem.PNum(1); pi2 = elem.PNum(4); - pi3 = elem.PNum(2); pi4 = elem.PNum(3); - break; - case 2: - pi1 = elem.PNum(1); pi2 = elem.PNum(3); - pi3 = elem.PNum(4); pi4 = elem.PNum(2); - break; - case 3: - pi1 = elem.PNum(2); pi2 = elem.PNum(4); - pi3 = elem.PNum(3); pi4 = elem.PNum(1); - break; - } - - - bool bface = 0; - for (int k = 0; k < belementsonnode[pi1].Size(); k++) - { - const Element2d & bel = - mesh[belementsonnode[pi1][k]]; - - bool bface1 = 1; - for (int l = 0; l < 3; l++) - if (bel[l] != pi1 && bel[l] != pi2 && bel[l] != pi3) - { - bface1 = 0; - break; - } - - if (bface1) - { - bface = 1; - break; - } - } - - if (bface) continue; - - - FlatArray row = elementsonnode[pi1]; - for (int k = 0; k < row.Size(); k++) - { - ElementIndex eli2 = row[k]; - - // cout << "\rei1 = " << eli1 << ", pi1 = " << pi1 << ", k = " << k << ", ei2 = " << eli2 - // << ", getne = " << mesh.GetNE(); - - if ( eli1 != eli2 ) - { - Element & elem2 = mesh[eli2]; - if (elem2.IsDeleted()) continue; - if (elem2.GetType() != TET) - continue; - - int comnodes=0; - for (int l = 1; l <= 4; l++) - if (elem2.PNum(l) == pi1 || elem2.PNum(l) == pi2 || - elem2.PNum(l) == pi3) - { - comnodes++; - } - else - { - pi5 = elem2.PNum(l); - } - - if (comnodes == 3) - { - bad1 = CalcBad (mesh.Points(), elem, 0) + - CalcBad (mesh.Points(), elem2, 0); - - if (!mesh.LegalTet(elem) || - !mesh.LegalTet(elem2)) - bad1 += 1e4; - - - el31.PNum(1) = pi1; - el31.PNum(2) = pi2; - el31.PNum(3) = pi5; - el31.PNum(4) = pi4; - el31.SetIndex (mattyp); - - el32.PNum(1) = pi2; - el32.PNum(2) = pi3; - el32.PNum(3) = pi5; - el32.PNum(4) = pi4; - el32.SetIndex (mattyp); - - el33.PNum(1) = pi3; - el33.PNum(2) = pi1; - el33.PNum(3) = pi5; - el33.PNum(4) = pi4; - el33.SetIndex (mattyp); - - bad2 = CalcBad (mesh.Points(), el31, 0) + - CalcBad (mesh.Points(), el32, 0) + - CalcBad (mesh.Points(), el33, 0); - - - el31.flags.illegal_valid = 0; - el32.flags.illegal_valid = 0; - el33.flags.illegal_valid = 0; - - if (!mesh.LegalTet(el31) || - !mesh.LegalTet(el32) || - !mesh.LegalTet(el33)) - bad2 += 1e4; - - - bool do_swap = (bad2 < bad1); - - if ( ((bad2 < 1e6) || (bad2 < 10 * bad1)) && - mesh.BoundaryEdge (pi4, pi5)) - do_swap = 1; - - if (do_swap) - { - // cout << "do swap, eli1 = " << eli1 << "; eli2 = " << eli2 << endl; - // (*mycout) << "2->3 " << flush; - cnt++; - - el31.flags.illegal_valid = 0; - el32.flags.illegal_valid = 0; - el33.flags.illegal_valid = 0; - - mesh[eli1] = el31; - mesh[eli2] = el32; - - ElementIndex neli = - mesh.AddVolumeElement (el33); - - /* - if (!LegalTet(el31) || !LegalTet(el32) || - !LegalTet(el33)) - { - cout << "Swap to illegal tets !!!" << endl; - } - */ - // cout << "neli = " << neli << endl; - for (int l = 0; l < 4; l++) - { - elementsonnode.Add (el31[l], eli1); - elementsonnode.Add (el32[l], eli2); - elementsonnode.Add (el33[l], neli); - } - - break; - } - } - } - } - } + if(mesh[eli].IsDeleted()) + continue; + if(SwapImprove2( mesh, goal, eli, j, elementsonnode, belementsonnode, false) < 0.0) + cnt++; } - PrintMessage (5, cnt, " swaps performed"); - - - /* - CalcSurfacesOfNode (); - for (i = 1; i <= GetNE(); i++) - if (volelements.Get(i).PNum(1)) - if (!LegalTet (volelements.Get(i))) - { - cout << "detected illegal tet, 2" << endl; - (*testout) << "detected illegal tet2: " << i << endl; - } - */ - - - bad1 = CalcTotalBad (mesh.Points(), mesh.VolumeElements()); + mesh.Compress(); + bad1 = mesh.CalcTotalBad (mp); (*testout) << "Total badness = " << bad1 << endl; (*testout) << "swapimprove2 done" << "\n"; - // (*mycout) << "Vol = " << CalcVolume (points, volelements) << "\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(nullopt, mp.only3D_domain_nr); + 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; } @@ -2648,7 +2820,7 @@ void MeshOptimize3d :: SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal) } } - BitArray original(GetNE()); + NgBitArray original(GetNE()); original.Set(); for (i = 1; i <= GetNSE(); i++) diff --git a/libsrc/meshing/improve3.hpp b/libsrc/meshing/improve3.hpp index 22de7a84..605c523c 100644 --- a/libsrc/meshing/improve3.hpp +++ b/libsrc/meshing/improve3.hpp @@ -3,7 +3,7 @@ extern double CalcTotalBad (const Mesh::T_POINTS & points, - const Mesh::T_VOLELEMENTS & elements, + const Array & elements, const MeshingParameters & mp); @@ -11,16 +11,32 @@ extern double CalcTotalBad (const Mesh::T_POINTS & points, class MeshOptimize3d { const MeshingParameters & mp; + public: MeshOptimize3d (const MeshingParameters & amp) : mp(amp) { ; } + + double CombineImproveEdge (Mesh & mesh, const MeshingParameters & mp, + Table & elements_of_point, + Array & elerrs, PointIndex pi0, PointIndex pi1, + FlatArray is_point_removed, bool check_only=false); + void CombineImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY); + void SplitImprove (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); void SwapImprove (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY, - const BitArray * working_elements = NULL); + const NgBitArray * working_elements = NULL); void SwapImproveSurface (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY, - const BitArray * working_elements = NULL, - const Array< Array* > * idmaps = NULL); + const NgBitArray * working_elements = NULL, + const NgArray< NgArray* > * idmaps = NULL); void SwapImprove2 (Mesh & mesh, OPTIMIZEGOAL goal = OPT_QUALITY); + double SwapImprove2 ( Mesh & mesh, OPTIMIZEGOAL goal, ElementIndex eli1, int face, Table & elementsonnode, TABLE & belementsonnode, bool check_only=false ); double CalcBad (const Mesh::T_POINTS & points, const Element & elem, double h) @@ -33,7 +49,7 @@ public: double CalcTotalBad (const Mesh::T_POINTS & points, - const Mesh::T_VOLELEMENTS & elements) + const Array & elements) { return netgen::CalcTotalBad (points, elements, mp); } @@ -61,7 +77,7 @@ extern int WrongOrientation (const Mesh::T_POINTS & points, const Element & el); class MinFunctionSum : public MinFunction { protected: - Array functions; + NgArray functions; public: @@ -81,12 +97,12 @@ public: class PointFunction1 : public MinFunction { Mesh::T_POINTS & points; - const Array & faces; + const NgArray & faces; const MeshingParameters & mp; double h; public: PointFunction1 (Mesh::T_POINTS & apoints, - const Array & afaces, + const NgArray & afaces, const MeshingParameters & amp, double ah); @@ -100,7 +116,7 @@ class JacobianPointFunction : public MinFunction { public: Mesh::T_POINTS & points; - const Mesh::T_VOLELEMENTS & elements; + const Array & elements; TABLE elementsonpoint; PointIndex actpind; @@ -109,7 +125,7 @@ public: public: JacobianPointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements); + const Array & aelements); virtual ~JacobianPointFunction () { ; } virtual void SetPointIndex (PointIndex aactpind); virtual double Func (const Vector & x) const; diff --git a/libsrc/meshing/localh.cpp b/libsrc/meshing/localh.cpp index 83faadad..b8121b8f 100644 --- a/libsrc/meshing/localh.cpp +++ b/libsrc/meshing/localh.cpp @@ -11,10 +11,6 @@ namespace netgen for (int i = 0; i < 3; i++) xmid[i] = 0.5 * (ax1[i] + ax2[i]); - for (int i = 0; i < 8; i++) - childs[i] = NULL; - father = NULL; - flags.cutboundary = 0; flags.isinner = 0; flags.oldcell = 0; @@ -23,6 +19,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)); @@ -88,11 +91,103 @@ namespace netgen delete root; } + unique_ptr LocalH :: Copy () + { + static Timer t("LocalH::Copy"); RegionTimer rt(t); + auto lh = make_unique(boundingbox, grading, dimension); + std::map mapping; + lh->boxes.SetSize(boxes.Size()); + + for(auto i : boxes.Range()) + { + lh->boxes[i] = new GradingBox(); + auto & bnew = *lh->boxes[i]; + auto & b = *boxes[i]; + bnew.xmid[0] = b.xmid[0]; + bnew.xmid[1] = b.xmid[1]; + bnew.xmid[2] = b.xmid[2]; + bnew.h2 = b.h2; + bnew.hopt = b.hopt; + bnew.flags = b.flags; + mapping[&b] = &bnew; + } + + for(auto i : boxes.Range()) + { + auto & bnew = *lh->boxes[i]; + auto & b = *boxes[i]; + for(auto k : Range(8)) + { + if(b.childs[k]) + bnew.childs[k] = mapping[b.childs[k]]; + } + + if(b.father) + bnew.father = mapping[b.father]; + } + + lh->root = mapping[root]; + return lh; + } + + unique_ptr LocalH :: Copy( const Box<3> & bbox ) + { + static Timer t("LocalH::Copy with bounding box"); RegionTimer rt(t); + auto lh = make_unique(boundingbox, grading, dimension); + std::map mapping; + lh->boxes.SetAllocSize(boxes.Size()); + + for(auto i : boxes.Range()) + { + auto & b = *boxes[i]; + auto h = b.H2(); + Vec<3> vh = {h,h,h}; + Box<3> box( b.PMid() - vh, b.PMid() + vh); + if(!box.Intersect(bbox)) + continue; + lh->boxes.Append(new GradingBox()); + auto & bnew = *lh->boxes.Last(); + bnew.xmid[0] = b.xmid[0]; + bnew.xmid[1] = b.xmid[1]; + bnew.xmid[2] = b.xmid[2]; + bnew.h2 = b.h2; + bnew.hopt = b.hopt; + bnew.flags = b.flags; + mapping[&b] = &bnew; + } + + for(auto i : boxes.Range()) + { + auto & b = *boxes[i]; + if(mapping.count(&b)==0) + continue; + + auto & bnew = *mapping[&b]; + for(auto k : Range(8)) + { + if(b.childs[k] && mapping.count(b.childs[k])) + bnew.childs[k] = mapping[b.childs[k]]; + } + + if(b.father && mapping.count(b.father)) + bnew.father = mapping[b.father]; + } + + lh->root = mapping[root]; + return lh; + + } + void LocalH :: Delete () { 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 +476,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]); @@ -392,8 +492,8 @@ namespace netgen void LocalH :: FindInnerBoxes (AdFront3 * adfront, int (*testinner)(const Point3d & p1)) { - static int timer = NgProfiler::CreateTimer ("LocalH::FindInnerBoxes"); - NgProfiler::RegionTimer reg (timer); + static Timer timer("LocalH::FindInnerBoxes"); + RegionTimer reg (timer); int nf = adfront->GetNF(); @@ -415,8 +515,8 @@ namespace netgen (*testout) << "inner = " << root->flags.pinner << " =?= " << testinner(Point3d(root->xmid[0], root->xmid[1], root->xmid[2])) << endl; - Array faceinds(nf); - Array faceboxes(nf); + NgArray faceinds(nf); + NgArray faceboxes(nf); for (int i = 1; i <= nf; i++) { @@ -432,8 +532,8 @@ namespace netgen void LocalH :: FindInnerBoxesRec2 (GradingBox * box, class AdFront3 * adfront, - Array & faceboxes, - Array & faceinds, int nfinbox) + NgArray & faceboxes, + NgArray & faceinds, int nfinbox) { if (!box) return; @@ -449,9 +549,9 @@ namespace netgen Box3d boxcfc(c,fc); - ArrayMem faceused; - ArrayMem faceused2; - ArrayMem facenotused; + NgArrayMem faceused; + NgArrayMem faceused2; + NgArrayMem facenotused; /* faceused.SetSize(0); @@ -547,13 +647,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); @@ -570,83 +676,74 @@ namespace netgen int nf = adfront->GetNFL(); Array faceinds(nf); - Array > faceboxes(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, - Array > & faceboxes, - Array & 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); - - ArrayMem faceused; - ArrayMem faceused2; - ArrayMem 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 +756,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 +831,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 +864,17 @@ namespace netgen } } - void LocalH :: GetInnerPoints (Array > & 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,9 +885,21 @@ namespace netgen } - - void LocalH :: GetOuterPoints (Array > & points) + 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) + { + static Timer t("LocalH::GetOuterPoints"); RegionTimer rt(t); for (int i = 0; i < boxes.Size(); i++) if (!boxes[i]->flags.isinner && !boxes[i]->flags.cutboundary) points.Append ( boxes[i] -> PMid()); diff --git a/libsrc/meshing/localh.hpp b/libsrc/meshing/localh.hpp index 361d506e..979a7ec3 100644 --- a/libsrc/meshing/localh.hpp +++ b/libsrc/meshing/localh.hpp @@ -20,9 +20,9 @@ namespace netgen /// half edgelength float h2; /// - GradingBox * childs[8]; + GradingBox * childs[8] = {nullptr}; /// - GradingBox * father; + GradingBox * father = nullptr; /// double hopt; /// @@ -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; } @@ -79,22 +89,29 @@ namespace netgen int dimension; public: /// - LocalH (Point<3> pmin, Point<3> pmax, double grading, int adimension = 3); + DLL_HEADER LocalH (Point<3> pmin, Point<3> pmax, double grading, int adimension = 3); /// LocalH (const Box<3> & box, double grading, int adimension = 3) : LocalH (box.PMin(), box.PMax(), grading, adimension) { ; } + /// Default ctor for archive + LocalH() = default; + + DLL_HEADER ~LocalH(); /// - ~LocalH(); + DLL_HEADER unique_ptr Copy(); + DLL_HEADER unique_ptr Copy( const Box<3> & bbox ); /// - void Delete(); + DLL_HEADER void Delete(); + /// + DLL_HEADER void DoArchive(Archive& ar); /// void SetGrading (double agrading) { grading = agrading; } /// - void SetH (Point<3> x, double h); + DLL_HEADER void SetH (Point<3> x, double h); /// - double GetH (Point<3> x) const; + DLL_HEADER double GetH (Point<3> x) const; /// minimal h in box (pmin, pmax) - double GetMinH (Point<3> pmin, Point<3> pmax) const; + DLL_HEADER double GetMinH (Point<3> pmin, Point<3> pmax) const; /// mark boxes intersecting with boundary-box // void CutBoundary (const Point3d & pmin, const Point3d & pmax) @@ -115,14 +132,17 @@ namespace netgen void ClearFlags () { ClearFlagsRec(root); } + void ClearRootFlags (); + /// widen refinement zone void WidenRefinement (); /// get points in inner elements - void GetInnerPoints (Array > & points); + void GetInnerPoints (NgArray > & points) const; + void GetInnerPointsRec (const GradingBox * box, NgArray > & points) const; /// get points in outer closure - void GetOuterPoints (Array > & points); + void GetOuterPoints (NgArray > & points); /// void Convexify (); @@ -147,8 +167,8 @@ namespace netgen /// void FindInnerBoxesRec2 (GradingBox * box, class AdFront3 * adfront, - Array & faceboxes, - Array & finds, int nfinbox); + NgArray & faceboxes, + NgArray & finds, int nfinbox); @@ -158,8 +178,8 @@ namespace netgen /// void FindInnerBoxesRec2 (GradingBox * box, class AdFront2 * adfront, - Array > & faceboxes, - Array & finds, int nfinbox); + FlatArray> faceboxes, + FlatArray finds); // , int nfinbox); @@ -172,6 +192,8 @@ namespace netgen /// void ConvexifyRec (GradingBox * box); + unique_ptr CopyRec( const Box<3> & bbox, GradingBox * current ); + friend ostream & operator<< (ostream & ost, const LocalH & loch); }; diff --git a/libsrc/meshing/meshclass.cpp b/libsrc/meshing/meshclass.cpp index 5ed9663e..a5595b23 100644 --- a/libsrc/meshing/meshclass.cpp +++ b/libsrc/meshing/meshclass.cpp @@ -1,26 +1,238 @@ #include #include +#include #include "meshing.hpp" +#include "../general/gzstream.h" + +#ifdef NG_PYTHON +// must be included to instantiate Archive::Shallow(NetgenGeometry&) +#include +#endif namespace netgen { + int Find3dElement (const Mesh& mesh, + const netgen::Point<3> & p, + double * lami, + const NgArray * const indices, + BoxTree<3> * searchtree, + const bool allowindex = true) + { + int ne = 0; + NgArray locels; + if (searchtree) + { + searchtree->GetIntersecting (p, p, locels); + ne = locels.Size(); + } + else + ne = mesh.GetNE(); + + for (int i = 1; i <= ne; i++) + { + int ii; + + if (searchtree) + ii = locels.Get(i); + else + ii = i; + + if(indices != NULL && indices->Size() > 0) + { + bool contained = indices->Contains(mesh.VolumeElement(ii).GetIndex()); + if((allowindex && !contained) || (!allowindex && contained)) continue; + } + + if(mesh.PointContainedIn3DElement(p,lami,ii)) + return ii; + } + + // Not found, try uncurved variant: + for (int i = 1; i <= ne; i++) + { + int ii; + + if (searchtree) + ii = locels.Get(i); + else + ii = i; + + if(indices != NULL && indices->Size() > 0) + { + bool contained = indices->Contains(mesh.VolumeElement(ii).GetIndex()); + if((allowindex && !contained) || (!allowindex && contained)) continue; + } + + + if(mesh.PointContainedIn3DElementOld(p,lami,ii)) + { + (*testout) << "WARNING: found element of point " << p <<" only for uncurved mesh" << endl; + return ii; + } + } + return 0; + } + + int Find2dElement (const Mesh& mesh, + const netgen::Point<3> & p, + double * lami, + const NgArray * const indices, + BoxTree<3> * searchtree, + const bool allowindex = true) + { + double vlam[3]; + int velement = 0; + + if(mesh.GetNE()) + velement = Find3dElement(mesh, p,vlam,NULL,searchtree,allowindex); + + //(*testout) << "p " << p << endl; + //(*testout) << "velement " << velement << endl; + + // first try to find a volume element containing p and project to face + if(velement!=0) + { + auto & topology = mesh.GetTopology(); + // NgArray faces; + // topology.GetElementFaces(velement,faces); + auto faces = Array (topology.GetFaces(ElementIndex(velement-1))); + + //(*testout) << "faces " << faces << endl; + + for(int i=0; iSize() != 0 && !indices->Contains(sel.GetIndex())) + continue; + + auto & el = mesh.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; + 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(mesh.PointContainedIn2DElement(p,lami,faces[i],true)) + return faces[i]; + } + } + + // Did't find any matching face of a volume element, search 2d elements directly + int ne; + + NgArray locels; + // TODO: build search tree for surface elements + if (!mesh.GetNE() && searchtree) + { + searchtree->GetIntersecting (p, p, locels); + ne = locels.Size(); + } + else + ne = mesh.GetNSE(); + + for (int i = 1; i <= ne; i++) + { + int ii; + + if (locels.Size()) + ii = locels.Get(i); + else + ii = i; + + if(indices != NULL && indices->Size() > 0) + { + bool contained = indices->Contains(mesh.SurfaceElement(ii).GetIndex()); + if((allowindex && !contained) || (!allowindex && contained)) continue; + } + + if(mesh.PointContainedIn2DElement(p,lami,ii)) return ii; + + } + return 0; + } + + int Find1dElement (const Mesh& mesh, + const netgen::Point<3> & p, + double * lami, + const NgArray * const indices, + BoxTree<3> * searchtree, + const bool allowindex = true) + { + double vlam[3]; + int velement = Find2dElement(mesh, p, vlam, NULL, searchtree, allowindex); + if(velement == 0) + return 0; + + vlam[2] = 1.-vlam[0] - vlam[1]; + NgArray edges; + auto & topology = mesh.GetTopology(); + 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 = mesh.SurfaceElement(velement); + if(el.GetType() == TRIG) + { + double seg_lam; + double lam; + auto seg = mesh.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; + } + static mutex buildsearchtree_mutex; Mesh :: Mesh () : topology(*this), surfarea(*this) { - // volelements.SetName ("vol elements"); - // surfelements.SetName ("surf elements"); - // points.SetName ("meshpoints"); + boundaryedges = nullptr; + surfelementht = nullptr; + segmentht = nullptr; - boundaryedges = NULL; - surfelementht = NULL; - segmentht = NULL; - - lochfunc = NULL; - mglevels = 1; - elementsearchtree = NULL; + lochfunc = {nullptr}; + // mglevels = 1; + elementsearchtree = nullptr; elementsearchtreets = NextTimeStamp(); majortimestamp = timestamp = NextTimeStamp(); hglob = 1e10; @@ -28,10 +240,9 @@ namespace netgen numvertices = -1; dimension = 3; - // topology = new MeshTopology (*this); - curvedelems = new CurvedElements (*this); - clusters = new AnisotropicClusters (*this); - ident = new Identifications (*this); + curvedelems = make_unique (*this); + clusters = make_unique (*this); + ident = make_unique (*this); hpelements = NULL; coarsemesh = NULL; @@ -45,25 +256,23 @@ namespace netgen // this->comm = netgen :: ng_comm; #ifdef PARALLEL - paralleltop = new ParallelMeshTopology (*this); + paralleltop = make_unique (*this); #endif } Mesh :: ~Mesh() { - // cout << "******************** deleting Mesh **********" << endl; - delete lochfunc; - delete boundaryedges; - delete surfelementht; - delete segmentht; - delete curvedelems; - delete clusters; - // delete topology; - delete ident; - delete elementsearchtree; - delete coarsemesh; - delete hpelements; + // delete lochfunc; + // delete boundaryedges; + // delete surfelementht; + // delete segmentht; + // delete curvedelems; + // delete clusters; + // delete ident; + // delete elementsearchtree; + // delete coarsemesh; + // delete hpelements; for (int i = 0; i < materials.Size(); i++) delete materials[i]; @@ -78,9 +287,12 @@ namespace netgen for (int i = 0; i < cd2names.Size(); i++) delete cd2names[i]; -#ifdef PARALLEL - delete paralleltop; -#endif + for (int i = 0; i < cd3names.Size(); i++) + delete cd3names[i]; + + // #ifdef PARALLEL + // delete paralleltop; + // #endif } void Mesh :: SetCommunicator(NgMPI_Comm acomm) @@ -90,25 +302,51 @@ namespace netgen Mesh & Mesh :: operator= (const Mesh & mesh2) { + geometry = mesh2.geometry; + dimension = mesh2.dimension; points = mesh2.points; - // eltyps = mesh2.eltyps; segments = mesh2.segments; surfelements = mesh2.surfelements; volelements = mesh2.volelements; lockedpoints = mesh2.lockedpoints; facedecoding = mesh2.facedecoding; dimension = mesh2.dimension; + hglob = mesh2.hglob; + hmin = mesh2.hmin; + maxhdomain = mesh2.maxhdomain; + + materials.SetSize( mesh2.materials.Size() ); + for ( int i = 0; i < mesh2.materials.Size(); i++ ) + 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()); for (int i=0; i < mesh2.cd2names.Size(); i++) if (mesh2.cd2names[i]) cd2names[i] = new string(*mesh2.cd2names[i]); else cd2names[i] = 0; + cd3names.SetSize(mesh2.cd3names.Size()); + for (int i=0; i < mesh2.cd3names.Size(); i++) + if (mesh2.cd3names[i]) cd3names[i] = new string(*mesh2.cd3names[i]); + else cd3names[i] = 0; + + numvertices = mesh2.numvertices; + return *this; } @@ -124,21 +362,18 @@ namespace netgen lockedpoints.SetSize(0); // surfacesonnode.SetSize(0); - delete boundaryedges; - boundaryedges = NULL; + // delete boundaryedges; + boundaryedges = nullptr; + segmentht = nullptr; + surfelementht = nullptr; openelements.SetSize(0); facedecoding.SetSize(0); - delete ident; - ident = new Identifications (*this); - // delete topology; - // topology = new MeshTopology (*this); + ident = make_unique (*this); topology = MeshTopology (*this); - delete curvedelems; - curvedelems = new CurvedElements (*this); - delete clusters; - clusters = new AnisotropicClusters (*this); + curvedelems = make_unique (*this); + clusters = make_unique (*this); for ( int i = 0; i < bcnames.Size(); i++ ) if ( bcnames[i] ) delete bcnames[i]; @@ -146,8 +381,7 @@ namespace netgen if (cd2names[i]) delete cd2names[i]; #ifdef PARALLEL - delete paralleltop; - paralleltop = new ParallelMeshTopology (*this); + paralleltop = make_unique (*this); #endif lock.UnLock(); @@ -158,10 +392,14 @@ namespace netgen void Mesh :: ClearSurfaceElements() { - surfelements.SetSize(0); + surfelements.SetSize(0); + /* for (int i = 0; i < facedecoding.Size(); i++) facedecoding[i].firstelement = -1; - + */ + for (auto & fd : facedecoding) + fd.firstelement = -1; + timestamp = NextTimeStamp(); } @@ -170,71 +408,30 @@ namespace netgen PointIndex Mesh :: AddPoint (const Point3d & p, int layer) { return AddPoint (p, layer, INNERPOINT); - /* - NgLock lock(mutex); - lock.Lock(); - - timestamp = NextTimeStamp(); - - PointIndex pi = points.End(); - points.Append ( MeshPoint (p, layer, INNERPOINT) ); - - lock.UnLock(); - - return pi; - */ } PointIndex Mesh :: AddPoint (const Point3d & p, int layer, POINTTYPE type) { - NgLock lock(mutex); - lock.Lock(); + + // PointIndex pi = points.End(); + PointIndex pi = *points.Range().end(); + if (points.Size() == points.AllocSize()) + { + NgLock lock(mutex); + lock.Lock(); + points.Append ( MeshPoint (p, layer, type) ); + lock.UnLock(); + } + else + { + points.Append ( MeshPoint (p, layer, type) ); + } timestamp = NextTimeStamp(); - PointIndex pi = points.End(); - points.Append ( MeshPoint (p, layer, type) ); - - lock.UnLock(); - return pi; } - /* -#ifdef PARALLEL - PointIndex Mesh :: AddPoint (const Point3d & p, bool isghost, int layer) - { - NgLock lock(mutex); - lock.Lock(); - - timestamp = NextTimeStamp(); - - PointIndex pi = points.Size() + PointIndex::BASE; - points.Append ( MeshPoint (p, layer, INNERPOINT) ); - - lock.UnLock(); - - return pi; - } - - PointIndex Mesh :: AddPoint (const Point3d & p, bool isghost, int layer, POINTTYPE type) - { - NgLock lock(mutex); - lock.Lock(); - - timestamp = NextTimeStamp(); - - PointIndex pi = points.Size() + PointIndex::BASE; - points.Append ( MeshPoint (p, layer, type) ); - - lock.UnLock(); - - return pi; - } - -#endif - */ - SegmentIndex Mesh :: AddSegment (const Segment & s) { @@ -281,42 +478,40 @@ namespace netgen SurfaceElementIndex Mesh :: AddSurfaceElement (const Element2d & el) { - NgLock lock(mutex); - lock.Lock(); timestamp = NextTimeStamp(); - int maxn = el[0]; + PointIndex maxn = el[0]; for (int i = 1; i < el.GetNP(); i++) if (el[i] > maxn) maxn = el[i]; - maxn += 1-PointIndex::BASE; - /* - if (maxn > ptyps.Size()) - { - int maxo = ptyps.Size(); - ptyps.SetSize (maxn); - for (i = maxo+PointIndex::BASE; - i < maxn+PointIndex::BASE; i++) - ptyps[i] = INNERPOINT; - - } - */ + maxn += 1-PointIndex::BASE; if (maxn <= points.Size()) { for (int i = 0; i < el.GetNP(); i++) if (points[el[i]].Type() > SURFACEPOINT) points[el[i]].SetType(SURFACEPOINT); } - /* - else - { - cerr << "surf points nrs > points.Size" << endl; - } */ + // if (maxn < points.End()) + if (maxn < *points.Range().end()) + for (PointIndex pi : el.PNums()) + if (points[pi].Type() > SURFACEPOINT) + points[pi].SetType(SURFACEPOINT); + SurfaceElementIndex si = surfelements.Size(); - surfelements.Append (el); + if (surfelements.AllocSize() == surfelements.Size()) + { + NgLock lock(mutex); + lock.Lock(); + surfelements.Append (el); + lock.UnLock(); + } + else + { + surfelements.Append (el); + } if (el.index<=0 || el.index > facedecoding.Size()) cerr << "has no facedecoding: fd.size = " << facedecoding.Size() << ", ind = " << el.index << endl; @@ -327,7 +522,6 @@ namespace netgen if (SurfaceArea().Valid()) SurfaceArea().Add (el); - lock.UnLock(); return si; } @@ -367,15 +561,14 @@ namespace netgen ElementIndex Mesh :: AddVolumeElement (const Element & el) { - NgLock lock(mutex); - lock.Lock(); - + /* int maxn = el[0]; for (int i = 1; i < el.GetNP(); i++) if (el[i] > maxn) maxn = el[i]; maxn += 1-PointIndex::BASE; - + */ + /* if (maxn > ptyps.Size()) { @@ -395,15 +588,26 @@ namespace netgen int ve = volelements.Size(); - volelements.Append (el); - volelements.Last().flags.illegal_valid = 0; + if (volelements.Size() == volelements.AllocSize()) + { + NgLock lock(mutex); + lock.Lock(); + volelements.Append (el); + lock.UnLock(); + } + else + { + volelements.Append (el); + } + volelements.Last().Flags().illegal_valid = 0; + volelements.Last().Flags().fixed = 0; + volelements.Last().Flags().deleted = 0; // while (volelements.Size() > eltyps.Size()) // eltyps.Append (FREEELEMENT); timestamp = NextTimeStamp(); - lock.UnLock(); return ve; } @@ -418,22 +622,34 @@ namespace netgen */ volelements[ei] = el; - volelements.Last().flags.illegal_valid = 0; + volelements[ei].Flags().illegal_valid = 0; + volelements[ei].Flags().fixed = 0; + volelements[ei].Flags().deleted = 0; } - void Mesh :: Save (const string & filename) const + void Mesh :: Save (const filesystem::path & filename) const { + string ext0 = filename.stem().extension().string(); + string ext = filename.extension().string(); + + if (ext0 == ".vol" && ext == ".bin") + { + BinaryOutArchive in(filename); + in & const_cast(*this); + return; + } + ostream * outfile; - if (filename.find(".vol.gz")!=string::npos) - outfile = new ogzstream(filename.c_str()); - else if (filename.find(".vol")!=string::npos) - outfile = new ofstream(filename.c_str()); + if (ext0 == ".vol" && ext == ".gz") + outfile = new ogzstream(filename); + else if (ext == ".vol") + outfile = new ofstream(filename); else - outfile = new ogzstream((filename+".vol.gz").c_str()); + outfile = new ogzstream(filesystem::path(filename).concat(".vol.gz")); Save(*outfile); delete outfile; @@ -443,12 +659,14 @@ namespace netgen void Mesh :: Save (ostream & outfile) const { + static Timer timer("Mesh::Save"); RegionTimer rt(timer); int i, j; double scale = 1; // globflags.GetNumFlag ("scale", 1); int inverttets = 0; // globflags.GetDefineFlag ("inverttets"); int invertsurf = 0; // globflags.GetDefineFlag ("invertsurfacemesh"); + outfile << "# Generated by NETGEN " << GetLibraryVersion("netgen") << endl << endl; outfile << "mesh3d" << "\n"; @@ -457,6 +675,13 @@ namespace netgen outfile << "geomtype\n" << int(geomtype) << "\n"; + outfile << "\n"; + outfile << "# surfnr\tdomin\tdomout\ttlosurf\tbcprop\n"; + outfile << "facedescriptors\n"; + outfile << GetNFD() << "\n"; + for(auto & fd : FaceDescriptors()) + outfile << fd.SurfNr() << ' ' << fd.DomainIn() << ' ' << fd.DomainOut() << ' ' << fd.TLOSurface() << ' ' << fd.BCProperty() << '\n'; + outfile << "\n"; outfile << "# surfnr bcnr domin domout np p1 p2 p3" @@ -477,8 +702,7 @@ namespace netgen outfile << GetNSE() << "\n"; - SurfaceElementIndex sei; - for (sei = 0; sei < GetNSE(); sei++) + for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) { if ((*this)[sei].GetIndex()) { @@ -616,12 +840,25 @@ 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) { outfile << "identifications\n"; - Array identpairs; + NgArray identpairs; int cnt = 0; for (i = 1; i <= ident -> GetMaxNr(); i++) { @@ -691,6 +928,18 @@ namespace netgen outfile << endl << endl; } + int cntcd3names = 0; + for (int ii = 0; ii=1.) cnt_sing++; - + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // if ((*this)[pi].Singularity()>=1.) cnt_sing++; + for (auto & p : points) + if (p.Singularity() >= 1.) cnt_sing++; + if (cnt_sing) { outfile << "singular_points" << endl << cnt_sing << endl; - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + for (PointIndex pi : points.Range()) if ((*this)[pi].Singularity()>=1.) outfile << int(pi) << "\t" << (*this)[pi].Singularity() << endl; } @@ -839,13 +1091,22 @@ namespace netgen outfile.width(8); outfile << GetFaceDescriptor(i).SurfNr()+1 << " "; outfile.width(12); - outfile << GetFaceDescriptor(i).SurfColour().X() << " "; + outfile << GetFaceDescriptor(i).SurfColour()[0] << " "; outfile.width(12); - outfile << GetFaceDescriptor(i).SurfColour().Y() << " "; + outfile << GetFaceDescriptor(i).SurfColour()[1] << " "; outfile.width(12); - outfile << GetFaceDescriptor(i).SurfColour().Z(); + outfile << GetFaceDescriptor(i).SurfColour()[2]; outfile << endl; } + + outfile << "face_transparencies" << endl << cnt_facedesc << endl; + for(i = 1; i <= cnt_facedesc; i++) + { + outfile.width(8); + outfile << GetFaceDescriptor(i).SurfNr()+1 << " "; + outfile.width(12); + outfile << GetFaceDescriptor(i).SurfColour()[3] << endl; + } } outfile << endl << endl << "endmesh" << endl << endl; @@ -855,17 +1116,27 @@ namespace netgen - void Mesh :: Load (const string & filename) + void Mesh :: Load (const filesystem::path & filename) { - cout << "filename = " << filename << endl; + PrintMessage (1, "filename = ", filename); + + string ext0 = filename.stem().extension().string(); + string ext = filename.extension().string(); + + if (ext0 == ".vol" && ext == ".bin") + { + BinaryInArchive in(filename); + in & (*this); + return; + } + istream * infile = NULL; - if (filename.find(".vol.gz") != string::npos) - infile = new igzstream (filename.c_str()); + if (ext0 == ".vol" && ext == ".gz") + infile = new igzstream (filename); else - infile = new ifstream (filename.c_str()); + infile = new ifstream (filename); - // ifstream infile(filename.c_str()); if (! (infile -> good()) ) throw NgException ("mesh file not found"); @@ -875,15 +1146,43 @@ namespace netgen + // Reads mandatory integer and optional string token from input stream + // used for parsing bcnames, cd2names etc. + void ReadNumberAndName( istream & infile, int & i, string & s ) + { + string line; + std::istringstream iline; + + bool empty_line = true; + + while(empty_line && infile) + { + std::getline(infile, line); + iline = std::istringstream{line}; + iline >> i; + + if(iline) + empty_line = false; + + iline >> s; + } + + if(!infile) + throw Exception("Reached end of file while parsing"); + } void Mesh :: Load (istream & infile) { + static Timer timer("Mesh::Load"); RegionTimer rt(timer); if (! (infile.good()) ) { cout << "cannot load mesh" << endl; throw NgException ("mesh file not found"); } + int rank = GetCommunicator().Rank(); + int ntasks = GetCommunicator().Size(); + char str[100]; int i, n; @@ -912,9 +1211,23 @@ namespace netgen geomtype = GEOM_TYPE(hi); } + if (strcmp (str, "facedescriptors") == 0) + { + int nfd; + infile >> nfd; + for(auto i : Range(nfd)) + { + int surfnr, domin, domout, tlosurf, bcprop; + infile >> surfnr >> domin >> domout >> tlosurf >> bcprop; + auto faceind = AddFaceDescriptor (FaceDescriptor(surfnr, domin, domout, tlosurf)); + GetFaceDescriptor(faceind).SetBCProperty(bcprop); + } + } + if (strcmp (str, "surfaceelements") == 0 || strcmp (str, "surfaceelementsgi")==0 || strcmp (str, "surfaceelementsuv") == 0) { + static Timer t1("read surface elements"); RegionTimer rt1(t1); infile >> n; PrintMessage (3, n, " surface elements"); @@ -979,6 +1292,7 @@ namespace netgen if (strcmp (str, "volumeelements") == 0) { + static Timer t1("read volume elements"); RegionTimer rt1(t1); infile >> n; PrintMessage (3, n, " volume elements"); for (i = 1; i <= n; i++) @@ -1004,6 +1318,7 @@ namespace netgen if (strcmp (str, "edgesegments") == 0) { + static Timer t1("read edge segments"); RegionTimer rt1(t1); infile >> n; for (i = 1; i <= n; i++) { @@ -1018,6 +1333,7 @@ namespace netgen if (strcmp (str, "edgesegmentsgi") == 0) { + static Timer t1("read edge segmentsgi"); RegionTimer rt1(t1); infile >> n; for (i = 1; i <= n; i++) { @@ -1032,6 +1348,7 @@ namespace netgen if (strcmp (str, "edgesegmentsgi2") == 0) { + static Timer t1("read edge segmentsgi2"); RegionTimer rt1(t1); int a; infile >> a; n=a; @@ -1065,6 +1382,7 @@ namespace netgen if (strcmp (str, "points") == 0) { + static Timer t1("read points"); RegionTimer rt1(t1); infile >> n; PrintMessage (3, n, " points"); for (i = 1; i <= n; i++) @@ -1079,6 +1397,20 @@ namespace netgen PrintMessage (3, n, " points done"); } + if (strcmp (str, "pointelements") == 0) + { + static Timer t1("read point elements"); RegionTimer rt1(t1); + 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; @@ -1107,11 +1439,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()); } } @@ -1119,27 +1451,16 @@ namespace netgen if ( strcmp (str, "bcnames" ) == 0 ) { infile >> n; - Array 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++) { @@ -1160,28 +1481,34 @@ namespace netgen if ( strcmp (str, "cd2names" ) == 0) { infile >> n; - Array 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 ); + cd3names[cd3nrs[i]-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"); } } @@ -1261,12 +1588,12 @@ namespace netgen for(i = 1; i <= n; i++) { int surfnr = 0; - Vec3d surfcolour(0.0,1.0,0.0); + Vec<4> surfcolour(0.0,1.0,0.0,1.0); infile >> surfnr - >> surfcolour.X() - >> surfcolour.Y() - >> surfcolour.Z(); + >> surfcolour[0] + >> surfcolour[1] + >> surfcolour[2]; surfnr--; @@ -1284,6 +1611,36 @@ namespace netgen } } + if (strcmp (str, "face_transparencies") == 0) + { + int cnt_facedesc = GetNFD(); + infile >> n; + int index = 1; + if(n == cnt_facedesc) + { + for(int index = 1; index <= n; index++) + { + int surfnr; + double transp; + infile >> surfnr >> transp; + surfnr--; + if(surfnr > 0) + { + for(int facedesc = 1; facedesc <= cnt_facedesc; facedesc++) + { + if(surfnr == GetFaceDescriptor(facedesc).SurfNr()) + { + auto& fd = GetFaceDescriptor(facedesc); + auto scol = fd.SurfColour(); + scol[3] = transp; + fd.SetSurfColour(scol); + } + } + } + } + } + } + if (strcmp (str, "endmesh") == 0) endmesh = true; @@ -1310,16 +1667,167 @@ 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; + + if(archive.GetVersion("netgen") >= "v6.2.2103-1") + { + archive.NeedsVersion("netgen", "v6.2.2103-1"); + archive & vol_partition & surf_partition & seg_partition; + } + + archive.Shallow(geometry); + archive & *curvedelems; + } + + if (comm.Rank() == 0) + return; + } +#endif + + archive & dimension; archive & points; archive & surfelements; archive & volelements; archive & segments; archive & facedecoding; - archive & materials & bcnames & cd2names; + archive & materials & bcnames & cd2names & cd3names; + archive & numvertices; archive & *ident; + // cout << "archive, ngsversion = " << archive.GetVersion("netgen") << endl; + if(archive.GetVersion("netgen") >= "v6.2.2103-1") + { + // cout << "do the partition" << endl; + archive.NeedsVersion("netgen", "v6.2.2103-1"); + archive & vol_partition & surf_partition & seg_partition; + } + // else + // cout << "no partition" << endl; + archive.Shallow(geometry); archive & *curvedelems; @@ -1341,9 +1849,9 @@ namespace netgen } - void Mesh :: Merge (const string & filename, const int surfindex_offset) + void Mesh :: Merge (const filesystem::path & filename, const int surfindex_offset) { - ifstream infile(filename.c_str()); + ifstream infile(filename); if (!infile.good()) throw NgException ("mesh file not found"); @@ -1609,12 +2117,14 @@ namespace netgen volelements.SetAllocSize(nel); } - - void Mesh :: BuildBoundaryEdges(void) + void Mesh :: BuildBoundaryEdges(bool rebuild) { - delete boundaryedges; + static Timer t("Mesh::BuildBoundaryEdges"); RegionTimer reg(t); + + if(!rebuild && boundaryedges) + return; - boundaryedges = new INDEX_2_CLOSED_HASHTABLE + boundaryedges = make_unique> (3 * (GetNSE() + GetNOpenElements()) + GetNSeg() + 1); @@ -1680,16 +2190,20 @@ namespace netgen void Mesh :: CalcSurfacesOfNode () { - static Timer t("Mesh::CalcSurfacesOfNode"); RegionTimer reg (t); + static Timer t("Mesh::CalcSurfacesOfNode"); RegionTimer reg (t); + static Timer tn2se("Mesh::CalcSurfacesOfNode - surf on node"); + static Timer tht("Mesh::CalcSurfacesOfNode - surfelementht"); // surfacesonnode.SetSize (GetNP()); TABLE surfacesonnode(GetNP()); - delete boundaryedges; - boundaryedges = NULL; + // delete boundaryedges; + // boundaryedges = NULL; + boundaryedges = nullptr; - delete surfelementht; + // delete surfelementht; + // surfelementht = nullptr; surfelementht = nullptr; - delete segmentht; + // delete segmentht; /* surfelementht = new INDEX_3_HASHTABLE (GetNSE()/4 + 1); @@ -1697,20 +2211,29 @@ namespace netgen */ if (dimension == 3) - surfelementht = new INDEX_3_CLOSED_HASHTABLE (3*GetNSE() + 1); - segmentht = new INDEX_2_CLOSED_HASHTABLE (3*GetNSeg() + 1); + surfelementht = make_unique> (3*GetNSE() + 1); + segmentht = make_unique> (3*GetNSeg() + 1); + tn2se.Start(); if (dimension == 3) + /* for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) { const Element2d & sel = surfelements[sei]; + */ + for (const Element2d & sel : surfelements) + { if (sel.IsDeleted()) continue; int si = sel.GetIndex(); + /* for (int j = 0; j < sel.GetNP(); j++) { PointIndex pi = sel[j]; + */ + for (PointIndex pi : sel.PNums()) + { if (!surfacesonnode[pi].Contains(si)) surfacesonnode.Add (pi, si); /* @@ -1743,7 +2266,9 @@ namespace netgen surfelementht -> AllocateElements(); */ - + tn2se.Stop(); + + tht.Start(); if (dimension==3) for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) { @@ -1757,14 +2282,20 @@ namespace netgen i3.Sort(); surfelementht -> Set (i3, sei); // war das wichtig ??? sel.GetIndex()); } - + tht.Stop(); + // int np = GetNP(); if (dimension == 3) { + static Timer t("Mesh::CalcSurfacesOfNode, pointloop"); RegionTimer reg (t); + /* for (PointIndex pi = points.Begin(); pi < points.End(); pi++) points[pi].SetType (INNERPOINT); - + */ + for (auto & p : points) + p.SetType (INNERPOINT); + if (GetNFD() == 0) { for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) @@ -1799,9 +2330,13 @@ namespace netgen } } + /* for (int i = 0; i < segments.Size(); i++) { const Segment & seg = segments[i]; + */ + for (const Segment & seg : segments) + { for (int j = 1; j <= 2; j++) { PointIndex hi = (j == 1) ? seg[0] : seg[1]; @@ -1814,6 +2349,8 @@ namespace netgen for (int i = 0; i < lockedpoints.Size(); i++) points[lockedpoints[i]].SetType(FIXEDPOINT); + for(const auto& pointel : pointelements) + points[pointel.pnum].SetType(FIXEDPOINT); /* for (i = 0; i < openelements.Size(); i++) @@ -1846,8 +2383,8 @@ namespace netgen } } - - void Mesh :: FixPoints (const BitArray & fixpoints) + // NgBitArray base is PointIndex::BASE ... + void Mesh :: FixPoints (const NgBitArray & fixpoints) { if (fixpoints.Size() != GetNP()) { @@ -1855,26 +2392,57 @@ namespace netgen return; } int np = GetNP(); + /* for (int i = 1; i <= np; i++) if (fixpoints.Test(i)) { points.Elem(i).SetType (FIXEDPOINT); } + */ + for (PointIndex pi : points.Range()) + if (fixpoints.Test(pi)) + points[pi].SetType(FIXEDPOINT); } void Mesh :: FindOpenElements (int dom) { static Timer t("Mesh::FindOpenElements"); RegionTimer reg (t); + static Timer t_table("Mesh::FindOpenElements - build table"); + static Timer t_pointloop("Mesh::FindOpenElements - pointloop"); int np = GetNP(); int ne = GetNE(); int nse = GetNSE(); + + t_table.Start(); - Array numonpoint(np); + auto elsonpoint = ngcore::CreateSortedTable( volelements.Range(), + [&](auto & table, ElementIndex ei) + { + const Element & el = (*this)[ei]; + if(el.IsDeleted()) return; + if (dom == 0 || dom == el.GetIndex()) + { + if (el.GetNP() == 4) + { + INDEX_4 i4(el[0], el[1], el[2], el[3]); + i4.Sort(); + table.Add (PointIndex(i4.I1()), ei); + table.Add (PointIndex(i4.I2()), ei); + } + else + { + for (PointIndex pi : el.PNums()) + table.Add(pi, ei); + } + } + }, GetNP()); + + NgArray numonpoint(np); + /* numonpoint = 0; - for (ElementIndex ei = 0; ei < ne; ei++) { const Element & el = (*this)[ei]; @@ -1911,12 +2479,13 @@ namespace netgen elsonpoint.Add (el[j], ei); } } + */ + t_table.Stop(); - Array hasface(GetNFD()); + NgArray hasface(GetNFD()); - int i; - for (i = 1; i <= GetNFD(); i++) + for (int i = 1; i <= GetNFD(); i++) { int domin = GetFaceDescriptor(i).DomainIn(); int domout = GetFaceDescriptor(i).DomainOut(); @@ -1985,24 +2554,26 @@ namespace netgen } - int ii; - PointIndex pi; - SurfaceElementIndex sei; + // PointIndex pi; + // SurfaceElementIndex sei; // Element2d hel; - - INDEX_3_CLOSED_HASHTABLE faceht(100); + struct tval { int index; PointIndex p4; }; openelements.SetSize(0); + + t_pointloop.Start(); - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + /* + INDEX_3_CLOSED_HASHTABLE faceht(100); + + for (PointIndex pi : points.Range()) if (selsonpoint[pi].Size()+elsonpoint[pi].Size()) { faceht.SetSize (2 * selsonpoint[pi].Size() + 4 * elsonpoint[pi].Size()); - FlatArray row = selsonpoint[pi]; - for (ii = 0; ii < row.Size(); ii++) + for (SurfaceElementIndex sei : selsonpoint[pi]) { - Element2d hel = SurfaceElement(row[ii]); + Element2d hel = SurfaceElement(sei); if (hel.GetType() == TRIG6) hel.SetType(TRIG); int ind = hel.GetIndex(); @@ -2013,10 +2584,11 @@ namespace netgen if (hel.PNum(1) == pi) { INDEX_3 i3(hel[0], hel[1], hel[2]); - INDEX_2 i2 (GetFaceDescriptor(ind).DomainIn(), - (hel.GetNP() == 3) - ? PointIndex (PointIndex::BASE-1) - : hel.PNum(4)); + tval i2; + i2.index = GetFaceDescriptor(ind).DomainIn(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel.PNum(4); faceht.Set (i3, i2); } } @@ -2028,20 +2600,19 @@ namespace netgen if (hel.PNum(1) == pi) { INDEX_3 i3(hel[0], hel[1], hel[2]); - INDEX_2 i2 (GetFaceDescriptor(ind).DomainOut(), - (hel.GetNP() == 3) - ? PointIndex (PointIndex::BASE-1) - : hel.PNum(4)); + tval i2; + i2.index = GetFaceDescriptor(ind).DomainOut(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel.PNum(4); faceht.Set (i3, i2); } } } - - FlatArray rowel = elsonpoint[pi]; - for (ii = 0; ii < rowel.Size(); ii++) + for (ElementIndex ei : elsonpoint[pi]) { - const Element & el = VolumeElement(rowel[ii]); + const Element & el = VolumeElement(ei); if (dom == 0 || el.GetIndex() == dom) { @@ -2058,15 +2629,15 @@ namespace netgen if (faceht.Used (i3)) { - INDEX_2 i2 = faceht.Get(i3); - if (i2.I1() == el.GetIndex()) + tval i2 = faceht.Get(i3); + if (i2.index == el.GetIndex()) { - i2.I1() = PointIndex::BASE-1; + i2.index = PointIndex::BASE-1; faceht.Set (i3, i2); } else { - if (i2.I1() == 0) + if (i2.index == 0) { PrintSysError ("more elements on face"); (*testout) << "more elements on face!!!" << endl; @@ -2084,41 +2655,176 @@ namespace netgen hel.Invert(); hel.NormalizeNumbering(); INDEX_3 i3(hel[0], hel[1], hel[2]); - INDEX_2 i2(el.GetIndex(), - (hel.GetNP() == 3) - ? PointIndex (PointIndex::BASE-1) - : hel[3]); + + tval i2; + i2.index = el.GetIndex(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel[3]; faceht.Set (i3, i2); } } } } } + for (int i = 0; i < faceht.Size(); i++) if (faceht.UsedPos (i)) { INDEX_3 i3; - INDEX_2 i2; + //INDEX_2 i2; + tval i2; faceht.GetData (i, i3, i2); - if (i2.I1() != PointIndex::BASE-1) + if (i2.index != PointIndex::BASE-1) { - // Element2d tri; - // tri.SetType ( (i2.I2() == PointIndex::BASE-1) ? TRIG : QUAD); - Element2d tri ( (i2.I2() == PointIndex::BASE-1) ? TRIG : QUAD); + Element2d tri ( (i2.p4 == PointIndex::BASE-1) ? TRIG : QUAD); for (int l = 0; l < 3; l++) tri[l] = i3.I(l+1); - tri.PNum(4) = i2.I2(); - tri.SetIndex (i2.I1()); - - // tri.Invert(); - + tri.PNum(4) = i2.p4; + tri.SetIndex (i2.index); openelements.Append (tri); } } } + */ + + size_t numtasks = 4*ngcore::TaskManager::GetNumThreads(); + Array> thread_openelements(numtasks); + ParallelJob + ( [&](TaskInfo & ti) + { + auto myrange = points.Range().Split(ti.task_nr, ti.ntasks); + INDEX_3_CLOSED_HASHTABLE faceht(100); + for (PointIndex pi : myrange) + if (selsonpoint[pi].Size()+elsonpoint[pi].Size()) + { + faceht.SetSize (2 * selsonpoint[pi].Size() + 4 * elsonpoint[pi].Size()); + + for (SurfaceElementIndex sei : selsonpoint[pi]) + { + Element2d hel = SurfaceElement(sei); + if (hel.GetType() == TRIG6) hel.SetType(TRIG); + int ind = hel.GetIndex(); + + if (GetFaceDescriptor(ind).DomainIn() && + (dom == 0 || dom == GetFaceDescriptor(ind).DomainIn()) ) + { + hel.NormalizeNumbering(); + if (hel.PNum(1) == pi) + { + INDEX_3 i3(hel[0], hel[1], hel[2]); + tval i2; + i2.index = GetFaceDescriptor(ind).DomainIn(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel.PNum(4); + faceht.Set (i3, i2); + } + } + if (GetFaceDescriptor(ind).DomainOut() && + (dom == 0 || dom == GetFaceDescriptor(ind).DomainOut()) ) + { + hel.Invert(); + hel.NormalizeNumbering(); + if (hel.PNum(1) == pi) + { + INDEX_3 i3(hel[0], hel[1], hel[2]); + tval i2; + i2.index = GetFaceDescriptor(ind).DomainOut(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel.PNum(4); + faceht.Set (i3, i2); + } + } + } + + for (ElementIndex ei : elsonpoint[pi]) + { + const Element & el = VolumeElement(ei); + if(el.IsDeleted()) continue; + + if (dom == 0 || el.GetIndex() == dom) + { + for (int j = 1; j <= el.GetNFaces(); j++) + { + Element2d hel(TRIG); + el.GetFace (j, hel); + hel.Invert(); + hel.NormalizeNumbering(); + + if (hel[0] == pi) + { + INDEX_3 i3(hel[0], hel[1], hel[2]); + + if (faceht.Used (i3)) + { + tval i2 = faceht.Get(i3); + if (i2.index == el.GetIndex()) + { + i2.index = PointIndex::BASE-1; + faceht.Set (i3, i2); + } + else + { + if (i2.index == 0) + { + PrintSysError ("more elements on face"); + (*testout) << "more elements on face!!!" << endl; + (*testout) << "el = " << el << endl; + (*testout) << "hel = " << hel << endl; + (*testout) << "face = " << i3 << endl; + (*testout) << "points = " << endl; + for (int jj = 1; jj <= 3; jj++) + (*testout) << "p = " << Point(i3.I(jj)) << endl; + } + } + } + else + { + hel.Invert(); + hel.NormalizeNumbering(); + INDEX_3 i3(hel[0], hel[1], hel[2]); + + tval i2; + i2.index = el.GetIndex(); + i2.p4 = (hel.GetNP() == 3) + ? PointIndex (PointIndex::INVALID) + : hel[3]; + faceht.Set (i3, i2); + } + } + } + } + } + + for (int i = 0; i < faceht.Size(); i++) + if (faceht.UsedPos (i)) + { + INDEX_3 i3; + tval i2; + faceht.GetData (i, i3, i2); + if (i2.index != PointIndex::BASE-1) + { + Element2d tri ( (i2.p4 == PointIndex::BASE-1) ? TRIG : QUAD); + for (int l = 0; l < 3; l++) + tri[l] = i3.I(l+1); + tri.PNum(4) = i2.p4; + tri.SetIndex (i2.index); + thread_openelements[ti.task_nr].Append (tri); + } + } + }}, numtasks); + + for (auto & a : thread_openelements) + for (auto & el : a) + openelements.Append (el); + + t_pointloop.Stop(); + int cnt3 = 0; - for (i = 0; i < openelements.Size(); i++) + for (int i = 0; i < openelements.Size(); i++) if (openelements[i].GetNP() == 3) cnt3++; @@ -2152,7 +2858,8 @@ namespace netgen for (int j = 1; j <= 3; j++) { PointIndex pi = sel.PNum(j); - if (pi < points.End()) + // if (pi < points.End()) + if (pi < *points.Range().end()) points[pi].SetType (FIXEDPOINT); } } @@ -2193,9 +2900,9 @@ namespace netgen // int i, j, k; // new version, general elements - // hash index: pnum1-2 - // hash data : surfnr, surfel-nr (pos) or segment nr(neg) - INDEX_2_HASHTABLE faceht(4 * GetNSE()+GetNSeg()+1); + // hash index: pnum1-2, surfnr + // hash data : surfel-nr (pos) or segment nr(neg) + INDEX_3_HASHTABLE faceht(4 * GetNSE()+GetNSeg()+1); PrintMessage (5, "Test Opensegments"); for (int i = 1; i <= GetNSeg(); i++) @@ -2204,8 +2911,8 @@ namespace netgen if (surfnr == 0 || seg.si == surfnr) { - INDEX_2 key(seg[0], seg[1]); - INDEX_2 data(seg.si, -i); + INDEX_3 key(seg[0], seg[1], seg.si); + int data = -i; if (faceht.Used (key)) { @@ -2218,6 +2925,8 @@ namespace netgen } + /* + // not possible with surfnr as hash-index for (int i = 1; i <= GetNSeg(); i++) { const Segment & seg = LineSegment (i); @@ -2232,7 +2941,9 @@ namespace netgen } } } + */ + // bool buggy = false; // ofstream bout("buggy.out"); @@ -2245,15 +2956,18 @@ namespace netgen { for (int j = 1; j <= el.GetNP(); j++) { - INDEX_2 seg (el.PNumMod(j), el.PNumMod(j+1)); - INDEX_2 data; + INDEX_3 seg (el.PNumMod(j), el.PNumMod(j+1), el.GetIndex()); + int data; if (seg.I1() < PointIndex::BASE || seg.I2() < PointIndex::BASE) cerr << "seg = " << seg << endl; if (faceht.Used(seg)) { + faceht.Set (seg, 0); + /* data = faceht.Get(seg); + if (data.I1() == el.GetIndex()) { data.I1() = 0; @@ -2264,46 +2978,16 @@ namespace netgen // buggy = true; PrintWarning ("hash table si not fitting for segment: ", seg.I1(), "-", seg.I2(), " other = ", - data.I2()); - // cout << "me: index = " << el.GetIndex() << ", el = " << el << endl; - - /* - bout << "has index = " << seg << endl; - bout << "hash value = " << faceht.HashValue (seg) << endl; - - if (data.I2() > 0) - { - int io = data.I2(); - cout << "other trig: index = " << SurfaceElement(io).GetIndex() - << ", el = " << SurfaceElement(io) << endl; - } - else - { - cout << "other seg " << -data.I2() << ", si = " << data.I1() << endl; - } - - - bout << "me: index = " << el.GetIndex() << ", el = " << el << endl; - if (data.I2() > 0) - { - int io = data.I2(); - bout << "other trig: index = " << SurfaceElement(io).GetIndex() - << ", el = " << SurfaceElement(io) << endl; - } - else - { - bout << "other seg " << -data.I2() << ", si = " << data.I1() << endl; - } - */ + data.I2(), ", surfnr = ", surfnr); } + */ } else { Swap (seg.I1(), seg.I2()); - data.I1() = el.GetIndex(); - data.I2() = i; - - faceht.Set (seg, data); + // data.I1() = el.GetIndex(); + // data.I2() = i; + faceht.Set (seg, i); } } } @@ -2339,21 +3023,21 @@ namespace netgen for (int i = 1; i <= faceht.GetNBags(); i++) for (int j = 1; j <= faceht.GetBagSize(i); j++) { - INDEX_2 i2; - INDEX_2 data; + INDEX_3 i2; + int data; faceht.GetData (i, j, i2, data); - if (data.I1()) // surfnr + if (data) // surfnr { Segment seg; seg[0] = i2.I1(); seg[1] = i2.I2(); - seg.si = data.I1(); + seg.si = i2.I3(); // find geomdata: - if (data.I2() > 0) + if (data > 0) { // segment due to triangle - const Element2d & el = SurfaceElement (data.I2()); + const Element2d & el = SurfaceElement (data); for (int k = 1; k <= el.GetNP(); k++) { if (seg[0] == el.PNum(k)) @@ -2367,7 +3051,7 @@ namespace netgen else { // segment due to line - const Segment & lseg = LineSegment (-data.I2()); + const Segment & lseg = LineSegment (-data); seg.geominfo[0] = lseg.geominfo[0]; seg.geominfo[1] = lseg.geominfo[1]; @@ -2408,9 +3092,13 @@ namespace netgen ptyps.Elem(seg[1]) = EDGEPOINT; } */ + /* for (int i = 1; i <= points.Size(); i++) points.Elem(i).SetType(SURFACEPOINT); - + */ + for (auto & p : points) + p.SetType (SURFACEPOINT); + for (int i = 1; i <= GetNSeg(); i++) { const Segment & seg = LineSegment (i); @@ -2458,7 +3146,7 @@ namespace netgen int np = GetNP(); FindOpenSegments(); - BitArray frontpoints(np+1); // for 0- and 1-based + NgBitArray frontpoints(np+1); // for 0- and 1-based frontpoints.Clear(); for (int i = 1; i <= GetNOpenSegments(); i++) @@ -2470,7 +3158,7 @@ namespace netgen for (int i = 1; i <= GetNSE(); i++) { - Element2d & sel = surfelements.Elem(i); + Element2d & sel = surfelements[i-1]; bool remove = false; for (int j = 1; j <= sel.GetNP(); j++) if (frontpoints.Test(sel.PNum(j))) @@ -2481,9 +3169,9 @@ namespace netgen for (int i = surfelements.Size(); i >= 1; i--) { - if (!surfelements.Elem(i).PNum(1).IsValid()) + if (!surfelements[i-1].PNum(1).IsValid()) { - surfelements.Elem(i) = surfelements.Last(); + surfelements[i-1] = surfelements.Last(); surfelements.DeleteLast(); } } @@ -2510,10 +3198,11 @@ namespace netgen void Mesh :: FreeOpenElementsEnvironment (int layers) { + static Timer timer("FreeOpenElementsEnvironment"); RegionTimer rt(timer); int i, j, k; PointIndex pi; const int large = 9999; - Array dist(GetNP()); + NgArray dist(GetNP()); dist = large; @@ -2554,7 +3243,7 @@ namespace netgen if (dist[el[j]] < elmin) elmin = dist[el[j]]; - el.flags.fixed = elmin > layers; + el.Flags().fixed = elmin > layers; // eltyps.Elem(i) = (elmin <= layers) ? // FREEELEMENT : FIXEDELEMENT; if (elmin <= layers) @@ -2574,7 +3263,7 @@ namespace netgen - void Mesh :: SetLocalH (netgen::Point<3> pmin, netgen::Point<3> pmax, double grading) + void Mesh :: SetLocalH (netgen::Point<3> pmin, netgen::Point<3> pmax, double grading, int layer) { using netgen::Point; Point<3> c = Center (pmin, pmax); @@ -2585,31 +3274,30 @@ namespace netgen Point<3> pmin2 = c - Vec<3> (d, d, d); Point<3> pmax2 = c + Vec<3> (d, d, d); - delete lochfunc; - lochfunc = new LocalH (pmin2, pmax2, grading, dimension); + SetLocalH(make_unique (pmin2, pmax2, grading, dimension), layer); } - void Mesh :: RestrictLocalH (const Point3d & p, double hloc) + void Mesh :: RestrictLocalH (const Point3d & p, double hloc, int layer) { if(hloc < hmin) hloc = hmin; //cout << "restrict h in " << p << " to " << hloc << endl; - if (!lochfunc) + if (!lochfunc[layer-1]) { PrintWarning("RestrictLocalH called, creating mesh-size tree"); Point3d boxmin, boxmax; GetBox (boxmin, boxmax); - SetLocalH (boxmin, boxmax, 0.8); + SetLocalH (boxmin, boxmax, 0.8, layer); } - lochfunc -> SetH (p, hloc); + lochfunc[layer-1] -> SetH (p, hloc); } void Mesh :: RestrictLocalHLine (const Point3d & p1, const Point3d & p2, - double hloc) + double hloc, int layer) { if(hloc < hmin) hloc = hmin; @@ -2622,7 +3310,7 @@ namespace netgen for (i = 0; i <= steps; i++) { Point3d p = p1 + (double(i)/double(steps) * v); - RestrictLocalH (p, hloc); + RestrictLocalH (p, hloc, layer); } } @@ -2646,7 +3334,7 @@ namespace netgen return 1e10; } - void Mesh :: SetMaxHDomain (const Array & mhd) + void Mesh :: SetMaxHDomain (const NgArray & mhd) { maxhdomain.SetSize(mhd.Size()); for (int i = 1; i <= mhd.Size(); i++) @@ -2654,24 +3342,26 @@ namespace netgen } - double Mesh :: GetH (const Point3d & p) const + double Mesh :: GetH (const Point3d & p, int layer) const { + const auto& lh = GetLocalH(layer); double hmin = hglob; - if (lochfunc) + if (lh) { - double hl = lochfunc->GetH (p); + double hl = lh->GetH (p); if (hl < hglob) hmin = hl; } return hmin; } - double Mesh :: GetMinH (const Point3d & pmin, const Point3d & pmax) + double Mesh :: GetMinH (const Point3d & pmin, const Point3d & pmax, int layer) { + const auto& lh = GetLocalH(layer); double hmin = hglob; - if (lochfunc) + if (lh) { - double hl = lochfunc->GetMinH (pmin, pmax); + double hl = lh->GetMinH (pmin, pmax); if (hl < hmin) hmin = hl; } @@ -2715,16 +3405,16 @@ namespace netgen - void Mesh :: CalcLocalH (double grading) + void Mesh :: CalcLocalH (double grading, int layer) { static Timer t("Mesh::CalcLocalH"); RegionTimer reg(t); - if (!lochfunc) + if (!lochfunc[layer-1]) { Point3d pmin, pmax; GetBox (pmin, pmax); // SetLocalH (pmin, pmax, mparam.grading); - SetLocalH (pmin, pmax, grading); + SetLocalH (pmin, pmax, grading, layer); } PrintMessage (3, @@ -2770,7 +3460,7 @@ namespace netgen const Point3d & p1 = points[el.PNum(1)]; const Point3d & p2 = points[el.PNum(2)]; const Point3d & p3 = points[el.PNum(3)]; - lochfunc->SetH (Center (p1, p2, p3), hel); + lochfunc[layer-1]->SetH (Center (p1, p2, p3), hel); } } else @@ -2778,12 +3468,12 @@ namespace netgen { const Point3d & p1 = points[el.PNum(1)]; const Point3d & p2 = points[el.PNum(2)]; - lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2)); + lochfunc[layer-1]->SetH (Center (p1, p2), 2 * Dist (p1, p2)); } { const Point3d & p1 = points[el.PNum(3)]; const Point3d & p2 = points[el.PNum(4)]; - lochfunc->SetH (Center (p1, p2), 2 * Dist (p1, p2)); + lochfunc[layer-1]->SetH (Center (p1, p2), 2 * Dist (p1, p2)); } } } @@ -2801,7 +3491,7 @@ namespace netgen */ if (!ident -> UsedSymmetric (seg[0], seg[1])) { - lochfunc->SetH (Center (p1, p2), Dist (p1, p2)); + lochfunc[layer-1]->SetH (Center (p1, p2), Dist (p1, p2)); } } /* @@ -2851,17 +3541,17 @@ namespace netgen } - void Mesh :: CalcLocalHFromPointDistances(double grading) + void Mesh :: CalcLocalHFromPointDistances(double grading, int layer) { PrintMessage (3, "Calculating local h from point distances"); - if (!lochfunc) + if (!lochfunc[layer-1]) { Point3d pmin, pmax; GetBox (pmin, pmax); // SetLocalH (pmin, pmax, mparam.grading); - SetLocalH (pmin, pmax, grading); + SetLocalH (pmin, pmax, grading, layer); } PointIndex i,j; @@ -2886,17 +3576,17 @@ namespace netgen } - void Mesh :: CalcLocalHFromSurfaceCurvature (double grading, double elperr) + void Mesh :: CalcLocalHFromSurfaceCurvature (double grading, double elperr, int layer) { PrintMessage (3, "Calculating local h from surface curvature"); - if (!lochfunc) + if (!lochfunc[layer-1]) { Point3d pmin, pmax; GetBox (pmin, pmax); // SetLocalH (pmin, pmax, mparam.grading); - SetLocalH (pmin, pmax, grading); + SetLocalH (pmin, pmax, grading, layer); } @@ -2940,12 +3630,12 @@ namespace netgen pi4++; pi4 = elother.PNum(pi4); - double rad = ComputeCylinderRadius (Point (i2.I1()), - Point (i2.I2()), - Point (pi3), - Point (pi4)); + double rad = ComputeCylinderRadius (Point (PointIndex(i2.I1())), + Point (PointIndex(i2.I2())), + Point (PointIndex(pi3)), + Point (PointIndex(pi4))); - RestrictLocalHLine (Point(i2.I1()), Point(i2.I2()), rad/elperr); + RestrictLocalHLine (Point(PointIndex(i2.I1())), Point(PointIndex(i2.I2())), rad/elperr); /* @@ -2983,8 +3673,8 @@ namespace netgen int nseg = GetNSeg(); int nse = GetNSE(); - Array normals(np); - BitArray linepoint(np); + NgArray normals(np); + NgBitArray linepoint(np); linepoint.Clear(); for (i = 1; i <= nseg; i++) @@ -3083,7 +3773,7 @@ namespace netgen } - void Mesh :: LoadLocalMeshSize (const string & meshsizefilename) + void Mesh :: LoadLocalMeshSize (const filesystem::path & meshsizefilename) { // Philippose - 10/03/2009 // Improve error checking when loading and reading @@ -3091,7 +3781,7 @@ namespace netgen if (meshsizefilename.empty()) return; - ifstream msf(meshsizefilename.c_str()); + ifstream msf(meshsizefilename); // Philippose - 09/03/2009 // Adding print message information in case the specified @@ -3150,6 +3840,18 @@ namespace netgen + void Mesh :: SetLocalH(shared_ptr loch, int layer) + { + if(layer>lochfunc.Size()) + { + auto pre_size = lochfunc.Size(); + lochfunc.SetSize(layer); + for(auto & func : lochfunc.Range(pre_size, layer-1)) + func = lochfunc[0]; + } + lochfunc[layer-1] = loch; + } + void Mesh :: GetBox (Point3d & pmin, Point3d & pmax, int dom) const { if (points.Size() == 0) @@ -3163,7 +3865,8 @@ namespace netgen pmin = Point3d (1e10, 1e10, 1e10); pmax = Point3d (-1e10, -1e10, -1e10); - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + for (PointIndex pi : points.Range()) { pmin.SetToMin ( (*this) [pi] ); pmax.SetToMax ( (*this) [pi] ); @@ -3212,7 +3915,8 @@ namespace netgen pmin = Point3d (1e10, 1e10, 1e10); pmax = Point3d (-1e10, -1e10, -1e10); - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + // for (PointIndex pi = points.Begin(); pi < points.End(); pi++) + for (PointIndex pi : points.Range()) if (points[pi].Type() <= ptyp) { pmin.SetToMin ( (*this) [pi] ); @@ -3225,9 +3929,9 @@ namespace netgen double Mesh :: ElementError (int eli, const MeshingParameters & mp) const { - const Element & el = volelements.Get(eli); - return CalcTetBadness (points.Get(el[0]), points.Get(el[1]), - points.Get(el[2]), points.Get(el[3]), -1, mp); + const Element & el = volelements[eli-1]; + return CalcTetBadness (points[el[0]], points[el[1]], + points[el[2]], points[el[3]], -1, mp); } void Mesh :: AddLockedPoint (PointIndex pi) @@ -3245,10 +3949,11 @@ namespace netgen void Mesh :: Compress () { static Timer t("Mesh::Compress"); RegionTimer reg(t); + NgLock lock(mutex); + lock.Lock(); - Array op2np(GetNP()); - Array hpoints; - BitArrayChar pused(GetNP()); + Array op2np(GetNP()); + Array pused(GetNP()); /* (*testout) << "volels: " << endl; @@ -3265,7 +3970,7 @@ namespace netgen if (volelements[i][0] <= PointIndex::BASE-1 || volelements[i].IsDeleted()) { - volelements.Delete(i); + volelements.DeleteElement(i); i--; } @@ -3273,52 +3978,76 @@ namespace netgen for (int i = 0; i < surfelements.Size(); i++) if (surfelements[i].IsDeleted()) { - surfelements.Delete(i); + surfelements.DeleteElement(i); i--; } for (int i = 0; i < segments.Size(); i++) if (segments[i][0] <= PointIndex::BASE-1) { - segments.Delete(i); + segments.DeleteElement(i); i--; } for(int i=0; i < segments.Size(); i++) if(segments[i].edgenr < 0) - segments.Delete(i--); + segments.DeleteElement(i--); - pused.Clear(); + pused = false; + /* for (int i = 0; i < volelements.Size(); i++) { const Element & el = volelements[i]; for (int j = 0; j < el.GetNP(); j++) - pused.Set (el[j]); + pused[el[j]] = true; } + */ + /* + for (const Element & el : volelements) + for (PointIndex pi : el.PNums()) + pused[pi] = true; + */ + ParallelForRange + (volelements.Range(), [&] (auto myrange) + { + for (const Element & el : volelements.Range(myrange)) + for (PointIndex pi : el.PNums()) + pused[pi] = true; + }); + + /* for (int i = 0; i < surfelements.Size(); i++) { const Element2d & el = surfelements[i]; for (int j = 0; j < el.GetNP(); j++) - pused.Set (el[j]); + pused[el[j]] = true; } - + */ + ParallelForRange + (surfelements.Range(), [&] (auto myrange) + { + for (const Element2d & el : surfelements.Range(myrange)) + for (PointIndex pi : el.PNums()) + pused[pi] = true; + }); + for (int i = 0; i < segments.Size(); i++) { const Segment & seg = segments[i]; - pused.Set (seg[0]); - pused.Set (seg[1]); + for (int j = 0; j < seg.GetNP(); j++) + pused[seg[j]] = true; } for (int i = 0; i < openelements.Size(); i++) { const Element2d & el = openelements[i]; for (int j = 0; j < el.GetNP(); j++) - pused.Set(el[j]); + pused[el[j]] = true; } for (int i = 0; i < lockedpoints.Size(); i++) - pused.Set (lockedpoints[i]); + pused[lockedpoints[i]] = true; /* @@ -3335,44 +4064,65 @@ namespace netgen */ // pused.Set(); - - int npi = PointIndex::BASE-1; - - for (PointIndex pi = points.Begin(); pi < points.End(); pi++) - if (pused.Test(pi)) - { - npi++; - op2np[pi] = npi; - hpoints.Append (points[pi]); - } - else - op2np[pi] = -1; - - - - points.SetSize(0); - for (int i = 0; i < hpoints.Size(); i++) - points.Append (hpoints[i]); - + + { + Array hpoints; + int npi = PointIndex::BASE; + for (PointIndex pi : points.Range()) + if (pused[pi]) + { + op2np[pi] = npi; + npi++; + hpoints.Append (points[pi]); + } + else + { + op2np[pi].Invalidate(); + } + + points.SetSize(0); + for (int i = 0; i < hpoints.Size(); i++) + points.Append (hpoints[i]); + } + + /* for (int i = 1; i <= volelements.Size(); i++) { Element & el = VolumeElement(i); for (int j = 0; j < el.GetNP(); j++) el[j] = op2np[el[j]]; } + */ + ParallelForRange + (volelements.Range(), [&] (auto myrange) + { + for (Element & el : volelements.Range(myrange)) + for (PointIndex & pi : el.PNums()) + pi = op2np[pi]; + }); + /* for (int i = 1; i <= surfelements.Size(); i++) { Element2d & el = SurfaceElement(i); for (int j = 0; j < el.GetNP(); j++) el[j] = op2np[el[j]]; } + */ + ParallelForRange + (surfelements.Range(), [&] (auto myrange) + { + for (Element2d & el : surfelements.Range(myrange)) + for (PointIndex & pi : el.PNums()) + pi = op2np[pi]; + }); + for (int i = 0; i < segments.Size(); i++) { Segment & seg = segments[i]; - seg[0] = op2np[seg[0]]; - seg[1] = op2np[seg[1]]; + for (int j = 0; j < seg.GetNP(); j++) + seg[j] = op2np[seg[j]]; } for (int i = 1; i <= openelements.Size(); i++) @@ -3401,6 +4151,7 @@ namespace netgen // FindOpenElements(); timestamp = NextTimeStamp(); + lock.UnLock(); } void Mesh :: OrderElements() @@ -3515,130 +4266,107 @@ namespace netgen { static Timer t("Mesh::CheckOverlappingBoundary"); RegionTimer reg(t); - int i, j, k; - Point3d pmin, pmax; GetBox (pmin, pmax); - BoxTree<3> setree(pmin, pmax); - Array inters; + BoxTree<3, SurfaceElementIndex> setree(pmin, pmax); + // NgArray inters; bool overlap = 0; bool incons_layers = 0; + for (Element2d & el : SurfaceElements()) + el.badel = false; - for (i = 1; i <= GetNSE(); i++) - SurfaceElement(i).badel = 0; - - - for (i = 1; i <= GetNSE(); i++) + for (SurfaceElementIndex sei : Range(SurfaceElements())) { - const Element2d & tri = SurfaceElement(i); + const Element2d & tri = SurfaceElement(sei); - Point3d tpmin (Point(tri[0])); - Point3d tpmax (tpmin); + Box<3> box(Box<3>::EMPTY_BOX); + for (PointIndex pi : tri.PNums()) + box.Add (Point(pi)); - for (k = 1; k < tri.GetNP(); k++) - { - tpmin.SetToMin (Point (tri[k])); - tpmax.SetToMax (Point (tri[k])); - } - Vec3d diag(tpmin, tpmax); - - tpmax = tpmax + 0.1 * diag; - tpmin = tpmin - 0.1 * diag; - - setree.Insert (tpmin, tpmax, i); + box.Increase(1e-3*box.Diam()); + setree.Insert (box, sei); } - for (i = 1; i <= GetNSE(); i++) - { - const Element2d & tri = SurfaceElement(i); - - Point3d tpmin (Point(tri[0])); - Point3d tpmax (tpmin); - - for (k = 1; k < tri.GetNP(); k++) - { - tpmin.SetToMin (Point (tri[k])); - tpmax.SetToMax (Point (tri[k])); - } - - setree.GetIntersecting (tpmin, tpmax, inters); - - for (j = 1; j <= inters.Size(); j++) - { - const Element2d & tri2 = SurfaceElement(inters.Get(j)); - - if ( (*this)[tri[0]].GetLayer() != (*this)[tri2[0]].GetLayer()) - continue; - - if ( (*this)[tri[0]].GetLayer() != (*this)[tri[1]].GetLayer() || - (*this)[tri[0]].GetLayer() != (*this)[tri[2]].GetLayer()) - { - incons_layers = 1; - cout << "inconsistent layers in triangle" << endl; - } - - - const netgen::Point<3> *trip1[3], *trip2[3]; - for (k = 1; k <= 3; k++) - { - trip1[k-1] = &Point (tri.PNum(k)); - trip2[k-1] = &Point (tri2.PNum(k)); - } - - if (IntersectTriangleTriangle (&trip1[0], &trip2[0])) - { - overlap = 1; - PrintWarning ("Intersecting elements " - ,i, " and ", inters.Get(j)); - - (*testout) << "Intersecting: " << endl; - (*testout) << "openelement " << i << " with open element " << inters.Get(j) << endl; - - cout << "el1 = " << tri << endl; - cout << "el2 = " << tri2 << endl; - cout << "layer1 = " << (*this)[tri[0]].GetLayer() << endl; - cout << "layer2 = " << (*this)[tri2[0]].GetLayer() << endl; - - - for (k = 1; k <= 3; k++) - (*testout) << tri.PNum(k) << " "; - (*testout) << endl; - for (k = 1; k <= 3; k++) - (*testout) << tri2.PNum(k) << " "; - (*testout) << endl; - - for (k = 0; k <= 2; k++) - (*testout) << *trip1[k] << " "; - (*testout) << endl; - for (k = 0; k <= 2; k++) - (*testout) << *trip2[k] << " "; - (*testout) << endl; - - (*testout) << "Face1 = " << GetFaceDescriptor(tri.GetIndex()) << endl; - (*testout) << "Face1 = " << GetFaceDescriptor(tri2.GetIndex()) << endl; - - /* - INDEX_3 i3(tri.PNum(1), tri.PNum(2), tri.PNum(3)); - i3.Sort(); - for (k = 1; k <= GetNSE(); k++) - { - const Element2d & el2 = SurfaceElement(k); - INDEX_3 i3b(el2.PNum(1), el2.PNum(2), el2.PNum(3)); - i3b.Sort(); - if (i3 == i3b) - { - SurfaceElement(k).badel = 1; - } - } - */ - SurfaceElement(i).badel = 1; - SurfaceElement(inters.Get(j)).badel = 1; - } - } - } + std::mutex m; + // for (SurfaceElementIndex sei : Range(SurfaceElements())) + ParallelForRange + (Range(SurfaceElements()), [&] (auto myrange) + { + for (SurfaceElementIndex sei : myrange) + { + const Element2d & tri = SurfaceElement(sei); + + Box<3> box(Box<3>::EMPTY_BOX); + for (PointIndex pi : tri.PNums()) + box.Add (Point(pi)); + + setree.GetFirstIntersecting + (box.PMin(), box.PMax(), + [&] (SurfaceElementIndex sej) + { + const Element2d & tri2 = SurfaceElement(sej); + + if ( (*this)[tri[0]].GetLayer() != (*this)[tri2[0]].GetLayer()) + return false; + + if ( (*this)[tri[0]].GetLayer() != (*this)[tri[1]].GetLayer() || + (*this)[tri[0]].GetLayer() != (*this)[tri[2]].GetLayer()) + { + incons_layers = 1; + // cout << "inconsistent layers in triangle" << endl; + } + + const netgen::Point<3> *trip1[3], *trip2[3]; + for (int k = 0; k < 3; k++) + { + trip1[k] = &Point (tri[k]); + trip2[k] = &Point (tri2[k]); + } + + if (IntersectTriangleTriangle (&trip1[0], &trip2[0])) + { + overlap = 1; + lock_guard guard(m); + if(!incons_layers) + { + PrintWarning ("Intersecting elements " + ,int(sei), " and ", int(sej)); + + (*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; + } + + for (int k = 1; k <= 3; k++) + (*testout) << tri.PNum(k) << " "; + (*testout) << endl; + for (int k = 1; k <= 3; k++) + (*testout) << tri2.PNum(k) << " "; + (*testout) << endl; + + for (int k = 0; k <= 2; k++) + (*testout) << *trip1[k] << " "; + (*testout) << endl; + for (int k = 0; k <= 2; k++) + (*testout) << *trip2[k] << " "; + (*testout) << endl; + (*testout) << "Face1 = " << GetFaceDescriptor(tri.GetIndex()) << endl; + (*testout) << "Face1 = " << GetFaceDescriptor(tri2.GetIndex()) << endl; + + SurfaceElement(sei).badel = 1; + SurfaceElement(sej).badel = 1; + } + return false; + }); + } + }); // bug 'fix' if (incons_layers) overlap = 0; @@ -3658,7 +4386,7 @@ namespace netgen for (i = 1; i <= ne; i++) { Element & el = (Element&) VolumeElement(i); - el.flags.badel = 0; + el.Flags().badel = 0; int nip = el.GetNIP(); for (j = 1; j <= nip; j++) { @@ -3667,7 +4395,7 @@ namespace netgen if (det > 0) { PrintError ("Element ", i , " has wrong orientation"); - el.flags.badel = 1; + el.Flags().badel = 1; } } } @@ -3675,9 +4403,54 @@ namespace netgen return 0; } + // Search for surface trigs with same vertices ( may happen for instance with close surfaces in stl geometies ) + int Mesh :: FindIllegalTrigs () + { + // Temporary table to store the vertex numbers of all triangles + INDEX_3_CLOSED_HASHTABLE temp_tab(3*GetNSE() + 1); + size_t cnt = 0; + for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) + { + const Element2d & sel = surfelements[sei]; + if (sel.IsDeleted()) continue; + + INDEX_3 i3(sel[0], sel[1], sel[2]); + i3.Sort(); + if(temp_tab.Used(i3)) + { + temp_tab.Set (i3, -1); + cnt++; + } + else + { + temp_tab.Set (i3, sei); + } + } + + illegal_trigs = make_unique> (2*cnt+1); + for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) + { + const Element2d & sel = surfelements[sei]; + if (sel.IsDeleted()) continue; + + INDEX_3 i3(sel[0], sel[1], sel[2]); + i3.Sort(); + if(temp_tab.Get(i3)==-1) + illegal_trigs -> Set (i3, 1); + } + return cnt; + } bool Mesh :: LegalTrig (const Element2d & el) const { + if(illegal_trigs) + { + INDEX_3 i3 (el[0], el[1], el[2]); + i3.Sort(); + if(illegal_trigs->Used(i3)) + return false; + } + return 1; if ( /* hp */ 1) // needed for old, simple hp-refinement { @@ -3705,6 +4478,44 @@ namespace netgen return 1; } + double Mesh :: CalcTotalBad (const MeshingParameters & mp ) + { + static Timer t("CalcTotalBad"); RegionTimer reg(t); + static constexpr int n_classes = 20; + + double sum = 0; + + tets_in_qualclass.SetSize(n_classes); + tets_in_qualclass = 0; + + ParallelForRange( IntRange(volelements.Size()), [&] (auto myrange) + { + double local_sum = 0.0; + double teterrpow = mp.opterrpow; + + std::array classes_local{}; + + for (auto i : myrange) + { + double elbad = pow (max2(CalcBad (points, volelements[i], 0, mp),1e-10), 1/teterrpow); + + int qualclass = int (n_classes / elbad + 1); + if (qualclass < 1) qualclass = 1; + if (qualclass > n_classes) qualclass = n_classes; + classes_local[qualclass-1]++; + + local_sum += elbad; + } + + AtomicAdd(sum, local_sum); + + for (auto i : Range(n_classes)) + AsAtomic(tets_in_qualclass[i]) += classes_local[i]; + }); + + return sum; + } + @@ -3794,7 +4605,7 @@ namespace netgen if (bface[i] && bface[j]) if (!segedge[pi3map[i][j]][pi4map[i][j]]) { - // 2 boundary faces withoud edge in between + // 2 boundary faces without edge in between el.SetLegal (0); return 0; } @@ -3877,14 +4688,36 @@ namespace netgen return ndom; } + void Mesh :: SetDimension (int dim) + { + if (dimension == 3 && dim == 2) + { + // change mesh-dim from 3 to 2 (currently needed for OCC) + for (auto str : materials) + delete str; + materials.SetSize(0); + for (auto str : bcnames) + materials.Append(str); + bcnames.SetSize(0); + for (auto str : cd2names) + bcnames.Append(str); + cd2names.SetSize(0); + for (auto str : cd3names) + cd2names.Append(str); + cd3names.SetSize(0); + for (auto & seg : LineSegments()) + seg.si = seg.edgenr; + } + dimension = dim; + } void Mesh :: SurfaceMeshOrientation () { int i, j; int nse = GetNSE(); - BitArray used(nse); + NgBitArray used(nse); used.Clear(); INDEX_2_HASHTABLE edges(nse+1); @@ -3909,7 +4742,7 @@ namespace netgen for (i = 1; i <= nse; i++) if (!used.Test(i)) { - Element2d & el = surfelements.Elem(i); + Element2d & el = surfelements[i-1]; int found = 0, foundrev = 0; for (j = 1; j <= 3; j++) { @@ -4323,8 +5156,7 @@ namespace netgen PrintMessage (4, "Rebuild element searchtree"); - delete elementsearchtree; - elementsearchtree = NULL; + elementsearchtree = nullptr; int ne = (dimension == 2) ? GetNSE() : GetNE(); if (dimension == 3 && !GetNE() && GetNSE()) @@ -4336,14 +5168,35 @@ namespace netgen { Box<3> box (Box<3>::EMPTY_BOX); for (SurfaceElementIndex sei = 0; sei < ne; sei++) - box.Add (points[surfelements[sei].PNums()]); + // box.Add (points[surfelements[sei].PNums()]); + for (auto pi : surfelements[sei].PNums()) + box.Add (points[pi]); box.Increase (1.01 * box.Diam()); - elementsearchtree = new BoxTree<3> (box); + elementsearchtree = make_unique> (box); for (SurfaceElementIndex sei = 0; sei < ne; sei++) { - box.Set (points[surfelements[sei].PNums()]); + // box.Set (points[surfelements[sei].PNums()]); + + Box<3> box (Box<3>::EMPTY_BOX); + for (auto pi : surfelements[sei].PNums()) + box.Add (points[pi]); + + auto & el = surfelements[sei]; + if(el.IsCurved() && curvedelems->IsSurfaceElementCurved(sei)) + { + netgen::Point<2> lami [4] = {netgen::Point<2>(0.5,0), netgen::Point<2>(0,0.5), netgen::Point<2>(0.5,0.5), netgen::Point<2>(1./3,1./3)}; + for (auto lam : lami) + { + netgen::Point<3> x; + Mat<3,2> Jac; + + curvedelems->CalcSurfaceTransformation(lam,sei,x,Jac); + box.Add (x); + } + box.Scale(1.2); + } elementsearchtree -> Insert (box, sei+1); } } @@ -4351,14 +5204,26 @@ namespace netgen { Box<3> box (Box<3>::EMPTY_BOX); for (ElementIndex ei = 0; ei < ne; ei++) - box.Add (points[volelements[ei].PNums()]); + // box.Add (points[volelements[ei].PNums()]); + for (auto pi : volelements[ei].PNums()) + box.Add (points[pi]); box.Increase (1.01 * box.Diam()); - elementsearchtree = new BoxTree<3> (box); + elementsearchtree = make_unique> (box); for (ElementIndex ei = 0; ei < ne; ei++) { - box.Set (points[volelements[ei].PNums()]); + // box.Set (points[volelements[ei].PNums()]); + + Box<3> box (Box<3>::EMPTY_BOX); + for (auto pi : volelements[ei].PNums()) + box.Add (points[pi]); + + auto & el = volelements[ei]; + if(el.IsCurved() && curvedelems->IsElementCurved(ei)) + box.Scale(1.2); + + elementsearchtree -> Insert (box, ei+1); } } @@ -4410,7 +5275,7 @@ namespace netgen Vec3d rhs, sol; const double eps = 1e-6; - Array loctrigs; + NgArray loctrigs; //SZ @@ -4423,6 +5288,34 @@ namespace netgen const Point3d & p3 = Point(el.PNum(3)); const Point3d & p4 = Point(el.PNum(4)); + if (el.GetOrder() > 1 || el.GetHpElnr() != -1) { + netgen::Point<2> lam(0.5,0.5); + Vec<3> rhs; + Vec<2> deltalam; + + netgen::Point<3> x; + Mat<3,2> Jac; + double delta = 1.; + const int maxits = 30; + int i = 0; + while(delta > 1e-16 && i < maxits) + { + curvedelems->CalcSurfaceTransformation(lam,element-1,x,Jac); + rhs = p - x; + Jac.Solve(rhs,deltalam); + lam += deltalam; + delta = deltalam.Length2(); + i++; + } + if(i == maxits) + return false; + lami[0] = lam[0]; + lami[1] = lam[1]; + if(lami[0] < -eps || lami[0] > 1+eps || lami[1] < -eps || lami[1] > 1+eps) + return false; + return true; + } + // Coefficients of Bilinear Mapping from Ref-Elem to global Elem // X = a + b x + c y + d x y Vec3d a = p1; @@ -4476,7 +5369,7 @@ namespace netgen */ lami[2]=0.; - double eps = 1.E-12; + // double eps = 1.E-12; double c1,c2,r; //First check if point is "exactly" a vertex point @@ -4771,6 +5664,56 @@ namespace netgen //(*testout) << "col1 " << col1 << " col2 " << col2 << " col3 " << col3 << " rhs " << rhs << endl; //(*testout) << "sol " << sol << endl; + if (SurfaceElement(element).GetType() ==TRIG6 || curvedelems->IsSurfaceElementCurved(element-1)) + { + // netgen::Point<2> lam(1./3,1./3); + netgen::Point<2> lam(sol.X(), sol.Y()); + if(SurfaceElement(element).GetType() != TRIG6) + { + lam[0] = 1-sol.X()-sol.Y(); + lam[1] = sol.X(); + } + Vec<3> rhs; + Vec<2> deltalam; + netgen::Point<3> x; + Mat<3,2> Jac,Jact; + + double delta=1; + + bool retval; + + int i = 0; + + const int maxits = 30; + while(delta > 1e-16 && iCalcSurfaceTransformation(lam,element-1,x,Jac); + rhs = p-x; + Jac.Solve(rhs,deltalam); + + lam += deltalam; + + delta = deltalam.Length2(); + + i++; + //(*testout) << "pcie i " << i << " delta " << delta << " p " << p << " x " << x << " lam " << lam << endl; + //<< "Jac " << Jac << endl; + } + + if(i==maxits) + return false; + + 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) { @@ -4907,7 +5850,7 @@ namespace netgen Vec3d rhs, sol; const double eps = 1.e-4; - Array loctets; + NgArray loctets; VolumeElement(element).GetTets (loctets); @@ -4938,8 +5881,8 @@ namespace netgen if (sol.X() >= -eps && sol.Y() >= -eps && sol.Z() >= -eps && sol.X() + sol.Y() + sol.Z() <= 1+eps) { - Array loctetsloc; - Array > pointsloc; + NgArray loctetsloc; + NgArray > pointsloc; VolumeElement(element).GetTetsLocal (loctetsloc); VolumeElement(element).GetNodesLocalNew (pointsloc); @@ -4971,7 +5914,7 @@ namespace netgen { if(index != -1) { - Array dummy(1); + NgArray dummy(1); dummy[0] = index; return GetElementOfPoint(p,lami,&dummy,build_searchtree,allowindex); } @@ -4984,133 +5927,21 @@ namespace netgen int Mesh :: GetElementOfPoint (const netgen::Point<3> & p, double lami[3], - const Array * const indices, + const NgArray * const indices, bool build_searchtree, const bool allowindex) const { - // const double pointtol = 1e-12; - // netgen::Point<3> pmin = p - Vec<3> (pointtol, pointtol, pointtol); - // netgen::Point<3> pmax = p + Vec<3> (pointtol, pointtol, pointtol); + if ( (dimension == 2 && !GetNSE()) || + (dimension == 3 && !GetNE() && !GetNSE()) ) + return -1; + + if (build_searchtree) + const_cast(*this).BuildElementSearchTree (); if (dimension == 2 || (dimension==3 && !GetNE() && GetNSE())) - { - int ne; - int ps_startelement = 0; // disable global buffering + return Find2dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex); - if(ps_startelement != 0 && ps_startelement <= GetNSE() && PointContainedIn2DElement(p,lami,ps_startelement)) - return ps_startelement; - - Array locels; - if (elementsearchtree || build_searchtree) - { - // update if necessary: - const_cast(*this).BuildElementSearchTree (); - // double tol = elementsearchtree->Tolerance(); - // netgen::Point<3> pmin = p - Vec<3> (tol, tol, tol); - // netgen::Point<3> pmax = p + Vec<3> (tol, tol, tol); - elementsearchtree->GetIntersecting (p, p, locels); - ne = locels.Size(); - } - else - ne = GetNSE(); - - for (int i = 1; i <= ne; i++) - { - int ii; - - if (elementsearchtree) - ii = locels.Get(i); - else - ii = i; - - if(ii == ps_startelement) continue; - - if(indices != NULL && indices->Size() > 0) - { - bool contained = indices->Contains(SurfaceElement(ii).GetIndex()); - if((allowindex && !contained) || (!allowindex && contained)) continue; - } - - if(PointContainedIn2DElement(p,lami,ii)) return ii; - - } - return 0; - } - else - - { - int ps_startelement = 0; // disable global buffering - // int i, j; - int ne; - - if(ps_startelement != 0 && PointContainedIn3DElement(p,lami,ps_startelement)) - return ps_startelement; - - Array locels; - if (elementsearchtree || build_searchtree) - { - // update if necessary: - const_cast(*this).BuildElementSearchTree (); - // double tol = elementsearchtree->Tolerance(); - // netgen::Point<3> pmin = p - Vec<3> (tol, tol, tol); - // netgen::Point<3> pmax = p + Vec<3> (tol, tol, tol); - elementsearchtree->GetIntersecting (p, p, locels); - ne = locels.Size(); - } - else - ne = GetNE(); - - for (int i = 1; i <= ne; i++) - { - int ii; - - if (elementsearchtree) - ii = locels.Get(i); - else - ii = i; - if(ii == ps_startelement) continue; - - if(indices != NULL && indices->Size() > 0) - { - bool contained = indices->Contains(VolumeElement(ii).GetIndex()); - if((allowindex && !contained) || (!allowindex && contained)) continue; - } - - if(PointContainedIn3DElement(p,lami,ii)) - { - ps_startelement = ii; - return ii; - } - } - - // Not found, try uncurved variant: - for (int i = 1; i <= ne; i++) - { - int ii; - - if (elementsearchtree) - ii = locels.Get(i); - else - ii = i; - - if(indices != NULL && indices->Size() > 0) - { - bool contained = indices->Contains(VolumeElement(ii).GetIndex()); - if((allowindex && !contained) || (!allowindex && contained)) continue; - } - - - if(PointContainedIn3DElementOld(p,lami,ii)) - { - ps_startelement = ii; - (*testout) << "WARNING: found element of point " << p <<" only for uncurved mesh" << endl; - return ii; - } - } - - - return 0; - } + return Find3dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex); } @@ -5123,7 +5954,7 @@ namespace netgen { if(index != -1) { - Array dummy(1); + NgArray dummy(1); dummy[0] = index; return GetSurfaceElementOfPoint(p,lami,&dummy,build_searchtree,allowindex); } @@ -5136,80 +5967,23 @@ namespace netgen int Mesh :: GetSurfaceElementOfPoint (const netgen::Point<3> & p, double lami[3], - const Array * const indices, + const NgArray * const indices, bool build_searchtree, const bool allowindex) const { + if (!GetNE() && build_searchtree) + const_cast(*this).BuildElementSearchTree (); + if (dimension == 2) - { - throw NgException("GetSurfaceElementOfPoint not yet implemented for 2D meshes"); - } + return Find1dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex); else - { - double vlam[3]; - int velement = GetElementOfPoint(p,vlam,NULL,build_searchtree,allowindex); - - //(*testout) << "p " << p << endl; - //(*testout) << "velement " << velement << endl; - - if (!GetNE() && GetNSE() ) - { - lami[0] = vlam[0]; - lami[1] = vlam[1]; - lami[2] = vlam[2]; - return velement; - } - - Array faces; - topology.GetElementFaces(velement,faces); - - //(*testout) << "faces " << faces << endl; - - for(int i=0; iSize() != 0) - { - if(indices->Contains(SurfaceElement(faces[i]).GetIndex()) && - PointContainedIn2DElement(p,lami,faces[i],true)) - return faces[i]; - } - else - { - if(PointContainedIn2DElement(p,lami,faces[i],true)) - { - //(*testout) << "found point " << p << " in sel " << faces[i] - // << ", lam " << lami[0] << ", " << lami[1] << ", " << lami[2] << endl; - return faces[i]; - } - } - } - - Array faces2; - topology.GetElementFaces(velement,faces2); - /* - cout << "no matching surf element" << endl - << "p = " << p << endl - << "faces-orig = " << faces2 << endl - << "faces = " << faces << endl - << ", vol el = " << velement - << ", vlam = " << vlam[0] << "," << vlam[1] << "," << vlam[2] << endl; - */ - } - + return Find2dElement(*this, p, lami, indices, elementsearchtree.get(), allowindex); return 0; } void Mesh::GetIntersectingVolEls(const Point3d& p1, const Point3d& p2, - Array & locels) const + NgArray & locels) const { elementsearchtree->GetIntersecting (p1, p2, locels); } @@ -5221,8 +5995,8 @@ namespace netgen int np = GetNP(); int nse = GetNSE(); - BitArray surfused(nse); - BitArray pused (np); + NgBitArray surfused(nse); + NgBitArray pused (np); surfused.Clear(); @@ -5333,7 +6107,7 @@ namespace netgen int fdi; int np = GetNP(); - BitArray usedp(np); + NgBitArray usedp(np); Array els_of_face; fdi = 1; @@ -5341,7 +6115,11 @@ namespace netgen { GetSurfaceElementsOfFace (fdi, els_of_face); - if (els_of_face.Size() == 0) continue; + if (els_of_face.Size() == 0) + { + fdi++; + continue; + } SurfaceElementIndex firstel = els_of_face[0]; @@ -5412,6 +6190,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++; @@ -5494,10 +6278,176 @@ namespace netgen */ } + void Mesh :: ZRefine(const string& name, const Array& slices) + { + auto nr = GetIdentifications().GetNr(name); + auto& identpts = GetIdentifications().GetIdentifiedPoints(); + UpdateTopology(); + + std::map, + Array> inserted_points; + BitArray mapped_points(GetNV()+1); + mapped_points = false; + + // Add new points + for(auto [p1p2, idnr] : identpts) + { + if(idnr != nr) + continue; + auto& ipts = inserted_points[{p1p2.I1(), p1p2.I2()}]; + auto p1 = Point(p1p2.I1()); + auto p2 = Point(p1p2.I2()); + ipts.Append(p1p2.I1()); + mapped_points.SetBit(p1p2.I1()); + for(auto slice : slices) + { + auto np = p1 + slice * (p2-p1); + auto npi = AddPoint(np); + ipts.Append(npi); + } + ipts.Append(p1p2.I2()); + } + + // Split segments + for(auto si : Range(segments)) + { + auto& seg = segments[si]; + auto p1 = seg[0]; + auto p2 = seg[1]; + + auto c1 = inserted_points.count({p1, p2}); + auto c2 = inserted_points.count({p2, p1}); + + if(c1 == 0 && c2 == 0) + continue; + + if(c2) + Swap(p1,p2); + + const auto& ipts = inserted_points[{p1,p2}]; + if(c2) + seg[1] = ipts[ipts.Size()-2]; + else + seg[1] = ipts[1]; + for(auto i : Range(size_t(1), ipts.Size()-1)) + { + Segment snew = seg; + if(c2) + { + snew[0] = ipts[ipts.Size()-1-i]; + snew[1] = ipts[ipts.Size()-2-i]; + } + else + { + snew[0] = ipts[i]; + snew[1] = ipts[i+1]; + } + AddSegment(snew); + } + } + + BitArray sel_done(surfelements.Size()); + sel_done = false; + + // Split surface elements + auto p2sel = CreatePoint2SurfaceElementTable(); + for(const auto& [pair, inserted] : inserted_points) + { + for(auto si : p2sel[pair.first]) + { + if(sel_done[si]) + continue; + sel_done.SetBit(si); + auto sel = surfelements[si]; + map> mapped_points; + int nmapped = 0; + for(auto i : Range(sel.GetNP())) + { + auto p1 = sel[i]; + auto p2 = sel[(i+1)%sel.GetNP()]; + auto c1 = inserted_points.count({p1, p2}); + auto c2 = inserted_points.count({p2, p1}); + if(c1 == 0 && c2 == 0) + continue; + if(c2) + Swap(p1, p2); + auto& ipts = inserted_points[{p1, p2}]; + auto& a1 = mapped_points[p1]; + auto& a2 = mapped_points[p2]; + a1 = ipts.Range(0, ipts.Size()-1); + a2 = ipts.Range(1, ipts.Size()); + nmapped = ipts.Size()-1; + } + for(auto i : Range(nmapped)) + { + Element2d nsel = sel; + for(auto& pi : nsel.PNums()) + if(mapped_points.count(pi)) + pi = mapped_points[pi][i]; + AddSurfaceElement(nsel); + } + if(nmapped) + surfelements[si].Delete(); + } + } + + // Split volume elements + BitArray vol_done(volelements.Size()); + vol_done = false; + auto p2el = CreatePoint2ElementTable(); // mapped_points); + for(const auto& [pair, inserted] : inserted_points) + { + for(auto ei : p2el[pair.first]) + { + if(vol_done[ei]) + continue; + vol_done.SetBit(ei); + auto el = volelements[ei]; + map> mapped_points; + int nmapped = 0; + // NgArray eledges; + // topology.GetElementEdges(ei+1, eledges); + // for(auto edgei : eledges) + for(auto edgei : topology.GetEdges(ElementIndex(ei))) + { + int p1, p2; + topology.GetEdgeVertices(edgei+1, p1, p2); + auto c1 = inserted_points.count({p1, p2}); + auto c2 = inserted_points.count({p2, p1}); + if(c1 == 0 && c2 == 0) + continue; + if(c2) + Swap(p1, p2); + auto& ipts = inserted_points[{p1, p2}]; + auto& a1 = mapped_points[p1]; + auto& a2 = mapped_points[p2]; + a1 = ipts.Range(0, ipts.Size()-1); + a2 = ipts.Range(1, ipts.Size()); + nmapped = ipts.Size()-1; + } + + for(auto i : Range(nmapped)) + { + Element nel = el; + for(auto& pi : nel.PNums()) + if(mapped_points.count(pi)) + pi = mapped_points[pi][i]; + AddVolumeElement(nel); + } + if(nmapped) + volelements[ei].Delete(); + } + } + + Compress(); + SetNextMajorTimeStamp(); + } void Mesh :: RebuildSurfaceElementLists () { + static Timer t("Mesh::LinkSurfaceElements"); RegionTimer reg (t); + for (int i = 0; i < facedecoding.Size(); i++) facedecoding[i].firstelement = -1; for (int i = surfelements.Size()-1; i >= 0; i--) @@ -5513,25 +6463,17 @@ namespace netgen static int timer = NgProfiler::CreateTimer ("GetSurfaceElementsOfFace"); NgProfiler::RegionTimer reg (timer); - /* - sei.SetSize (0); - for (SurfaceElementIndex i = 0; i < GetNSE(); i++) - { - if ( (*this)[i].GetIndex () == facenr && (*this)[i][0] >= PointIndex::BASE && - !(*this)[i].IsDeleted() ) - { - sei.Append (i); - } - } - */ + if(facenr==0) + { + sei.SetSize(GetNSE()); + ParallelForRange( IntRange(GetNSE()), [&sei] (auto myrange) + { + for(auto i : myrange) + sei[i] = i; + }); + return; + } - /* Philippose - 01/10/2009 - Commented out the following lines, and activated the originally - commented out lines above because of a bug which causes corruption - of the variable "facedecoding" when a mesh is converted to second order - */ - - // int size1 = sei.Size(); sei.SetSize(0); SurfaceElementIndex si = facedecoding[facenr-1].firstelement; @@ -5545,16 +6487,6 @@ namespace netgen si = (*this)[si].next; } - - /* - // *testout << "with list = " << endl << sei << endl; - - if (size1 != sei.Size()) - { - cout << "size mismatch" << endl; - exit(1); - } - */ } @@ -5576,7 +6508,7 @@ namespace netgen if (el.GetType() != TET) { - VolumeElement(i).flags.badel = 0; + VolumeElement(i).Flags().badel = 0; continue; } @@ -5658,7 +6590,7 @@ namespace netgen } - VolumeElement(i).flags.badel = badel; + VolumeElement(i).Flags().badel = badel; if (badel) badtets++; } @@ -5691,10 +6623,18 @@ namespace netgen int Mesh :: MarkIllegalElements () { - int cnt = 0; - for (auto & el : VolumeElements()) - if (!LegalTet (el)) - cnt++; + if(!boundaryedges) + BuildBoundaryEdges(); + + atomic cnt = 0; + ParallelForRange( Range(volelements), [&] (auto myrange) + { + int cnt_local = 0; + for(auto & el : volelements.Range(myrange)) + if (!LegalTet (el)) + cnt_local++; + cnt += cnt_local; + }); return cnt; } @@ -5732,7 +6672,7 @@ namespace netgen // } - // void Mesh :: GetIdentificationMap (int identnr, Array & identmap) const + // void Mesh :: GetIdentificationMap (int identnr, NgArray & identmap) const // { // int i, j; @@ -5755,7 +6695,7 @@ namespace netgen // } - // void Mesh :: GetIdentificationPairs (int identnr, Array & identpairs) const + // void Mesh :: GetIdentificationPairs (int identnr, NgArray & identpairs) const // { // int i, j; @@ -5774,7 +6714,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 { @@ -5814,34 +6810,37 @@ namespace netgen void Mesh :: ComputeNVertices () { - int i, j, nv; - int ne = GetNE(); - // int nse = GetNSE(); numvertices = 0; - for (i = 1; i <= ne; i++) - { - const Element & el = VolumeElement(i); - nv = el.GetNV(); - for (j = 0; j < nv; j++) - if (el[j] > numvertices) - numvertices = el[j]; - } /* - for (i = 1; i <= nse; i++) - { - const Element2d & el = SurfaceElement(i); - nv = el.GetNV(); - for (j = 1; j <= nv; j++) - if (el.PNum(j) > numvertices) - numvertices = el.PNum(j); - } - */ - for (auto & el : SurfaceElements()) + for (const Element & el : VolumeElements()) + for (PointIndex v : el.Vertices()) + if (v > numvertices) numvertices = v; + + for (const Element2d & el : SurfaceElements()) for (PointIndex v : el.Vertices()) if (v > numvertices) numvertices = v; - numvertices += 1- PointIndex::BASE; + numvertices += 1-PointIndex::BASE; + */ + numvertices = 0; + numvertices = + ParallelReduce (VolumeElements().Size(), + [&](size_t nr) + { + return int(Max(VolumeElements()[nr].Vertices())); + }, + [](auto a, auto b) { return a > b ? a : b; }, + numvertices); + numvertices = + ParallelReduce (SurfaceElements().Size(), + [&](size_t nr) + { + return int(Max(SurfaceElements()[nr].Vertices())); + }, + [](auto a, auto b) { return a > b ? a : b; }, + numvertices); + numvertices += 1-PointIndex::BASE; } int Mesh :: GetNV () const @@ -5871,6 +6870,68 @@ namespace netgen } + Table Mesh :: CreatePoint2ElementTable(std::optional points, int domain) const + { + if(points) + { + const auto & free_points = *points; + return ngcore::CreateSortedTable( volelements.Range(), + [&](auto & table, ElementIndex ei) + { + const auto & el = (*this)[ei]; + if(el.IsDeleted()) + return; + + if(domain && el.GetIndex() != domain) + return; + + for (PointIndex pi : el.PNums()) + if(free_points[pi]) + table.Add (pi, ei); + }, GetNP()); + } + else + return ngcore::CreateSortedTable( volelements.Range(), + [&](auto & table, ElementIndex ei) + { + const auto & el = (*this)[ei]; + if(el.IsDeleted()) + return; + + if(domain && el.GetIndex() != domain) + return; + + for (PointIndex pi : el.PNums()) + table.Add (pi, ei); + }, GetNP()); + } + + Table Mesh :: CreatePoint2SurfaceElementTable( int faceindex ) const + { + static Timer timer("Mesh::CreatePoint2SurfaceElementTable"); RegionTimer rt(timer); + + if(faceindex==0) + { + return ngcore::CreateSortedTable( surfelements.Range(), + [&](auto & table, SurfaceElementIndex ei) + { + for (PointIndex pi : (*this)[ei].PNums()) + table.Add (pi, ei); + }, GetNP()); + } + + Array face_els; + GetSurfaceElementsOfFace(faceindex, face_els); + return ngcore::CreateSortedTable( face_els.Range(), + [&](auto & table, size_t i) + { + for (PointIndex pi : (*this)[face_els[i]].PNums()) + table.Add (pi, face_els[i]); + }, GetNP()); + } + + + /* void Mesh :: BuildConnectedNodes () { @@ -5995,12 +7056,13 @@ 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); + clusters->Update(); (*tracer)("call update clusters", true); #ifdef PARALLEL if (paralleltop) @@ -6009,6 +7071,7 @@ namespace netgen paralleltop->UpdateCoarseGrid(); } #endif + updateSignal.Emit(); } void Mesh :: BuildCurvedElements (const Refinement * ref, int aorder, bool arational) @@ -6083,14 +7146,11 @@ namespace netgen int oldsize = bcnames.Size(); bcnames.SetSize (bcnr+1); // keeps contents for (int i = oldsize; i <= bcnr; i++) - bcnames[i] = nullptr; + bcnames[i] = new string("default"); } if ( bcnames[bcnr] ) delete bcnames[bcnr]; - if ( abcname != "default" ) - bcnames[bcnr] = new string ( abcname ); - else - bcnames[bcnr] = nullptr; + bcnames[bcnr] = new string ( abcname ); for (auto & fd : facedecoding) if (fd.BCProperty() <= bcnames.Size()) @@ -6105,7 +7165,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]; @@ -6134,7 +7194,7 @@ namespace netgen cd2names[i] = nullptr; } //if (cd2names[cd2nr]) delete cd2names[cd2nr]; - if (abcname != "default") + if (abcname != "default" && abcname != "") cd2names[cd2nr] = new string(abcname); else cd2names[cd2nr] = nullptr; @@ -6182,7 +7242,16 @@ namespace netgen else cd3names[cd3nr] = nullptr; } - + + int Mesh :: AddCD3Name (const string & aname) + { + for (int i = 0; i < cd3names.Size(); i++) + if (*cd3names[i] == aname) + return i; + cd3names.Append (new string(aname)); + return cd3names.Size()-1; + } + string Mesh :: cd3_default_name = "default"; const string & Mesh :: GetCD3Name (int cd3nr) const { @@ -6199,16 +7268,31 @@ namespace netgen return defaultstring; } - void Mesh :: SetUserData(const char * id, Array & data) + + NgArray & Mesh :: GetRegionNamesCD (int codim) + { + switch (codim) + { + case 0: return materials; + case 1: return bcnames; + case 2: return cd2names; + case 3: return cd3names; + default: throw Exception("don't have regions of co-dimension "+ToString(codim)); + } + } + + + + void Mesh :: SetUserData(const char * id, NgArray & data) { if(userdata_int.Used(id)) delete userdata_int[id]; - Array * newdata = new Array(data); + NgArray * newdata = new NgArray(data); userdata_int.Set(id,newdata); } - bool Mesh :: GetUserData(const char * id, Array & data, int shift) const + bool Mesh :: GetUserData(const char * id, NgArray & data, int shift) const { if(userdata_int.Used(id)) { @@ -6224,16 +7308,16 @@ namespace netgen return false; } } - void Mesh :: SetUserData(const char * id, Array & data) + void Mesh :: SetUserData(const char * id, NgArray & data) { if(userdata_double.Used(id)) delete userdata_double[id]; - Array * newdata = new Array(data); + NgArray * newdata = new NgArray(data); userdata_double.Set(id,newdata); } - bool Mesh :: GetUserData(const char * id, Array & data, int shift) const + bool Mesh :: GetUserData(const char * id, NgArray & data, int shift) const { if(userdata_double.Used(id)) { @@ -6279,4 +7363,132 @@ 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); + */ + + Array point_map(GetNP()); + Array point_map1(GetNP()); + + nm.Points().SetSize(0); + + for(auto pi : Range(points)) + { + auto & p = m[pi]; + + auto v = p_plane-p; + auto l = v.Length(); + + if(l < eps || fabs(v*n_plane)/l < eps) + { + auto npi = nm.AddPoint(p, p.GetLayer(), p.Type()); + point_map[pi] = npi; + point_map1[pi] = npi; + } + else + { + auto new_point = p + 2*(v*n_plane)*n_plane; + point_map1[pi] = nm.AddPoint(p, p.GetLayer(), p.Type()); + point_map[pi] = nm.AddPoint( new_point, p.GetLayer(), p.Type() ); + } + } + + for(auto & el : nm.VolumeElements()) + for(auto i : Range(el.GetNP())) + el[i] = point_map1[el[i]]; + for(auto & el : nm.SurfaceElements()) + for(auto i : Range(el.GetNP())) + el[i] = point_map1[el[i]]; + for(auto & el : nm.LineSegments()) + for(auto i : Range(el.GetNP())) + el[i] = point_map1[el[i]]; + + 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)) + { + nel.Invert(); + 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); + } + + nm.ComputeNVertices(); + return nm_; + } + } diff --git a/libsrc/meshing/meshclass.hpp b/libsrc/meshing/meshclass.hpp index ecd4936a..22ac8960 100644 --- a/libsrc/meshing/meshclass.hpp +++ b/libsrc/meshing/meshclass.hpp @@ -11,22 +11,29 @@ The mesh class */ +#include + namespace netgen { + using namespace std; + + static constexpr int MPI_TAG_MESH = 210; + + enum resthtype { RESTRICTH_FACE, RESTRICTH_EDGE, RESTRICTH_SURFACEELEMENT, RESTRICTH_POINT, RESTRICTH_SEGMENT }; class HPRefElement; - - + class CurvedElements; + class AnisotropicClusters; + class ParallelMeshTopology; + /// 2d/3d mesh class Mesh { public: - typedef ::netgen::T_POINTS T_POINTS; - typedef Array T_VOLELEMENTS; - // typedef Array T_SURFELEMENTS; - typedef Array T_SURFELEMENTS; + // typedef Array T_POINTS; + typedef netgen::T_POINTS T_POINTS; private: /// point coordinates @@ -36,11 +43,11 @@ namespace netgen NgMPI_Comm comm; /// line-segments at edges - Array segments; + Array segments; /// surface elements, 2d-inner elements - T_SURFELEMENTS surfelements; + Array surfelements; /// volume elements - T_VOLELEMENTS volelements; + Array volelements; /// points will be fixed forever Array lockedpoints; @@ -48,29 +55,32 @@ namespace netgen /// surface indices at boundary nodes // TABLE surfacesonnode; /// boundary edges (1..normal bedge, 2..segment) - INDEX_2_CLOSED_HASHTABLE * boundaryedges; + unique_ptr> boundaryedges; /// - INDEX_2_CLOSED_HASHTABLE * segmentht; + unique_ptr> segmentht; /// - INDEX_3_CLOSED_HASHTABLE * surfelementht; + unique_ptr> surfelementht; + unique_ptr> illegal_trigs; /// faces of rest-solid - Array openelements; - /// open segmenets for surface meshing - Array opensegments; + NgArray openelements; + /// open segments for surface meshing + NgArray opensegments; + + Array tets_in_qualclass; /** - Representation of local mesh-size h + Representation of local mesh-size h (one function per mesh layer) */ - LocalH * lochfunc; + Array> lochfunc; /// double hglob; /// double hmin; /// - Array maxhdomain; + NgArray maxhdomain; /** the face-index of the surface element maps into @@ -83,39 +93,39 @@ namespace netgen the edge-index of the line element maps into this table. */ - Array edgedecoding; + NgArray edgedecoding; /// sub-domain materials - Array materials; + NgArray materials; /// labels for boundary conditions - Array bcnames; + NgArray bcnames; /// labels for co dim 2 bboundary conditions - Array cd2names; + NgArray cd2names; /// labels for co dim 3 bbboundary conditions - Array cd3names; + NgArray cd3names; /// Periodic surface, close surface, etc. identifications - Identifications * ident; + unique_ptr ident; /// number of vertices (if < 0, use np) int numvertices; /// geometric search tree for interval intersection search - BoxTree<3> * elementsearchtree; + unique_ptr> elementsearchtree; /// time stamp for tree mutable int elementsearchtreets; /// element -> face, element -> edge etc ... MeshTopology topology; /// methods for high order elements - class CurvedElements * curvedelems; + unique_ptr curvedelems; /// nodes identified by close points - class AnisotropicClusters * clusters; + unique_ptr clusters; /// space dimension (2 or 3) int dimension; @@ -125,18 +135,18 @@ namespace netgen /// changed after finishing global algorithm (improve, ...) int majortimestamp; - /// mesh access semaphors. + /// mesh access semaphores. NgMutex mutex; - /// mesh access semaphors. + /// mesh access semaphores. NgMutex majormutex; - SymbolTable< Array* > userdata_int; - SymbolTable< Array* > userdata_double; + SymbolTable< NgArray* > userdata_int; + SymbolTable< NgArray* > userdata_double; - mutable Array< Point3d > pointcurves; - mutable Array pointcurves_startpoint; - mutable Array pointcurves_red,pointcurves_green,pointcurves_blue; + mutable NgArray< Point3d > pointcurves; + mutable NgArray pointcurves_startpoint; + mutable NgArray pointcurves_red,pointcurves_green,pointcurves_blue; /// start element for point search (GetElementOfPoint) @@ -145,43 +155,45 @@ namespace netgen #ifdef PARALLEL /// connection to parallel meshes - class ParallelMeshTopology * paralleltop; - + unique_ptr paralleltop; #endif shared_ptr geometry; - private: - void BuildBoundaryEdges(void); public: - bool PointContainedIn2DElement(const Point3d & p, + DLL_HEADER void BuildBoundaryEdges(bool rebuild=true); + + DLL_HEADER bool PointContainedIn2DElement(const Point3d & p, double lami[3], const int element, bool consider3D = false) const; - bool PointContainedIn3DElement(const Point3d & p, + DLL_HEADER bool PointContainedIn3DElement(const Point3d & p, double lami[3], const int element) const; - bool PointContainedIn3DElementOld(const Point3d & p, + DLL_HEADER bool PointContainedIn3DElementOld(const Point3d & p, double lami[3], const int element) const; public: + Signal<> updateSignal; // store coarse mesh before hp-refinement - Array * hpelements; - Mesh * coarsemesh; + unique_ptr> hpelements; + unique_ptr coarsemesh; /// number of refinement levels - int mglevels; + // int mglevels; + // number of vertices on each refinement level: + NgArray level_nv; /// refinement hierarchy - Array,PointIndex::BASE> mlbetweennodes; + NgArray,PointIndex::BASE> mlbetweennodes; /// parent element of volume element - Array mlparentelement; + NgArray mlparentelement; /// parent element of surface element - Array mlparentsurfaceelement; + NgArray mlparentsurfaceelement; @@ -224,10 +236,18 @@ namespace netgen auto GetNP () const { return points.Size(); } // [[deprecated("Use Point(PointIndex) instead of int !")]] - MeshPoint & Point(int i) { return points.Elem(i); } + MeshPoint & Point(int i) + { + // return points.Elem(i); + return Point (PointIndex(i+PointIndex::BASE-1)); + } MeshPoint & Point(PointIndex pi) { return points[pi]; } // [[deprecated("Use Point(PointIndex) instead of int !")]] - const MeshPoint & Point(int i) const { return points.Get(i); } + const MeshPoint & Point(int i) const + { + // return points.Get(i); + return Point (PointIndex(i+PointIndex::BASE-1)); + } const MeshPoint & Point(PointIndex pi) const { return points[pi]; } const MeshPoint & operator[] (PointIndex pi) const { return points[pi]; } @@ -240,8 +260,8 @@ namespace netgen DLL_HEADER SegmentIndex AddSegment (const Segment & s); void DeleteSegment (int segnr) { - segments.Elem(segnr)[0].Invalidate(); - segments.Elem(segnr)[1].Invalidate(); + segments[segnr-1][0].Invalidate(); + segments[segnr-1][1].Invalidate(); } /* void FullDeleteSegment (int segnr) // von wem ist das ??? @@ -252,19 +272,15 @@ namespace netgen int GetNSeg () const { return segments.Size(); } // [[deprecated("Use LineSegment(SegmentIndex) instead of int !")]] - Segment & LineSegment(int i) { return segments.Elem(i); } + Segment & LineSegment(int i) { return segments[i-1]; } // [[deprecated("Use LineSegment(SegmentIndex) instead of int !")]] - const Segment & LineSegment(int i) const { return segments.Get(i); } + const Segment & LineSegment(int i) const { return segments[i-1]; } Segment & LineSegment(SegmentIndex si) { return segments[si]; } const Segment & LineSegment(SegmentIndex si) const { return segments[si]; } const Segment & operator[] (SegmentIndex si) const { return segments[si]; } Segment & operator[] (SegmentIndex si) { return segments[si]; } - /* - const Array & LineSegments() const { return segments; } - Array & LineSegments() { return segments; } - */ const auto & LineSegments() const { return segments; } auto & LineSegments() { return segments; } @@ -274,19 +290,35 @@ namespace netgen // write to pre-allocated container, thread-safe DLL_HEADER void SetSurfaceElement (SurfaceElementIndex sei, const Element2d & el); - // [[deprecated("Use DeleteSurfaceElement(SurfaceElementIndex) instead of int !")]] + [[deprecated("Use Delete(SurfaceElementIndex) instead of int !")]] void DeleteSurfaceElement (int eli) - { + { + /* surfelements.Elem(eli).Delete(); surfelements.Elem(eli).PNum(1).Invalidate(); surfelements.Elem(eli).PNum(2).Invalidate(); surfelements.Elem(eli).PNum(3).Invalidate(); + */ + surfelements[eli-1].Delete(); + /* + surfelements[eli-1].PNum(1).Invalidate(); + surfelements[eli-1].PNum(2).Invalidate(); + surfelements[eli-1].PNum(3).Invalidate(); + */ timestamp = NextTimeStamp(); } + [[deprecated("Use Delete(SurfaceElementIndex) instead !")]] void DeleteSurfaceElement (SurfaceElementIndex eli) { - for (auto & p : surfelements[eli].PNums()) p.Invalidate(); + // for (auto & p : surfelements[eli].PNums()) p.Invalidate(); + surfelements[eli].Delete(); + timestamp = NextTimeStamp(); + } + + void Delete (SurfaceElementIndex eli) + { + // for (auto & p : surfelements[eli].PNums()) p.Invalidate(); surfelements[eli].Delete(); timestamp = NextTimeStamp(); } @@ -294,10 +326,12 @@ namespace netgen auto GetNSE () const { return surfelements.Size(); } // [[deprecated("Use SurfaceElement(SurfaceElementIndex) instead of int !")]] - Element2d & SurfaceElement(int i) { return surfelements.Elem(i); } + Element2d & SurfaceElement(int i) { return surfelements[i-1]; } // [[deprecated("Use SurfaceElement(SurfaceElementIndex) instead of int !")]] - const Element2d & SurfaceElement(int i) const { return surfelements.Get(i); } + const Element2d & SurfaceElement(int i) const { return surfelements[i-1]; } + // [[deprecated("Use mesh[](SurfaceElementIndex) instead !")]] Element2d & SurfaceElement(SurfaceElementIndex i) { return surfelements[i]; } + // [[deprecated("Use mesh[](SurfaceElementIndex) instead !")]] const Element2d & SurfaceElement(SurfaceElementIndex i) const { return surfelements[i]; } const Element2d & operator[] (SurfaceElementIndex ei) const @@ -305,8 +339,8 @@ namespace netgen Element2d & operator[] (SurfaceElementIndex ei) { return surfelements[ei]; } - const T_SURFELEMENTS & SurfaceElements() const { return surfelements; } - T_SURFELEMENTS & SurfaceElements() { return surfelements; } + const auto & SurfaceElements() const { return surfelements; } + auto & SurfaceElements() { return surfelements; } DLL_HEADER void RebuildSurfaceElementLists (); @@ -319,21 +353,19 @@ namespace netgen auto GetNE () const { return volelements.Size(); } // [[deprecated("Use VolumeElement(ElementIndex) instead of int !")]] - Element & VolumeElement(int i) { return volelements.Elem(i); } + Element & VolumeElement(int i) { return volelements[i-1]; } // [[deprecated("Use VolumeElement(ElementIndex) instead of int !")]] - const Element & VolumeElement(int i) const { return volelements.Get(i); } + const Element & VolumeElement(int i) const { return volelements[i-1]; } + // [[deprecated("Use mesh[](VolumeElementIndex) instead !")]] Element & VolumeElement(ElementIndex i) { return volelements[i]; } + // [[deprecated("Use mesh[](VolumeElementIndex) instead !")]] const Element & VolumeElement(ElementIndex i) const { return volelements[i]; } - const Element & operator[] (ElementIndex ei) const - { return volelements[ei]; } - Element & operator[] (ElementIndex ei) - { return volelements[ei]; } - - + const Element & operator[] (ElementIndex ei) const { return volelements[ei]; } + Element & operator[] (ElementIndex ei) { return volelements[ei]; } ELEMENTTYPE ElementType (ElementIndex i) const - { return (volelements[i].flags.fixed) ? FIXEDELEMENT : FREEELEMENT; } + { return (volelements[i].Flags().fixed) ? FIXEDELEMENT : FREEELEMENT; } const auto & VolumeElements() const { return volelements; } auto & VolumeElements() { return volelements; } @@ -352,13 +384,13 @@ namespace netgen DLL_HEADER int GetNDomains() const; /// int GetDimension() const { return dimension; } - void SetDimension (int dim) { dimension = dim; } + DLL_HEADER void SetDimension (int dim); // { dimension = dim; } /// sets internal tables DLL_HEADER void CalcSurfacesOfNode (); /// additional (temporarily) fix points - void FixPoints (const BitArray & fixpoints); + void FixPoints (const NgBitArray & fixpoints); /** finds elements without neighbour and @@ -406,22 +438,22 @@ namespace netgen */ DLL_HEADER double AverageH (int surfnr = 0) const; /// Calculates localh - DLL_HEADER void CalcLocalH (double grading); + DLL_HEADER void CalcLocalH (double grading, int layer=1); /// - DLL_HEADER void SetLocalH (netgen::Point<3> pmin, netgen::Point<3> pmax, double grading); + DLL_HEADER void SetLocalH (netgen::Point<3> pmin, netgen::Point<3> pmax, double grading, int layer=1); /// - DLL_HEADER void RestrictLocalH (const Point3d & p, double hloc); + DLL_HEADER void RestrictLocalH (const Point3d & p, double hloc, int layer=1); /// DLL_HEADER void RestrictLocalHLine (const Point3d & p1, const Point3d & p2, - double hloc); + double hloc, int layer=1); /// number of elements per radius - DLL_HEADER void CalcLocalHFromSurfaceCurvature(double grading, double elperr); + DLL_HEADER void CalcLocalHFromSurfaceCurvature(double grading, double elperr, int layer=1); /// - DLL_HEADER void CalcLocalHFromPointDistances(double grading); + DLL_HEADER void CalcLocalHFromPointDistances(double grading, int layer=1); /// DLL_HEADER void RestrictLocalH (resthtype rht, int nr, double loch); /// - DLL_HEADER void LoadLocalMeshSize (const string & meshsizefilename); + DLL_HEADER void LoadLocalMeshSize (const filesystem::path & meshsizefilename); /// DLL_HEADER void SetGlobalH (double h); /// @@ -429,17 +461,27 @@ namespace netgen /// DLL_HEADER double MaxHDomain (int dom) const; /// - DLL_HEADER void SetMaxHDomain (const Array & mhd); + DLL_HEADER void SetMaxHDomain (const NgArray & mhd); /// - DLL_HEADER double GetH (const Point3d & p) const; + DLL_HEADER double GetH (const Point3d & p, int layer=1) const; + DLL_HEADER double GetH (PointIndex pi) const { return GetH(points[pi], points[pi].GetLayer()); } /// - double GetMinH (const Point3d & pmin, const Point3d & pmax); + double GetMinH (const Point3d & pmin, const Point3d & pmax, int layer=1); /// - bool HasLocalHFunction () { return lochfunc != nullptr; } + bool HasLocalHFunction (int layer=1) { return lochfunc[layer-1] != nullptr; } /// - LocalH & LocalHFunction () { return * lochfunc; } + LocalH & LocalHFunction (int layer=1) { return * lochfunc[layer-1]; } + + shared_ptr GetLocalH(int layer=1) const + { + if(lochfunc.Size() == 1) + return lochfunc[0]; + return lochfunc[layer-1]; + } + DLL_HEADER void SetLocalH(shared_ptr loch, int layer=1); + /// - bool LocalHFunctionGenerated(void) const { return (lochfunc != NULL); } + bool LocalHFunctionGenerated(int layer=1) const { return (lochfunc[layer-1] != NULL); } /// Find bounding box DLL_HEADER void GetBox (Point3d & pmin, Point3d & pmax, int dom = -1) const; @@ -455,6 +497,8 @@ namespace netgen { return openelements.Get(i); } auto & OpenElements() const { return openelements; } + + auto & OpenElements() { return openelements; } /// are also quads open elements bool HasOpenQuads () const; @@ -467,7 +511,8 @@ namespace netgen /// Refines mesh and projects points to true surface // void Refine (int levels, const CSGeometry * geom); - + + void ZRefine(const string& name, const Array& slices); bool BoundaryEdge (PointIndex pi1, PointIndex pi2) const { @@ -479,6 +524,11 @@ namespace netgen return boundaryedges->Used (i2); } + void DeleteBoundaryEdges () + { + boundaryedges = nullptr; + } + bool IsSegment (PointIndex pi1, PointIndex pi2) const { INDEX_2 i2 (pi1, pi2); @@ -509,11 +559,11 @@ namespace netgen /// DLL_HEADER void Merge (istream & infile, const int surfindex_offset = 0); /// - DLL_HEADER void Save (const string & filename) const; + DLL_HEADER void Save (const filesystem::path & filename) const; /// - DLL_HEADER void Load (const string & filename); + DLL_HEADER void Load (const filesystem::path & filename); /// - DLL_HEADER void Merge (const string & filename, const int surfindex_offset = 0); + DLL_HEADER void Merge (const filesystem::path & filename, const int surfindex_offset = 0); DLL_HEADER void DoArchive (Archive & archive); @@ -521,19 +571,23 @@ namespace netgen DLL_HEADER void ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal = OPT_QUALITY); /// - void ImproveMeshJacobian (const MeshingParameters & mp, OPTIMIZEGOAL goal = OPT_QUALITY, const BitArray * usepoint = NULL); + void ImproveMeshJacobian (const MeshingParameters & mp, OPTIMIZEGOAL goal = OPT_QUALITY, const NgBitArray * usepoint = NULL); /// void ImproveMeshJacobianOnSurface (const MeshingParameters & mp, - const BitArray & usepoint, - const Array< Vec<3>* > & nv, + const NgBitArray & usepoint, + const NgArray< Vec<3>* > & nv, OPTIMIZEGOAL goal = OPT_QUALITY, - const Array< Array* > * idmaps = NULL); + const NgArray< NgArray* > * idmaps = NULL); /** free nodes in environment of openelements for optimiztion */ void FreeOpenElementsEnvironment (int layers); + + DLL_HEADER double CalcTotalBad (const MeshingParameters & mp); + FlatArray GetQualityHistogram() { return tets_in_qualclass; } + /// bool LegalTet (Element & el) const { @@ -546,6 +600,10 @@ namespace netgen /// + // Find trigs with same vertices + // return: number of illegal trigs + int FindIllegalTrigs (); + bool LegalTrig (const Element2d & el) const; /** if values non-null, return values in 4-double array: @@ -573,30 +631,30 @@ namespace netgen void SetPointSearchStartElement(const int el) const {ps_startelement = el;} /// gives element of point, barycentric coordinates - int GetElementOfPoint (const netgen::Point<3> & p, + DLL_HEADER int GetElementOfPoint (const netgen::Point<3> & p, double * lami, bool build_searchtree = 0, const int index = -1, const bool allowindex = true) const; - int GetElementOfPoint (const netgen::Point<3> & p, + DLL_HEADER int GetElementOfPoint (const netgen::Point<3> & p, double * lami, - const Array * const indices, + const NgArray * const indices, bool build_searchtree = 0, const bool allowindex = true) const; - int GetSurfaceElementOfPoint (const netgen::Point<3> & p, + DLL_HEADER int GetSurfaceElementOfPoint (const netgen::Point<3> & p, double * lami, bool build_searchtree = 0, const int index = -1, const bool allowindex = true) const; - int GetSurfaceElementOfPoint (const netgen::Point<3> & p, + DLL_HEADER int GetSurfaceElementOfPoint (const netgen::Point<3> & p, double * lami, - const Array * const indices, + const NgArray * const indices, bool build_searchtree = 0, const bool allowindex = true) const; /// give list of vol elements which are int the box(p1,p2) void GetIntersectingVolEls(const Point3d& p1, const Point3d& p2, - Array & locels) const; + NgArray & locels) const; /// int AddFaceDescriptor(const FaceDescriptor& fd) @@ -605,7 +663,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); /// @@ -638,6 +696,7 @@ namespace netgen DLL_HEADER void SetNCD3Names (int ncd3n); DLL_HEADER void SetCD3Name (int cd3nr, const string & abcname); + DLL_HEADER int AddCD3Name (const string & aname); DLL_HEADER const string & GetCD3Name (int cd3nr ) const; DLL_HEADER static string cd3_default_name; @@ -652,6 +711,9 @@ namespace netgen string * GetBCNamePtr (int bcnr) const { return (bcnr < bcnames.Size() && bcnames[bcnr]) ? bcnames[bcnr] : &default_bc; } + + NgArray & GetRegionNamesCD (int codim); + /// void ClearFaceDescriptors() { facedecoding.SetSize(0); } @@ -660,16 +722,28 @@ namespace netgen int GetNFD () const { return facedecoding.Size(); } + const FaceDescriptor & GetFaceDescriptor (const Element2d & el) const + { return facedecoding[el.GetIndex()-1]; } + const FaceDescriptor & GetFaceDescriptor (int i) const - { return facedecoding.Get(i); } + { return facedecoding[i-1]; } + // { return facedecoding.Get(i); } + + auto & FaceDescriptors () const { return facedecoding; } const EdgeDescriptor & GetEdgeDescriptor (int i) const { return edgedecoding[i]; } /// - FaceDescriptor & GetFaceDescriptor (int i) - { return facedecoding.Elem(i); } + FaceDescriptor & GetFaceDescriptor (int i) + { return facedecoding[i-1]; } + // { return facedecoding.Elem(i); } + + int IdentifyPeriodicBoundaries(const string& s1, + const string& s2, + const Transformation<3>& mapping, + double pointTolerance); // #ifdef NONE // /* @@ -687,9 +761,9 @@ namespace netgen // } // /// - // void GetIdentificationMap (int identnr, Array & identmap) const; + // void GetIdentificationMap (int identnr, NgArray & identmap) const; // /// - // void GetIdentificationPairs (int identnr, Array & identpairs) const; + // void GetIdentificationPairs (int identnr, NgArray & identpairs) const; // /// // int GetMaxIdentificationNr () const // { @@ -704,35 +778,37 @@ namespace netgen /// bool HasIdentifications() const { return ident != nullptr; } - void InitPointCurve(double red = 1, double green = 0, double blue = 0) const; - void AddPointCurvePoint(const Point3d & pt) const; - int GetNumPointCurves(void) const; - int GetNumPointsOfPointCurve(int curve) const; - Point3d & GetPointCurvePoint(int curve, int n) const; - void GetPointCurveColor(int curve, double & red, double & green, double & blue) const; + DLL_HEADER void InitPointCurve(double red = 1, double green = 0, double blue = 0) const; + DLL_HEADER void AddPointCurvePoint(const Point3d & pt) const; + DLL_HEADER int GetNumPointCurves(void) const; + DLL_HEADER int GetNumPointsOfPointCurve(int curve) const; + DLL_HEADER Point3d & GetPointCurvePoint(int curve, int n) const; + DLL_HEADER void GetPointCurveColor(int curve, double & red, double & green, double & blue) const; /// find number of vertices - void ComputeNVertices (); + DLL_HEADER void ComputeNVertices (); /// number of vertices (no edge-midpoints) - int GetNV () const; + DLL_HEADER int GetNV () const; /// remove edge points - void SetNP (int np); + DLL_HEADER void SetNP (int np); + DLL_HEADER Table CreatePoint2ElementTable(std::optional points = std::nullopt, int domain = 0) const; + DLL_HEADER Table CreatePoint2SurfaceElementTable( int faceindex=0 ) const; DLL_HEADER bool PureTrigMesh (int faceindex = 0) const; 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; } @@ -751,7 +827,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) { @@ -765,8 +841,12 @@ namespace netgen void ReCalc () { area = 0; + /* for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) Add (mesh[sei]); + */ + for (const Element2d & el : mesh.SurfaceElements()) + Add (el); valid = true; } @@ -795,8 +875,9 @@ namespace netgen shared_ptr GetGeometry() const - { - return geometry; + { + static auto global_geometry = make_shared(); + return geometry ? geometry : global_geometry; } void SetGeometry (shared_ptr geom) { @@ -804,13 +885,13 @@ namespace netgen } /// - void SetUserData(const char * id, Array & data); + void SetUserData(const char * id, NgArray & data); /// - bool GetUserData(const char * id, Array & data, int shift = 0) const; + bool GetUserData(const char * id, NgArray & data, int shift = 0) const; /// - void SetUserData(const char * id, Array & data); + void SetUserData(const char * id, NgArray & data); /// - bool GetUserData(const char * id, Array & data, int shift = 0) const; + bool GetUserData(const char * id, NgArray & data, int shift = 0) const; /// friend void OptimizeRestart (Mesh & mesh3d); @@ -819,22 +900,20 @@ namespace netgen /// friend class Meshing3; - + // only for saving the geometry enum GEOM_TYPE { NO_GEOM = 0, GEOM_2D = 1, GEOM_CSG = 10, GEOM_STL = 11, GEOM_OCC = 12, GEOM_ACIS = 13 }; GEOM_TYPE geomtype; - #ifdef PARALLEL /// returns parallel topology class ParallelMeshTopology & GetParallelTopology () const { return *paralleltop; } - /// distributes the master-mesh to local meshes - void Distribute (); - void Distribute (Array & volume_weights, Array & surface_weights, - Array & segment_weights); + DLL_HEADER void Distribute (); + DLL_HEADER void Distribute (NgArray & volume_weights, NgArray & surface_weights, + NgArray & segment_weights); /// find connection to parallel meshes @@ -844,35 +923,47 @@ namespace netgen // void FindExchangeFaces (); /// use metis to decompose master mesh - void ParallelMetis (); // Array & neloc ); - void ParallelMetis (Array & volume_weights, Array & surface_weights, - Array & segment_weights); + DLL_HEADER void ParallelMetis (int nproc); // NgArray & neloc ); + DLL_HEADER void ParallelMetis (NgArray & volume_weights, NgArray & surface_weights, + NgArray & segment_weights); - void PartHybridMesh (); // Array & neloc ); - void PartDualHybridMesh (); // Array & neloc ); - void PartDualHybridMesh2D (); // ( Array & neloc ); + void PartHybridMesh (); // NgArray & neloc ); + void PartDualHybridMesh (); // NgArray & neloc ); + void PartDualHybridMesh2D (); // ( NgArray & neloc ); /// send mesh from master to local procs void SendRecvMesh (); /// send mesh to parallel machine, keep global mesh at master - void SendMesh ( ) const; // Mesh * mastermesh, Array & neloc) const; + void SendMesh ( ) const; // Mesh * mastermesh, NgArray & neloc) const; /// loads a mesh sent from master processor void ReceiveParallelMesh (); - Array vol_partition; - Array surf_partition; - Array seg_partition; #else + void ParallelMetis (int /* nproc */) {} void Distribute () {} void SendRecvMesh () {} - void Distribute (Array & volume_weights, Array & surface_weights, - Array & segment_weights){ } + void Distribute (NgArray & volume_weights, NgArray & surface_weights, + NgArray & segment_weights){ } #endif + NgArray vol_partition; + NgArray surf_partition; + NgArray seg_partition; + 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) @@ -882,6 +973,24 @@ namespace netgen return ost; } + + + FlatArray MeshTopology :: GetEdges (SurfaceElementIndex elnr) const + { + return FlatArray(GetNEdges ( (*mesh)[elnr].GetType()), &surfedges[elnr][0]); + } + + FlatArray MeshTopology :: GetEdges (ElementIndex elnr) const + { + return FlatArray(GetNEdges ( (*mesh)[elnr].GetType()), &edges[elnr][0]); + } + + FlatArray MeshTopology :: GetFaces (ElementIndex elnr) const + { + return FlatArray(GetNFaces ( (*mesh)[elnr].GetType()), &faces[elnr][0]); + } + + } #endif diff --git a/libsrc/meshing/meshfunc.cpp b/libsrc/meshing/meshfunc.cpp index e3fab9b8..a9a05f20 100644 --- a/libsrc/meshing/meshfunc.cpp +++ b/libsrc/meshing/meshfunc.cpp @@ -1,5 +1,8 @@ +#include + #include #include "meshing.hpp" +#include "debugging.hpp" namespace netgen { @@ -10,307 +13,612 @@ namespace netgen extern const char * pyramidrules2[]; extern const char * hexrules[]; + struct MeshingData + { + int domain; + + // mesh for one domain (contains all adjacent surface elements) + unique_ptr mesh; + + // maps from local (domain) mesh to global mesh + Array pmap; + + // Array connected_pairs; + + MeshingParameters mp; + + unique_ptr meshing; + }; + + // extract surface meshes belonging to individual domains + Array DivideMesh(Mesh & mesh, const MeshingParameters & mp) + { + static Timer timer("DivideMesh"); RegionTimer rt(timer); + + Array ret; + auto num_domains = mesh.GetNDomains(); + + if(num_domains==1 || mp.only3D_domain_nr) + { + ret.SetSize(1); + // no need to divide mesh, just fill in meshing data + ret[0].domain = 1; + if(mp.only3D_domain_nr) + ret[0].domain = mp.only3D_domain_nr; + + ret[0].mesh.reset(&mesh); // careful, this unique_ptr must not delete &mesh! (it will be released in MergeMeshes after meshing) + ret[0].mp = mp; + return ret; + } + ret.SetSize(num_domains); + + Array> ipmap; + ipmap.SetSize(num_domains); + auto dim = mesh.GetDimension(); + auto num_points = mesh.GetNP(); + auto num_facedescriptors = mesh.GetNFD(); + + for(auto i : Range(ret)) + { + auto & md = ret[i]; + md.domain = i+1; + + md.mp = mp; + md.mp.maxh = min2 (mp.maxh, mesh.MaxHDomain(md.domain)); + + ret[i].mesh = make_unique(); + auto & m = *ret[i].mesh; + + m.SetLocalH(mesh.GetLocalH()); + + ipmap[i].SetSize(num_points); + ipmap[i] = PointIndex::INVALID; + m.SetDimension( mesh.GetDimension() ); + m.SetGeometry( mesh.GetGeometry() ); + + for(auto i : Range(1, num_facedescriptors+1)) + m.AddFaceDescriptor( mesh.GetFaceDescriptor(i) ); + } + + // mark used points for each domain, add surface elements (with wrong point numbers) to domain mesh + for(const auto & sel : mesh.SurfaceElements()) + { + const auto & fd = mesh.GetFaceDescriptor(sel.GetIndex()); + int dom_in = fd.DomainIn(); + int dom_out = fd.DomainOut(); + + for( auto dom : {dom_in, dom_out} ) + { + if(dom==0) + continue; + + auto & sels = ret[dom-1].mesh->SurfaceElements(); + for(auto pi : sel.PNums()) + ipmap[dom-1][pi] = 1; + sels.Append(sel); + } + } + + // mark used points for already existing volume elements, add them (with wrong point numbers) to domain mesh + for(const auto & el : mesh.VolumeElements()) + { + auto dom = el.GetIndex(); + + auto & els = ret[dom-1].mesh->VolumeElements(); + for(auto pi : el.PNums()) + ipmap[dom-1][pi] = 1; + els.Append(el); + } + + // mark locked/fixed points for each domain TODO: domain bounding box to add only relevant points? + for(auto pi : mesh.LockedPoints()) + for(auto i : Range(ret)) + ipmap[i][pi] = 2; + + // add used points to domain mesh, build point mapping + for(auto i : Range(ret)) + { + auto & m = *ret[i].mesh; + auto & pmap = ret[i].pmap; + + for(auto pi : Range(ipmap[i])) + if(ipmap[i][pi]) + { + const auto& mp = mesh[pi]; + auto pi_new = m.AddPoint( mp, mp.GetLayer(), mp.Type() ); + if(ipmap[i][pi] == 2) + mesh.AddLockedPoint(pi_new); + ipmap[i][pi] = pi_new; + pmap.Append( pi ); + } + } + + // add segments + for(auto i : Range(ret)) + { + auto & imap = ipmap[i]; + auto & m = *ret[i].mesh; + for(auto seg : mesh.LineSegments()) + if(imap[seg[0]].IsValid() && imap[seg[1]].IsValid()) + { + seg[0] = imap[seg[0]]; + seg[1] = imap[seg[1]]; + m.AddSegment(seg); + } + } + + auto & identifications = mesh.GetIdentifications(); + + for(auto i : Range(ret)) + { + auto & m = *ret[i].mesh; + auto & imap = ipmap[i]; + auto nmax = identifications.GetMaxNr (); + auto & m_ident = m.GetIdentifications(); + + for (auto & sel : m.SurfaceElements()) + for(auto & pi : sel.PNums()) + pi = imap[pi]; + + for (auto & el : m.VolumeElements()) + for(auto & pi : el.PNums()) + pi = imap[pi]; + + for(auto n : Range(1,nmax+1)) + { + NgArray pairs; + identifications.GetPairs(n, pairs); + + for(auto pair : pairs) + { + auto pi0 = imap[pair[0]]; + auto pi1 = imap[pair[1]]; + if(!pi0.IsValid() || !pi1.IsValid()) + continue; + + m_ident.Add(pi0, pi1, n); + } + m_ident.SetType( n, identifications.GetType(n) ); + } + } + return ret; + } + + // Add between identified surface elements (only consider closesurface identifications) + void FillCloseSurface( MeshingData & md) + { + static Timer timer("FillCloseSurface"); RegionTimer rtimer(timer); + + auto & mesh = *md.mesh; + auto & identifications = mesh.GetIdentifications(); + auto nmax = identifications.GetMaxNr(); + + bool have_closesurfaces = false; + for(auto i : Range(1,nmax+1)) + if(identifications.GetType(i) == Identifications::CLOSESURFACES) + have_closesurfaces = true; + if(!have_closesurfaces) + return; + + NgArray map; + std::set> hex_faces; + for(auto identnr : Range(1,nmax+1)) + { + if(identifications.GetType(identnr) != Identifications::CLOSESURFACES) + continue; + + identifications.GetMap(identnr, map); + mesh.FindOpenElements(md.domain); + + for(auto & sel : mesh.OpenElements()) + { + // For quads: check if this open element is already closed by a hex + // this happens when we have identifications in two directions + if(sel.GetNP() == 4) + { + Element2d face = sel; + face.NormalizeNumbering(); + if(hex_faces.count({face[0], face[1], face[2]})) + continue; + } + bool is_mapped = true; + for(auto pi : sel.PNums()) + if(!PointIndex(map[pi]).IsValid()) + is_mapped = false; + + if(!is_mapped) + continue; + + // insert prism/hex + auto np = sel.GetNP(); + Element el(2*np); + std::set pis; + for(auto i : Range(np)) + { + el[i] = sel[i]; + el[i+np] = map[sel[i]]; + pis.insert(sel[i]); + pis.insert(map[sel[i]]); + } + + // degenerate element (mapped element onto itself, might happen for surface elements connecting two identified faces) + if(pis.size() < 2*np) + continue; + + // check if new element is inside current domain + auto p0 = mesh[sel[0]]; + Vec<3> n = -Cross(mesh[sel[1]] - p0, mesh[sel[2]] - p0 ); + + if(n*(mesh[el[np]]-p0) < 0.0) + continue; + + el.SetIndex(md.domain); + mesh.AddVolumeElement(el); + if(el.NP()==8) + { + // remember all adjacent faces of the new hex (to skip corresponding openelements accordingly) + for(auto facei : Range(1,7)) + { + Element2d face; + el.GetFace(facei, face); + face.NormalizeNumbering(); + hex_faces.insert({face[0], face[1], face[2]}); + } + } + } + } + } + + void CloseOpenQuads( MeshingData & md) + { + static Timer t("CloseOpenQuads"); RegionTimer rt(t); + auto & mesh = *md.mesh; + auto domain = md.domain; + MeshingParameters & mp = md.mp; + + int oldne; + if (multithread.terminate) + return; + + mesh.CalcSurfacesOfNode(); + mesh.FindOpenElements(domain); + + if (!mesh.GetNOpenElements()) + return; + + for (int qstep = 0; qstep <= 3; qstep++) + { + if (qstep == 0 && !mp.try_hexes) continue; + + if (mesh.HasOpenQuads()) + { + string rulefile = ngdir; + + const char ** rulep = NULL; + switch (qstep) + { + case 0: + rulep = hexrules; + break; + case 1: + rulep = prismrules2; + break; + case 2: // connect pyramid to triangle + rulep = pyramidrules2; + break; + case 3: // connect to vis-a-vis point + rulep = pyramidrules; + break; + } + + Meshing3 meshing(rulep); + + MeshingParameters mpquad = mp; + + mpquad.giveuptol = mp.giveuptolopenquads; + mpquad.baseelnp = 4; + mpquad.starshapeclass = 1000; + mpquad.check_impossible = qstep == 1; // for prisms only (air domain in trafo) + + + for (PointIndex pi : mesh.Points().Range()) + meshing.AddPoint (mesh[pi], pi); + + NgArray connectednodes; + for (int nr = 1; nr <= mesh.GetIdentifications().GetMaxNr(); nr++) + if (mesh.GetIdentifications().GetType(nr) != Identifications::PERIODIC) + { + mesh.GetIdentifications().GetPairs (nr, connectednodes); + for (auto pair : connectednodes) + { + meshing.AddConnectedPair (pair); + meshing.AddConnectedPair ({pair[1], pair[0]}); + } + } + + for (int i = 1; i <= mesh.GetNOpenElements(); i++) + { + Element2d hel = mesh.OpenElement(i); + meshing.AddBoundaryElement (hel); + } + + oldne = mesh.GetNE(); + + meshing.GenerateMesh (mesh, mpquad); + + for (int i = oldne + 1; i <= mesh.GetNE(); i++) + mesh.VolumeElement(i).SetIndex (domain); + + (*testout) + << "mesh has " << mesh.GetNE() << " prism/pyramid elements" << endl; + + mesh.FindOpenElements(domain); + } + } + + + if (mesh.HasOpenQuads()) + { + if(debugparam.write_mesh_on_error) + md.mesh->Save("open_quads_"+ToString(md.domain)+".vol.gz"); + PrintSysError ("mesh has still open quads"); + throw NgException ("Stop meshing since too many attempts"); + // return MESHING3_GIVEUP; + } + } + + + void MeshDomain( MeshingData & md) + { + auto & mesh = *md.mesh; + auto domain = md.domain; + MeshingParameters & mp = md.mp; + + mesh.CalcSurfacesOfNode(); + mesh.FindOpenElements(md.domain); + + md.meshing = make_unique(nullptr); + for (PointIndex pi : mesh.Points().Range()) + md.meshing->AddPoint (mesh[pi], pi); + + for (int i = 1; i <= mesh.GetNOpenElements(); i++) + md.meshing->AddBoundaryElement (mesh.OpenElement(i)); + + if (mp.delaunay && mesh.GetNOpenElements()) + { + int oldne = mesh.GetNE(); + + md.meshing->Delaunay (mesh, domain, mp); + + for (int i = oldne + 1; i <= mesh.GetNE(); i++) + mesh.VolumeElement(i).SetIndex (domain); + + PrintMessage (3, mesh.GetNP(), " points, ", + mesh.GetNE(), " elements"); + mesh.FindOpenElements(domain); + } + + Box<3> domain_bbox( Box<3>::EMPTY_BOX ); + + for (auto & sel : mesh.SurfaceElements()) + { + if (sel.IsDeleted() ) continue; + + for (auto pi : sel.PNums()) + domain_bbox.Add (mesh[pi]); + } + domain_bbox.Increase (0.01 * domain_bbox.Diam()); + + int cntsteps = 0; + int meshed; + if (mesh.GetNOpenElements()) + do + { + if (multithread.terminate) + break; + + mesh.FindOpenElements(domain); + PrintMessage (5, mesh.GetNOpenElements(), " open faces"); + // GetOpenElements( mesh, domain )->Save("open_"+ToString(cntsteps)+".vol"); + cntsteps++; + + + if (cntsteps > mp.maxoutersteps) + { + if(debugparam.write_mesh_on_error) + md.mesh->Save("meshing_error_domain_"+ToString(md.domain)+".vol.gz"); + throw NgException ("Stop meshing since too many attempts in domain " + ToString(md.domain)); + } + + PrintMessage (1, "start tetmeshing"); + + Meshing3 meshing(tetrules); + + Array glob2loc(mesh.GetNP()); + glob2loc = PointIndex::INVALID; + + for (PointIndex pi : mesh.Points().Range()) + if (domain_bbox.IsIn (mesh[pi])) + glob2loc[pi] = meshing.AddPoint (mesh[pi], pi); + + for (auto sel : mesh.OpenElements() ) + { + for(auto & pi : sel.PNums()) + pi = glob2loc[pi]; + meshing.AddBoundaryElement (sel); + } + + int oldne = mesh.GetNE(); + + mp.giveuptol = 15 + 10 * cntsteps; + mp.sloppy = 5; + meshing.GenerateMesh (mesh, mp); + + for (ElementIndex ei = oldne; ei < mesh.GetNE(); ei++) + mesh[ei].SetIndex (domain); + + + mesh.CalcSurfacesOfNode(); + mesh.FindOpenElements(domain); + + // teterrpow = 2; + if (mesh.GetNOpenElements() != 0) + { + meshed = 0; + PrintMessage (5, mesh.GetNOpenElements(), " open faces found"); + + MeshOptimize3d optmesh(mp); + + const char * optstr = "mcmstmcmstmcmstmcm"; + for (size_t j = 1; j <= strlen(optstr); j++) + { + mesh.FindOpenElements(); + mesh.CalcSurfacesOfNode(); + mesh.FreeOpenElementsEnvironment(2); + mesh.CalcSurfacesOfNode(); + + switch (optstr[j-1]) + { + case 'c': optmesh.CombineImprove(mesh, OPT_REST); break; + case 'd': optmesh.SplitImprove(mesh, OPT_REST); break; + case 's': optmesh.SwapImprove(mesh, OPT_REST); break; + case 't': optmesh.SwapImprove2(mesh, OPT_REST); break; + case 'm': mesh.ImproveMesh(mp, OPT_REST); break; + } + + } + + mesh.FindOpenElements(domain); + PrintMessage (3, "Call remove problem"); + RemoveProblem (mesh, domain); + mesh.FindOpenElements(domain); + } + else + { + meshed = 1; + PrintMessage (1, "Success !"); + } + } + while (!meshed); + + { + PrintMessage (3, "Check subdomain ", domain, " / ", mesh.GetNDomains()); + + mesh.FindOpenElements(domain); + + bool res = (mesh.CheckConsistentBoundary() != 0); + if (res) + { + if(debugparam.write_mesh_on_error) + md.mesh->Save("inconsistent_surface_domain_"+ToString(md.domain)+".vol.gz"); + PrintError ("Surface mesh not consistent"); + throw NgException ("Stop meshing since surface mesh not consistent"); + } + } + } + + void MergeMeshes( Mesh & mesh, Array & md ) + { + // todo: optimize: count elements, alloc all memory, copy vol elements in parallel + static Timer t("MergeMeshes"); RegionTimer rt(t); + if(md.Size()==1) + { + // assume that mesh was never divided, no need to do anything + if(&mesh != md[0].mesh.get()) + throw Exception("Illegal Mesh pointer in MeshingData"); + + md[0].mesh.release(); + return; + } + + mesh.VolumeElements().DeleteAll(); + for(auto & m_ : md) + { + auto first_new_pi = m_.pmap.Range().Next(); + auto & m = *m_.mesh; + Array pmap(m.Points().Size()); + for(auto pi : Range(PointIndex(PointIndex::BASE), first_new_pi)) + pmap[pi] = m_.pmap[pi]; + + for (auto pi : Range(first_new_pi, m.Points().Range().Next())) + pmap[pi] = mesh.AddPoint(m[pi]); + + + for ( auto el : m.VolumeElements() ) + { + for (auto i : Range(el.GetNP())) + el[i] = pmap[el[i]]; + el.SetIndex(m_.domain); + mesh.AddVolumeElement(el); + } + } + } + + void MergeMeshes( Mesh & mesh, FlatArray meshes, PointIndex first_new_pi ) + { + // todo: optimize: count elements, alloc all memory, copy vol elements in parallel + static Timer t("MergeMeshes"); RegionTimer rt(t); + for(auto & m : meshes) + { + Array pmap(m.Points().Size()); + for(auto pi : Range(PointIndex(PointIndex::BASE), first_new_pi)) + pmap[pi] = pi; + + for (auto pi : Range(first_new_pi, m.Points().Range().Next())) + pmap[pi] = mesh.AddPoint(m[pi]); + + + for ( auto el : m.VolumeElements() ) + { + for (auto i : Range(el.GetNP())) + el[i] = pmap[el[i]]; + mesh.AddVolumeElement(el); + } + } + } // extern double teterrpow; - MESHING3_RESULT MeshVolume (MeshingParameters & mp, Mesh& mesh3d) + MESHING3_RESULT MeshVolume (const MeshingParameters & mp, Mesh& mesh3d) { static Timer t("MeshVolume"); RegionTimer reg(t); - - int oldne; - int meshed; - - Array connectednodes; - - if (!mesh3d.HasLocalHFunction()) mesh3d.CalcLocalH(mp.grading); mesh3d.Compress(); - // mesh3d.PrintMemInfo (cout); - if (mp.checkoverlappingboundary) - if (mesh3d.CheckOverlappingBoundary()) - throw NgException ("Stop meshing since boundary mesh is overlapping"); - int nonconsist = 0; - for (int k = 1; k <= mesh3d.GetNDomains(); k++) - { - if(mp.only3D_domain_nr && mp.only3D_domain_nr !=k) - continue; - PrintMessage (3, "Check subdomain ", k, " / ", mesh3d.GetNDomains()); + if(mesh3d.GetNDomains()==0) + return MESHING3_OK; - mesh3d.FindOpenElements(k); + if (!mesh3d.HasLocalHFunction()) + mesh3d.CalcLocalH(mp.grading); - /* - bool res = mesh3d.CheckOverlappingBoundary(); - if (res) - { - PrintError ("Surface is overlapping !!"); - nonconsist = 1; - } - */ + auto md = DivideMesh(mesh3d, mp); - bool res = (mesh3d.CheckConsistentBoundary() != 0); - if (res) - { - PrintError ("Surface mesh not consistent"); - nonconsist = 1; - } - } - - if (nonconsist) - { - PrintError ("Stop meshing since surface mesh not consistent"); - throw NgException ("Stop meshing since surface mesh not consistent"); - } - - double globmaxh = mp.maxh; - - for (int k = 1; k <= mesh3d.GetNDomains(); k++) + try { - if(mp.only3D_domain_nr && mp.only3D_domain_nr !=k) - continue; - if (multithread.terminate) - break; - - PrintMessage (2, ""); - PrintMessage (1, "Meshing subdomain ", k, " of ", mesh3d.GetNDomains()); - (*testout) << "Meshing subdomain " << k << endl; - - mp.maxh = min2 (globmaxh, mesh3d.MaxHDomain(k)); + ParallelFor( md.Range(), [&](int i) + { + if (mp.checkoverlappingboundary) + if (md[i].mesh->CheckOverlappingBoundary()) + { + if(debugparam.write_mesh_on_error) + md[i].mesh->Save("overlapping_mesh_domain_"+ToString(md[i].domain)+".vol.gz"); + throw NgException ("Stop meshing since boundary mesh is overlapping"); + } - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(k); - - if (!mesh3d.GetNOpenElements()) - continue; - - - - Box<3> domain_bbox( Box<3>::EMPTY_BOX ); - - for (SurfaceElementIndex sei = 0; sei < mesh3d.GetNSE(); sei++) - { - const Element2d & el = mesh3d[sei]; - if (el.IsDeleted() ) continue; - - if (mesh3d.GetFaceDescriptor(el.GetIndex()).DomainIn() == k || - mesh3d.GetFaceDescriptor(el.GetIndex()).DomainOut() == k) - - for (int j = 0; j < el.GetNP(); j++) - domain_bbox.Add (mesh3d[el[j]]); - } - domain_bbox.Increase (0.01 * domain_bbox.Diam()); - - - for (int qstep = 0; qstep <= 3; qstep++) - // for (int qstep = 0; qstep <= 0; qstep++) // for hex-filling - { - if (qstep == 0 && !mp.try_hexes) continue; - - // cout << "openquads = " << mesh3d.HasOpenQuads() << endl; - if (mesh3d.HasOpenQuads()) - { - string rulefile = ngdir; - - const char ** rulep = NULL; - switch (qstep) - { - case 0: - rulefile = "/Users/joachim/gitlab/netgen/rules/hexa.rls"; - rulep = hexrules; - break; - case 1: - rulefile += "/rules/prisms2.rls"; - rulep = prismrules2; - break; - case 2: // connect pyramid to triangle - rulefile += "/rules/pyramids2.rls"; - rulep = pyramidrules2; - break; - case 3: // connect to vis-a-vis point - rulefile += "/rules/pyramids.rls"; - rulep = pyramidrules; - break; - } - - // Meshing3 meshing(rulefile); - Meshing3 meshing(rulep); - - MeshingParameters mpquad = mp; - - mpquad.giveuptol = 15; - mpquad.baseelnp = 4; - mpquad.starshapeclass = 1000; - mpquad.check_impossible = qstep == 1; // for prisms only (air domain in trafo) - - - for (PointIndex pi = mesh3d.Points().Begin(); pi < mesh3d.Points().End(); pi++) - meshing.AddPoint (mesh3d[pi], pi); - - /* - mesh3d.GetIdentifications().GetPairs (0, connectednodes); - for (int i = 1; i <= connectednodes.Size(); i++) - meshing.AddConnectedPair (connectednodes.Get(i)); - */ - for (int nr = 1; nr <= mesh3d.GetIdentifications().GetMaxNr(); nr++) - if (mesh3d.GetIdentifications().GetType(nr) != Identifications::PERIODIC) - { - mesh3d.GetIdentifications().GetPairs (nr, connectednodes); - for (auto pair : connectednodes) - meshing.AddConnectedPair (pair); - } - - for (int i = 1; i <= mesh3d.GetNOpenElements(); i++) - { - Element2d hel = mesh3d.OpenElement(i); - meshing.AddBoundaryElement (hel); - } - - oldne = mesh3d.GetNE(); - - meshing.GenerateMesh (mesh3d, mpquad); - - for (int i = oldne + 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (k); - - (*testout) - << "mesh has " << mesh3d.GetNE() << " prism/pyramid elements" << endl; - - mesh3d.FindOpenElements(k); - } - } - - - if (mesh3d.HasOpenQuads()) - { - PrintSysError ("mesh has still open quads"); - throw NgException ("Stop meshing since too many attempts"); - // return MESHING3_GIVEUP; - } - - - if (mp.delaunay && mesh3d.GetNOpenElements()) - { - Meshing3 meshing((const char**)NULL); - - mesh3d.FindOpenElements(k); - - /* - for (PointIndex pi = mesh3d.Points().Begin(); pi < mesh3d.Points().End(); pi++) - meshing.AddPoint (mesh3d[pi], pi); - */ - for (PointIndex pi : mesh3d.Points().Range()) - meshing.AddPoint (mesh3d[pi], pi); - - for (int i = 1; i <= mesh3d.GetNOpenElements(); i++) - meshing.AddBoundaryElement (mesh3d.OpenElement(i)); - - oldne = mesh3d.GetNE(); - - meshing.Delaunay (mesh3d, k, mp); - - for (int i = oldne + 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (k); - - PrintMessage (3, mesh3d.GetNP(), " points, ", - mesh3d.GetNE(), " elements"); - } - - - int cntsteps = 0; - if (mesh3d.GetNOpenElements()) - do - { - if (multithread.terminate) - break; - - mesh3d.FindOpenElements(k); - PrintMessage (5, mesh3d.GetNOpenElements(), " open faces"); - cntsteps++; - - if (cntsteps > mp.maxoutersteps) - throw NgException ("Stop meshing since too many attempts"); - - string rulefile = ngdir + "/tetra.rls"; - PrintMessage (1, "start tetmeshing"); - - // Meshing3 meshing(rulefile); - Meshing3 meshing(tetrules); - - Array glob2loc(mesh3d.GetNP()); - glob2loc = -1; - - for (PointIndex pi = mesh3d.Points().Begin(); pi < mesh3d.Points().End(); pi++) - if (domain_bbox.IsIn (mesh3d[pi])) - glob2loc[pi] = - meshing.AddPoint (mesh3d[pi], pi); - - for (int i = 1; i <= mesh3d.GetNOpenElements(); i++) - { - Element2d hel = mesh3d.OpenElement(i); - for (int j = 0; j < hel.GetNP(); j++) - hel[j] = glob2loc[hel[j]]; - meshing.AddBoundaryElement (hel); - // meshing.AddBoundaryElement (mesh3d.OpenElement(i)); - } - - oldne = mesh3d.GetNE(); - - mp.giveuptol = 15 + 10 * cntsteps; - mp.sloppy = 5; - meshing.GenerateMesh (mesh3d, mp); - - for (ElementIndex ei = oldne; ei < mesh3d.GetNE(); ei++) - mesh3d[ei].SetIndex (k); - - - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(k); - - // teterrpow = 2; - if (mesh3d.GetNOpenElements() != 0) - { - meshed = 0; - PrintMessage (5, mesh3d.GetNOpenElements(), " open faces found"); - - MeshOptimize3d optmesh(mp); - - const char * optstr = "mcmstmcmstmcmstmcm"; - for (size_t j = 1; j <= strlen(optstr); j++) - { - mesh3d.CalcSurfacesOfNode(); - mesh3d.FreeOpenElementsEnvironment(2); - mesh3d.CalcSurfacesOfNode(); - - switch (optstr[j-1]) - { - case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break; - case 'd': optmesh.SplitImprove(mesh3d, OPT_REST); break; - case 's': optmesh.SwapImprove(mesh3d, OPT_REST); break; - case 't': optmesh.SwapImprove2(mesh3d, OPT_REST); break; - case 'm': mesh3d.ImproveMesh(mp, OPT_REST); break; - } - - } - - mesh3d.FindOpenElements(k); - PrintMessage (3, "Call remove problem"); - RemoveProblem (mesh3d, k); - mesh3d.FindOpenElements(k); - } - else - { - meshed = 1; - PrintMessage (1, "Success !"); - } - } - while (!meshed); - - PrintMessage (1, mesh3d.GetNP(), " points, ", - mesh3d.GetNE(), " elements"); + if(md[i].mesh->GetGeometry()->GetGeomType() == Mesh::GEOM_OCC) + FillCloseSurface( md[i] ); + CloseOpenQuads( md[i] ); + MeshDomain(md[i]); + }, md.Size()); } - - mp.maxh = globmaxh; + catch(...) + { + MergeMeshes(mesh3d, md); + return MESHING3_GIVEUP; + } + + MergeMeshes(mesh3d, md); MeshQuality3d (mesh3d); @@ -318,330 +626,14 @@ namespace netgen } - - - /* - - - MESHING3_RESULT MeshVolumeOld (MeshingParameters & mp, Mesh& mesh3d) - { - int i, k, oldne; - - - int meshed; - int cntsteps; - - - PlotStatistics3d * pstat; - if (globflags.GetNumFlag("silentflag", 1) <= 2) - pstat = new XPlotStatistics3d; - else - pstat = new TerminalPlotStatistics3d; - - cntsteps = 0; - do - { - cntsteps++; - if (cntsteps > mp.maxoutersteps) - { - return MESHING3_OUTERSTEPSEXCEEDED; - } - - - int noldp = mesh3d.GetNP(); - - - if ( (cntsteps == 1) && globflags.GetDefineFlag ("delaunay")) - { - cntsteps ++; - - mesh3d.CalcSurfacesOfNode(); - - - for (k = 1; k <= mesh3d.GetNDomains(); k++) - { - Meshing3 meshing(NULL, pstat); - - mesh3d.FindOpenElements(k); - - for (i = 1; i <= noldp; i++) - meshing.AddPoint (mesh3d.Point(i), i); - - for (i = 1; i <= mesh3d.GetNOpenElements(); i++) - { - if (mesh3d.OpenElement(i).GetIndex() == k) - meshing.AddBoundaryElement (mesh3d.OpenElement(i)); - } - - oldne = mesh3d.GetNE(); - if (globflags.GetDefineFlag ("blockfill")) - { - if (!globflags.GetDefineFlag ("localh")) - meshing.BlockFill - (mesh3d, mp.h * globflags.GetNumFlag ("relblockfillh", 1)); - else - meshing.BlockFillLocalH (mesh3d); - } - - MeshingParameters mpd; - meshing.Delaunay (mesh3d, mpd); - - for (i = oldne + 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (k); - } - } - - noldp = mesh3d.GetNP(); - - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(); - for (k = 1; k <= mesh3d.GetNDomains(); k++) - { - Meshing3 meshing(globflags.GetStringFlag ("rules3d", NULL), pstat); - - Point3d pmin, pmax; - mesh3d.GetBox (pmin, pmax, k); - - rot.SetCenter (Center (pmin, pmax)); - - for (i = 1; i <= noldp; i++) - meshing.AddPoint (mesh3d.Point(i), i); - - for (i = 1; i <= mesh3d.GetNOpenElements(); i++) - { - if (mesh3d.OpenElement(i).GetIndex() == k) - meshing.AddBoundaryElement (mesh3d.OpenElement(i)); - } - - oldne = mesh3d.GetNE(); - - - if ( (cntsteps == 1) && globflags.GetDefineFlag ("blockfill")) - { - if (!globflags.GetDefineFlag ("localh")) - { - meshing.BlockFill - (mesh3d, - mp.h * globflags.GetNumFlag ("relblockfillh", 1)); - } - else - { - meshing.BlockFillLocalH (mesh3d); - } - } - - - mp.giveuptol = int(globflags.GetNumFlag ("giveuptol", 15)); - - meshing.GenerateMesh (mesh3d, mp); - - for (i = oldne + 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (k); - } - - - - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(); - - teterrpow = 2; - if (mesh3d.GetNOpenElements() != 0) - { - meshed = 0; - (*mycout) << "Open elements found, old" << endl; - const char * optstr = "mcmcmcmcm"; - int j; - for (j = 1; j <= strlen(optstr); j++) - switch (optstr[j-1]) - { - case 'c': mesh3d.CombineImprove(); break; - case 'd': mesh3d.SplitImprove(); break; - case 's': mesh3d.SwapImprove(); break; - case 'm': mesh3d.ImproveMesh(2); break; - } - - (*mycout) << "Call remove" << endl; - RemoveProblem (mesh3d); - (*mycout) << "Problem removed" << endl; - } - else - meshed = 1; - } - while (!meshed); - - MeshQuality3d (mesh3d); - - return MESHING3_OK; - } - - */ - - - - - /* - MESHING3_RESULT MeshMixedVolume(MeshingParameters & mp, Mesh& mesh3d) - { - int i, j; - MESHING3_RESULT res; - Point3d pmin, pmax; - - mp.giveuptol = 10; - mp.baseelnp = 4; - mp.starshapeclass = 100; - - // TerminalPlotStatistics3d pstat; - - Meshing3 meshing1("pyramids.rls"); - for (i = 1; i <= mesh3d.GetNP(); i++) - meshing1.AddPoint (mesh3d.Point(i), i); - - mesh3d.FindOpenElements(); - for (i = 1; i <= mesh3d.GetNOpenElements(); i++) - if (mesh3d.OpenElement(i).GetIndex() == 1) - meshing1.AddBoundaryElement (mesh3d.OpenElement(i)); - - res = meshing1.GenerateMesh (mesh3d, mp); - - mesh3d.GetBox (pmin, pmax); - PrintMessage (1, "Mesh pyramids, res = ", res); - if (res) - exit (1); - - - for (i = 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (1); - - // do delaunay - - mp.baseelnp = 0; - mp.starshapeclass = 5; - - Meshing3 meshing2(NULL); - for (i = 1; i <= mesh3d.GetNP(); i++) - meshing2.AddPoint (mesh3d.Point(i), i); - - mesh3d.FindOpenElements(); - for (i = 1; i <= mesh3d.GetNOpenElements(); i++) - if (mesh3d.OpenElement(i).GetIndex() == 1) - meshing2.AddBoundaryElement (mesh3d.OpenElement(i)); - - MeshingParameters mpd; - meshing2.Delaunay (mesh3d, mpd); - - for (i = 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (1); - - - mp.baseelnp = 0; - mp.giveuptol = 10; - - for (int trials = 1; trials <= 50; trials++) - { - if (multithread.terminate) - return MESHING3_TERMINATE; - - Meshing3 meshing3("tetra.rls"); - for (i = 1; i <= mesh3d.GetNP(); i++) - meshing3.AddPoint (mesh3d.Point(i), i); - - mesh3d.FindOpenElements(); - for (i = 1; i <= mesh3d.GetNOpenElements(); i++) - if (mesh3d.OpenElement(i).GetIndex() == 1) - meshing3.AddBoundaryElement (mesh3d.OpenElement(i)); - - if (trials > 1) - CheckSurfaceMesh2 (mesh3d); - res = meshing3.GenerateMesh (mesh3d, mp); - - for (i = 1; i <= mesh3d.GetNE(); i++) - mesh3d.VolumeElement(i).SetIndex (1); - - if (res == 0) break; - - - - for (i = 1; i <= mesh3d.GetNE(); i++) - { - const Element & el = mesh3d.VolumeElement(i); - if (el.GetNP() != 4) - { - for (j = 1; j <= el.GetNP(); j++) - mesh3d.AddLockedPoint (el.PNum(j)); - } - } - - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(); - - MeshOptimize3d optmesh; - - teterrpow = 2; - const char * optstr = "mcmcmcmcm"; - for (j = 1; j <= strlen(optstr); j++) - switch (optstr[j-1]) - { - case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break; - case 'd': optmesh.SplitImprove(mesh3d); break; - case 's': optmesh.SwapImprove(mesh3d); break; - case 'm': mesh3d.ImproveMesh(); break; - } - - RemoveProblem (mesh3d); - } - - - PrintMessage (1, "Meshing tets, res = ", res); - if (res) - { - mesh3d.FindOpenElements(); - PrintSysError (1, "Open elements: ", mesh3d.GetNOpenElements()); - exit (1); - } - - - - for (i = 1; i <= mesh3d.GetNE(); i++) - { - const Element & el = mesh3d.VolumeElement(i); - if (el.GetNP() != 4) - { - for (j = 1; j <= el.GetNP(); j++) - mesh3d.AddLockedPoint (el.PNum(j)); - } - } - - mesh3d.CalcSurfacesOfNode(); - mesh3d.FindOpenElements(); - - MeshOptimize3d optmesh; - - teterrpow = 2; - const char * optstr = "mcmcmcmcm"; - for (j = 1; j <= strlen(optstr); j++) - switch (optstr[j-1]) - { - case 'c': optmesh.CombineImprove(mesh3d, OPT_REST); break; - case 'd': optmesh.SplitImprove(mesh3d); break; - case 's': optmesh.SwapImprove(mesh3d); break; - case 'm': mesh3d.ImproveMesh(); break; - } - - - return MESHING3_OK; - } -*/ - - - - - - - MESHING3_RESULT OptimizeVolume (MeshingParameters & mp, + MESHING3_RESULT OptimizeVolume (const MeshingParameters & mp, Mesh & mesh3d) // const CSGeometry * geometry) { static Timer t("OptimizeVolume"); RegionTimer reg(t); + RegionTaskManager rtm(mp.parallel_meshing ? mp.nthreads : 0); + const char* savetask = multithread.task; + multithread.task = "Optimize Volume"; int i; @@ -659,7 +651,7 @@ namespace netgen */ mesh3d.CalcSurfacesOfNode(); - for (i = 1; i <= mp.optsteps3d; i++) + for (auto i : Range(mp.optsteps3d)) { if (multithread.terminate) break; @@ -668,15 +660,17 @@ namespace netgen // teterrpow = mp.opterrpow; // for (size_t j = 1; j <= strlen(mp.optimize3d); j++) - for (size_t j = 1; j <= mp.optimize3d.length(); j++) + for (auto j : Range(mp.optimize3d.size())) { + multithread.percent = 100.* (double(j)/mp.optimize3d.size() + i)/mp.optsteps3d; if (multithread.terminate) break; - switch (mp.optimize3d[j-1]) + switch (mp.optimize3d[j]) { 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; @@ -690,10 +684,11 @@ namespace netgen case 'j': mesh3d.ImproveMeshJacobian(mp); break; } } - mesh3d.mglevels = 1; + // mesh3d.mglevels = 1; MeshQuality3d (mesh3d); } + multithread.task = savetask; return MESHING3_OK; } diff --git a/libsrc/meshing/meshfunc.hpp b/libsrc/meshing/meshfunc.hpp index f39c0a8f..fdbdef4e 100644 --- a/libsrc/meshing/meshfunc.hpp +++ b/libsrc/meshing/meshfunc.hpp @@ -16,16 +16,16 @@ class Mesh; // class CSGeometry; /// Build tet-mesh -MESHING3_RESULT MeshVolume (MeshingParameters & mp, Mesh& mesh3d); +DLL_HEADER MESHING3_RESULT MeshVolume (const MeshingParameters & mp, Mesh& mesh3d); /// Build mixed-element mesh // MESHING3_RESULT MeshMixedVolume (MeshingParameters & mp, Mesh& mesh3d); /// Optimize tet-mesh -MESHING3_RESULT OptimizeVolume (MeshingParameters & mp, Mesh& mesh3d); +DLL_HEADER MESHING3_RESULT OptimizeVolume (const MeshingParameters & mp, Mesh& mesh3d); // const CSGeometry * geometry = NULL); -void RemoveIllegalElements (Mesh & mesh3d); +DLL_HEADER void RemoveIllegalElements (Mesh & mesh3d); enum MESHING_STEP { diff --git a/libsrc/meshing/meshfunc2d.cpp b/libsrc/meshing/meshfunc2d.cpp index de6d8d70..84f5d399 100644 --- a/libsrc/meshing/meshfunc2d.cpp +++ b/libsrc/meshing/meshfunc2d.cpp @@ -6,11 +6,33 @@ namespace netgen DLL_HEADER void Optimize2d (Mesh & mesh, MeshingParameters & mp) { - static int timer = NgProfiler::CreateTimer ("optimize2d"); - NgProfiler::RegionTimer reg(timer); + static Timer timer("optimize2d"); RegionTimer reg(timer); mesh.CalcSurfacesOfNode(); + bool secondorder = mesh.GetNP() > mesh.GetNV(); + + + if (secondorder) + { + for (SurfaceElementIndex ei = 0; ei < mesh.GetNSE(); ei++) + mesh[ei].SetType(TRIG); + } + 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; @@ -22,36 +44,67 @@ namespace netgen { case 's': { // topological swap - MeshOptimize2d meshopt; + MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.EdgeSwapping (mesh, 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; + MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.EdgeSwapping (mesh, 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': { - MeshOptimize2d meshopt; + MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.ImproveMesh(mesh, mp); + meshopt.ImproveMesh(mp); break; } case 'c': { - MeshOptimize2d meshopt; + MeshOptimize2d meshopt(mesh); meshopt.SetMetricWeight (mp.elsizeweight); - meshopt.CombineImprove(mesh); + meshopt.CombineImprove(); break; } default: cerr << "Optimization code " << optstr[j-1] << " not defined" << endl; } } + mesh.Compress(); // better: compress in individual steps, if necessary + if (secondorder) + { + mesh.GetGeometry()->GetRefinement().MakeSecondOrder(mesh); + } } } diff --git a/libsrc/meshing/meshing.hpp b/libsrc/meshing/meshing.hpp index 8ed81256..52a0dfc2 100644 --- a/libsrc/meshing/meshing.hpp +++ b/libsrc/meshing/meshing.hpp @@ -42,13 +42,12 @@ namespace netgen #define _INCLUDE_MORE +#include "findip.hpp" +#include "findip2.hpp" #include "meshing3.hpp" #include "improve3.hpp" -#include "findip.hpp" -#include "findip2.hpp" - #include "curvedelems.hpp" #include "clusters.hpp" @@ -58,14 +57,14 @@ 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" -#endif #endif diff --git a/libsrc/meshing/meshing2.cpp b/libsrc/meshing/meshing2.cpp index 2a9ebf08..deadcd10 100644 --- a/libsrc/meshing/meshing2.cpp +++ b/libsrc/meshing/meshing2.cpp @@ -1,33 +1,75 @@ #include #include "meshing.hpp" +#include "visual_interface.hpp" namespace netgen { - static void glrender (int wait); - - - // global variable for visualization -// static Array locpoints; -// static Array legalpoints; -// static Array plainpoints; -// static Array plainzones; -// static Array loclines; -// // static int geomtrig; -// //static const char * rname; -// static int cntelem, trials, nfaces; -// static int oldnl; -// static int qualclass; - - - Meshing2 :: Meshing2 (const MeshingParameters & mp, const Box<3> & aboundingbox) + static void glrender (int wait) { - boundingbox = aboundingbox; - - LoadRules (NULL, mp.quad); + // cout << "plot adfront" << endl; + + if (multithread.drawing) + { + // vssurfacemeshing.DrawScene(); + Render (); + + if (wait || multithread.testmode) + { + multithread.pause = 1; + } + while (multithread.pause); + } + } + + ostream& operator << (ostream& ost, const MultiPointGeomInfo& mpgi) + { + for(auto i : Range(mpgi.GetNPGI())) + { + ost << "gi[" << i << "] = " << mpgi.GetPGI(i+1) << endl; + } + return ost; + } + + static Array> global_trig_rules; + static Array> global_quad_rules; + + + Meshing2 :: Meshing2 (const NetgenGeometry& ageo, + const MeshingParameters & mp, + const Box<3> & aboundingbox) + : adfront(aboundingbox), boundingbox(aboundingbox), geo(ageo) + { + static Timer t("Mesing2::Meshing2"); RegionTimer r(t); + + auto & globalrules = mp.quad ? global_quad_rules : global_trig_rules; + + { + static mutex mut; + lock_guard guard(mut); + if (!globalrules.Size()) + { + LoadRules (NULL, mp.quad); + for (auto & rule : rules) + globalrules.Append (make_unique(*rule)); + rules.SetSize(0); + } + /* + else + { + for (auto i : globalrules.Range()) + rules.Append (globalrules[i].get()); + } + */ + } + for (auto i : globalrules.Range()) + rules.Append (make_unique(*globalrules[i])); + // LoadRules ("rules/quad.rls"); // LoadRules ("rules/triangle.rls"); - adfront = new AdFront2(boundingbox); + + + // adfront = new AdFront2(boundingbox); starttime = GetTime(); maxarea = -1; @@ -35,18 +77,19 @@ namespace netgen Meshing2 :: ~Meshing2 () - { - delete adfront; - for (int i = 0; i < rules.Size(); i++) - delete rules[i]; - } + { ; } - void Meshing2 :: AddPoint (const Point3d & p, PointIndex globind, - MultiPointGeomInfo * mgi, - bool pointonsurface) + int Meshing2 :: AddPoint (const Point3d & p, PointIndex globind, + MultiPointGeomInfo * mgi, + bool pointonsurface) { //(*testout) << "add point " << globind << endl; - adfront ->AddPoint (p, globind, mgi, pointonsurface); + return adfront.AddPoint (p, globind, mgi, pointonsurface); + } + + PointIndex Meshing2 :: GetGlobalIndex(int pi) const + { + return adfront.GetGlobalIndex(pi); } void Meshing2 :: AddBoundaryElement (int i1, int i2, @@ -57,7 +100,7 @@ namespace netgen { PrintSysError ("addboundaryelement: illegal geominfo"); } - adfront -> AddLine (i1-1, i2-1, gi1, gi2); + adfront.AddLine (i1-1, i2-1, gi1, gi2); } @@ -95,7 +138,7 @@ namespace netgen } - double Meshing2 :: CalcLocalH (const Point3d & /* p */, double gh) const + double Meshing2 :: CalcLocalH (const Point<3> & /* p */, double gh) const { return gh; } @@ -104,42 +147,48 @@ namespace netgen // static Vec3d ex, ey; // static Point3d globp1; - void Meshing2 :: DefineTransformation (const Point3d & p1, const Point3d & p2, - const PointGeomInfo * geominfo1, - const PointGeomInfo * geominfo2) + void Meshing2 :: DefineTransformation (const Point<3> & ap1, + const Point<3> & ap2, + const PointGeomInfo * gi1, + const PointGeomInfo * gi2) { - globp1 = p1; - ex = p2 - p1; - ex /= ex.Length(); - ey.X() = -ex.Y(); - ey.Y() = ex.X(); - ey.Z() = 0; + p1 = ap1; + p2 = ap2; + auto n1 = geo.GetNormal(gi1->trignum, p1, gi1); + auto n2 = geo.GetNormal(gi2->trignum, p2, gi2); + + ez = 0.5 * (n1+n2); + ez.Normalize(); + ex = (p2-p1).Normalize(); + ez -= (ez*ex)*ex; + ez.Normalize(); + ey = Cross(ez, ex); } - void Meshing2 :: TransformToPlain (const Point3d & locpoint, - const MultiPointGeomInfo & geominf, - Point2d & plainpoint, double h, int & zone) + void Meshing2 :: TransformToPlain (const Point<3> & locpoint, + const MultiPointGeomInfo & geominfo, + Point<2> & plainpoint, double h, int & zone) { - Vec3d p1p (globp1, locpoint); + auto& gi = geominfo.GetPGI(1); + auto n = geo.GetNormal(gi.trignum, locpoint, &gi); + auto p1p = locpoint - p1; + plainpoint(0) = (p1p * ex) / h; + plainpoint(1) = (p1p * ey) / h; - // p1p = locpoint - globp1; - p1p /= h; - plainpoint.X() = p1p * ex; - plainpoint.Y() = p1p * ey; - zone = 0; + if(n*ez < 0) + zone = -1; + else + zone = 0; } - int Meshing2 :: TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, + int Meshing2 :: TransformFromPlain (const Point<2> & plainpoint, + Point<3> & locpoint, PointGeomInfo & gi, double h) { - Vec3d p1p; - gi.trignum = 1; - - p1p = plainpoint.X() * ex + plainpoint.Y() * ey; - p1p *= h; - locpoint = globp1 + p1p; + locpoint = p1 + (h*plainpoint(0)) * ex + (h* plainpoint(1)) * ey; + if (!geo.ProjectPointGI(gi.trignum, locpoint, gi)) + gi = geo.ProjectPoint(gi.trignum, locpoint); return 0; } @@ -175,9 +224,9 @@ namespace netgen } void Meshing2 :: - GetChartBoundary (Array & points, - Array & points3d, - Array & lines, double h) const + GetChartBoundary (NgArray> & points, + NgArray> & points3d, + NgArray & lines, double h) const { points.SetSize (0); points3d.SetSize (0); @@ -193,9 +242,9 @@ namespace netgen - MESHING2_RESULT Meshing2 :: GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr) + MESHING2_RESULT Meshing2 :: GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr, int layer) { - static int timer = NgProfiler::CreateTimer ("surface meshing"); + static Timer timer("surface meshing"); RegionTimer reg(timer); static int timer1 = NgProfiler::CreateTimer ("surface meshing1"); static int timer2 = NgProfiler::CreateTimer ("surface meshing2"); @@ -206,22 +255,19 @@ namespace netgen static int ts3 = NgProfiler::CreateTimer ("surface meshing start 3"); - NgProfiler::RegionTimer reg (timer); - NgProfiler::StartTimer (ts1); - Array pindex, lindex; - Array delpoints, dellines; + NgArray pindex, lindex; + NgArray delpoints, dellines; - Array upgeominfo; // unique info - Array mpgeominfo; // multiple info + NgArray upgeominfo; // unique info + NgArray mpgeominfo; // multiple info - Array locelements; + NgArray locelements; int z1, z2, oldnp(-1); bool found; int rulenr(-1); - Point<3> p1, p2; const PointGeomInfo * blgeominfo1; const PointGeomInfo * blgeominfo2; @@ -229,16 +275,21 @@ namespace netgen bool morerisc; bool debugflag; - double h, his, hshould; + // double h; - - Array locpoints; - Array legalpoints; - Array plainpoints; - Array plainzones; - Array loclines; + auto locpointsptr = make_shared>>(); + auto& locpoints = *locpointsptr; + NgArray legalpoints; + auto plainpointsptr = make_shared>>(); + auto& plainpoints = *plainpointsptr; + NgArray plainzones; + auto loclinesptr = make_shared>(); + auto &loclines = *loclinesptr; int cntelem = 0, trials = 0, nfaces = 0; int oldnl = 0; + + UpdateVisSurfaceMeshData(oldnl, locpointsptr, loclinesptr, plainpointsptr); + int qualclass; @@ -247,8 +298,8 @@ namespace netgen BoxTree<3> surfeltree (boundingbox.PMin(), boundingbox.PMax()); - Array intersecttrias; - Array critpoints; + NgArray intersecttrias; + NgArray critpoints; // test for doubled edges //INDEX_2_HASHTABLE doubleedge(300000); @@ -258,14 +309,14 @@ namespace netgen StartMesh(); - Array chartboundpoints; - Array chartboundpoints3d; - Array chartboundlines; + NgArray> chartboundpoints; + NgArray> chartboundpoints3d; + NgArray chartboundlines; // illegal points: points with more then 50 elements per node int maxlegalpoint(-1), maxlegalline(-1); - Array trigsonnode; - Array illegalpoint; + NgArray trigsonnode; + NgArray illegalpoint; trigsonnode.SetSize (mesh.GetNP()); illegalpoint.SetSize (mesh.GetNP()); @@ -342,7 +393,7 @@ namespace netgen const char * savetask = multithread.task; multithread.task = "Surface meshing"; - adfront ->SetStartFront (); + adfront.SetStartFront (); int plotnexttrial = 999; @@ -351,7 +402,11 @@ namespace netgen NgProfiler::StopTimer (ts3); - while (!adfront ->Empty() && !multithread.terminate) + static Timer tloop("surfacemeshing mainloop"); + // static Timer tgetlocals("surfacemeshing getlocals"); + { + RegionTimer rloop(tloop); + while (!adfront.Empty() && !multithread.terminate) { NgProfiler::RegionTimer reg1 (timer1); @@ -366,13 +421,13 @@ namespace netgen multithread.percent = 0; */ - locpoints.SetSize(0); - loclines.SetSize(0); - pindex.SetSize(0); - lindex.SetSize(0); - delpoints.SetSize(0); - dellines.SetSize(0); - locelements.SetSize(0); + locpoints.SetSize0(); + loclines.SetSize0(); + pindex.SetSize0(); + lindex.SetSize0(); + delpoints.SetSize0(); + dellines.SetSize0(); + locelements.SetSize0(); @@ -390,11 +445,11 @@ namespace netgen // unique-pgi, multi-pgi - upgeominfo.SetSize(0); - mpgeominfo.SetSize(0); + upgeominfo.SetSize0(); + mpgeominfo.SetSize0(); - nfaces = adfront->GetNFL(); + nfaces = adfront.GetNFL(); trials ++; @@ -405,32 +460,32 @@ namespace netgen { (*testout) << foundmap.Get(i) << "/" << canuse.Get(i) << "/" - << ruleused.Get(i) << " map/can/use rule " << rules.Get(i)->Name() << "\n"; + << ruleused.Get(i) << " map/can/use rule " << rules[i-1]->Name() << "\n"; } (*testout) << "\n"; } - - int baselineindex = adfront -> SelectBaseLine (p1, p2, blgeominfo1, blgeominfo2, qualclass); - + Point<3> p1, p2; + int baselineindex = adfront.SelectBaseLine (p1, p2, blgeominfo1, blgeominfo2, qualclass); found = 1; - his = Dist (p1, p2); + double his = Dist (p1, p2); - Point3d pmid = Center (p1, p2); - hshould = CalcLocalH (pmid, mesh.GetH (pmid)); + Point<3> pmid = Center (p1, p2); + double hshould = CalcLocalH (pmid, mesh.GetH (pmid, layer)); if (gh < hshould) hshould = gh; mesh.RestrictLocalH (pmid, hshould); - h = hshould; + double h = hshould; double hinner = (3 + qualclass) * max2 (his, hshould); - adfront ->GetLocals (baselineindex, locpoints, mpgeominfo, loclines, + // tgetlocals.Start(); + adfront.GetLocals (baselineindex, locpoints, mpgeominfo, loclines, pindex, lindex, 2*hinner); - + // tgetlocals.Stop(); NgProfiler::RegionTimer reg2 (timer2); @@ -442,7 +497,7 @@ namespace netgen if (qualclass > mp.giveuptol2d) { PrintMessage (3, "give up with qualclass ", qualclass); - PrintMessage (3, "number of frontlines = ", adfront->GetNFL()); + PrintMessage (3, "number of frontlines = ", adfront.GetNFL()); // throw NgException ("Give up 2d meshing"); break; } @@ -458,8 +513,8 @@ namespace netgen morerisc = 0; - PointIndex gpi1 = adfront -> GetGlobalIndex (pindex.Get(loclines[0].I1())); - PointIndex gpi2 = adfront -> GetGlobalIndex (pindex.Get(loclines[0].I2())); + PointIndex gpi1 = adfront.GetGlobalIndex (pindex.Get(loclines[0].I1())); + PointIndex gpi2 = adfront.GetGlobalIndex (pindex.Get(loclines[0].I2())); debugflag = @@ -481,7 +536,7 @@ namespace netgen cout << "set debugflag" << endl; } - if (debugparam.haltlargequalclass && qualclass > 50) + if (debugparam.haltlargequalclass && qualclass == 50) debugflag = 1; // problem recognition ! @@ -494,12 +549,14 @@ namespace netgen } - Point2d p12d, p22d; + // Point2d p12d, p22d; if (found) { oldnp = locpoints.Size(); oldnl = loclines.Size(); + + UpdateVisSurfaceMeshData(oldnl); if (debugflag) (*testout) << "define new transformation" << endl; @@ -517,6 +574,16 @@ namespace netgen *testout << "3d points: " << endl << locpoints << endl; } + + for (size_t i = 0; i < locpoints.Size(); i++) + { + Point<2> pp; + TransformToPlain (locpoints[i], mpgeominfo[i], + pp, h, plainzones[i]); + plainpoints[i] = pp; + } + + /* for (int i = 1; i <= locpoints.Size(); i++) { // (*testout) << "pindex(i) = " << pindex[i-1] << endl; @@ -527,6 +594,7 @@ namespace netgen // (*testout) << plainpoints.Get(i).X() << " " << plainpoints.Get(i).Y() << endl; //(*testout) << "transform " << locpoints.Get(i) << " to " << plainpoints.Get(i).X() << " " << plainpoints.Get(i).Y() << endl; } + */ // (*testout) << endl << endl << endl; @@ -534,8 +602,8 @@ namespace netgen *testout << "2d points: " << endl << plainpoints << endl; - p12d = plainpoints.Get(1); - p22d = plainpoints.Get(2); + // p12d = plainpoints.Get(1); + // p22d = plainpoints.Get(2); /* // last idea on friday @@ -581,22 +649,19 @@ namespace netgen if (IsLineVertexOnChart (locpoints.Get(loclines.Get(i).I1()), locpoints.Get(loclines.Get(i).I2()), innerp, - adfront->GetLineGeomInfo (lindex.Get(i), innerp))) + adfront.GetLineGeomInfo (lindex.Get(i), innerp))) // pgeominfo.Get(loclines.Get(i).I(innerp)))) { if (!morerisc) { // use one end of line - int pini, pouti; - Vec2d v; + int pini = loclines.Get(i).I(innerp); + int pouti = loclines.Get(i).I(3-innerp); - pini = loclines.Get(i).I(innerp); - pouti = loclines.Get(i).I(3-innerp); - - Point2d pin (plainpoints.Get(pini)); - Point2d pout (plainpoints.Get(pouti)); - v = pout - pin; + const auto& pin = plainpoints.Get(pini); + const auto& pout = plainpoints.Get(pouti); + auto v = pout - pin; double len = v.Length(); if (len <= 1e-6) (*testout) << "WARNING(js): inner-outer: short vector" << endl; @@ -610,12 +675,12 @@ namespace netgen v *= -1; */ - Point2d newpout = pin + 1000 * v; + Point<2> newpout = pin + 1000. * v; newpout = pout; plainpoints.Append (newpout); - Point3d pout3d = locpoints.Get(pouti); + auto pout3d = locpoints.Get(pouti); locpoints.Append (pout3d); plainzones.Append (0); @@ -653,31 +718,34 @@ namespace netgen legalpoints.SetSize(plainpoints.Size()); + legalpoints = 1; + /* for (int i = 1; i <= legalpoints.Size(); i++) legalpoints.Elem(i) = 1; - + */ + double avy = 0; - for (int i = 1; i <= plainpoints.Size(); i++) - avy += plainpoints.Elem(i).Y(); + for (size_t i = 0; i < plainpoints.Size(); i++) + avy += plainpoints[i][1]; avy *= 1./plainpoints.Size(); - for (int i = 1; i <= plainpoints.Size(); i++) + for (auto i : Range(plainpoints)) { - if (plainzones.Elem(i) < 0) + if (plainzones[i] < 0) { - plainpoints.Elem(i) = Point2d (1e4, 1e4); - legalpoints.Elem(i) = 0; + plainpoints[i] = {1e4, 1e4}; + legalpoints[i] = 0; } - if (pindex.Elem(i) == -1) + if (pindex[i] == -1) { - legalpoints.Elem(i) = 0; + legalpoints[i] = 0; } - if (plainpoints.Elem(i).Y() < -1e-10*avy) // changed + if (plainpoints[i][1] < -1e-10*avy) // changed { - legalpoints.Elem(i) = 0; + legalpoints[i] = 0; } } /* @@ -735,6 +803,7 @@ namespace netgen { for (int i = 1; i <= chartboundpoints.Size(); i++) { + pindex.Append(-1); plainpoints.Append (chartboundpoints.Get(i)); locpoints.Append (chartboundpoints3d.Get(i)); legalpoints.Append (0); @@ -760,12 +829,14 @@ namespace netgen { multithread.drawing = 1; glrender(1); - cout << "qualclass 100, nfl = " << adfront->GetNFL() << endl; + cout << "qualclass 100, nfl = " << adfront.GetNFL() << endl; } */ if (found) { + // static Timer t("ApplyRules"); + // RegionTimer r(t); rulenr = ApplyRules (plainpoints, legalpoints, maxlegalpoint, loclines, maxlegalline, locelements, dellines, qualclass, mp); @@ -802,9 +873,12 @@ namespace netgen for (int i = oldnp+1; i <= plainpoints.Size(); i++) { + Point<3> locp; + upgeominfo.Elem(i) = *blgeominfo1; int err = - TransformFromPlain (plainpoints.Elem(i), locpoints.Elem(i), + TransformFromPlain (plainpoints.Elem(i), locp, upgeominfo.Elem(i), h); + locpoints.Elem(i) = locp; if (err) { @@ -820,7 +894,7 @@ namespace netgen // for (i = 1; i <= oldnl; i++) - // adfront -> ResetClass (lindex[i]); + // adfront.ResetClass (lindex[i]); /* @@ -949,7 +1023,7 @@ namespace netgen for (j = 1; j <= 2; j++) { upgeominfo.Elem(loclines.Get(dellines.Get(i)).I(j)) = - adfront -> GetLineGeomInfo (lindex.Get(dellines.Get(i)), j); + adfront.GetLineGeomInfo (lindex.Get(dellines.Get(i)), j); } */ @@ -1147,7 +1221,7 @@ namespace netgen // cout << "overlap !!!" << endl; #endif for (int k = 1; k <= 5; k++) - adfront -> IncrementClass (lindex.Get(1)); + adfront.IncrementClass (lindex.Get(1)); found = 0; @@ -1181,10 +1255,10 @@ namespace netgen int nlgpi2 = loclines.Get(i).I2(); if (nlgpi1 <= pindex.Size() && nlgpi2 <= pindex.Size()) { - nlgpi1 = adfront->GetGlobalIndex (pindex.Get(nlgpi1)); - nlgpi2 = adfront->GetGlobalIndex (pindex.Get(nlgpi2)); + nlgpi1 = adfront.GetGlobalIndex (pindex.Get(nlgpi1)); + nlgpi2 = adfront.GetGlobalIndex (pindex.Get(nlgpi2)); - int exval = adfront->ExistsLine (nlgpi1, nlgpi2); + int exval = adfront.ExistsLine (nlgpi1, nlgpi2); if (exval) { cout << "ERROR: new line exits, val = " << exval << endl; @@ -1213,8 +1287,8 @@ namespace netgen int tpi2 = locelements.Get(i).PNumMod (j+1); if (tpi1 <= pindex.Size() && tpi2 <= pindex.Size()) { - tpi1 = adfront->GetGlobalIndex (pindex.Get(tpi1)); - tpi2 = adfront->GetGlobalIndex (pindex.Get(tpi2)); + tpi1 = adfront.GetGlobalIndex (pindex.Get(tpi1)); + tpi2 = adfront.GetGlobalIndex (pindex.Get(tpi2)); if (doubleedge.Used (INDEX_2(tpi1, tpi2))) { @@ -1243,7 +1317,7 @@ namespace netgen for (int i = oldnp+1; i <= locpoints.Size(); i++) { PointIndex globind = mesh.AddPoint (locpoints.Get(i)); - pindex.Elem(i) = adfront -> AddPoint (locpoints.Get(i), globind); + pindex.Elem(i) = adfront.AddPoint (locpoints.Get(i), globind); } for (int i = oldnl+1; i <= loclines.Size(); i++) @@ -1273,7 +1347,7 @@ namespace netgen cout << "new el: illegal geominfo" << endl; } - adfront -> AddLine (pindex.Get(loclines.Get(i).I1()), + adfront.AddLine (pindex.Get(loclines.Get(i).I1()), pindex.Get(loclines.Get(i).I2()), upgeominfo.Get(loclines.Get(i).I1()), upgeominfo.Get(loclines.Get(i).I2())); @@ -1298,7 +1372,7 @@ namespace netgen { mtri.PNum(j) = locelements.Elem(i).PNum(j) = - adfront -> GetGlobalIndex (pindex.Get(locelements.Get(i).PNum(j))); + adfront.GetGlobalIndex (pindex.Get(locelements.Get(i).PNum(j))); } @@ -1377,7 +1451,7 @@ namespace netgen } for (int i = 1; i <= dellines.Size(); i++) - adfront -> DeleteLine (lindex.Get(dellines.Get(i))); + adfront.DeleteLine (lindex.Get(dellines.Get(i))); // rname = rules.Get(rulenr)->Name(); #ifdef MYGRAPH @@ -1400,8 +1474,8 @@ namespace netgen if ( debugparam.haltsuccess || debugflag ) { - // adfront -> PrintOpenSegments (*testout); - cout << "success of rule" << rules.Get(rulenr)->Name() << endl; + // adfront.PrintOpenSegments (*testout); + cout << "success of rule" << rules[rulenr-1]->Name() << endl; multithread.drawing = 1; multithread.testmode = 1; multithread.pause = 1; @@ -1417,14 +1491,17 @@ namespace netgen } */ - (*testout) << "success of rule" << rules.Get(rulenr)->Name() << endl; + (*testout) << "success of rule" << rules[rulenr-1]->Name() << endl; (*testout) << "trials = " << trials << endl; (*testout) << "locpoints " << endl; for (int i = 1; i <= pindex.Size(); i++) - (*testout) << adfront->GetGlobalIndex (pindex.Get(i)) << endl; + (*testout) << adfront.GetGlobalIndex (pindex.Get(i)) << endl; (*testout) << "old number of lines = " << oldnl << endl; + + UpdateVisSurfaceMeshData(oldnl); + for (int i = 1; i <= loclines.Size(); i++) { (*testout) << "line "; @@ -1433,7 +1510,7 @@ namespace netgen int hi = 0; if (loclines.Get(i).I(j) >= 1 && loclines.Get(i).I(j) <= pindex.Size()) - hi = adfront->GetGlobalIndex (pindex.Get(loclines.Get(i).I(j))); + hi = adfront.GetGlobalIndex (pindex.Get(loclines.Get(i).I(j))); (*testout) << hi << " "; } @@ -1452,7 +1529,7 @@ namespace netgen } else { - adfront -> IncrementClass (lindex.Get(1)); + adfront.IncrementClass (lindex.Get(1)); if ( debugparam.haltnosuccess || debugflag ) { @@ -1485,7 +1562,7 @@ namespace netgen int hi = 0; if (loclines.Get(i).I(j) >= 1 && loclines.Get(i).I(j) <= pindex.Size()) - hi = adfront->GetGlobalIndex (pindex.Get(loclines.Get(i).I(j))); + hi = adfront.GetGlobalIndex (pindex.Get(loclines.Get(i).I(j))); (*testout) << hi << " "; } @@ -1520,11 +1597,11 @@ namespace netgen } } - + } PrintMessage (3, "Surface meshing done"); - adfront->PrintOpenSegments (*testout); + adfront.PrintOpenSegments (*testout); multithread.task = savetask; @@ -1532,434 +1609,12 @@ namespace netgen EndMesh (); - if (!adfront->Empty()) + if (!adfront.Empty()) return MESHING2_GIVEUP; return MESHING2_OK; } - - - - - - - } - - - - - -// #define OPENGL -#ifdef OPENGLxx - -/* *********************** Draw Surface Meshing **************** */ - - -#include -#include - -namespace netgen -{ - - extern STLGeometry * stlgeometry; - extern Mesh * mesh; - VisualSceneSurfaceMeshing vssurfacemeshing; - - - - void glrender (int wait) - { - // cout << "plot adfront" << endl; - - if (multithread.drawing) - { - // vssurfacemeshing.Render(); - Render (); - - if (wait || multithread.testmode) - { - multithread.pause = 1; - } - while (multithread.pause); - } - } - - - - VisualSceneSurfaceMeshing :: VisualSceneSurfaceMeshing () - : VisualScene() - { - ; - } - - VisualSceneSurfaceMeshing :: ~VisualSceneSurfaceMeshing () - { - ; - } - - void VisualSceneSurfaceMeshing :: DrawScene () - { - int i, j, k; - - if (loclines.Size() != changeval) - { - center = Point<3>(0,0,-5); - rad = 0.1; - - CalcTransformationMatrices(); - changeval = loclines.Size(); - } - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - SetLight(); - - // glEnable (GL_COLOR_MATERIAL); - - // glDisable (GL_SHADING); - // glColor3f (0.0f, 1.0f, 1.0f); - // glLineWidth (1.0f); - // glShadeModel (GL_SMOOTH); - - // glCallList (linelists.Get(1)); - - // SetLight(); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - glShadeModel (GL_SMOOTH); - // glDisable (GL_COLOR_MATERIAL); - glEnable (GL_COLOR_MATERIAL); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // glEnable (GL_LIGHTING); - - double shine = vispar.shininess; - double transp = vispar.transp; - - glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine); - glLogicOp (GL_COPY); - - - - /* - - float mat_col[] = { 0.2, 0.2, 0.8, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - float mat_colbl[] = { 0.8, 0.2, 0.2, 1 }; - float mat_cololdl[] = { 0.2, 0.8, 0.2, 1 }; - float mat_colnewl[] = { 0.8, 0.8, 0.2, 1 }; - - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glPolygonOffset (1, -1); - glLineWidth (3); - - for (i = 1; i <= loclines.Size(); i++) - { - if (i == 1) - { - glEnable (GL_POLYGON_OFFSET_FILL); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbl); - } - else if (i <= oldnl) - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_cololdl); - else - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colnewl); - - int pi1 = loclines.Get(i).I1(); - int pi2 = loclines.Get(i).I2(); - - if (pi1 >= 1 && pi2 >= 1) - { - Point3d p1 = locpoints.Get(pi1); - Point3d p2 = locpoints.Get(pi2); - - glBegin (GL_LINES); - glVertex3f (p1.X(), p1.Y(), p1.Z()); - glVertex3f (p2.X(), p2.Y(), p2.Z()); - glEnd(); - } - - glDisable (GL_POLYGON_OFFSET_FILL); - } - - - glLineWidth (1); - - - glPointSize (5); - float mat_colp[] = { 1, 0, 0, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); - glBegin (GL_POINTS); - for (i = 1; i <= locpoints.Size(); i++) - { - Point3d p = locpoints.Get(i); - glVertex3f (p.X(), p.Y(), p.Z()); - } - glEnd(); - - - glPopMatrix(); - */ - - float mat_colp[] = { 1, 0, 0, 1 }; - - float mat_col2d1[] = { 1, 0.5, 0.5, 1 }; - float mat_col2d[] = { 1, 1, 1, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); - - double scalex = 0.1, scaley = 0.1; - - glBegin (GL_LINES); - for (i = 1; i <= loclines.Size(); i++) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); - if (i == 1) - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d1); - - int pi1 = loclines.Get(i).I1(); - int pi2 = loclines.Get(i).I2(); - - if (pi1 >= 1 && pi2 >= 1) - { - Point2d p1 = plainpoints.Get(pi1); - Point2d p2 = plainpoints.Get(pi2); - - glBegin (GL_LINES); - glVertex3f (scalex * p1.X(), scaley * p1.Y(), -5); - glVertex3f (scalex * p2.X(), scaley * p2.Y(), -5); - glEnd(); - } - } - glEnd (); - - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); - glBegin (GL_POINTS); - for (i = 1; i <= plainpoints.Size(); i++) - { - Point2d p = plainpoints.Get(i); - glVertex3f (scalex * p.X(), scaley * p.Y(), -5); - } - glEnd(); - - - - - - - glDisable (GL_POLYGON_OFFSET_FILL); - - glPopMatrix(); - DrawCoordinateCross (); - DrawNetgenLogo (); - glFinish(); - - /* - glDisable (GL_POLYGON_OFFSET_FILL); - - // cout << "draw surfacemeshing" << endl; - // - // if (changeval != stlgeometry->GetNT()) - // BuildScene(); - // changeval = stlgeometry->GetNT(); - - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - SetLight(); - - glPushMatrix(); - glLoadMatrixf (transmat); - glMultMatrixf (rotmat); - - glShadeModel (GL_SMOOTH); - glDisable (GL_COLOR_MATERIAL); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - float mat_spec_col[] = { 1, 1, 1, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col); - - double shine = vispar.shininess; - double transp = vispar.transp; - - glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine); - glLogicOp (GL_COPY); - - - float mat_col[] = { 0.2, 0.2, 0.8, transp }; - float mat_colrt[] = { 0.2, 0.8, 0.8, transp }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - glColor3f (1.0f, 1.0f, 1.0f); - - glEnable (GL_NORMALIZE); - - // glBegin (GL_TRIANGLES); - // for (j = 1; j <= stlgeometry -> GetNT(); j++) - // { - // glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - // if (j == geomtrig) - // glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colrt); - - - // const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j); - // glNormal3f (tria.normal.X(), - // tria.normal.Y(), - // tria.normal.Z()); - - // for (k = 0; k < 3; k++) - // { - // glVertex3f (tria.pts[k].X(), - // tria.pts[k].Y(), - // tria.pts[k].Z()); - // } - // } - // glEnd (); - - - - glDisable (GL_POLYGON_OFFSET_FILL); - - float mat_colbl[] = { 0.8, 0.2, 0.2, 1 }; - float mat_cololdl[] = { 0.2, 0.8, 0.2, 1 }; - float mat_colnewl[] = { 0.8, 0.8, 0.2, 1 }; - - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glPolygonOffset (1, -1); - glLineWidth (3); - - for (i = 1; i <= loclines.Size(); i++) - { - if (i == 1) - { - glEnable (GL_POLYGON_OFFSET_FILL); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbl); - } - else if (i <= oldnl) - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_cololdl); - else - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colnewl); - - int pi1 = loclines.Get(i).I1(); - int pi2 = loclines.Get(i).I2(); - - if (pi1 >= 1 && pi2 >= 1) - { - Point3d p1 = locpoints.Get(pi1); - Point3d p2 = locpoints.Get(pi2); - - glBegin (GL_LINES); - glVertex3f (p1.X(), p1.Y(), p1.Z()); - glVertex3f (p2.X(), p2.Y(), p2.Z()); - glEnd(); - } - - glDisable (GL_POLYGON_OFFSET_FILL); - } - - - glLineWidth (1); - - - glPointSize (5); - float mat_colp[] = { 1, 0, 0, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); - glBegin (GL_POINTS); - for (i = 1; i <= locpoints.Size(); i++) - { - Point3d p = locpoints.Get(i); - glVertex3f (p.X(), p.Y(), p.Z()); - } - glEnd(); - - - glPopMatrix(); - - - float mat_col2d1[] = { 1, 0.5, 0.5, 1 }; - float mat_col2d[] = { 1, 1, 1, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); - - double scalex = 0.1, scaley = 0.1; - - glBegin (GL_LINES); - for (i = 1; i <= loclines.Size(); i++) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); - if (i == 1) - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d1); - - int pi1 = loclines.Get(i).I1(); - int pi2 = loclines.Get(i).I2(); - - if (pi1 >= 1 && pi2 >= 1) - { - Point2d p1 = plainpoints.Get(pi1); - Point2d p2 = plainpoints.Get(pi2); - - glBegin (GL_LINES); - glVertex3f (scalex * p1.X(), scaley * p1.Y(), -5); - glVertex3f (scalex * p2.X(), scaley * p2.Y(), -5); - glEnd(); - } - } - glEnd (); - - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); - glBegin (GL_POINTS); - for (i = 1; i <= plainpoints.Size(); i++) - { - Point2d p = plainpoints.Get(i); - glVertex3f (scalex * p.X(), scaley * p.Y(), -5); - } - glEnd(); - - glFinish(); -*/ - } - - - void VisualSceneSurfaceMeshing :: BuildScene (int zoomall) - { - int i, j, k; - /* - center = stlgeometry -> GetBoundingBox().Center(); - rad = stlgeometry -> GetBoundingBox().Diam() / 2; - - CalcTransformationMatrices(); - */ - } - -} - - -#else -namespace netgen -{ - void glrender (int wait) - { ; } -} -#endif diff --git a/libsrc/meshing/meshing2.hpp b/libsrc/meshing/meshing2.hpp index 4e145964..2af29296 100644 --- a/libsrc/meshing/meshing2.hpp +++ b/libsrc/meshing/meshing2.hpp @@ -29,11 +29,11 @@ derive from Meshing2, and replace transformation. class Meshing2 { /// the current advancing front - AdFront2 * adfront; + AdFront2 adfront; /// rules for mesh generation - Array rules; + Array> rules; /// statistics - Array ruleused, canuse, foundmap; + NgArray ruleused, canuse, foundmap; /// Box<3> boundingbox; /// @@ -41,12 +41,16 @@ class Meshing2 /// double maxarea; - Vec3d ex, ey; - Point3d globp1; + Vec3d ex, ey, ez; + Point<3> p1, p2; + + const NetgenGeometry& geo; public: /// - DLL_HEADER Meshing2 (const MeshingParameters & mp, const Box<3> & aboundingbox); + DLL_HEADER Meshing2 (const NetgenGeometry& geo, + const MeshingParameters & mp, + const Box<3> & aboundingbox); /// DLL_HEADER virtual ~Meshing2 (); @@ -55,15 +59,16 @@ public: void LoadRules (const char * filename, bool quad); /// - DLL_HEADER MESHING2_RESULT GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr); + DLL_HEADER MESHING2_RESULT GenerateMesh (Mesh & mesh, const MeshingParameters & mp, double gh, int facenr, int layer=1); DLL_HEADER void Delaunay (Mesh & mesh, int domainnr, const MeshingParameters & mp); DLL_HEADER void BlockFillLocalH (Mesh & mesh, const MeshingParameters & mp); /// - DLL_HEADER void AddPoint (const Point3d & p, PointIndex globind, MultiPointGeomInfo * mgi = NULL, + DLL_HEADER int AddPoint (const Point3d & p, PointIndex globind, MultiPointGeomInfo * mgi = NULL, bool pointonsurface = true); + DLL_HEADER PointIndex GetGlobalIndex(int pi) const; /// DLL_HEADER void AddBoundaryElement (INDEX i1, INDEX i2, @@ -81,19 +86,19 @@ protected: /// virtual void EndMesh (); /// - virtual double CalcLocalH (const Point3d & p, double gh) const; + virtual double CalcLocalH (const Point<3> & p, double gh) const; /// - virtual void DefineTransformation (const Point3d & p1, const Point3d & p2, + virtual void DefineTransformation (const Point<3> & p1, const Point<3> & p2, const PointGeomInfo * geominfo1, const PointGeomInfo * geominfo2); /// - virtual void TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo & geominfo, - Point2d & plainpoint, double h, int & zone); + virtual void TransformToPlain (const Point<3> & locpoint, const MultiPointGeomInfo & geominfo, + Point<2> & plainpoint, double h, int & zone); /// return 0 .. ok /// return >0 .. cannot transform point to true surface - virtual int TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, + virtual int TransformFromPlain (const Point<2>& plainpoint, + Point<3> & locpoint, PointGeomInfo & geominfo, double h); @@ -126,21 +131,21 @@ protected: /* get (projected) boundary of current chart */ - virtual void GetChartBoundary (Array & points, - Array & points3d, - Array & lines, double p) const; + virtual void GetChartBoundary (NgArray> & points, + NgArray> & points3d, + NgArray & lines, double p) const; virtual double Area () const; /** Applies 2D rules. Tests all 2D rules */ - int ApplyRules (Array & lpoints, - Array & legalpoints, + int ApplyRules (NgArray> & lpoints, + NgArray & legalpoints, int maxlegalpoint, - Array & llines, + NgArray & llines, int maxlegelline, - Array & elements, Array & dellines, + NgArray & elements, NgArray & dellines, int tolerance, const MeshingParameters & mp); diff --git a/libsrc/meshing/meshing3.cpp b/libsrc/meshing/meshing3.cpp index 1aa99c83..2d5436f3 100644 --- a/libsrc/meshing/meshing3.cpp +++ b/libsrc/meshing/meshing3.cpp @@ -76,8 +76,8 @@ Meshing3 :: ~Meshing3 () /* // was war das ???? -static double CalcLocH (const Array & locpoints, - const Array & locfaces, +static double CalcLocH (const NgArray & locpoints, + const NgArray & locfaces, double h) { return h; @@ -169,10 +169,10 @@ MESHING3_RESULT Meshing3 :: GenerateMesh (Mesh & mesh, const MeshingParameters & mp) { static Timer t("Meshing3::GenerateMesh"); RegionTimer reg(t); - static Timer meshing3_timer_a("Meshing3::GenerateMesh a", 2); - static Timer meshing3_timer_b("Meshing3::GenerateMesh b", 2); - static Timer meshing3_timer_c("Meshing3::GenerateMesh c", 1); - static Timer meshing3_timer_d("Meshing3::GenerateMesh d", 2); + // static Timer meshing3_timer_a("Meshing3::GenerateMesh a", 2); + // static Timer meshing3_timer_b("Meshing3::GenerateMesh b", 2); + // static Timer meshing3_timer_c("Meshing3::GenerateMesh c", 1); + // static Timer meshing3_timer_d("Meshing3::GenerateMesh d", 2); // static int meshing3_timer = NgProfiler::CreateTimer ("Meshing3::GenerateMesh"); // static int meshing3_timer_a = NgProfiler::CreateTimer ("Meshing3::GenerateMesh a"); // static int meshing3_timer_b = NgProfiler::CreateTimer ("Meshing3::GenerateMesh b"); @@ -181,16 +181,16 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) // NgProfiler::RegionTimer reg (meshing3_timer); - Array locpoints; // local points - Array locfaces; // local faces - Array pindex; // mapping from local to front point numbering - Array allowpoint; // point is allowed ? - Array findex; // mapping from local to front face numbering + NgArray locpoints; // local points + NgArray locfaces; // local faces + NgArray pindex; // mapping from local to front point numbering + NgArray allowpoint; // point is allowed ? + NgArray findex; // mapping from local to front face numbering //INDEX_2_HASHTABLE connectedpairs(100); // connecgted pairs for prism meshing - Array plainpoints; // points in reference coordinates - Array delpoints, delfaces; // points and lines to be deleted - Array locelements; // new generated elements + NgArray plainpoints; // points in reference coordinates + NgArray delpoints, delfaces; // points and lines to be deleted + NgArray locelements; // new generated elements int j, oldnp, oldnf; int found; @@ -211,10 +211,10 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) // for star-shaped domain meshing - Array grouppoints; - Array groupfaces; - Array grouppindex; - Array groupfindex; + NgArray grouppoints; + NgArray groupfaces; + NgArray grouppindex; + NgArray groupfindex; float minerr; @@ -223,10 +223,10 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) // int giveup = 0; - Array tempnewpoints; - Array tempnewfaces; - Array tempdelfaces; - Array templocelements; + NgArray tempnewpoints; + NgArray tempnewfaces; + NgArray tempdelfaces; + NgArray templocelements; stat.h = mp.maxh; @@ -293,13 +293,13 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) double hinner = hmax * (1 + stat.qualclass); double houter = hmax * (1 + 2 * stat.qualclass); - meshing3_timer_a.Start(); + // meshing3_timer_a.Start(); stat.qualclass = adfront -> GetLocals (baseelem, locpoints, locfaces, pindex, findex, connectedpairs, houter, hinner, locfacesplit); - meshing3_timer_a.Stop(); + // meshing3_timer_a.Stop(); // (*testout) << "locfaces = " << endl << locfaces << endl; @@ -355,7 +355,7 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) if (stat.qualclass >= mp.starshapeclass && mp.baseelnp != 4) { - NgProfiler::RegionTimer reg1 (meshing3_timer_b); + // NgProfiler::RegionTimer reg1 (meshing3_timer_b); // star-shaped domain removing grouppoints.SetSize (0); @@ -478,7 +478,7 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) } // NgProfiler::StartTimer (meshing3_timer_c); - meshing3_timer_c.Start(); + // meshing3_timer_c.Start(); found = ApplyRules (plainpoints, allowpoint, locfaces, locfacesplit, connectedpairs, @@ -488,12 +488,12 @@ GenerateMesh (Mesh & mesh, const MeshingParameters & mp) if (found >= 0) impossible = 0; if (found < 0) found = 0; - meshing3_timer_c.Stop(); + // meshing3_timer_c.Stop(); // NgProfiler::StopTimer (meshing3_timer_c); if (!found) loktestmode = 0; - NgProfiler::RegionTimer reg2 (meshing3_timer_d); + // NgProfiler::RegionTimer reg2 (meshing3_timer_d); if (loktestmode) { @@ -774,9 +774,9 @@ void Meshing3 :: BlockFill (Mesh & mesh, double gh) PrintMessage (5, "n1 = ", n1, " n2 = ", n2, " n3 = ", n3); - Array inner(n); - Array pointnr(n); - Array frontpointnr(n); + NgArray inner(n); + NgArray pointnr(n); + NgArray frontpointnr(n); // initialize inner to 1 @@ -1107,7 +1107,7 @@ void Meshing3 :: BlockFillLocalH (Mesh & mesh, PrintMessage (3, "blockfill local h"); - Array > npoints; + NgArray > npoints; adfront -> CreateTrees(); @@ -1144,11 +1144,18 @@ void Meshing3 :: BlockFillLocalH (Mesh & mesh, if (mp.maxh < maxh) maxh = mp.maxh; + auto loch_ptr = mesh.LocalHFunction().Copy(bbox); + auto & loch = *loch_ptr; + bool changed; + static Timer t1("loop1"); + t1.Start(); do { - mesh.LocalHFunction().ClearFlags(); + loch.ClearFlags(); + static Timer tbox("adfront-bbox"); + tbox.Start(); for (int i = 1; i <= adfront->GetNF(); i++) { const MiniElement2d & el = adfront->GetFace(i); @@ -1161,26 +1168,28 @@ void Meshing3 :: BlockFillLocalH (Mesh & mesh, double filld = filldist * bbox.Diam(); bbox.Increase (filld); - mesh.LocalHFunction().CutBoundary (bbox); // .PMin(), bbox.PMax()); + loch.CutBoundary (bbox); // .PMin(), bbox.PMax()); } + tbox.Stop(); // locadfront = adfront; - mesh.LocalHFunction().FindInnerBoxes (adfront, NULL); + loch.FindInnerBoxes (adfront, NULL); npoints.SetSize(0); - mesh.LocalHFunction().GetInnerPoints (npoints); + loch.GetInnerPoints (npoints); changed = false; for (int i = 1; i <= npoints.Size(); i++) { - if (mesh.LocalHFunction().GetH(npoints.Get(i)) > 1.5 * maxh) + if (loch.GetH(npoints.Get(i)) > 1.5 * maxh) { - mesh.LocalHFunction().SetH (npoints.Get(i), maxh); + loch.SetH (npoints.Get(i), maxh); changed = true; } } } while (changed); + t1.Stop(); if (debugparam.slowchecks) (*testout) << "Blockfill with points: " << endl; @@ -1208,6 +1217,8 @@ void Meshing3 :: BlockFillLocalH (Mesh & mesh, // find outer points + static Timer tloch2("build loch2"); + tloch2.Start(); loch2.ClearFlags(); for (int i = 1; i <= adfront->GetNF(); i++) @@ -1245,6 +1256,7 @@ void Meshing3 :: BlockFillLocalH (Mesh & mesh, // loch2.CutBoundary (pmin, pmax); loch2.CutBoundary (Box<3> (pmin, pmax)); // pmin, pmax); } + tloch2.Stop(); // locadfront = adfront; loch2.FindInnerBoxes (adfront, NULL); diff --git a/libsrc/meshing/meshing3.hpp b/libsrc/meshing/meshing3.hpp index 2acd209d..65b153b9 100644 --- a/libsrc/meshing/meshing3.hpp +++ b/libsrc/meshing/meshing3.hpp @@ -21,11 +21,11 @@ class Meshing3 /// current state of front AdFront3 * adfront; /// 3d generation rules - Array rules; + NgArray rules; /// counts how often a rule is used - Array ruleused, canuse, foundmap; + NgArray ruleused, canuse, foundmap; /// describes, why a rule is not applied - Array problems; + NgArray problems; /// tolerance criterion double tolfak; public: @@ -42,12 +42,12 @@ public: MESHING3_RESULT GenerateMesh (Mesh & mesh, const MeshingParameters & mp); /// - int ApplyRules (Array & lpoints, - Array & allowpoint, - Array & lfaces, INDEX lfacesplit, + int ApplyRules (NgArray & lpoints, + NgArray & allowpoint, + NgArray & lfaces, INDEX lfacesplit, INDEX_2_HASHTABLE & connectedpairs, - Array & elements, - Array & delfaces, int tolerance, + NgArray & elements, + NgArray & delfaces, int tolerance, double sloppy, int rotind1, float & retminerr); diff --git a/libsrc/meshing/meshtool.cpp b/libsrc/meshing/meshtool.cpp index b807cb1a..3368cf66 100644 --- a/libsrc/meshing/meshtool.cpp +++ b/libsrc/meshing/meshtool.cpp @@ -129,7 +129,7 @@ namespace netgen void MeshQuality2d (const Mesh & mesh) { int ncl = 20, cl; - Array incl(ncl); + NgArray incl(ncl); INDEX i; SurfaceElementIndex sei; double qual; @@ -540,7 +540,7 @@ namespace netgen /* - double CalcVolume (const Array & points, + double CalcVolume (const NgArray & points, const Element & el) { Vec3d v1 = points.Get(el.PNum(2)) - @@ -554,8 +554,8 @@ namespace netgen } */ - double CalcVolume (const Array & points, - const Array & elements) + double CalcVolume (const NgArray & points, + const NgArray & elements) { double vol; Vec3d v1, v2, v3; @@ -574,11 +574,11 @@ namespace netgen - void MeshQuality3d (const Mesh & mesh, Array * inclass) + void MeshQuality3d (const Mesh & mesh, NgArray * inclass) { int ncl = 20; signed int cl; - Array incl(ncl); + NgArray incl(ncl); INDEX i; double qual; double sum = 0; @@ -697,7 +697,7 @@ namespace netgen #ifdef OLD void Save2DMesh ( const Mesh & mesh2d, - const Array * splines, + const NgArray * splines, ostream & outfile) { @@ -964,7 +964,7 @@ namespace netgen mesh.FindOpenElements(domainnr); int np = mesh.GetNP(); - BitArrayChar ppoints(np); + Array ppoints(np); // int ndom = mesh.GetNDomains(); @@ -972,7 +972,7 @@ namespace netgen // for (k = 1; k <= ndom; k++) k = domainnr; { - ppoints.Clear(); + ppoints = false; for (i = 1; i <= mesh.GetNOpenElements(); i++) { @@ -980,7 +980,7 @@ namespace netgen if (sel.GetIndex() == k) { for (j = 1; j <= sel.GetNP(); j++) - ppoints.Set (sel.PNum(j)); + ppoints[sel.PNum(j)] = true; } } @@ -991,7 +991,7 @@ namespace netgen { int todel = 0; for (j = 0; j < el.GetNP(); j++) - if (ppoints.Test (el[j])) + if (ppoints[el[j]]) todel = 1; if (el.GetNP() != 4) diff --git a/libsrc/meshing/meshtool.hpp b/libsrc/meshing/meshtool.hpp index 1da22bb0..4bfac0e3 100644 --- a/libsrc/meshing/meshtool.hpp +++ b/libsrc/meshing/meshtool.hpp @@ -7,7 +7,7 @@ extern void MeshQuality2d (const Mesh & mesh); /// extern void MeshQuality3d (const Mesh & mesh, - Array * inclass = NULL); + NgArray * inclass = NULL); /// extern void SaveEdges (const Mesh & mesh, @@ -23,17 +23,17 @@ extern void SaveSurfaceMesh (const Mesh & mesh, /// extern void Save2DMesh ( const Mesh & mesh2d, - const Array * splines, + const NgArray * splines, ostream & outfile); */ class Surface; /// extern void SaveVolumeMesh ( - const Array & points, - const Array & elements, - const Array & volelements, - const Array & surfaces, + const NgArray & points, + const NgArray & elements, + const NgArray & volelements, + const NgArray & surfaces, char * filename); /// @@ -61,13 +61,13 @@ extern double CalcTetBadnessGrad (const Point3d & p1, const Point3d & p2, /** Calculates volume of an element. The volume of the tetrahedron el is computed */ -// extern double CalcVolume (const Array & points, +// extern double CalcVolume (const NgArray & points, // const Element & el); /** The total volume of all elements is computed. This function calculates the volume of the mesh */ -extern double CalcVolume (const Array & points, - const Array & elements); +extern double CalcVolume (const NgArray & points, + const NgArray & elements); /// extern int CheckSurfaceMesh (const Mesh & mesh); diff --git a/libsrc/meshing/meshtype.cpp b/libsrc/meshing/meshtype.cpp index 806b6c39..068c1ed8 100644 --- a/libsrc/meshing/meshtype.cpp +++ b/libsrc/meshing/meshtype.cpp @@ -7,18 +7,12 @@ namespace netgen int MultiPointGeomInfo :: AddPointGeomInfo (const PointGeomInfo & gi) { - for (int k = 0; k < cnt; k++) - if (mgi[k].trignum == gi.trignum) + for (auto & pgi : mgi) + if (pgi.trignum == gi.trignum) return 0; - if (cnt < MULTIPOINTGEOMINFO_MAX) - { - mgi[cnt] = gi; - cnt++; - return 0; - } - - throw NgException ("Please report error: MPGI Size too small\n"); + mgi.Append(gi); + return 0; } @@ -26,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 }; @@ -36,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 ); @@ -51,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 @@ -59,8 +149,8 @@ namespace netgen Segment :: Segment() : is_curved(false) { - pnums[0] = -1; - pnums[1] = -1; + pnums[0] = PointIndex::INVALID; + pnums[1] = PointIndex::INVALID; edgenr = -1; singedge_left = 0.; @@ -75,86 +165,27 @@ namespace netgen surfnr1 = -1; surfnr2 = -1; - pnums[2] = -1; + pnums[2] = PointIndex::INVALID; meshdocval = 0; - /* - geominfo[0].trignum=-1; - geominfo[1].trignum=-1; + geominfo[0].trignum=-1; + geominfo[1].trignum=-1; + /* epgeominfo[0].edgenr = 1; epgeominfo[0].dist = 0; epgeominfo[1].edgenr = 1; epgeominfo[1].dist = 0; */ - - bcname = nullptr; } - Segment::Segment (const Segment & other) - : - edgenr(other.edgenr), - singedge_left(other.singedge_left), - singedge_right(other.singedge_right), - seginfo(other.seginfo), - si(other.si), - domin(other.domin), - domout(other.domout), - tlosurf(other.tlosurf), - geominfo(), - surfnr1(other.surfnr1), - surfnr2(other.surfnr2), - epgeominfo(), - meshdocval(other.meshdocval), - is_curved(other.is_curved), - hp_elnr(other.hp_elnr) - { - for (int j = 0; j < 3; j++) - pnums[j] = other.pnums[j]; - - geominfo[0] = other.geominfo[0]; - geominfo[1] = other.geominfo[1]; - epgeominfo[0] = other.epgeominfo[0]; - epgeominfo[1] = other.epgeominfo[1]; - bcname = other.bcname; - } - - Segment& Segment::operator=(const Segment & other) - { - if (&other != this) - { - pnums[0] = other[0]; - pnums[1] = other[1]; - edgenr = other.edgenr; - singedge_left = other.singedge_left; - singedge_right = other.singedge_right; - seginfo = other.seginfo; - si = other.si; - domin = other.domin; - domout = other.domout; - tlosurf = other.tlosurf; - geominfo[0] = other.geominfo[0]; - geominfo[1] = other.geominfo[1]; - surfnr1 = other.surfnr1; - surfnr2 = other.surfnr2; - epgeominfo[0] = other.epgeominfo[0]; - epgeominfo[1] = other.epgeominfo[1]; - pnums[2] = other.pnums[2]; - meshdocval = other.meshdocval; - hp_elnr = other.hp_elnr; - bcname = other.bcname; - is_curved = other.is_curved; - } - - return *this; - } - 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 compatibility & epgeominfo[0].edgenr & epgeominfo[1].edgenr; } @@ -167,7 +198,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++) @@ -186,12 +218,12 @@ namespace netgen strongrefflag = false; is_curved = false; } - */ + Element2d :: Element2d (int anp) { for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++) { - pnum[i] = 0; + pnum[i].Invalidate(); geominfo[i].trignum = 0; } np = anp; @@ -216,7 +248,7 @@ namespace netgen { for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++) { - pnum[i] = 0; + pnum[i].Invalidate(); geominfo[i].trignum = 0; } @@ -241,9 +273,9 @@ namespace netgen pnum[2] = pi3; np = 3; typ = TRIG; - pnum[3] = 0; - pnum[4] = 0; - pnum[5] = 0; + + for (int i = 3; i < ELEMENT2D_MAXPOINTS; i++) + pnum[i].Invalidate(); for (int i = 0; i < ELEMENT2D_MAXPOINTS; i++) geominfo[i].trignum = 0; @@ -301,9 +333,9 @@ namespace netgen void Element2d :: GetBox (const T_POINTS & points, Box3d & box) const { - box.SetPoint (points.Get(pnum[0])); + box.SetPoint (points[pnum[0]]); for (unsigned i = 1; i < np; i++) - box.AddPoint (points.Get(pnum[i])); + box.AddPoint (points[pnum[i]]); } bool Element2d :: operator==(const Element2d & el2) const @@ -398,8 +430,8 @@ namespace netgen - Array ipdtrig; - Array ipdquad; + NgArray ipdtrig; + NgArray ipdquad; int Element2d :: GetNIP () const @@ -415,7 +447,7 @@ namespace netgen } void Element2d :: - GetIntegrationPoint (int ip, Point2d & p, double & weight) const + GetIntegrationPoint (int ip, Point<2> & p, double & weight) const { static double eltriqp[1][3] = { @@ -439,13 +471,13 @@ namespace netgen PrintSysError ("Element2d::GetIntegrationPoint, illegal type ", int(typ)); } - p.X() = pp[0]; - p.Y() = pp[1]; + p[0] = pp[0]; + p[1] = pp[1]; weight = pp[2]; } void Element2d :: - GetTransformation (int ip, const Array & points, + GetTransformation (int ip, const NgArray> & points, DenseMatrix & trans) const { int np = GetNP(); @@ -453,7 +485,7 @@ namespace netgen pmat.SetSize (2, np); dshape.SetSize (2, np); - Point2d p; + Point<2> p; double w; GetPointMatrix (points, pmat); @@ -498,7 +530,7 @@ namespace netgen } - void Element2d :: GetShape (const Point2d & p, Vector & shape) const + void Element2d :: GetShape (const Point<2> & p, Vector & shape) const { if (shape.Size() != GetNP()) { @@ -509,15 +541,15 @@ namespace netgen switch (typ) { case TRIG: - shape(0) = 1 - p.X() - p.Y(); - shape(1) = p.X(); - shape(2) = p.Y(); + shape(0) = 1 - p[0] - p[1]; + shape(1) = p[0]; + shape(2) = p[1]; break; case QUAD: - shape(0) = (1-p.X()) * (1-p.Y()); - shape(1) = p.X() * (1-p.Y()); - shape(2) = p.X() * p.Y(); - shape(3) = (1-p.X()) * p.Y(); + shape(0) = (1-p[0]) * (1-p[1]); + shape(1) = p[0] * (1-p[1]); + shape(2) = p[0] * p[1]; + shape(3) = (1-p[0]) * p[1]; break; default: PrintSysError ("Element2d::GetShape, illegal type ", int(typ)); @@ -587,7 +619,7 @@ namespace netgen void Element2d :: - GetDShape (const Point2d & p, DenseMatrix & dshape) const + GetDShape (const Point<2> & p, DenseMatrix & dshape) const { #ifdef DEBUG if (dshape.Height() != 2 || dshape.Width() != np) @@ -608,14 +640,14 @@ namespace netgen dshape.Elem(2, 3) = 1; break; case QUAD: - dshape.Elem(1, 1) = -(1-p.Y()); - dshape.Elem(1, 2) = (1-p.Y()); - dshape.Elem(1, 3) = p.Y(); - dshape.Elem(1, 4) = -p.Y(); - dshape.Elem(2, 1) = -(1-p.X()); - dshape.Elem(2, 2) = -p.X(); - dshape.Elem(2, 3) = p.X(); - dshape.Elem(2, 4) = (1-p.X()); + dshape.Elem(1, 1) = -(1-p[1]); + dshape.Elem(1, 2) = (1-p[1]); + dshape.Elem(1, 3) = p[1]; + dshape.Elem(1, 4) = -p[1]; + dshape.Elem(2, 1) = -(1-p[0]); + dshape.Elem(2, 2) = -p[0]; + dshape.Elem(2, 3) = p[0]; + dshape.Elem(2, 4) = (1-p[0]); break; default: @@ -664,7 +696,7 @@ namespace netgen void Element2d :: - GetPointMatrix (const Array & points, + GetPointMatrix (const NgArray> & points, DenseMatrix & pmat) const { int np = GetNP(); @@ -679,9 +711,9 @@ namespace netgen for (int i = 1; i <= np; i++) { - const Point2d & p = points.Get(PNum(i)); - pmat.Elem(1, i) = p.X(); - pmat.Elem(2, i) = p.Y(); + const auto& p = points.Get(PNum(i)); + pmat.Elem(1, i) = p[0]; + pmat.Elem(2, i) = p[1]; } } @@ -689,7 +721,7 @@ namespace netgen - double Element2d :: CalcJacobianBadness (const Array & points) const + double Element2d :: CalcJacobianBadness (const NgArray> & points) const { int i, j; int nip = GetNIP(); @@ -733,8 +765,8 @@ namespace netgen }; double Element2d :: - CalcJacobianBadnessDirDeriv (const Array & points, - int pi, Vec2d & dir, double & dd) const + CalcJacobianBadnessDirDeriv (const NgArray> & points, + int pi, Vec<2> & dir, double & dd) const { if (typ == QUAD) { @@ -743,14 +775,14 @@ namespace netgen for (int j = 0; j < 4; j++) { - const Point2d & p = points.Get( (*this)[j] ); - pmat(0, j) = p.X(); - pmat(1, j) = p.Y(); + const auto& p = points.Get( (*this)[j] ); + pmat(0, j) = p[0]; + pmat(1, j) = p[1]; } vmat = 0.0; - vmat(0, pi-1) = dir.X(); - vmat(1, pi-1) = dir.Y(); + vmat(0, pi-1) = dir[0]; + vmat(1, pi-1) = dir[1]; double err = 0; dd = 0; @@ -820,8 +852,8 @@ namespace netgen GetPointMatrix (points, pmat); vmat = 0.0; - vmat.Elem(1, pi) = dir.X(); - vmat.Elem(2, pi) = dir.Y(); + vmat.Elem(1, pi) = dir[0]; + vmat.Elem(2, pi) = dir[1]; double err = 0; @@ -885,9 +917,9 @@ namespace netgen for (i = 1; i <= GetNP(); i++) { - Point3d p = points.Get(PNum(i)); - pmat.Elem(1, i) = p.X() * t1(0) + p.Y() * t1(1) + p.Z() * t1(2); - pmat.Elem(2, i) = p.X() * t2(0) + p.Y() * t2(1) + p.Z() * t2(2); + const auto& p = points[PNum(i)]; + pmat.Elem(1, i) = p[0] * t1(0) + p[1] * t1(1) + p[2] * t1(2); + pmat.Elem(2, i) = p[0] * t2(0) + p[1] * t2(1) + p[2] * t2(2); } double err = 0; @@ -927,10 +959,10 @@ namespace netgen for (int i = 1; i <= GetNIP(); i++) { IntegrationPointData * ipd = new IntegrationPointData; - Point2d hp; + Point<2> hp; GetIntegrationPoint (i, hp, ipd->weight); - ipd->p(0) = hp.X(); - ipd->p(1) = hp.Y(); + ipd->p(0) = hp[0]; + ipd->p(1) = hp[1]; ipd->p(2) = 0; ipd->shape.SetSize(GetNP()); @@ -1006,9 +1038,8 @@ namespace netgen Element :: Element (int anp) { np = anp; - int i; - for (i = 0; i < ELEMENT_MAXPOINTS; i++) - pnum[i] = 0; + for (int i = 0; i < ELEMENT_MAXPOINTS; i++) + pnum[i].Invalidate(); index = 0; flags.marked = 1; flags.badel = 0; @@ -1057,9 +1088,8 @@ namespace netgen { SetType (type); - int i; - for (i = 0; i < ELEMENT_MAXPOINTS; i++) - pnum[i] = 0; + for (int i = 0; i < ELEMENT_MAXPOINTS; i++) + pnum[i].Invalidate(); index = 0; flags.marked = 1; flags.badel = 0; @@ -1140,6 +1170,7 @@ namespace netgen default: break; cerr << "Element::SetType unknown type " << int(typ) << endl; } + is_curved = (np > 4); } @@ -1179,17 +1210,17 @@ namespace netgen void Element :: GetBox (const T_POINTS & points, Box3d & box) const { - box.SetPoint (points.Get(PNum(1))); - box.AddPoint (points.Get(PNum(2))); - box.AddPoint (points.Get(PNum(3))); - box.AddPoint (points.Get(PNum(4))); + box.SetPoint (points[PNum(1)]); + box.AddPoint (points[PNum(2)]); + box.AddPoint (points[PNum(3)]); + box.AddPoint (points[PNum(4)]); } double Element :: Volume (const T_POINTS & points) const { - Vec<3> v1 = points.Get(PNum(2)) - points.Get(PNum(1)); - Vec<3> v2 = points.Get(PNum(3)) - points.Get(PNum(1)); - Vec<3> v3 = points.Get(PNum(4)) - points.Get(PNum(1)); + Vec<3> v1 = points[PNum(2)] - points[PNum(1)]; + Vec<3> v2 = points[PNum(3)] - points[PNum(1)]; + Vec<3> v3 = points[PNum(4)] - points[PNum(1)]; return -(Cross (v1, v2) * v3) / 6; } @@ -1282,7 +1313,7 @@ namespace netgen - void Element :: GetTets (Array & locels) const + void Element :: GetTets (NgArray & locels) const { GetTetsLocal (locels); int i, j; @@ -1291,7 +1322,7 @@ namespace netgen locels.Elem(i).PNum(j) = PNum ( locels.Elem(i).PNum(j) ); } - void Element :: GetTetsLocal (Array & locels) const + void Element :: GetTetsLocal (NgArray & locels) const { int i, j; locels.SetSize(0); @@ -1399,7 +1430,7 @@ namespace netgen #ifdef OLD - void Element :: GetNodesLocal (Array & points) const + void Element :: GetNodesLocal (NgArray & points) const { const static double tetpoints[4][3] = { { 0, 0, 0 }, @@ -1483,7 +1514,7 @@ namespace netgen } default: { - cout << "GetNodesLocal not impelemented for element " << GetType() << endl; + cout << "GetNodesLocal not implemented for element " << GetType() << endl; np = 0; } } @@ -1499,7 +1530,7 @@ namespace netgen - void Element :: GetNodesLocalNew (Array > & points) const + void Element :: GetNodesLocalNew (NgArray > & points) const { const static double tetpoints[4][3] = { @@ -1589,7 +1620,7 @@ namespace netgen } default: { - cout << "GetNodesLocal not impelemented for element " << GetType() << endl; + cout << "GetNodesLocal not implemented for element " << GetType() << endl; np = 0; pp = NULL; } @@ -1616,7 +1647,7 @@ namespace netgen - void Element :: GetSurfaceTriangles (Array & surftrigs) const + void Element :: GetSurfaceTriangles (NgArray & surftrigs) const { static int tet4trigs[][3] = { { 2, 3, 4 }, @@ -1729,8 +1760,8 @@ namespace netgen - Array< shared_ptr < IntegrationPointData > > ipdtet; - Array< shared_ptr < IntegrationPointData > > ipdtet10; + NgArray< shared_ptr < IntegrationPointData > > ipdtet; + NgArray< shared_ptr < IntegrationPointData > > ipdtet10; @@ -1835,8 +1866,6 @@ namespace netgen void Element :: GetShape (const Point<3> & hp, Vector & shape) const { - Point3d p = hp; - if (shape.Size() != GetNP()) { cerr << "Element::GetShape: Length not fitting" << endl; @@ -1847,18 +1876,18 @@ namespace netgen { case TET: { - shape(0) = 1 - p.X() - p.Y() - p.Z(); - shape(1) = p.X(); - shape(2) = p.Y(); - shape(3) = p.Z(); + shape(0) = 1 - hp[0] - hp[1] - hp[2]; + shape(1) = hp[0]; + shape(2) = hp[1]; + shape(3) = hp[2]; break; } case TET10: { - double lam1 = 1 - p.X() - p.Y() - p.Z(); - double lam2 = p.X(); - double lam3 = p.Y(); - double lam4 = p.Z(); + double lam1 = 1 - hp[0] - hp[1] - hp[2]; + double lam2 = hp[0]; + double lam3 = hp[1]; + double lam4 = hp[2]; shape(4) = 4 * lam1 * lam2; shape(5) = 4 * lam1 * lam3; @@ -1876,7 +1905,6 @@ namespace netgen case PRISM: { - Point<3> hp = p; shape(0) = hp(0) * (1-hp(2)); shape(1) = hp(1) * (1-hp(2)); shape(2) = (1-hp(0)-hp(1)) * (1-hp(2)); @@ -1887,7 +1915,6 @@ namespace netgen } case HEX: { - Point<3> hp = p; shape(0) = (1-hp(0))*(1-hp(1))*(1-hp(2)); shape(1) = ( hp(0))*(1-hp(1))*(1-hp(2)); shape(2) = ( hp(0))*( hp(1))*(1-hp(2)); @@ -2078,8 +2105,6 @@ namespace netgen void Element :: GetDShape (const Point<3> & hp, DenseMatrix & dshape) const { - Point3d p = hp; - int np = GetNP(); if (dshape.Height() != 3 || dshape.Width() != np) { @@ -2090,16 +2115,16 @@ namespace netgen double eps = 1e-6; Vector shaper(np), shapel(np); - for (int i = 1; i <= 3; i++) + for (auto i : Range(3)) { - Point3d pr(p), pl(p); - pr.X(i) += eps; - pl.X(i) -= eps; + Point<3> pr(hp), pl(hp); + pr[i] += eps; + pl[i] -= eps; GetShape (pr, shaper); GetShape (pl, shapel); for (int j = 0; j < np; j++) - dshape(i-1, j) = (shaper(j) - shapel(j)) / (2 * eps); + dshape(i, j) = (shaper(j) - shapel(j)) / (2 * eps); } } @@ -2145,7 +2170,7 @@ namespace netgen { int np = GetNP(); double eps = 1e-6; - ArrayMem mem(2*np); + NgArrayMem mem(2*np); TFlatVector shaper(np, &mem[0]); TFlatVector shapel(np, &mem[np]); // Vector shaper(np), shapel(np); @@ -2172,8 +2197,8 @@ namespace netgen template void Element2d::GetDShapeNew> (const Point<2,SIMD> &, MatrixFixWidth<2,SIMD> &) const; - template void Element :: GetShapeNew (const Point<3,double> & p, TFlatVector shape) const; - template void Element :: GetShapeNew (const Point<3,SIMD> & p, TFlatVector> shape) const; + template DLL_HEADER void Element :: GetShapeNew (const Point<3,double> & p, TFlatVector shape) const; + template DLL_HEADER void Element :: GetShapeNew (const Point<3,SIMD> & p, TFlatVector> shape) const; template void Element::GetDShapeNew (const Point<3> &, MatrixFixWidth<3> &) const; template void Element::GetDShapeNew> (const Point<3,SIMD> &, MatrixFixWidth<3,SIMD> &) const; @@ -2186,10 +2211,10 @@ namespace netgen int np = GetNP(); for (int i = 1; i <= np; i++) { - const Point3d & p = points.Get(PNum(i)); - pmat.Elem(1, i) = p.X(); - pmat.Elem(2, i) = p.Y(); - pmat.Elem(3, i) = p.Z(); + const auto& p = points[PNum(i)]; + pmat.Elem(1, i) = p[0]; + pmat.Elem(2, i) = p[1]; + pmat.Elem(3, i) = p[2]; } } @@ -2444,7 +2469,7 @@ namespace netgen domin_singular = domout_singular = 0.; // Philippose - 06/07/2009 // Initialise surface colour - surfcolour = Vec3d(0.0,1.0,0.0); + surfcolour = Vec<4>(0.0,1.0,0.0,1.0); tlosurf = -1; // bcname = 0; firstelement = -1; @@ -2467,7 +2492,7 @@ namespace netgen domout = domouti; // Philippose - 06/07/2009 // Initialise surface colour - surfcolour = Vec3d(0.0,1.0,0.0); + surfcolour = Vec<4>(0.0,1.0,0.0,1.0); tlosurf = tlosurfi; bcprop = surfnri; domin_singular = domout_singular = 0.; @@ -2482,7 +2507,7 @@ namespace netgen domout = seg.domout+1; // Philippose - 06/07/2009 // Initialise surface colour - surfcolour = Vec3d(0.0,1.0,0.0); + surfcolour = Vec<4>(0.0,1.0,0.0,1.0); tlosurf = seg.tlosurf+1; bcprop = 0; domin_singular = domout_singular = 0.; @@ -2499,7 +2524,7 @@ namespace netgen tlosurf == seg.tlosurf+1; } - string FaceDescriptor :: default_bcname = "default"; + // string FaceDescriptor :: default_bcname = "default"; /* const string & FaceDescriptor :: GetBCName () const { @@ -2512,16 +2537,15 @@ namespace netgen void FaceDescriptor :: SetBCName (string * bcn) { if (bcn) - bcname = bcn; + bcname = *bcn; else - bcn = &default_bcname; + bcname = "default"; } void FaceDescriptor :: DoArchive (Archive & ar) { ar & surfnr & domin & domout & tlosurf & bcprop - & surfcolour.X() & surfcolour.Y() & surfcolour.Z() - & bcname + & surfcolour & bcname & domin_singular & domout_singular ; // don't need: firstelement } @@ -2607,6 +2631,7 @@ namespace netgen identifiedpoints_nr.Set (tripl, 1); if (identnr > maxidentnr) maxidentnr = identnr; + names.SetSize(maxidentnr); if (identnr+1 > idpoints_table.Size()) idpoints_table.ChangeSize (identnr+1); @@ -2649,7 +2674,7 @@ namespace netgen } - void Identifications :: GetMap (int identnr, Array & identmap, bool symmetric) const + void Identifications :: GetMap (int identnr, NgArray & identmap, bool symmetric) const { identmap.SetSize (mesh.GetNP()); identmap = 0; @@ -2687,7 +2712,7 @@ namespace netgen void Identifications :: GetPairs (int identnr, - Array & identpairs) const + NgArray & identpairs) const { identpairs.SetSize(0); @@ -2815,7 +2840,9 @@ namespace netgen << " elementorder = " << elementorder << endl << " quad = " << quad << endl << " inverttets = " << inverttets << endl - << " inverttrigs = " << inverttrigs << endl; + << " inverttrigs = " << inverttrigs << endl + << "closeedge enabled = " << closeedgefac.has_value() << endl + << "closeedgefac = " << closeedgefac.value_or(0.) << endl; } /* @@ -2871,6 +2898,7 @@ namespace netgen haltsegment = 0; haltsegmentp1 = 0; haltsegmentp2 = 0; + write_mesh_on_error = false; }; diff --git a/libsrc/meshing/meshtype.hpp b/libsrc/meshing/meshtype.hpp index 5af489fd..5c174b30 100644 --- a/libsrc/meshing/meshtype.hpp +++ b/libsrc/meshing/meshtype.hpp @@ -49,7 +49,7 @@ namespace netgen #define ELEMENT2D_MAXPOINTS 8 - enum POINTTYPE { FIXEDPOINT = 1, EDGEPOINT = 2, SURFACEPOINT = 3, INNERPOINT = 4 }; + enum POINTTYPE : unsigned char { FIXEDPOINT = 1, EDGEPOINT = 2, SURFACEPOINT = 3, INNERPOINT = 4 }; enum ELEMENTTYPE { FREEELEMENT, FIXEDELEMENT }; enum OPTIMIZEGOAL { OPT_QUALITY, OPT_CONFORM, OPT_REST, OPT_WORSTCASE, OPT_LEGAL }; @@ -97,19 +97,22 @@ namespace netgen -#define MULTIPOINTGEOMINFO_MAX 100 class MultiPointGeomInfo { - int cnt; - PointGeomInfo mgi[MULTIPOINTGEOMINFO_MAX]; + ArrayMem mgi; public: - MultiPointGeomInfo () { cnt = 0; } int AddPointGeomInfo (const PointGeomInfo & gi); - void Init () { cnt = 0; } - void DeleteAll () { cnt = 0; } + void Init () { mgi.SetSize(0); } + void DeleteAll () { mgi.SetSize(0); } - int GetNPGI () const { return cnt; } + int GetNPGI () const { return mgi.Size(); } const PointGeomInfo & GetPGI (int i) const { return mgi[i-1]; } + + MultiPointGeomInfo () = default; + MultiPointGeomInfo (const MultiPointGeomInfo&) = default; + MultiPointGeomInfo (MultiPointGeomInfo &&) = default; + MultiPointGeomInfo & operator= (const MultiPointGeomInfo&) = delete; + MultiPointGeomInfo & operator= (MultiPointGeomInfo&&) = default; }; @@ -123,17 +126,9 @@ namespace netgen public: EdgePointGeomInfo () - : edgenr(0), body(0), dist(0.0), u(0.0), v(0.0) { ; } + : edgenr(-1), body(0), dist(0.0), u(0.0), v(0.0) { ; } - - EdgePointGeomInfo & operator= (const EdgePointGeomInfo & gi2) - { - edgenr = gi2.edgenr; - body = gi2.body; - dist = gi2.dist; - u = gi2.u; v = gi2.v; - return *this; - } + EdgePointGeomInfo & operator= (const EdgePointGeomInfo & gi2) = default; }; inline ostream & operator<< (ostream & ost, const EdgePointGeomInfo & gi) @@ -150,30 +145,54 @@ namespace netgen { int i; public: + class t_invalid { public: constexpr t_invalid() = default; }; + static constexpr t_invalid INVALID{}; + PointIndex () = default; PointIndex (const PointIndex&) = default; PointIndex (PointIndex &&) = default; PointIndex & operator= (const PointIndex&) = default; PointIndex & operator= (PointIndex&&) = default; - PointIndex (int ai) : i(ai) { ; } + constexpr PointIndex (int ai) : i(ai) + { +#ifdef DEBUG + if (ai < PointIndex::BASE) + cout << "illegal PointIndex, use PointIndex::INVALID instead" << endl; + // throw Exception("illegal PointIndex, use PointIndex::INVALID instead"); +#endif + } + constexpr PointIndex (t_invalid inv) : i(PointIndex::BASE-1) { ; } // PointIndex & operator= (const PointIndex &ai) { i = ai.i; return *this; } - operator int () const { return i; } + constexpr operator int () const { return i; } PointIndex operator++ (int) { PointIndex hi(*this); i++; return hi; } PointIndex operator-- (int) { PointIndex hi(*this); i--; return hi; } - PointIndex operator++ () { i++; return *this; } + 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 - enum { BASE = 0 }; + static constexpr size_t BASE = 0; #else - enum { BASE = 1 }; + static constexpr size_t BASE = 1; #endif void DoArchive (Archive & ar) { ar & i; } }; +} + +namespace ngcore +{ + template<> + constexpr netgen::PointIndex IndexBASE () { return netgen::PointIndex(netgen::PointIndex::BASE); } +} + +namespace netgen +{ + + inline istream & operator>> (istream & ist, PointIndex & pi) { int i; ist >> i; pi = PointIndex(i); return ist; @@ -193,20 +212,32 @@ 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; } + constexpr operator int () const { return i; } ElementIndex operator++ (int) { return ElementIndex(i++); } ElementIndex operator-- (int) { return ElementIndex(i--); } ElementIndex & operator++ () { ++i; return *this; } @@ -229,22 +260,24 @@ namespace netgen int i; public: SurfaceElementIndex () = default; - SurfaceElementIndex (int ai) : i(ai) { ; } + constexpr SurfaceElementIndex (int ai) : i(ai) { ; } /* SurfaceElementIndex & operator= (const SurfaceElementIndex & ai) { i = ai.i; return *this; } */ SurfaceElementIndex & operator= (const SurfaceElementIndex & ai) = default; SurfaceElementIndex & operator= (int ai) { i = ai; return *this; } - operator int () const { return i; } + constexpr operator int () const { return i; } SurfaceElementIndex operator++ (int) { SurfaceElementIndex hi(*this); i++; return hi; } SurfaceElementIndex operator-- (int) { SurfaceElementIndex hi(*this); i--; return hi; } SurfaceElementIndex & operator++ () { ++i; return *this; } SurfaceElementIndex & operator-- () { --i; return *this; } SurfaceElementIndex & operator+= (int inc) { i+=inc; return *this; } - void DoArchive (Archive & ar) { ar & i; } }; + + inline void SetInvalid (SurfaceElementIndex & id) { id = -1; } + inline bool IsInvalid (SurfaceElementIndex & id) { return id == -1; } inline istream & operator>> (istream & ist, SurfaceElementIndex & pi) { @@ -260,16 +293,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; } - SegmentIndex & operator++ (int) { i++; return *this; } - SegmentIndex & operator-- (int) { i--; return *this; } + 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; @@ -289,8 +328,8 @@ namespace netgen */ class MeshPoint : public Point<3> { - int layer; double singular; // singular factor for hp-refinement + int layer; POINTTYPE type; @@ -301,7 +340,7 @@ namespace netgen } MeshPoint (const Point<3> & ap, int alayer = 1, POINTTYPE apt = INNERPOINT) - : Point<3> (ap), layer(alayer), singular(0.),type(apt) + : Point<3> (ap), singular(0.), layer(alayer), type(apt) { ; } @@ -330,8 +369,11 @@ namespace netgen void DoArchive (Archive & ar) { - ar & x[0] & x[1] & x[2] & layer & singular; - ar & (unsigned char&)(type); + // ar & x[0] & x[1] & x[2] & layer & singular; + // ar.Do(&x[0], 3); + // ar & layer & singular; + // ar & (unsigned char&)(type); + ar.DoPacked (x[0], x[1], x[2], layer, singular, (unsigned char&)(type)); } }; @@ -343,7 +385,8 @@ namespace netgen - typedef Array T_POINTS; + // typedef NgArray T_POINTS; + typedef Array T_POINTS; @@ -358,13 +401,13 @@ 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 refflag; // marked for refinement bool badel:1; - bool refflag:1; // marked for refinement bool strongrefflag:1; bool deleted:1; // element is deleted @@ -377,20 +420,45 @@ namespace netgen unsigned int orderx:6; unsigned int ordery:6; - // #ifdef PARALLEL - // int partitionNumber; - // #endif - /// a linked list for all segments in the same face SurfaceElementIndex next; + /// + int hp_elnr; public: + static auto GetDataLayout() + { + return std::map({ + { "pnum", offsetof(Element2d, pnum)}, + { "index", offsetof(Element2d, index) }, + { "np", offsetof(Element2d, np) }, + { "refine", offsetof(Element2d, refflag) } + }); + } + /// - Element2d () = default; + DLL_HEADER Element2d (); Element2d (const Element2d &) = default; Element2d (Element2d &&) = default; Element2d & operator= (const Element2d &) = default; Element2d & operator= (Element2d &&) = default; + Element2d & operator= (initializer_list list) + { + size_t cnt = 0; + for (auto val : list) + pnum[cnt++] = val; + return *this; + } + Element2d & operator= (initializer_list> list) + { + size_t cnt = 0; + for (auto val : list) + { + pnum[cnt] = get<0>(val); + geominfo[cnt++] = get<1>(val); + } + return *this; + } /// DLL_HEADER Element2d (int anp); /// @@ -428,7 +496,7 @@ namespace netgen { #ifdef DEBUG if (typ != QUAD && typ != QUAD6 && typ != QUAD8) - PrintSysError ("element2d::GetNV not implemented for typ", typ); + PrintSysError ("element2d::GetNV not implemented for typ", int(typ)); #endif return 4; } @@ -456,12 +524,14 @@ namespace netgen /// const PointIndex & operator[] (int i) const { return pnum[i]; } - FlatArray PNums () const - { return FlatArray (np, &pnum[0]); } - FlatArray PNums () - { return FlatArray (np, &pnum[0]); } - auto Vertices() const - { return FlatArray (GetNV(), &pnum[0]); } + auto PNums () const { return FlatArray (np, &pnum[0]); } + auto PNums () { return FlatArray (np, &pnum[0]); } + template + auto PNums() const { return FlatArray (NP, &pnum[0]); } + auto Vertices() const { return FlatArray (GetNV(), &pnum[0]); } + + auto GeomInfo() const { return FlatArray (np, &geominfo[0]); } + auto GeomInfo() { return FlatArray (np, &geominfo[0]); } /// PointIndex & PNum (int i) { return pnum[i-1]; } @@ -489,15 +559,25 @@ namespace netgen if (ar.Output()) { _np = np; _typ = typ; _curved = is_curved; _vis = visible; _deleted = deleted; } - ar & _np & _typ & index & _curved & _vis & _deleted; + // ar & _np & _typ & index & _curved & _vis & _deleted; + ar.DoPacked (_np, _typ, index, _curved, _vis, _deleted); // ar & next; don't need if (ar.Input()) { np = _np; typ = ELEMENT_TYPE(_typ); is_curved = _curved; visible = _vis; deleted = _deleted; } + /* for (size_t i = 0; i < np; i++) ar & pnum[i]; + */ + static_assert(sizeof(int) == sizeof (PointIndex)); + ar.Do( (int*)&pnum[0], np); } +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif + + void SetIndex (int si) { index = si; } /// int GetIndex () const { return index; } @@ -511,13 +591,15 @@ namespace netgen void SetOrder (int ox, int oy, int /* oz */) { orderx = ox; ordery = oy;} void SetOrder (int ox, int oy) { orderx = ox; ordery = oy;} + int GetHpElnr() const { return hp_elnr; } + void SetHpElnr(int _hp_elnr) { hp_elnr = _hp_elnr; } /// void GetBox (const T_POINTS & points, Box3d & box) const; /// invert orientation inline void Invert (); /// - void Invert2 (); + DLL_HEADER void Invert2 (); /// first point number is smallest inline void NormalizeNumbering (); /// @@ -531,41 +613,41 @@ namespace netgen /// get number of 'integration points' int GetNIP () const; - void GetIntegrationPoint (int ip, Point2d & p, double & weight) const; + void GetIntegrationPoint (int ip, Point<2> & p, double & weight) const; - void GetTransformation (int ip, const Array & points, + void GetTransformation (int ip, const NgArray> & points, class DenseMatrix & trans) const; void GetTransformation (int ip, class DenseMatrix & pmat, class DenseMatrix & trans) const; - void GetShape (const Point2d & p, class Vector & shape) const; - void GetShapeNew (const Point<2> & p, class FlatVector & shape) const; + void GetShape (const Point<2> & p, class Vector & shape) const; + DLL_HEADER void GetShapeNew (const Point<2> & p, class FlatVector & shape) const; template - void GetShapeNew (const Point<2,T> & p, TFlatVector shape) const; + DLL_HEADER void GetShapeNew (const Point<2,T> & p, TFlatVector shape) const; /// matrix 2 * np - void GetDShape (const Point2d & p, class DenseMatrix & dshape) const; + DLL_HEADER void GetDShape (const Point<2> & p, class DenseMatrix & dshape) const; template - void GetDShapeNew (const Point<2,T> & p, class MatrixFixWidth<2,T> & dshape) const; + DLL_HEADER void GetDShapeNew (const Point<2,T> & p, class MatrixFixWidth<2,T> & dshape) const; /// matrix 2 * np - void GetPointMatrix (const Array & points, + void GetPointMatrix (const NgArray> & points, class DenseMatrix & pmat) const; void ComputeIntegrationPointData () const; - double CalcJacobianBadness (const Array & points) const; + double CalcJacobianBadness (const NgArray> & points) const; double CalcJacobianBadness (const T_POINTS & points, const Vec<3> & n) const; - double CalcJacobianBadnessDirDeriv (const Array & points, - int pi, Vec2d & dir, double & dd) const; + double CalcJacobianBadnessDirDeriv (const NgArray> & points, + int pi, Vec<2> & dir, double & dd) const; void Delete () { deleted = 1; - for (PointIndex & p : pnum) p.Invalidate(); + // for (PointIndex & p : pnum) p.Invalidate(); } bool IsDeleted () const @@ -579,17 +661,17 @@ namespace netgen // Philippose - 08 August 2010 // Access functions for the new property: visible - void Visible(bool vis = 1) + void Visible(bool vis = true) { visible = vis; } bool IsVisible () const { return visible; } - void SetRefinementFlag (bool rflag = 1) + void SetRefinementFlag (bool rflag = true) { refflag = rflag; } bool TestRefinementFlag () const { return refflag; } - void SetStrongRefinementFlag (bool rflag = 1) + void SetStrongRefinementFlag (bool rflag = true) { strongrefflag = rflag; } bool TestStrongRefinementFlag () const { return strongrefflag; } @@ -603,17 +685,6 @@ namespace netgen bool operator==(const Element2d & el2) const; int HasFace(const Element2d& el) const; - /// - int meshdocval; - /// - int hp_elnr; - - /* -#ifdef PARALLEL - int GetPartition () const { return partitionNumber; } - void SetPartition (int nr) { partitionNumber = nr; }; -#endif - */ }; ostream & operator<<(ostream & s, const Element2d & el); @@ -649,24 +720,10 @@ 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; - /// - class flagstruct { - public: - bool marked:1; // marked for refinement - bool badel:1; // angles worse then limit - bool reverse:1; // for refinement a la Bey - bool illegal:1; // illegal, will be split or swapped - bool illegal_valid:1; // is illegal-flag valid ? - bool badness_valid:1; // is badness valid ? - bool refflag:1; // mark element for refinement - bool strongrefflag:1; - bool deleted:1; // element is deleted, will be removed from array - bool fixed:1; // don't change element in optimization - }; + int8_t np; /// sub-domain index - short int index; + int index; /// order for hp-FEM unsigned int orderx:6; unsigned int ordery:6; @@ -677,15 +734,34 @@ namespace netgen /// stored shape-badness of element float badness; bool is_curved:1; // element is (high order) curved - - // #ifdef PARALLEL - /// number of partition for parallel computation - // int partitionNumber; - // #endif + class flagstruct { + public: + bool refflag; // mark element for refinement + bool marked:1; // marked for refinement + bool badel:1; // angles worse then limit + bool reverse:1; // for refinement a la Bey + bool illegal:1; // illegal, will be split or swapped + bool illegal_valid:1; // is illegal-flag valid ? + bool badness_valid:1; // is badness valid ? + bool strongrefflag:1; + bool deleted:1; // element is deleted, will be removed from array + bool fixed:1; // don't change element in optimization + }; - public: flagstruct flags; + int hp_elnr; + public: + + static auto GetDataLayout() + { + return std::map({ + { "pnum", offsetof(Element, pnum)}, + { "index", offsetof(Element, index) }, + { "np", offsetof(Element, np) }, + { "refine", offsetof(Element, flags.refflag) } + }); + } /// DLL_HEADER Element () = default; @@ -695,16 +771,19 @@ namespace netgen Element & operator= (Element &&) = default; /// - Element (int anp); + DLL_HEADER Element (int anp); /// - Element (ELEMENT_TYPE type); + DLL_HEADER Element (ELEMENT_TYPE type); /// // Element & operator= (const Element & el2); + + const flagstruct& Flags() const { return flags; } + flagstruct& Flags() { return flags; } /// - void SetNP (int anp); + DLL_HEADER void SetNP (int anp); /// - void SetType (ELEMENT_TYPE atyp); + DLL_HEADER void SetType (ELEMENT_TYPE atyp); /// int GetNP () const { return np; } /// @@ -728,14 +807,14 @@ namespace netgen return 8; default: // not a 3D element #ifdef DEBUG - PrintSysError ("Element3d::GetNV not implemented for typ ", typ); + PrintSysError ("Element3d::GetNV not implemented for typ ", int(typ)); #endif __assume(false); return -1; } } - bool operator==(const Element & el2) const; + DLL_HEADER bool operator==(const Element & el2) const; // old style: int NP () const { return np; } @@ -748,8 +827,12 @@ namespace netgen /// const PointIndex & operator[] (int i) const { return pnum[i]; } - FlatArray PNums () const - { return FlatArray (np, &pnum[0]); } + auto PNums () const { return FlatArray (np, &pnum[0]); } + auto PNums () { return FlatArray (np, &pnum[0]); } + template + auto PNums() const { return FlatArray (NP, &pnum[0]); } + + FlatArray Vertices() const { return { GetNV(), &pnum[0] }; } /// PointIndex & PNum (int i) { return pnum[i-1]; } @@ -763,14 +846,36 @@ namespace netgen void DoArchive (Archive & ar) { short _np, _typ; + bool _curved; if (ar.Output()) - { _np = np; _typ = typ; } - ar & _np & _typ & index; + { _np = np; _typ = typ; _curved = is_curved; } + // ar & _np & _typ & index & _curved; + ar.DoPacked (_np, _typ, index, _curved); + if (ar.Input()) - { np = _np; typ = ELEMENT_TYPE(_typ); } - for (size_t i = 0; i < np; i++) - ar & pnum[i]; + { + np = _np; + typ = ELEMENT_TYPE(_typ); + is_curved = _curved; + flags.marked = 1; + flags.badel = 0; + flags.reverse = 0; + flags.illegal = 0; + flags.illegal_valid = 0; + flags.badness_valid = 0; + flags.refflag = 1; + flags.strongrefflag = false; + flags.deleted = 0; + flags.fixed = 0; + } + + static_assert(sizeof(int) == sizeof (PointIndex)); + ar.Do( (int*)&pnum[0], np); } + +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif /// void SetIndex (int si) { index = si; } @@ -807,7 +912,7 @@ namespace netgen return 6; default: #ifdef DEBUG - PrintSysError ("element3d::GetNFaces not implemented for typ", typ) + PrintSysError ("element3d::GetNFaces not implemented for typ", int(typ)) #endif ; } @@ -816,20 +921,23 @@ namespace netgen /// inline void GetFace (int i, Element2d & face) const; /// - void GetFace2 (int i, Element2d & face) const; + DLL_HEADER void GetFace2 (int i, Element2d & face) const; /// - void Invert (); + DLL_HEADER void Invert (); + + int GetHpElnr() const { return hp_elnr; } + void SetHpElnr(int _hp_elnr) { hp_elnr = _hp_elnr; } /// split into 4 node tets - void GetTets (Array & locels) const; + void GetTets (NgArray & locels) const; /// split into 4 node tets, local point nrs - void GetTetsLocal (Array & locels) const; + void GetTetsLocal (NgArray & locels) const; /// returns coordinates of nodes - // void GetNodesLocal (Array > & points) const; - void GetNodesLocalNew (Array > & points) const; + // void GetNodesLocal (NgArray > & points) const; + void GetNodesLocalNew (NgArray > & points) const; /// split surface into 3 node trigs - void GetSurfaceTriangles (Array & surftrigs) const; + DLL_HEADER void GetSurfaceTriangles (NgArray & surftrigs) const; /// get number of 'integration points' @@ -844,7 +952,7 @@ namespace netgen void GetShape (const Point<3> & p, class Vector & shape) const; // void GetShapeNew (const Point<3> & p, class FlatVector & shape) const; template - void GetShapeNew (const Point<3,T> & p, TFlatVector shape) const; + DLL_HEADER void GetShapeNew (const Point<3,T> & p, TFlatVector shape) const; /// matrix 2 * np void GetDShape (const Point<3> & p, class DenseMatrix & dshape) const; template @@ -904,16 +1012,6 @@ namespace netgen bool IsCurved () const { return is_curved; } void SetCurved (bool acurved) { is_curved = acurved; } - /* -#ifdef PARALLEL - int GetPartition () const { return partitionNumber; } - void SetPartition (int nr) { partitionNumber = nr; }; -#else - int GetPartition () const { return 0; } -#endif - */ - - int hp_elnr; }; ostream & operator<<(ostream & s, const Element & el); @@ -931,10 +1029,7 @@ namespace netgen public: /// DLL_HEADER Segment(); - DLL_HEADER Segment (const Segment& other); - - ~Segment() - { ; } + Segment (const Segment& other) = default; // friend ostream & operator<<(ostream & s, const Segment & seg); @@ -950,7 +1045,7 @@ namespace netgen /// surface decoding index int si; - /// co dim 2 deconding index + /// co dim 2 decoding index int cd2i; /// domain number inner side int domin; @@ -970,16 +1065,8 @@ namespace netgen /// int meshdocval; - // #ifdef PARALLEL - /// number of partition for parallel computation - // int partitionNumber; - // #endif - - private: - string* bcname; bool is_curved; - - public: + int hp_elnr; /* PointIndex operator[] (int i) const { return (i == 0) ? p1 : p2; } @@ -988,37 +1075,22 @@ namespace netgen { return (i == 0) ? p1 : p2; } */ - Segment& operator=(const Segment & other); + Segment& operator=(const Segment & other) = default; - 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] < 0) ? 2 : 3; + 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] < 0) ? SEGMENT : SEGMENT3; + return pnums[2].IsValid() ? SEGMENT3 : SEGMENT; } PointIndex & operator[] (int i) { return pnums[i]; } @@ -1027,17 +1099,12 @@ namespace netgen bool IsCurved () const { return is_curved; } void SetCurved (bool acurved) { is_curved = acurved; } - - /* -#ifdef PARALLEL - int GetPartition () const { return partitionNumber; } - void SetPartition (int nr) { partitionNumber = nr; }; -#else - int GetPartition () const { return 0; } -#endif - */ void DoArchive (Archive & ar); +#ifdef PARALLEL + static MPI_Datatype MyGetMPIType(); +#endif + }; ostream & operator<<(ostream & s, const Segment & seg); @@ -1076,11 +1143,12 @@ namespace netgen // Add capability to store surface colours along with // other face data /// surface colour (Default: R=0.0 ; G=1.0 ; B=0.0) - Vec3d surfcolour; + Vec<4> surfcolour; /// - static string default_bcname; - string * bcname = &default_bcname; + // static string default_bcname; + // string * bcname = &default_bcname; + string bcname = "default"; /// root of linked list SurfaceElementIndex firstelement; @@ -1108,18 +1176,19 @@ namespace netgen // Philippose - 06/07/2009 // Get Surface colour - Vec3d SurfColour () const { return surfcolour; } - DLL_HEADER const string & GetBCName () const { return *bcname; } + Vec<4> SurfColour () const { return surfcolour; } + /* DLL_HEADER */ const string & GetBCName () const { return bcname; } // string * BCNamePtr () { return bcname; } // const string * BCNamePtr () const { return bcname; } void SetSurfNr (int sn) { surfnr = sn; } void SetDomainIn (int di) { domin = di; } void SetDomainOut (int dom) { domout = dom; } void SetBCProperty (int bc) { bcprop = bc; } - void SetBCName (string * bcn); // { bcname = bcn; } + DLL_HEADER void SetBCName (string * bcn); // { bcname = bcn; } + void SetBCName (const string & bcn) { bcname = bcn; } // Philippose - 06/07/2009 // Set the surface colour - void SetSurfColour (Vec3d colour) { surfcolour = colour; } + void SetSurfColour (Vec<4> colour) { surfcolour = colour; } void SetDomainInSingular (double v) { domin_singular = v; } void SetDomainOutSingular (double v) { domout_singular = v; } @@ -1133,6 +1202,7 @@ namespace netgen ostream & operator<< (ostream & s, const FaceDescriptor & fd); + class EdgeDescriptor { @@ -1162,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; /** @@ -1179,13 +1250,13 @@ namespace netgen // P .. plot, pause // c .. combine **/ - string optimize2d = "smsmsmSmSmSm"; + string optimize2d = "smcmSmcmSmcm"; /// number of 2d optimization steps int optsteps2d = 3; /// power of error (to approximate max err optimization) double opterrpow = 2; /// do block filling ? - int blockfill = 1; + bool blockfill = true; /// block filling up to distance double filldist = 0.1; /// radius of local environment (times h) @@ -1193,31 +1264,35 @@ namespace netgen /// radius of active environment (times h) double relinnersafety = 3; /// use local h ? - int uselocalh = 1; + bool uselocalh = true; /// grading for local h double grading = 0.3; - /// use delaunay meshing - int delaunay = 1; + /// 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 double minh = 0.0; /// file for meshsize string meshsizefilename = ""; + /// restrict h based on close edges + optional closeedgefac = nullopt; /// start surfacemeshing from everywhere in surface - int startinsurface = 0; + bool startinsurface = false; /// check overlapping surfaces (debug) - int checkoverlap = 1; + bool checkoverlap = true; /// check overlapping surface mesh before volume meshing - int checkoverlappingboundary = 1; + bool checkoverlappingboundary = true; /// check chart boundary (sometimes too restrictive) - int checkchartboundary = 1; + bool checkchartboundary = true; /// safety factor for curvatures (elements per radius) double curvaturesafety = 2; /// minimal number of segments per edge double segmentsperedge = 1; /// use parallel threads - int parthread = 0; + bool parthread = 0; /// weight of element size w.r.t element shape double elsizeweight = 0.2; /// init with default values @@ -1233,6 +1308,9 @@ namespace netgen int giveuptol2d = 200; /// give up quality class, 3d meshing int giveuptol = 10; + /// give up quality class for closing open quads, > 100 for + /// free pyramids + int giveuptolopenquads = 15; /// maximal outer steps int maxoutersteps = 10; /// class starting star-shape filling @@ -1245,24 +1323,29 @@ namespace netgen /// limit for max element angle (150-180) double badellimit = 175; - bool check_impossible = 0; + bool check_impossible = false; int only3D_domain_nr = 0; /// - int secondorder = 0; + bool secondorder = false; /// high order element curvature int elementorder = 1; /// quad-dominated surface meshing - int quad = 0; + bool quad = false; /// bool try_hexes = false; /// - int inverttets = 0; + bool inverttets = false; /// - int inverttrigs = 0; + bool inverttrigs = false; /// - int autozrefine = 0; + bool autozrefine = false; + + bool parallel_meshing = true; + int nthreads = 4; + + Flags geometrySpecificParameters; /// MeshingParameters (); /// @@ -1280,6 +1363,7 @@ namespace netgen public: Point<3> pnt; double h; + int layer = 1; MeshSizePoint (Point<3> _pnt, double _h) : pnt(_pnt), h(_h) { ; } MeshSizePoint () = default; MeshSizePoint (const MeshSizePoint &) = default; @@ -1287,10 +1371,10 @@ namespace netgen MeshSizePoint & operator= (const MeshSizePoint &) = default; MeshSizePoint & operator= (MeshSizePoint &&) = default; }; - Array meshsize_points; + NgArray meshsize_points; void (*render_function)(bool) = NULL; - void Render(bool blocking = false) + void Render(bool blocking = false) const { if (render_function) (*render_function)(blocking); @@ -1333,6 +1417,8 @@ namespace netgen /// int haltfacenr; /// + bool write_mesh_on_error; + /// DebugParameters (); }; @@ -1426,10 +1512,11 @@ namespace netgen /// sorted by identification nr TABLE idpoints_table; - Array type; + NgArray type; /// number of identifications (or, actually used identifications ?) int maxidentnr; + Array names; public: /// @@ -1444,7 +1531,12 @@ namespace netgen identification nr identnr */ DLL_HEADER void Add (PointIndex pi1, PointIndex pi2, int identnr); - + void Add (PointIndex pi1, PointIndex pi2, string name, ID_TYPE type) + { + auto nr = GetNr(name); + Add(pi1, pi2, nr); + SetType(nr, type); + } int Get (PointIndex pi1, PointIndex pi2) const; int GetSymmetric (PointIndex pi1, PointIndex pi2) const; @@ -1472,7 +1564,7 @@ namespace netgen } /// - void GetMap (int identnr, Array & identmap, bool symmetric = false) const; + void GetMap (int identnr, NgArray & identmap, bool symmetric = false) const; /// ID_TYPE GetType(int identnr) const { @@ -1489,10 +1581,17 @@ namespace netgen } /// - void GetPairs (int identnr, Array & identpairs) const; + DLL_HEADER void GetPairs (int identnr, NgArray & identpairs) const; /// int GetMaxNr () const { return maxidentnr; } + int GetNr(string name) + { + if(!names.Contains(name)) + names.Append(name); + return names.Pos(name)+1; + } + /// remove secondorder void SetMaxPointNr (int maxpnum); @@ -1503,6 +1602,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/msghandler.cpp b/libsrc/meshing/msghandler.cpp index abb7a20f..1b19d2a1 100644 --- a/libsrc/meshing/msghandler.cpp +++ b/libsrc/meshing/msghandler.cpp @@ -3,7 +3,7 @@ namespace netgen { -int printmessage_importance = 5; + // int printmessage_importance = 3; int printwarnings = 1; int printerrors = 1; int printdots = 1; @@ -112,8 +112,8 @@ void PrintTime(const MyStr& s1, const MyStr& s2, const MyStr& s3, const MyStr& s } -static Array msgstatus_stack(0); -static Array threadpercent_stack(0); +static NgArray msgstatus_stack(0); +static NgArray threadpercent_stack(0); static MyStr msgstatus = ""; diff --git a/libsrc/meshing/netrule2.cpp b/libsrc/meshing/netrule2.cpp index ace0d0f8..238adb7f 100644 --- a/libsrc/meshing/netrule2.cpp +++ b/libsrc/meshing/netrule2.cpp @@ -6,18 +6,20 @@ namespace netgen netrule :: netrule () { - name = new char[1]; - name[0] = char(0); + // name = new char[1]; + // name[0] = char(0); quality = 0; } netrule :: ~netrule() { - delete [] name; + // delete [] name; + /* for(int i = 0; i < oldutofreearea_i.Size(); i++) delete oldutofreearea_i[i]; for(int i = 0; i < freezone_i.Size(); i++) delete freezone_i[i]; + */ } @@ -37,13 +39,13 @@ void netrule :: SetFreeZoneTransformation (const Vector & devp, int tolclass) if (tolclass <= oldutofreearea_i.Size()) { - oldutofreearea_i[tolclass-1] -> Mult (devp, devfree); + oldutofreearea_i[tolclass-1].Mult (devp, devfree); - Array & fzi = *freezone_i[tolclass-1]; + auto& fzi = freezone_i[tolclass-1]; for (int i = 0; i < fzs; i++) { - transfreezone[i].X() = fzi[i].X() + devfree[2*i]; - transfreezone[i].Y() = fzi[i].Y() + devfree[2*i+1]; + transfreezone[i][0] = fzi[i][0] + devfree[2*i]; + transfreezone[i][1] = fzi[i][1] + devfree[2*i+1]; } } else @@ -57,32 +59,32 @@ void netrule :: SetFreeZoneTransformation (const Vector & devp, int tolclass) for (int i = 0; i < fzs; i++) { - transfreezone[i].X() = lam1 * freezone[i].X() + lam2 * freezonelimit[i].X() + devfree[2*i]; - transfreezone[i].Y() = lam1 * freezone[i].Y() + lam2 * freezonelimit[i].Y() + devfree[2*i+1]; + transfreezone[i][0] = lam1 * freezone[i][0] + lam2 * freezonelimit[i][0] + devfree[2*i]; + transfreezone[i][1] = lam1 * freezone[i][1] + lam2 * freezonelimit[i][1] + devfree[2*i+1]; } } if (fzs > 0) { - fzmaxx = fzminx = transfreezone[0].X(); - fzmaxy = fzminy = transfreezone[0].Y(); + fzmaxx = fzminx = transfreezone[0][0]; + fzmaxy = fzminy = transfreezone[0][1]; } for (int i = 1; i < fzs; i++) { - if (transfreezone[i].X() > fzmaxx) fzmaxx = transfreezone[i].X(); - if (transfreezone[i].X() < fzminx) fzminx = transfreezone[i].X(); - if (transfreezone[i].Y() > fzmaxy) fzmaxy = transfreezone[i].Y(); - if (transfreezone[i].Y() < fzminy) fzminy = transfreezone[i].Y(); + if (transfreezone[i][0] > fzmaxx) fzmaxx = transfreezone[i][0]; + if (transfreezone[i][0] < fzminx) fzminx = transfreezone[i][0]; + if (transfreezone[i][1] > fzmaxy) fzmaxy = transfreezone[i][1]; + if (transfreezone[i][1] < fzminy) fzminy = transfreezone[i][1]; } for (int i = 0; i < fzs; i++) { - Point2d p1 = transfreezone[i]; - Point2d p2 = transfreezone[(i+1) % fzs]; + const auto& p1 = transfreezone[i]; + const auto& p2 = transfreezone[(i+1) % fzs]; - Vec2d vn (p2.Y() - p1.Y(), p1.X() - p2.X()); + Vec<2> vn = { p2[1] - p1[1], p1[0] - p2[0] }; double len2 = vn.Length2(); @@ -96,9 +98,9 @@ void netrule :: SetFreeZoneTransformation (const Vector & devp, int tolclass) { vn /= sqrt (len2); // scaling necessary ? - freesetinequ(i,0) = vn.X(); - freesetinequ(i,1) = vn.Y(); - freesetinequ(i,2) = -(p1.X() * vn.X() + p1.Y() * vn.Y()); + freesetinequ(i,0) = vn[0]; + freesetinequ(i,1) = vn[1]; + freesetinequ(i,2) = -(p1[0] * vn[0] + p1[1] * vn[1]); } } } @@ -110,45 +112,45 @@ int netrule :: IsInFreeZone2 (const Point2d & p) const for (int i = 0; i < transfreezone.Size(); i++) { if (freesetinequ(i, 0) * p.X() + - freesetinequ(i, 1) * p.Y() + + freesetinequ(i, 1) * p[1] + freesetinequ(i, 2) > 0) return 0; } return 1; } */ -int netrule :: IsLineInFreeZone2 (const Point2d & p1, const Point2d & p2) const +int netrule :: IsLineInFreeZone2 (const Point<2> & p1, const Point<2> & p2) const { - if ( (p1.X() > fzmaxx && p2.X() > fzmaxx) || - (p1.X() < fzminx && p2.X() < fzminx) || - (p1.Y() > fzmaxy && p2.Y() > fzmaxy) || - (p1.Y() < fzminy && p2.Y() < fzminy) ) return 0; + if ( (p1[0] > fzmaxx && p2[0] > fzmaxx) || + (p1[0] < fzminx && p2[0] < fzminx) || + (p1[1] > fzmaxy && p2[1] > fzmaxy) || + (p1[1] < fzminy && p2[1] < fzminy) ) return 0; for (int i = 1; i <= transfreezone.Size(); i++) { - if (freesetinequ.Get(i, 1) * p1.X() + freesetinequ.Get(i, 2) * p1.Y() + + if (freesetinequ.Get(i, 1) * p1[0] + freesetinequ.Get(i, 2) * p1[1] + freesetinequ.Get(i, 3) > -1e-8 && // -1e-6 - freesetinequ.Get(i, 1) * p2.X() + freesetinequ.Get(i, 2) * p2.Y() + + freesetinequ.Get(i, 1) * p2[0] + freesetinequ.Get(i, 2) * p2[1] + freesetinequ.Get(i, 3) > -1e-8 // -1e-6 ) return 0; } - double nx = (p2.Y() - p1.Y()); - double ny = -(p2.X() - p1.X()); + double nx = (p2[1] - p1[1]); + double ny = -(p2[0] - p1[0]); double nl = sqrt (nx * nx + ny * ny); if (nl > 1e-8) { nx /= nl; ny /= nl; - double c = - (p1.X() * nx + p1.Y() * ny); + double c = - (p1[0] * nx + p1[1] * ny); bool allleft = true; bool allright = true; for (int i = 1; i <= transfreezone.Size(); i++) { - bool left = transfreezone.Get(i).X() * nx + transfreezone.Get(i).Y() * ny + c < 1e-7; - bool right = transfreezone.Get(i).X() * nx + transfreezone.Get(i).Y() * ny + c > -1e-7; + bool left = transfreezone.Get(i)[0] * nx + transfreezone.Get(i)[1] * ny + c < 1e-7; + bool right = transfreezone.Get(i)[0] * nx + transfreezone.Get(i)[1] * ny + c > -1e-7; if (!left) allleft = false; if (!right) allright = false; } @@ -187,33 +189,12 @@ float netrule :: CalcPointDist (int pi, const Point2d & p) const } */ -float netrule :: CalcLineError (int li, const Vec2d & v) const +float netrule :: CalcLineError (int li, const Vec<2> & v) const { - float dx = v.X() - linevecs.Get(li).X(); - float dy = v.Y() - linevecs.Get(li).Y(); + float dx = v[0] - linevecs.Get(li)[0]; + float dy = v[1] - linevecs.Get(li)[1]; const threefloat * ltf = &linetolerances.Get(li); return ltf->f1 * dx * dx + ltf->f2 * dx * dy + ltf->f3 * dy * dy; } - - - - -/* -int GetNRules () - { - return rules.Size(); - } -*/ - - - - - - - - - - - -} +} // namespace netgen diff --git a/libsrc/meshing/netrule3.cpp b/libsrc/meshing/netrule3.cpp index de0e35e4..956b6309 100644 --- a/libsrc/meshing/netrule3.cpp +++ b/libsrc/meshing/netrule3.cpp @@ -77,6 +77,7 @@ void vnetrule :: SetFreeZoneTransformation (const Vector & allp, int tolclass) fzbox.SetPoint (transfreezone.Elem(1)); for (i = 2; i <= freezone.Size(); i++) fzbox.AddPoint (transfreezone.Elem(i)); + fzbox.IncreaseRel(1e-8); // MARK(setfz3); @@ -84,7 +85,7 @@ void vnetrule :: SetFreeZoneTransformation (const Vector & allp, int tolclass) for (fs = 1; fs <= freesets.Size(); fs++) { - Array & freesetfaces = *freefaces.Get(fs); + NgArray & freesetfaces = *freefaces.Get(fs); DenseMatrix & freesetinequ = *freefaceinequ.Get(fs); for (i = 1; i <= freesetfaces.Size(); i++) @@ -142,9 +143,9 @@ int vnetrule :: ConvexFreeZone () const { const DenseMatrix & freesetinequ = *freefaceinequ.Get(fs); - // const Array & freeset = *freesets.Get(fs); - const Array & freesetedges = *freeedges.Get(fs); - // const Array & freesetfaces = *freefaces.Get(fs); + // const NgArray & freeset = *freesets.Get(fs); + const NgArray & freesetedges = *freeedges.Get(fs); + // const NgArray & freesetfaces = *freefaces.Get(fs); for (i = 1; i <= freesetedges.Size(); i++) { @@ -175,7 +176,7 @@ int vnetrule :: IsInFreeZone (const Point3d & p) const for (fs = 1; fs <= freesets.Size(); fs++) { inthis = 1; - Array & freesetfaces = *freefaces.Get(fs); + NgArray & freesetfaces = *freefaces.Get(fs); DenseMatrix & freesetinequ = *freefaceinequ.Get(fs); for (i = 1; i <= freesetfaces.Size() && inthis; i++) @@ -195,13 +196,13 @@ int vnetrule :: IsInFreeZone (const Point3d & p) const int vnetrule :: IsTriangleInFreeZone (const Point3d & p1, const Point3d & p2, const Point3d & p3, - const Array & pi, int newone) + const NgArray & pi, int newone) { int fs; int infreeset, cannot = 0; - ArrayMem pfi(3), pfi2(3); + NgArrayMem pfi(3), pfi2(3); // convert from local index to freeset index int i, j; @@ -218,7 +219,7 @@ int vnetrule :: IsTriangleInFreeZone (const Point3d & p1, for (fs = 1; fs <= freesets.Size(); fs++) { - const Array & freeseti = *freesets.Get(fs); + const NgArray & freeseti = *freesets.Get(fs); for (i = 1; i <= 3; i++) { pfi2.Elem(i) = 0; @@ -239,7 +240,7 @@ int vnetrule :: IsTriangleInFreeZone (const Point3d & p1, int vnetrule :: IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2, const Point3d & p3, int fs, - const Array & pi, int newone) + const NgArray & pi, int newone) { int i, ii; Vec3d n; @@ -251,13 +252,13 @@ int vnetrule :: IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2, double hpx, hpy, hpz, v1x, v1y, v1z, v2x, v2y, v2z; int act1, act2, act3, it; int cntout; - Array activefaces; + NgArray activefaces; int isin; // MARK(triinfz); - Array & freesetfaces = *freefaces.Get(fs); + NgArray & freesetfaces = *freefaces.Get(fs); DenseMatrix & freesetinequ = *freefaceinequ.Get(fs); @@ -575,7 +576,7 @@ int vnetrule :: IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2, case 3: trivec = (p3 - p2); break; } - Array lpi(freezonepi.Size()); + NgArray lpi(freezonepi.Size()); for (i = 1; i <= lpi.Size(); i++) lpi.Elem(i) = 0; lpi.Elem(pi1) = 1; @@ -614,7 +615,7 @@ int vnetrule :: IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2, { // MARK(triinfz3); - Array lpi(freezonepi.Size()); + NgArray lpi(freezonepi.Size()); for (i = 1; i <= lpi.Size(); i++) lpi.Elem(i) = 0; @@ -862,13 +863,13 @@ int vnetrule :: IsQuadInFreeZone (const Point3d & p1, const Point3d & p2, const Point3d & p3, const Point3d & p4, - const Array & pi, int newone) + const NgArray & pi, int newone) { int fs; int infreeset, cannot = 0; - ArrayMem pfi(4), pfi2(4); + NgArrayMem pfi(4), pfi2(4); // convert from local index to freeset index int i, j; @@ -885,7 +886,7 @@ int vnetrule :: IsQuadInFreeZone (const Point3d & p1, for (fs = 1; fs <= freesets.Size(); fs++) { - const Array & freeseti = *freesets.Get(fs); + const NgArray & freeseti = *freesets.Get(fs); for (i = 1; i <= 4; i++) { pfi2.Elem(i) = 0; @@ -905,7 +906,7 @@ int vnetrule :: IsQuadInFreeZone (const Point3d & p1, int vnetrule :: IsQuadInFreeSet (const Point3d & p1, const Point3d & p2, const Point3d & p3, const Point3d & p4, - int fs, const Array & pi, int newone) + int fs, const NgArray & pi, int newone) { int i; @@ -931,7 +932,7 @@ int vnetrule :: IsQuadInFreeSet (const Point3d & p1, const Point3d & p2, return 1; } - ArrayMem pi3(3); + NgArrayMem pi3(3); int res; pi3.Elem(1) = pi.Get(1); @@ -985,9 +986,9 @@ float vnetrule :: CalcPointDist (int pi, const Point3d & p) const int vnetrule :: TestOk () const { - Array cntpused(points.Size()); - Array edge1, edge2; - Array delf(faces.Size()); + NgArray cntpused(points.Size()); + NgArray edge1, edge2; + NgArray delf(faces.Size()); int i, j, k; int pi1, pi2; int found; diff --git a/libsrc/meshing/parallelmesh.cpp b/libsrc/meshing/parallelmesh.cpp index 1e2061d9..185c9eab 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 number + 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,48 +208,67 @@ namespace netgen void Mesh :: SendMesh () const { - Array sendrequests; - + static Timer tsend("SendMesh"); RegionTimer reg(tsend); + static Timer tbuildvertex("SendMesh::BuildVertex"); + static Timer tbuildvertexa("SendMesh::BuildVertex a"); + static Timer tbuildvertexb("SendMesh::BuildVertex b"); + static Timer tbuilddistpnums("SendMesh::Build_distpnums"); + static Timer tbuildelementtable("SendMesh::Build_elementtable"); + 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(); - const_cast(GetTopology()).Update(); + // If the topology is not already updated, we do not need to + // build edges/faces. + auto & top = const_cast(GetTopology()); + if(top.NeedsUpdate()) { + top.SetBuildVertex2Element(false); + top.SetBuildEdges(false); + top.SetBuildFaces(false); + top.Update(); + } PrintMessage ( 3, "Sending nr of elements"); 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"); 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; + } + - Array num_segs_on_proc(ntasks); + NgArray num_segs_on_proc(ntasks); num_segs_on_proc = 0; for (SegmentIndex ei = 0; ei < GetNSeg(); ei++) // num_segs_on_proc[(*this)[ei].GetPartition()]++; @@ -141,8 +298,8 @@ namespace netgen **/ /** First, we build tables for vertex identification. **/ - Array per_pairs; - Array pp2; + NgArray per_pairs; + NgArray pp2; auto & idents = GetIdentifications(); bool has_periodic = false; for (int idnr = 1; idnr < idents.GetMaxNr()+1; idnr++) @@ -152,7 +309,7 @@ namespace netgen idents.GetPairs(idnr, pp2); per_pairs.Append(pp2); } - Array npvs(GetNV()); + NgArray npvs(GetNV()); npvs = 0; for (int k = 0; k < per_pairs.Size(); k++) { npvs[per_pairs[k].I1()]++; @@ -171,7 +328,7 @@ namespace netgen /** The same table as per_verts, but TRANSITIVE!! **/ auto iterate_per_verts_trans = [&](auto f){ - Array allvs; + NgArray allvs; for (int k = PointIndex::BASE; k < GetNV()+PointIndex::BASE; k++) { allvs.SetSize(0); @@ -207,39 +364,41 @@ namespace netgen } /** Now we build the vertex-data to send to the workers. **/ - Array vert_flag (GetNV()); - Array num_procs_on_vert (GetNV()); - Array num_verts_on_proc (ntasks); + tbuildvertex.Start(); + NgArray vert_flag (GetNV()); + NgArray num_procs_on_vert (GetNV()); + NgArray num_verts_on_proc (ntasks); num_verts_on_proc = 0; num_procs_on_vert = 0; + auto iterate_vertices = [&](auto f) { vert_flag = -1; for (int dest = 1; dest < ntasks; 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); - } - 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); - } - FlatArray segs = segs_of_proc[dest]; + for (auto ei : els_of_proc[dest]) + for (auto pnum : (*this)[ei].PNums()) + f(pnum, 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++) { const Segment & el = (*this) [segs[hi]]; for (int i = 0; i < 2; i++) f(el[i], dest); } + */ + for (auto segi : segs_of_proc[dest]) + for (auto pnum : (*this)[segi].PNums()) + f(pnum, dest); } }; /** count vertices per proc and procs per vertex **/ + tbuildvertexa.Start(); iterate_vertices([&](auto vertex, auto dest){ auto countit = [&] (auto vertex, auto dest) { if (vert_flag[vertex] < dest) @@ -247,14 +406,17 @@ namespace netgen vert_flag[vertex] = dest; num_verts_on_proc[dest]++; num_procs_on_vert[vertex]++; - GetParallelTopology().SetDistantPNum (dest, vertex); } }; countit(vertex, dest); - auto pers = per_verts_trans[vertex]; - for(int j = 0; j < pers.Size(); j++) - countit(pers[j], dest); + for (auto v : per_verts_trans[vertex]) + countit(v, dest); }); + tbuildvertexa.Stop(); + + + tbuildvertexb.Start(); + TABLE verts_of_proc (num_verts_on_proc); TABLE procs_of_vert (num_procs_on_vert); TABLE loc_num_of_vert (num_procs_on_vert); @@ -268,55 +430,55 @@ namespace netgen } }; addit(vertex, dest); - auto pers = per_verts_trans[vertex]; - for(int j = 0; j < pers.Size(); j++) - addit(pers[j], dest); + for (auto v : per_verts_trans[vertex]) + addit(v, dest); }); + tbuildvertexb.Stop(); /** local vertex numbers on distant procs (I think this was only used for debugging??) **/ for (int vert = 1; vert <= GetNP(); vert++ ) { - FlatArray procs = procs_of_vert[vert]; + NgFlatArray procs = procs_of_vert[vert]; for (int j = 0; j < procs.Size(); j++) { int dest = procs[j]; - verts_of_proc.Add (dest, vert); + // !! we also use this as offsets for MPI-type, if this is changed, also change ReceiveParallelMesh + verts_of_proc.Add (dest, vert - IndexBASE()); loc_num_of_vert.Add (vert, verts_of_proc[dest].Size()); } } + tbuildvertex.Stop(); PrintMessage ( 3, "Sending Vertices - vertices"); + Array point_types(ntasks-1); for (int dest = 1; dest < ntasks; dest++) { - FlatArray verts = verts_of_proc[dest]; - sendrequests.Append (MyMPI_ISend (verts, dest, MPI_TAG_MESH+1, comm)); + NgFlatArray verts = verts_of_proc[dest]; + // 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(); int numv = verts.Size(); - MPI_Datatype newtype; - Array blocklen (numv); + NgArray blocklen (numv); blocklen = 1; - MPI_Type_indexed (numv, &blocklen[0], - reinterpret_cast (&verts[0]), - mptype, &newtype); - MPI_Type_commit (&newtype); + MPI_Type_indexed (numv, (numv == 0) ? nullptr : &blocklen[0], + (numv == 0) ? nullptr : reinterpret_cast (&verts[0]), + mptype, &point_types[dest-1]); + MPI_Type_commit (&point_types[dest-1]); MPI_Request request; - MPI_Isend( &points[0], 1, newtype, dest, MPI_TAG_MESH+1, comm, &request); + MPI_Isend( points.Data(), 1, point_types[dest-1], dest, MPI_TAG_MESH+1, comm, &request); sendrequests.Append (request); } - Array num_distpnums(ntasks); - num_distpnums = 0; - /** - Next, we send the identifications themselfs. + Next, we send the identifications themselves. Info about periodic identifications sent to each proc is an array of integers. @@ -327,7 +489,7 @@ namespace netgen **/ PrintMessage ( 3, "Sending Vertices - identifications"); int maxidentnr = idents.GetMaxNr(); - Array ppd_sizes(ntasks); + NgArray ppd_sizes(ntasks); ppd_sizes = 1 + 2*maxidentnr; for (int idnr = 1; idnr < idents.GetMaxNr()+1; idnr++) { @@ -373,23 +535,28 @@ namespace netgen } 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"); + + tbuilddistpnums.Start(); + Array num_distpnums(ntasks); + num_distpnums = 0; for (int vert = 1; vert <= GetNP(); vert++) { FlatArray procs = procs_of_vert[vert]; - for (int j = 0; j < procs.Size(); j++) - num_distpnums[procs[j]] += 3 * (procs.Size()-1); + 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++) { - FlatArray procs = procs_of_vert[vert]; + NgFlatArray procs = procs_of_vert[vert]; for (int j = 0; j < procs.Size(); j++) for (int k = 0; k < procs.Size(); k++) if (j != k) @@ -399,14 +566,17 @@ namespace netgen distpnums.Add (procs[j], loc_num_of_vert[vert][k]); } } - + + tbuilddistpnums.Stop(); + 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" ); + tbuildelementtable.Start(); Array elarraysize (ntasks); elarraysize = 0; for ( int ei = 1; ei <= GetNE(); ei++) @@ -417,7 +587,7 @@ namespace netgen elarraysize[dest] += 3 + el.GetNP(); } - TABLE elementarrays(elarraysize); + DynamicTable elementarrays(elarraysize); for (int ei = 1; ei <= GetNE(); ei++) { @@ -431,9 +601,10 @@ namespace netgen for (int i = 0; i < el.GetNP(); i++) elementarrays.Add (dest, el[i]); } - + tbuildelementtable.Stop(); + for (int dest = 1; dest < ntasks; dest ++ ) - 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" ); @@ -450,24 +621,24 @@ 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" ); // build sel-identification size_t nse = GetNSE(); - Array ided_sel(nse); + NgArray ided_sel(nse); ided_sel = -1; bool has_ided_sels = false; if(GetNE() && has_periodic) //we can only have identified surf-els if we have vol-els (right?) { - Array os1, os2; + Array os1, os2; for(SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) { if(ided_sel[sei]!=-1) continue; const Element2d & sel = (*this)[sei]; - FlatArray points = sel.PNums(); + auto points = sel.PNums(); auto ided1 = per_verts[points[0]]; os1.SetSize(0); for (int j = 0; j < ided1.Size(); j++) @@ -481,7 +652,7 @@ namespace netgen os2.Append(GetTopology().GetVertexSurfaceElements(ided2[l])); for (int m = 0; m points2 = sel2.PNums(); + auto points2 = sel2.PNums(); has_ided_sels = true; ided_sel[sei] = os1[0]; ided_sel[os1[0]] = sei; @@ -515,35 +686,25 @@ namespace netgen }; 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 **/ PrintMessage ( 3, "Sending Edge Segments"); auto iterate_segs1 = [&](auto f) { - Array osegs1, osegs2, osegs_both; - Array type1, type2; + NgArray osegs1, osegs2, osegs_both; + NgArray type1, type2; for(SegmentIndex segi = 0; segi < GetNSeg(); segi++) { const Segment & seg = (*this)[segi]; @@ -596,7 +757,7 @@ namespace netgen } } }; - Array per_seg_size(GetNSeg()); + NgArray per_seg_size(GetNSeg()); per_seg_size = 0; iterate_segs1([&](SegmentIndex segi1, SegmentIndex segi2) { per_seg_size[segi1]++; }); @@ -605,7 +766,7 @@ namespace netgen { per_seg.Add(segi1, segi2); }); // make per_seg transitive auto iterate_per_seg_trans = [&](auto f){ - Array allsegs; + NgArray allsegs; for (SegmentIndex segi = 0; segi < GetNSeg(); segi++) { allsegs.SetSize(0); @@ -629,17 +790,17 @@ namespace netgen f(segi, allsegs); } }; - iterate_per_seg_trans([&](SegmentIndex segi, Array & segs){ + iterate_per_seg_trans([&](SegmentIndex segi, NgArray & segs){ for (int j = 0; j < segs.Size(); j++) per_seg_size[segi] = segs.Size(); }); TABLE per_seg_trans(per_seg_size); - iterate_per_seg_trans([&](SegmentIndex segi, Array & segs){ + iterate_per_seg_trans([&](SegmentIndex segi, NgArray & segs){ for (int j = 0; j < segs.Size(); j++) per_seg_trans.Add(segi, segs[j]); }); // build segment data - Array dests; + NgArray dests; auto iterate_segs2 = [&](auto f) { for (SegmentIndex segi = 0; segi nloc_seg(ntasks); + NgArray nloc_seg(ntasks); // bufsize = 1; //was originally this - why?? bufsize = 0; nloc_seg = 0; @@ -668,7 +829,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); @@ -686,64 +847,130 @@ namespace netgen segm_buf.Add (dest, seg.singedge_right); segm_buf.Add (dest, seg.singedge_left); }); - // distrubute segment data + // distribute 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); - PrintMessage ( 3, "Sending domain+bc - names"); + // clean up MPI-datatypes we allocated earlier + for (auto t : point_types) + { MPI_Type_free(&t); } - sendrequests.SetSize(6*(ntasks-1)); - /** Send bc-names **/ - int nbcs = bcnames.Size(); - Array bcname_sizes(nbcs); - int tot_bcsize = 0; - for(int k=0;ksize() : 0; - tot_bcsize += bcname_sizes[k]; - } - char compiled_bcnames[tot_bcsize]; - tot_bcsize = 0; - for(int k=0;k SetNV_Loc2Glob (0); + paralleltop -> SetNV (0); + paralleltop -> EnumeratePointsGlobally(); + PrintMessage ( 3, "Sending names"); + + sendrequests.SetSize(3*ntasks); + /** Send bc/mat/cd*-names **/ + // nr of names + 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++) + 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]); + for (int k = 0; k < nnames[2]; k++) func(cd2names[k]); + for (int k = 0; k < nnames[3]; k++) func(cd3names[k]); + }; + // sizes of names + NgArray name_sizes(tot_nn); + 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+7, comm, &sendrequests[ntasks+k]); + // names + int strs = 0; + iterate_names([&](auto ptr) { strs += (ptr==NULL) ? 0 : ptr->size(); }); + NgArray compiled_names(strs); + strs = 0; + iterate_names([&](auto ptr) { + if (ptr==NULL) return; + auto& name = *ptr; + 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+7, comm, &sendrequests[2*ntasks+k]); + + PrintMessage ( 3, "wait for names"); + + MyMPI_WaitAll (sendrequests); - for(int k=1;k(1, &nbcs), k, MPI_TAG_MESH+6, comm); - sendrequests[6*(k-1)+1] = MyMPI_ISend(bcname_sizes, k, MPI_TAG_MESH+6, comm); - (void) MPI_Isend(compiled_bcnames, tot_bcsize, MPI_CHAR, k, MPI_TAG_MESH+6, comm, &sendrequests[6*(k-1)+2]); - } + comm.Barrier(); + + PrintMessage( 3, "Clean up local memory"); + + auto & self = const_cast(*this); + self.points = T_POINTS(0); + 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) { + delete ptr; + ptr = nullptr; + } + }; + /* + cleanup_ptr(self.boundaryedges); + cleanup_ptr(self.segmentht); + cleanup_ptr(self.surfelementht); + */ + self.boundaryedges = nullptr; + self.segmentht = nullptr; + self.surfelementht = nullptr; - /** Send mat-names **/ - int nmats = materials.Size(); - Array mat_sizes(nmats); - int tot_matsize = 0; - for(int k=0;ksize() : 0; - tot_matsize += mat_sizes[k]; - } - char compiled_mats[tot_matsize]; - tot_matsize = 0; - for(int k=0;k(1, &nmats), k, MPI_TAG_MESH+6, comm); - sendrequests[6*(k-1)+4] = MyMPI_ISend(mat_sizes, k, MPI_TAG_MESH+6, comm); - (void) MPI_Isend(compiled_mats, tot_matsize, MPI_CHAR, k, MPI_TAG_MESH+6, comm, &sendrequests[6*(k-1)+5]); - } + self.openelements = NgArray(0); + self.opensegments = NgArray(0); + self.numvertices = 0; + self.mlbetweennodes = NgArray,PointIndex::BASE> (0); + self.mlparentelement = NgArray(0); + self.mlparentsurfaceelement = NgArray(0); + self.curvedelems = make_unique (self); + self.clusters = make_unique (self); + self.ident = make_unique (self); + self.topology = MeshTopology(*this); + self.topology.Update(); + self.BuildElementSearchTree(); + + // const_cast(*this).DeleteMesh(); - /* now wait ... **/ - PrintMessage( 3, "now wait for domain+bc - names"); - - MPI_Waitall (sendrequests.Size(), &sendrequests[0], MPI_STATUS_IGNORE); + // paralleltop -> SetNV (0); + // paralleltop->EnumeratePointsGlobally(); PrintMessage( 3, "send mesh complete"); - - MPI_Barrier(comm); } @@ -753,47 +980,46 @@ namespace netgen - // slaves receive the mesh from the master + // workers receive the mesh from the master void Mesh :: ReceiveParallelMesh ( ) { - int timer = NgProfiler::CreateTimer ("ReceiveParallelMesh"); - int timer_pts = NgProfiler::CreateTimer ("Receive points"); - int timer_els = NgProfiler::CreateTimer ("Receive elements"); - int timer_sels = NgProfiler::CreateTimer ("Receive surface elements"); - NgProfiler::RegionTimer reg(timer); + Timer timer("ReceiveParallelMesh"); + Timer timer_pts("Receive points"); + Timer timer_els("Receive elements"); + Timer timer_sels("Receive surface elements"); + RegionTimer reg(timer); NgMPI_Comm comm = GetCommunicator(); int id = comm.Rank(); 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); + timer_pts.Start(); Array verts; - MyMPI_Recv (verts, 0, MPI_TAG_MESH+1, comm); + 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); for (int vert = 0; vert < numvert; vert++) { - int globvert = verts[vert]; - paralleltop->SetLoc2Glob_Vert ( vert+1, globvert ); + int globvert = verts[vert] + IndexBASE(); + // paralleltop->SetLoc2Glob_Vert ( vert+1, globvert ); + paralleltop->L2G (PointIndex(vert+PointIndex::BASE)) = globvert; glob2loc_vert_ht.Set (globvert, vert+1); } @@ -802,24 +1028,21 @@ namespace netgen MPI_Datatype mptype = MeshPoint::MyGetMPIType(); MPI_Status status; - MPI_Recv( &points[1], numvert, mptype, 0, MPI_TAG_MESH+1, comm, &status); + MPI_Recv( points.Data(), numvert, mptype, 0, MPI_TAG_MESH+1, comm, &status); Array pp_data; - MyMPI_Recv(pp_data, 0, MPI_TAG_MESH+1, comm); + 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++) { int npairs = pp_data[maxidentnr+idnr]; - FlatArray pairdata(2*npairs, &pp_data[offset]); + NgFlatArray pairdata(2*npairs, &pp_data[offset]); offset += 2*npairs; for (int k = 0; k 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); + timer_pts.Stop(); *testout << "got " << numvert << " vertices" << endl; + { - Element el; - + RegionTimer reg(timer_els); + Array elarray; - MyMPI_Recv (elarray, 0, MPI_TAG_MESH+2, comm); - - NgProfiler::RegionTimer reg(timer_els); + comm.Recv (elarray, 0, MPI_TAG_MESH+2); for (int ind = 0, elnum = 1; ind < elarray.Size(); elnum++) { paralleltop->SetLoc2Glob_VolEl ( elnum, elarray[ind++]); - - el.SetIndex(elarray[ind++]); - el.SetNP(elarray[ind++]); + + int index = elarray[ind++]; + Element el(elarray[ind++]); + el.SetIndex(index); for ( int j = 0; j < el.GetNP(); j++) el[j] = glob2loc_vert_ht.Get (elarray[ind++]); @@ -862,7 +1086,7 @@ namespace netgen { Array fddata; - MyMPI_Recv (fddata, 0, MPI_TAG_MESH+3, comm); + comm.Recv (fddata, 0, MPI_TAG_MESH+3); for (int i = 0; i < fddata.Size(); i += 6) { int faceind = AddFaceDescriptor @@ -874,41 +1098,35 @@ namespace netgen } { - NgProfiler::RegionTimer reg(timer_sels); - Array selbuf; + RegionTimer reg(timer_sels); + 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++; + } } { + // NgArray segmbuf; + // MyMPI_Recv ( segmbuf, 0, MPI_TAG_MESH+5, comm); Array segmbuf; - MyMPI_Recv ( segmbuf, 0, MPI_TAG_MESH+5, comm); + comm.Recv (segmbuf, 0, MPI_TAG_MESH+5); Segment seg; int globsegi; @@ -947,47 +1165,56 @@ namespace netgen AddSegment (seg); segi++; } - } } - /** Recv bc-names **/ - int nbcs; - MyMPI_Recv(nbcs, 0, MPI_TAG_MESH+6, comm); - Array bcs(nbcs); - MyMPI_Recv(bcs, 0, MPI_TAG_MESH+6, comm); - int size_bc = 0; - for(int k=0;k0) SetBCName(k, string(&compiled_bcnames[cnt], bcs[k])); - cnt += bcs[k]; + { /** 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; + } } - /** Recv mat-names **/ - int nmats; - MyMPI_Recv(nmats, 0, MPI_TAG_MESH+6, comm); - Array matsz(nmats); - MyMPI_Recv(matsz, 0, MPI_TAG_MESH+6, comm); - int size_mats = 0; - for(int k=0;k0) SetMaterial(k+1, string(&compiled_mats[cnt], matsz[k])); - cnt += matsz[k]; - } + // paralleltop -> SetNV_Loc2Glob (0); + paralleltop -> EnumeratePointsGlobally(); + /** Recv bc-names **/ + 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]); + cd3names.SetSize(nnames[3]); + + 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+7, comm, MPI_STATUS_IGNORE); + int tot_size = 0; + for (int k = 0; k < tot_nn; k++) tot_size += name_sizes[k]; - MPI_Barrier(comm); + NgArray compiled_names(tot_size); + 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) { + for (int k = 0; k < array.Size(); k++) { + int s = name_sizes[tot_nn]; + array[k] = new string(&compiled_names[tot_size], s); + tot_nn++; + tot_size += s; + } + }; + write_names(materials); + write_names(bcnames); + write_names(cd2names); + write_names(cd3names); + + comm.Barrier(); int timerloc = NgProfiler::CreateTimer ("Update local mesh"); int timerloc2 = NgProfiler::CreateTimer ("CalcSurfacesOfNode"); @@ -1011,7 +1238,7 @@ namespace netgen clusters -> Update(); // paralleltop -> UpdateCoarseGrid(); - + // paralleltop->EnumeratePointsGlobally(); SetNextMajorTimeStamp(); } @@ -1019,7 +1246,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 () { @@ -1030,7 +1257,9 @@ namespace netgen if (id != 0 || ntasks == 1 ) return; #ifdef METIS - ParallelMetis (); + if (vol_partition.Size() < GetNE() || surf_partition.Size() < GetNSE() || + seg_partition.Size() < GetNSeg()) + ParallelMetis (comm.Size()); #else for (ElementIndex ei = 0; ei < GetNE(); ei++) (*this)[ei].SetPartition(ntasks * ei/GetNE() + 1); @@ -1049,7 +1278,7 @@ namespace netgen #ifdef METIS5 - void Mesh :: ParallelMetis ( ) + void Mesh :: ParallelMetis (int nproc) { PrintMessage (3, "call metis 5 ..."); @@ -1059,7 +1288,7 @@ namespace netgen idx_t ne = GetNE() + GetNSE() + GetNSeg(); idx_t nn = GetNP(); - Array eptr, eind; + NgArray eptr, eind; for (int i = 0; i < GetNE(); i++) { eptr.Append (eind.Size()); @@ -1082,9 +1311,9 @@ namespace netgen eind.Append (el[1]-1); } eptr.Append (eind.Size()); - Array epart(ne), npart(nn); + NgArray epart(ne), npart(nn); - idxtype nparts = GetCommunicator().Size()-1; + idxtype nparts = nproc-1; // GetCommunicator().Size()-1; vol_partition.SetSize(GetNE()); surf_partition.SetSize(GetNSE()); @@ -1108,10 +1337,15 @@ namespace netgen idxtype edgecut; - idxtype ncommon = 3; + idxtype ncommon = GetDimension(); + PrintMessage (3, "metis start"); + + static Timer tm("metis library"); + tm.Start(); METIS_PartMeshDual (&ne, &nn, &eptr[0], &eind[0], NULL, NULL, &ncommon, &nparts, NULL, NULL, &edgecut, &epart[0], &npart[0]); + tm.Stop(); /* METIS_PartMeshNodal (&ne, &nn, &eptr[0], &eind[0], NULL, NULL, &nparts, @@ -1134,7 +1368,7 @@ namespace netgen // surface elements attached to volume elements - Array boundarypoints (GetNP()); + NgArray boundarypoints (GetNP()); boundarypoints = false; if(GetDimension() == 3) @@ -1154,7 +1388,7 @@ namespace netgen // Build Pnt2Element table, boundary points only - Array cnt(GetNP()); + NgArray cnt(GetNP()); cnt = 0; auto loop_els_2d = [&](auto f) { @@ -1202,8 +1436,8 @@ namespace netgen { Element2d & sel = (*this)[sei]; PointIndex pi1 = sel[0]; - // FlatArray els = pnt2el[pi1]; - FlatArray els = pnt2el[pi1]; + // NgFlatArray els = pnt2el[pi1]; + NgFlatArray els = pnt2el[pi1]; // sel.SetPartition (-1); surf_partition[sei] = -1; @@ -1241,7 +1475,7 @@ namespace netgen { Segment & sel = (*this)[si]; PointIndex pi1 = sel[0]; - FlatArray els = pnt2el[pi1]; + NgFlatArray els = pnt2el[pi1]; // sel.SetPartition (-1); seg_partition[si] = -1; @@ -1282,7 +1516,7 @@ namespace netgen seg_partition[segi] = -1; PointIndex pi1 = seg[0]; - FlatArray sels = pnt2el[pi1]; + NgFlatArray sels = pnt2el[pi1]; for (int j = 0; j < sels.Size(); j++) { SurfaceElementIndex sei = sels[j]; @@ -1319,9 +1553,9 @@ 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 (Array & volume_weights , Array & surface_weights, Array & segment_weights) + void Mesh :: Distribute (NgArray & volume_weights , NgArray & surface_weights, NgArray & segment_weights) { NgMPI_Comm comm = GetCommunicator(); int id = comm.Rank(); @@ -1349,7 +1583,7 @@ namespace netgen #ifdef METIS5 - void Mesh :: ParallelMetis (Array & volume_weights , Array & surface_weights, Array & segment_weights) + void Mesh :: ParallelMetis (NgArray & volume_weights , NgArray & surface_weights, NgArray & segment_weights) { PrintMessage (3, "call metis 5 with weights ..."); @@ -1363,7 +1597,7 @@ namespace netgen idx_t ne = GetNE() + GetNSE() + GetNSeg(); idx_t nn = GetNP(); - Array eptr, eind , nwgt; + NgArray eptr, eind , nwgt; for (int i = 0; i < GetNE(); i++) { eptr.Append (eind.Size()); @@ -1413,7 +1647,7 @@ namespace netgen } eptr.Append (eind.Size()); - Array epart(ne), npart(nn); + NgArray epart(ne), npart(nn); idxtype nparts = GetCommunicator().Size()-1; vol_partition.SetSize(GetNE()); @@ -1526,7 +1760,7 @@ namespace netgen // uniform (TET) mesh, JS int npe = VolumeElement(1).GetNP(); - Array elmnts(ne*npe); + NgArray elmnts(ne*npe); int etype; if (elementtype == TET) @@ -1543,7 +1777,7 @@ namespace netgen int nparts = ntasks-1; int ncommon = 3; int edgecut; - Array epart(ne), npart(nn); + NgArray epart(ne), npart(nn); // if ( ntasks == 1 ) // { @@ -1575,7 +1809,7 @@ namespace netgen cout << "call metis(5)_PartMeshDual ... " << endl; // idx_t options[METIS_NOPTIONS]; - Array eptr(ne+1); + NgArray eptr(ne+1); for (int j = 0; j < ne+1; j++) eptr[j] = 4*j; @@ -1642,7 +1876,7 @@ namespace netgen xadj = new idxtype[nn+1]; part = new idxtype[nn]; - Array cnt(nn+1); + NgArray cnt(nn+1); cnt = 0; for ( int edge = 1; edge <= nedges; edge++ ) @@ -1674,7 +1908,7 @@ namespace netgen for ( int vert = 0; vert < nn; vert++ ) { - FlatArray array ( cnt[vert], &adjacency[ xadj[vert] ] ); + NgFlatArray array ( cnt[vert], &adjacency[ xadj[vert] ] ); BubbleSort(array); } @@ -1685,7 +1919,7 @@ namespace netgen cout << "currently not supported (metis5), A" << endl; #endif - Array nodesinpart(ntasks); + NgArray nodesinpart(ntasks); vol_partition.SetSize(ne); for ( int el = 1; el <= ne; el++ ) { @@ -1715,7 +1949,7 @@ namespace netgen } - void Mesh :: PartDualHybridMesh ( ) // Array & neloc ) + void Mesh :: PartDualHybridMesh ( ) // NgArray & neloc ) { #ifdef METIS int ne = GetNE(); @@ -1735,15 +1969,15 @@ namespace netgen int edgecut; idxtype * part; - Array facevolels1(nfaces), facevolels2(nfaces); + NgArray facevolels1(nfaces), facevolels2(nfaces); facevolels1 = -1; facevolels2 = -1; - Array elfaces; + NgArray elfaces; xadj = new idxtype[ne+1]; part = new idxtype[ne]; - Array cnt(ne+1); + NgArray cnt(ne+1); cnt = 0; for ( int el=1; el <= ne; el++ ) @@ -1786,7 +2020,7 @@ namespace netgen for ( int el = 0; el < ne; el++ ) { - FlatArray array ( cnt[el], &adjacency[ xadj[el] ] ); + NgFlatArray array ( cnt[el], &adjacency[ xadj[el] ] ); BubbleSort(array); } @@ -1803,7 +2037,7 @@ namespace netgen NgProfiler::StopTimer (timermetis); - Array nodesinpart(ntasks); + NgArray nodesinpart(ntasks); vol_partition.SetSize(ne); for ( int el = 1; el <= ne; el++ ) @@ -1841,11 +2075,11 @@ namespace netgen idxtype ne = GetNSE(); int nv = GetNV(); - Array xadj(ne+1); - Array adjacency(ne*4); + NgArray xadj(ne+1); + NgArray adjacency(ne*4); // first, build the vertex 2 element table: - Array cnt(nv); + NgArray cnt(nv); cnt = 0; for (SurfaceElementIndex sei = 0; sei < GetNSE(); sei++) for (int j = 0; j < (*this)[sei].GetNP(); j++) @@ -1859,7 +2093,7 @@ namespace netgen // find all neighbour elements int cntnb = 0; - Array marks(ne); // to visit each neighbour just once + NgArray marks(ne); // to visit each neighbour just once marks = -1; for (SurfaceElementIndex sei = 0; sei < ne; sei++) { @@ -1899,7 +2133,7 @@ namespace netgen idxtype nparts = ntasks - 1; idxtype edgecut; - Array part(ne); + NgArray part(ne); for ( int el = 0; el < ne; el++ ) BubbleSort (adjacency.Range (xadj[el], xadj[el+1])); diff --git a/libsrc/meshing/paralleltop.cpp b/libsrc/meshing/paralleltop.cpp index 53d78dfb..79e784ec 100644 --- a/libsrc/meshing/paralleltop.cpp +++ b/libsrc/meshing/paralleltop.cpp @@ -1,4 +1,4 @@ -#ifdef PARALLEL +// #ifdef PARALLEL #include @@ -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,23 +306,23 @@ namespace netgen *testout << "ParallelMeshTopology :: UpdateCoarseGridGlobal" << endl; const MeshTopology & topology = mesh.GetTopology(); - MPI_Comm comm = mesh.GetCommunicator(); - + auto comm = mesh.GetCommunicator(); + if ( id == 0 ) { - Array*> sendarrays(ntasks); + NgArray*> sendarrays(ntasks); for (int dest = 1; dest < ntasks; dest++) - sendarrays[dest] = new Array; + sendarrays[dest] = new NgArray; - Array edges, faces; + NgArray edges, faces; for (int el = 1; el <= mesh.GetNE(); el++) { topology.GetElementFaces (el, faces); topology.GetElementEdges (el, edges); const Element & volel = mesh.VolumeElement (el); - // Array & sendarray = *sendarrays[volel.GetPartition()]; - Array & sendarray = *sendarrays[mesh.vol_partition[el-1]]; + // NgArray & sendarray = *sendarrays[volel.GetPartition()]; + NgArray & sendarray = *sendarrays[mesh.vol_partition[el-1]]; for ( int i = 0; i < edges.Size(); i++ ) sendarray.Append (edges[i]); @@ -154,8 +334,8 @@ namespace netgen { topology.GetSurfaceElementEdges (el, edges); const Element2d & surfel = mesh.SurfaceElement (el); - // Array & sendarray = *sendarrays[surfel.GetPartition()]; - Array & sendarray = *sendarrays[mesh.surf_partition[el-1]]; + // NgArray & sendarray = *sendarrays[surfel.GetPartition()]; + NgArray & sendarray = *sendarrays[mesh.surf_partition[el-1]]; for ( int i = 0; i < edges.Size(); i++ ) sendarray.Append (edges[i]); @@ -164,8 +344,9 @@ namespace netgen 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]; @@ -174,12 +355,14 @@ namespace netgen else { + // NgArray recvarray; + // MyMPI_Recv (recvarray, 0, MPI_TAG_MESH+10, comm); Array recvarray; - MyMPI_Recv (recvarray, 0, MPI_TAG_MESH+10, comm); + comm.Recv (recvarray, 0, MPI_TAG_MESH+10); // MyMPI_Recv (recvarray, 0, MPI_TAG_MESH+10, comm); int ii = 0; - Array faces, edges; + NgArray faces, edges; for (int volel = 1; volel <= mesh.GetNE(); volel++) { @@ -205,11 +388,184 @@ 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(); + + Array 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); + DynamicTable 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); + DynamicTable 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); + DynamicTable 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]); + } + } + } + + DynamicTable recv_verts(ntasks); + // MyMPI_ExchangeTable (send_verts, recv_verts, MPI_TAG_MESH+9, comm); + comm.ExchangeTable (send_verts, recv_verts, MPI_TAG_MESH+9); + + for (int dest = 0; dest < ntasks; dest++) + if (dest != id) + { + loc2exchange = -1; + int cnt = 0; + + for (PointIndex pi : dest2vert[dest]) + loc2exchange[pi] = cnt++; + + FlatArray 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); + DynamicTable 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 +588,7 @@ namespace netgen // UpdateCoarseGridGlobal(); - + /* // MPI_Barrier (MPI_COMM_WORLD); MPI_Group MPI_GROUP_comm; @@ -244,151 +600,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(); - Array cnt_send(ntasks-1); + Array 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); - - Array 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++; - - FlatArray 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; - } - } - } - } - } - } - - Array sendarray, recvarray; + // NgArray sendarray, recvarray; // cout << "UpdateCoarseGrid - edges" << endl; // static int timerv = NgProfiler::CreateTimer ("UpdateCoarseGrid - ex vertices"); @@ -405,12 +629,13 @@ 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]++; - TABLE dest2vert(cnt_send); + for (int dist : GetDistantProcs(pi)) + cnt_send[dist]++; + // TABLE dest2vert(cnt_send); + DynamicTable 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,85 +643,80 @@ 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); + // TABLE dest2edge(cnt_send); + DynamicTable dest2edge(cnt_send); for (int & v : cnt_send) v *= 2; - TABLE send_edges(cnt_send); + // TABLE send_edges(cnt_send); + DynamicTable send_edges(cnt_send); 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); } - Array loc2exchange(mesh.GetNV()); - for (int dest = 1; dest < ntasks; dest++) + NgArray loc2exchange(mesh.GetNV()); + 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); + DynamicTable recv_edges(ntasks); + // MyMPI_ExchangeTable (send_edges, recv_edges, MPI_TAG_MESH+9, comm); + comm.ExchangeTable (send_edges, recv_edges, MPI_TAG_MESH+9); // 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]; - FlatArray 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); } - FlatArray recvarray = recv_edges[dest-1]; + FlatArray 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,202 +724,121 @@ namespace netgen NgProfiler::StopTimer (timere); - // MPI_Barrier (MPI_LocalComm); - // cout << "UpdateCoarseGrid - faces" << endl; if (mesh.GetDimension() == 3) { NgProfiler::StartTimer (timerf); - Array verts; + NgArray verts; // exchange faces cnt_send = 0; 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); + // TABLE dest2face(cnt_send); + DynamicTable 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); - Array loc2exchange(mesh.GetNV()); - for (int dest = 1; dest < ntasks; dest++) + // TABLE send_faces(cnt_send); + DynamicTable send_faces(cnt_send); + NgArray loc2exchange(mesh.GetNV()); + 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); + DynamicTable recv_faces(ntasks); + // MyMPI_ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+9, comm); + comm.ExchangeTable (send_faces, recv_faces, MPI_TAG_MESH+9); // 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++; - - FlatArray 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); } - FlatArray recvarray = recv_faces[dest-1]; + FlatArray 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); } } - - - - - - /* - Array 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; - Array 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) - { - FlatArray 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); } } -#endif +// #endif diff --git a/libsrc/meshing/paralleltop.hpp b/libsrc/meshing/paralleltop.hpp index cb4b41ab..f9d60497 100644 --- a/libsrc/meshing/paralleltop.hpp +++ b/libsrc/meshing/paralleltop.hpp @@ -14,11 +14,15 @@ 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; - Array glob_vert, glob_edge, glob_face; - Array glob_el, glob_surfel, glob_segm; + // will get rid of them + NgArray glob_edge, glob_face; + NgArray glob_el, glob_surfel, glob_segm; bool is_updated; @@ -30,79 +34,135 @@ namespace netgen void Reset (); void Print() const; + void UpdateCoarseGrid(); + [[deprecated("should not need it anymore")]] 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 GetGlobalPNum (PointIndex locnum) const { return glob_vert[locnum-PointIndex::BASE]; } + [[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]; } - void GetDistantFaceNums (int locfacenum, Array & distfacenums ) const + [[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]; } - void GetDistantEdgeNums (int locedgenum, Array & distedgenums ) const + [[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]; } + [[deprecated("Use GetDistantProcs(..)!")]] FlatArray GetDistantPNums (int locnum) const { return loc2distvert[locnum]; } - FlatArray GetDistantFaceNums (int locnum) const { return loc2distface[locnum]; } - FlatArray GetDistantEdgeNums (int locnum) const { return loc2distedge[locnum]; } + + + [[deprecated("Use GetDistantProcs(..).Contains instead!")]] bool IsExchangeVert (int dest, int vnum) const { return loc2distvert[vnum-1].Contains (dest); } }; - } diff --git a/libsrc/meshing/parser2.cpp b/libsrc/meshing/parser2.cpp index 2e8530b8..03a4b87e 100644 --- a/libsrc/meshing/parser2.cpp +++ b/libsrc/meshing/parser2.cpp @@ -42,7 +42,7 @@ void netrule :: LoadRule (istream & ist) { char buf[256]; char ch; - Point2d p; + Point<2> p; INDEX_2 lin; int i, j; DenseMatrix tempoldutonewu(20, 20), tempoldutofreearea(20, 20), @@ -60,10 +60,13 @@ void netrule :: LoadRule (istream & ist) ist.get (buf, sizeof(buf), '"'); ist.get (ch); - // if(name != NULL) + // if(name != NULL) + /* delete [] name; name = new char[strlen (buf) + 1]; strcpy (name, buf); + */ + name = string(buf); //(*testout) << "name " << name << endl; // (*mycout) << "Rule " << name << " found." << endl; @@ -85,9 +88,9 @@ void netrule :: LoadRule (istream & ist) while (ch == '(') { - ist >> p.X(); + ist >> p[0]; ist >> ch; // ',' - ist >> p.Y(); + ist >> p[1]; ist >> ch; // ')' points.Append (p); @@ -188,9 +191,9 @@ void netrule :: LoadRule (istream & ist) while (ch == '(') { - ist >> p.X(); + ist >> p[0]; ist >> ch; // ',' - ist >> p.Y(); + ist >> p[1]; ist >> ch; // ')' points.Append (p); @@ -249,9 +252,9 @@ void netrule :: LoadRule (istream & ist) while (ch == '(') { - ist >> p.X(); + ist >> p[0]; ist >> ch; // ',' - ist >> p.Y(); + ist >> p[1]; ist >> ch; // ')' freezone.Append (p); @@ -294,9 +297,9 @@ void netrule :: LoadRule (istream & ist) { freepi++; - ist >> p.X(); + ist >> p[0]; ist >> ch; // ',' - ist >> p.Y(); + ist >> p[1]; ist >> ch; // ')' freezonelimit.Elem(freepi) = p; @@ -429,7 +432,7 @@ void netrule :: LoadRule (istream & ist) { char ok; int minn; - Array pnearness (noldp); + NgArray pnearness (noldp); for (i = 1; i <= pnearness.Size(); i++) pnearness.Elem(i) = 1000; @@ -474,14 +477,14 @@ void netrule :: LoadRule (istream & ist) { double lam1 = 1.0/(i+1); - oldutofreearea_i[i] = new DenseMatrix (oldutofreearea.Height(), oldutofreearea.Width()); - DenseMatrix & mati = *oldutofreearea_i[i]; + oldutofreearea_i[i] = std::move(DenseMatrix (oldutofreearea.Height(), oldutofreearea.Width())); + DenseMatrix & mati = oldutofreearea_i[i]; for (j = 0; j < oldutofreearea.Height(); j++) for (int k = 0; k < oldutofreearea.Width(); k++) mati(j,k) = lam1 * oldutofreearea(j,k) + (1 - lam1) * oldutofreearealimit(j,k); - freezone_i[i] = new Array (freezone.Size()); - Array & fzi = *freezone_i[i]; + freezone_i[i] = NgArray> (freezone.Size()); + auto& fzi = freezone_i[i]; for (int j = 0; j < freezone.Size(); j++) fzi[j] = freezonelimit[j] + lam1 * (freezone[j] - freezonelimit[j]); } @@ -578,7 +581,9 @@ void Meshing2 :: LoadRules (const char * filename, bool quad) delete ist; exit (1); } - + + Timer t("Parsing rules"); + t.Start(); while (!ist->eof()) { buf[0] = 0; @@ -587,17 +592,18 @@ void Meshing2 :: LoadRules (const char * filename, bool quad) if (strcmp (buf, "rule") == 0) { //(*testout) << "found rule" << endl; - netrule * rule = new netrule; + auto rule = make_unique(); //(*testout) << "fr1" << endl; rule -> LoadRule(*ist); //(*testout) << "fr2" << endl; - rules.Append (rule); + rules.Append (std::move(rule)); } //(*testout) << "loop" << endl; } //(*testout) << "POS3" << endl; - + t.Stop(); + delete ist; //delete [] tr1; } diff --git a/libsrc/meshing/parser3.cpp b/libsrc/meshing/parser3.cpp index a5c6fd4b..5f5a6365 100644 --- a/libsrc/meshing/parser3.cpp +++ b/libsrc/meshing/parser3.cpp @@ -53,8 +53,8 @@ void LoadVMatrixLine (istream & ist, DenseMatrix & m, int line) int vnetrule :: NeighbourTrianglePoint (const threeint & t1, const threeint & t2) const { - Array tr1(3); - Array tr2(3); + NgArray tr1(3); + NgArray tr2(3); tr1.Elem(1)=t1.i1; tr1.Elem(2)=t1.i2; tr1.Elem(3)=t1.i3; @@ -472,7 +472,7 @@ void vnetrule :: LoadRule (istream & ist) else if (strcmp (buf, "freeset") == 0) { - freesets.Append (new Array); + freesets.Append (new NgArray); ist >> ch; @@ -706,7 +706,7 @@ void vnetrule :: LoadRule (istream & ist) if (freesets.Size() == 0) { - freesets.Append (new Array); + freesets.Append (new NgArray); for (i = 1; i <= freezone.Size(); i++) freesets.Elem(1)->Append(i); } @@ -736,10 +736,10 @@ void vnetrule :: LoadRule (istream & ist) for (fs = 1; fs <= freesets.Size(); fs++) { - freefaces.Append (new Array); + freefaces.Append (new NgArray); - Array & freeset = *freesets.Elem(fs); - Array & freesetfaces = *freefaces.Last(); + NgArray & freeset = *freesets.Elem(fs); + NgArray & freesetfaces = *freefaces.Last(); for (ii1 = 1; ii1 <= freeset.Size(); ii1++) for (ii2 = 1; ii2 <= freeset.Size(); ii2++) @@ -785,7 +785,7 @@ void vnetrule :: LoadRule (istream & ist) { int minn; - // Array pnearness (noldp); + // NgArray pnearness (noldp); pnearness.SetSize (noldp); for (i = 1; i <= pnearness.Size(); i++) @@ -874,11 +874,11 @@ void vnetrule :: LoadRule (istream & ist) //Table of edges: for (fs = 1; fs <= freesets.Size(); fs++) { - freeedges.Append (new Array); + freeedges.Append (new NgArray); - // Array & freeset = *freesets.Get(fs); - Array & freesetedges = *freeedges.Last(); - Array & freesetfaces = *freefaces.Get(fs); + // NgArray & freeset = *freesets.Get(fs); + NgArray & freesetedges = *freeedges.Last(); + NgArray & freesetfaces = *freefaces.Get(fs); int k,l; INDEX ind; diff --git a/libsrc/meshing/prism2rls.cpp b/libsrc/meshing/prism2rls.cpp deleted file mode 100644 index 7e696554..00000000 --- a/libsrc/meshing/prism2rls.cpp +++ /dev/null @@ -1,457 +0,0 @@ -namespace netgen -{ -const char * prismrules2[] = { -"tolfak 0.5\n",\ -"\n",\ -"rule \"prism on quad\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"(1, 5, 6, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 5 6 8;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"(1, 5, 6, 4);\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 5 6 8;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quad\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 6, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 6, 4);\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quada\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"fill prism\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 3 quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"flat prism\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(0.5, 0.866, 0);\n",\ -"(0, 0, -1);\n",\ -"(1, 0, -1);\n",\ -"(0.5, 0.866, -1);\n",\ -"\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(5, 4, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 4);\n",\ -"(4, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(5, 3, 6);\n",\ -"(3, 1, 6);\n",\ -"(6, 1, 4);\n",\ -"\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 5, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"endrule\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/prism2rls_2.cpp b/libsrc/meshing/prism2rls_2.cpp deleted file mode 100644 index baf0bef3..00000000 --- a/libsrc/meshing/prism2rls_2.cpp +++ /dev/null @@ -1,446 +0,0 @@ -namespace netgen -{ -const char * prismrules2[] = { -"tolfak 0.5\n",\ -"\n",\ -"rule \"prism on quad\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"(1, 5, 6, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"(1, 5, 6, 4);\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quad\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 6, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 6, 4);\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.3 P1, -0.1 P2, -0.1 P3, 0.3 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0.25 P1, 0 P2, 0 P3, 0.25 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 2 quada\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3, 6);\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ -0.1 P1, 0.3 P2, 0.3 P3, -0.1 P4, 0.3 P5, 0.3 P6 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 0 P1, 0.25 P2, 0.25 P3, 0 P4, 0.25 P5, 0.25 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5 6 7;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 6;\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"fill prism\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"(4, 3, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"prism on 3 quad, one trig\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0, -0.86);\n",\ -"(0.5, 1, -0.86);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 5, 6, 3) del;\n",\ -"(5, 1, 4, 6) del;\n",\ -"(1, 5, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 6, 3);\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 5, 2, 4, 6, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5;\n",\ -"\n",\ -"freeset\n",\ -"2 3 4 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"flat prism\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(0.5, 0.866, 0);\n",\ -"(0, 0, -1);\n",\ -"(1, 0, -1);\n",\ -"(0.5, 0.866, -1);\n",\ -"\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(5, 4, 6) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 4);\n",\ -"(4, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(5, 3, 6);\n",\ -"(3, 1, 6);\n",\ -"(6, 1, 4);\n",\ -"\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 5, 4, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"endrule\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/pyramid2rls.cpp b/libsrc/meshing/pyramid2rls.cpp deleted file mode 100644 index a97e7f13..00000000 --- a/libsrc/meshing/pyramid2rls.cpp +++ /dev/null @@ -1,309 +0,0 @@ -namespace netgen -{ -const char * pyramidrules2[] = { -"tolfak 0.5\n",\ -"\n",\ -"rule \"Pyramid on quad\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.5, -0.5) \n",\ -" { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\ -" { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"small Pyramid on quad\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.5, -0.1 )\n",\ -" { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\ -" { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"connect pyramid\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"pyramid with one trig\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 1, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 0.34 P2, 0.34 P3, 0.34 P5, -0.02 P1 };\n",\ -"{ 0.34 P3, 0.34 P4, 0.34 P5, -0.02 P1 };\n",\ -"{ 0.34 P1, 0.34 P4, 0.34 P5, -0.02 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 0.333 P2, 0.333 P3, 0.334 P5, 0 P1 };\n",\ -"{ 0.333 P3, 0.333 P4, 0.334 P5, 0 P1 };\n",\ -"{ 0.333 P1, 0.333 P4, 0.334 P5, 0 P2 };\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 5);\n",\ -"\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"freeset\n",\ -"2 3 5 6;\n",\ -"freeset\n",\ -"3 4 5 7;\n",\ -"freeset \n",\ -"1 4 5 8;\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"pyramid with two trig\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 1, 5) del;\n",\ -"(3, 2, 5) del;\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"pyramid with two trig, left\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 1, 5) del;\n",\ -"(1, 4, 5) del;\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(3, 4, 5);\n",\ -"(2, 3, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/pyramidrls.cpp b/libsrc/meshing/pyramidrls.cpp deleted file mode 100644 index d4e997c1..00000000 --- a/libsrc/meshing/pyramidrls.cpp +++ /dev/null @@ -1,263 +0,0 @@ -namespace netgen -{ -const char * pyramidrules[] = { -"tolfak 0.5\n",\ -"\n",\ -"rule \"Pyramid on quad\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.5, -0.5) \n",\ -" { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\ -" { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"small Pyramid on quad\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.5, -0.1 )\n",\ -" { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 } \n",\ -" { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.4 P5, -0.1 P1, -0.1 P2, -0.1 P3, -0.1 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"connect pyramid\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 5);\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"pyramid with one trig\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 1, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(2, 3, 5);\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 0.34 P2, 0.34 P3, 0.34 P5, -0.02 P1 };\n",\ -"{ 0.34 P3, 0.34 P4, 0.34 P5, -0.02 P1 };\n",\ -"{ 0.34 P1, 0.34 P4, 0.34 P5, -0.02 P3 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 0.333 P2, 0.333 P3, 0.334 P5, 0 P1 };\n",\ -"{ 0.333 P3, 0.333 P4, 0.334 P5, 0 P1 };\n",\ -"{ 0.333 P1, 0.333 P4, 0.334 P5, 0 P3 };\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 3, 4, 5);\n",\ -"\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"freeset\n",\ -"2 3 5 6;\n",\ -"freeset\n",\ -"3 4 5 7;\n",\ -"freeset \n",\ -"1 4 5 8;\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"pyramid with two trig\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(1, 1, 0);\n",\ -"(0, 1, 0);\n",\ -"(0.5, 0.5, -0.5);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3, 4) del;\n",\ -"(2, 1, 5) del;\n",\ -"(3, 2, 5) del;\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(3, 4, 5);\n",\ -"(4, 1, 5);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/python_mesh.cpp b/libsrc/meshing/python_mesh.cpp index 652dd6ce..363d6f93 100644 --- a/libsrc/meshing/python_mesh.cpp +++ b/libsrc/meshing/python_mesh.cpp @@ -1,12 +1,73 @@ #ifdef NG_PYTHON +#include + #include <../general/ngpython.hpp> +#include +#include "python_mesh.hpp" #include #include "meshing.hpp" // #include // #include #include <../interface/writeuser.hpp> +#include <../include/nginterface.h> +#include <../general/gzstream.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; @@ -17,47 +78,10 @@ namespace netgen { extern bool netgen_executable_started; extern shared_ptr ng_geometry; -#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 + extern void Optimize2d (Mesh & mesh, MeshingParameters & mp); } -template -void ExportArray (py::module &m) -{ - using TA = Array; - string name = string("Array_") + typeid(T).name(); - py::class_>(m, name.c_str()) - .def ("__len__", [] ( Array &self ) { return self.Size(); } ) - .def ("__getitem__", - FunctionPointer ([](Array & self, TIND i) -> T& - { - if (i < BASE || i >= BASE+self.Size()) - throw py::index_error(); - return self[i]; - }), - py::return_value_policy::reference) - .def("__iter__", [] ( TA & self) { - return py::make_iterator (self.begin(),self.end()); - }, py::keep_alive<0,1>()) // keep array alive while iterator is used - - ; -} - void TranslateException (const NgException & ex) { string err = string("Netgen exception: ")+ex.What(); @@ -66,8 +90,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; @@ -86,7 +117,23 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) m.def("_PushStatus", [](string s) { PushStatus(MyStr(s)); }); m.def("_SetThreadPercentage", [](double percent) { SetThreadPercent(percent); }); + py::enum_(m,"IdentificationType") + .value("UNDEFINED", Identifications::UNDEFINED) + .value("PERIODIC", Identifications::PERIODIC) + .value("CLOSESURFACES", Identifications::CLOSESURFACES) + .value("CLOSEEDGES", Identifications::CLOSEEDGES) + ; + + py::implicitly_convertible(); + 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) @@ -96,27 +143,29 @@ 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++) - procs[i] = proc_list[i]; + { procs[i] = proc_list[i]; } if (!procs.Contains(c.Rank())) - throw Exception("rank "+ToString(c.Rank())+" not in subcomm"); - MPI_Comm subcomm = MyMPI_SubCommunicator(c, procs); - return NgMPI_Comm(subcomm, true); + { throw Exception("rank "+ToString(c.Rank())+" not in subcomm"); } + return c.SubCommunicator(procs); }, py::arg("procs")); ; +#ifdef NG_MPI4PY + py::implicitly_convertible(); +#endif // NG_MPI4PY py::class_(m, "NGDummyArgument") @@ -125,6 +174,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>()) @@ -132,8 +185,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>()) @@ -141,50 +201,88 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def("__getitem__", [](Point<2>& self, int index) { return self[index]; }) ; - m.def ("Pnt", FunctionPointer - ([](double x, double y, double z) { return global_trafo(Point<3>(x,y,z)); })); - m.def ("Pnt", FunctionPointer - ([](double x, double y) { return Point<2>(x,y); })); + py::implicitly_convertible>(); - /* - // duplicated functions ???? - m.def ("Pnt", FunctionPointer - ([](double x, double y, double z) { return Point<3>(x,y,z); })); - m.def ("Pnt", FunctionPointer - ([](double x, double y) { return Point<2>(x,y); })); - */ + 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); }); + m.def("Pnt", [](py::array_t np_array) + { + int dim = np_array.size(); + if(!(dim == 2 || dim == 3)) + throw Exception("Invalid dimension of input array!"); + if(dim == 2) + return py::cast(Point<2>(np_array.at(0), + np_array.at(1))); + return py::cast(global_trafo(Point<3>(np_array.at(0), + np_array.at(1), + np_array.at(2)))); + }); 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) .def(py::self-py::self) .def(-py::self) .def(double()*py::self) + .def(py::self*double()) .def("Norm", &Vec<2>::Length) .def("__getitem__", [](Vec<2>& vec, int index) { return vec[index]; }) .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) .def(py::self-py::self) .def(-py::self) .def(double()*py::self) + .def(py::self*double()) .def("Norm", &Vec<3>::Length) .def("__getitem__", [](Vec<3>& vec, int index) { return vec[index]; }) .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) + { + int dim = np_array.size(); + if(!(dim == 2 || dim == 3)) + throw Exception("Invalid dimension of input array!"); + if(dim == 2) + return py::cast(Vec<2>(np_array.at(0), + np_array.at(1))); + return py::cast(global_trafo(Vec<3>(np_array.at(0), + np_array.at(1), + np_array.at(2)))); + }); + m.def ("Vec", FunctionPointer ([] (double x, double y) { return Vec<2>(x,y); })); py::class_> (m, "Trafo") - .def(py::init>()) + .def(py::init>(), "a translation") + .def(py::init,Vec<3>,double>(), "a rotation given by point on axes, direction of axes, angle") + .def("__mul__", [](Transformation<3> a, Transformation<3> b)->Transformation<3> + { Transformation<3> res; res.Combine(a,b); return res; }) .def("__call__", [] (Transformation<3> trafo, Point<3> p) { return trafo(p); }) ; @@ -266,24 +364,24 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def(py::init>()) .def("__str__", &ToString) .def("__repr__", &ToString) - .def_property_readonly("p", FunctionPointer([](const MeshPoint & self) - { - py::list l; - l.append ( py::cast(self[0]) ); - l.append ( py::cast(self[1]) ); - l.append ( py::cast(self[2]) ); - return py::tuple(l); - })) - .def("__getitem__", FunctionPointer([](const MeshPoint & self, int index) { + .def_property_readonly("p", [](const MeshPoint & self) + { + py::list l; + l.append ( py::cast(self[0]) ); + l.append ( py::cast(self[1]) ); + l.append ( py::cast(self[2]) ); + return py::tuple(l); + }) + .def("__getitem__", [](const MeshPoint & self, int index) { if(index<0 || index>2) throw py::index_error(); return self[index]; - })) - .def("__setitem__", FunctionPointer([](MeshPoint & self, int index, double val) { + }) + .def("__setitem__", [](MeshPoint & self, int index, double val) { if(index<0 || index>2) throw py::index_error(); self(index) = val; - })) + }) ; py::class_(m, "Element3D") @@ -317,7 +415,8 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ) .def("__repr__", &ToString) .def_property("index", &Element::GetIndex, &Element::SetIndex) - .def_property("curved", &Element::IsCurved, &Element::SetCurved) + .def_property("curved", &Element::IsCurved, &Element::SetCurved) + .def_property("refine", &Element::TestRefinementFlag, &Element::SetRefinementFlag) .def_property_readonly("vertices", FunctionPointer ([](const Element & self) -> py::list { @@ -336,36 +435,61 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) })) ; + if(ngcore_have_numpy) + { + auto data_layout = Element::GetDataLayout(); + + py::detail::npy_format_descriptor::register_dtype({ + py::detail::field_descriptor { + "nodes", data_layout["pnum"], + ELEMENT_MAXPOINTS * sizeof(PointIndex), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + py::detail::field_descriptor { + "index", data_layout["index"], sizeof(int), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + py::detail::field_descriptor { + "np", data_layout["np"], sizeof(int8_t), + py::format_descriptor::format(), + pybind11::dtype("int8") }, + py::detail::field_descriptor { + "refine", data_layout["refine"], sizeof(bool), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() } + }); + } + py::class_(m, "Element2D") - .def(py::init ([](int index, py::list vertices) + .def(py::init ([](int index, std::vector vertices) { Element2d * newel = nullptr; - if (py::len(vertices) == 3) + if (vertices.size() == 3) { newel = new Element2d(TRIG); for (int i = 0; i < 3; i++) - (*newel)[i] = py::extract(vertices[i])(); + (*newel)[i] = vertices[i]; newel->SetIndex(index); } - else if (py::len(vertices) == 4) + else if (vertices.size() == 4) { newel = new Element2d(QUAD); for (int i = 0; i < 4; i++) - (*newel)[i] = py::extract(vertices[i])(); + (*newel)[i] = vertices[i]; newel->SetIndex(index); } - else if (py::len(vertices) == 6) + else if (vertices.size() == 6) { newel = new Element2d(TRIG6); for(int i = 0; i<6; i++) - (*newel)[i] = py::extract(vertices[i])(); + (*newel)[i] = vertices[i]; newel->SetIndex(index); } - else if (py::len(vertices) == 8) + else if (vertices.size() == 8) { newel = new Element2d(QUAD8); for(int i = 0; i<8; i++) - (*newel)[i] = py::extract(vertices[i])(); + (*newel)[i] = vertices[i]; newel->SetIndex(index); } else @@ -377,6 +501,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ) .def_property("index", &Element2d::GetIndex, &Element2d::SetIndex) .def_property("curved", &Element2d::IsCurved, &Element2d::SetCurved) + .def_property("refine", &Element2d::TestRefinementFlag, &Element2d::SetRefinementFlag) .def_property_readonly("vertices", FunctionPointer([](const Element2d & self) -> py::list { @@ -392,11 +517,36 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) for (int i = 0; i < self.GetNP(); i++) li.append (py::cast(self[i])); return li; - })) + })) ; + if(ngcore_have_numpy) + { + auto data_layout = Element2d::GetDataLayout(); + py::detail::npy_format_descriptor::register_dtype({ + py::detail::field_descriptor { + "nodes", data_layout["pnum"], + ELEMENT2D_MAXPOINTS * sizeof(PointIndex), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + py::detail::field_descriptor { + "index", data_layout["index"], sizeof(int), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + py::detail::field_descriptor { + "np", data_layout["np"], sizeof(int8_t), + py::format_descriptor::format(), + pybind11::dtype("int8") }, + py::detail::field_descriptor { + "refine", data_layout["refine"], sizeof(bool), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() } + }); + } + py::class_(m, "Element1D") - .def(py::init([](py::list vertices, py::list surfaces, int index, int edgenr) + .def(py::init([](py::list vertices, py::list surfaces, int index, int edgenr, + py::list trignums) { Segment * newel = new Segment(); for (int i = 0; i < 2; i++) @@ -407,6 +557,8 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) newel -> epgeominfo[1].edgenr = edgenr; // needed for codim2 in 3d newel -> edgenr = index; + for(auto i : Range(len(trignums))) + newel->geominfo[i].trignum = py::cast(trignums[i]); if (len(surfaces)) { newel->surfnr1 = py::extract(surfaces[0])(); @@ -418,6 +570,7 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) py::arg("surfaces")=py::list(), py::arg("index")=1, py::arg("edgenr")=1, + py::arg("trignums")=py::list(), // for stl "create segment element" ) .def("__repr__", &ToString) @@ -455,6 +608,20 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) })) ; + if(ngcore_have_numpy) + { + py::detail::npy_format_descriptor::register_dtype({ + py::detail::field_descriptor { + "nodes", offsetof(Segment, pnums), + 3 * sizeof(PointIndex), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + py::detail::field_descriptor { + "index", offsetof(Segment, edgenr), sizeof(int), + py::format_descriptor::format(), + py::detail::npy_format_descriptor::dtype() }, + }); + } py::class_(m, "Element0D") .def(py::init([](PointIndex vertex, int index) @@ -508,28 +675,49 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) [](FaceDescriptor & self) -> string { return self.GetBCName(); }, [](FaceDescriptor & self, string name) { self.SetBCName(new string(name)); } // memleak ) - .def("SetSurfaceColor", [](FaceDescriptor & self, py::list color ) - { - Vec3d c; - c.X() = py::extract(color[0])(); - c.Y() = py::extract(color[1])(); - c.Z() = py::extract(color[2])(); - self.SetSurfColour(c); - }) + .def_property("color", + [](const FaceDescriptor& self) + { + auto sc = self.SurfColour(); + return py::make_tuple(sc[0], sc[1], sc[2]); + }, + [](FaceDescriptor& self, py::tuple col) + { + Vec<4> sc = 1; + sc[0] = py::cast(col[0]); + sc[1] = py::cast(col[1]); + sc[2] = py::cast(col[2]); + if(py::len(col) > 3) + sc[3] = py::cast(col[3]); + self.SetSurfColour(sc); + } + ) + .def_property("transparency", + [](const FaceDescriptor& self) + { + return self.SurfColour()[3]; + }, + [](FaceDescriptor& self, double val) + { + auto sc = self.SurfColour(); + sc[3] = val; + self.SetSurfColour(sc); + }) ; - ExportArray(m); - ExportArray(m); - ExportArray(m); + ExportArray(m); + ExportArray(m); + ExportArray(m); ExportArray(m); - ExportArray(m); + ExportArray(m); ExportArray(m); py::implicitly_convertible< int, PointIndex>(); py::class_> (m, "NetgenGeometry", py::dynamic_attr()) + .def("RestrictH", &NetgenGeometry::RestrictH) ; py::class_>(m, "Mesh") @@ -562,6 +750,11 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) */ .def_property_readonly("_timestamp", &Mesh::GetTimeStamp) + .def_property_readonly("ne", [](Mesh& m) { return m.GetNE(); }) + .def("Partition", [](shared_ptr self, int numproc) { + self->ParallelMetis(numproc); + }, py::arg("numproc")) + .def("Distribute", [](shared_ptr self, NgMPI_Comm comm) { self->SetCommunicator(comm); if(comm.Size()==1) return self; @@ -571,12 +764,12 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) else self->SendRecvMesh(); return self; }, py::arg("comm")) - .def("Receive", [](NgMPI_Comm comm) { + .def_static("Receive", [](NgMPI_Comm comm) -> shared_ptr { auto mesh = make_shared(); mesh->SetCommunicator(comm); mesh->SendRecvMesh(); return mesh; - }) + }, py::arg("comm")) .def("Load", FunctionPointer ([](shared_ptr self, const string & filename) { @@ -603,29 +796,33 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) return; } - istream * infile; + istream * infile = nullptr; Array buf; // for distributing geometry! int strs; if( id == 0) { - - if (filename.substr (filename.length()-3, 3) == ".gz") + if (filename.length() > 8 && filename.substr (filename.length()-8, 8) == ".vol.bin") + mesh -> Load(filename); + else if (filename.substr (filename.length()-3, 3) == ".gz") infile = new igzstream (filename.c_str()); else infile = new ifstream (filename.c_str()); - mesh -> Load(*infile); - // make string from rest of file (for geometry info!) - // (this might be empty, in which case we take the global ng_geometry) - stringstream geom_part; - geom_part << infile->rdbuf(); - string geom_part_string = geom_part.str(); - strs = geom_part_string.size(); - // buf = new char[strs]; - buf.SetSize(strs); - memcpy(&buf[0], geom_part_string.c_str(), strs*sizeof(char)); + if(infile) + { + mesh -> Load(*infile); + // make string from rest of file (for geometry info!) + // (this might be empty, in which case we take the global ng_geometry) + stringstream geom_part; + geom_part << infile->rdbuf(); + string geom_part_string = geom_part.str(); + strs = geom_part_string.size(); + // buf = new char[strs]; + buf.SetSize(strs); + memcpy(buf.Data(), geom_part_string.c_str(), strs*sizeof(char)); + delete infile; + } - delete infile; if (ntasks > 1) { @@ -650,9 +847,9 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) bool endfile = false; int n, dummy; - Array segment_weights; - Array surface_weights; - Array volume_weights; + NgArray segment_weights; + NgArray surface_weights; + NgArray volume_weights; while (weightsfile.good() && !endfile) { @@ -695,32 +892,34 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) } if(ntasks>1) { -#ifdef PARALLEL + // #ifdef PARALLEL /** Scatter the geometry-string (no dummy-implementation in mpi_interface) **/ + /* int strs = buf.Size(); MyMPI_Bcast(strs, comm); if(strs>0) MyMPI_Bcast(buf, comm); -#endif + */ + comm.Bcast(buf); + // #endif } shared_ptr geo; if(buf.Size()) { // if we had geom-info in the file, take it - istringstream geom_infile(string((const char*)&buf[0], buf.Size())); + istringstream geom_infile(string((const char*)buf.Data(), buf.Size())); geo = geometryregister.LoadFromMeshFile(geom_infile); } if(geo!=nullptr) mesh->SetGeometry(geo); else if(ng_geometry!=nullptr) mesh->SetGeometry(ng_geometry); }),py::call_guard()) - // static_cast(&Mesh::Load)) - .def("Save", static_cast(&Mesh::Save),py::call_guard()) + .def("Save", static_cast(&Mesh::Save),py::call_guard()) .def("Export", [] (Mesh & self, string filename, string format) { if (WriteUserFormat (format, self, /* *self.GetGeometry(), */ filename)) { string err = string ("nothing known about format")+format; - Array names, extensions; + NgArray names, extensions; RegisterUserFormats (names, extensions); err += "\navailable formats are:\n"; for (auto name : names) @@ -733,15 +932,15 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def_property("dim", &Mesh::GetDimension, &Mesh::SetDimension) .def("Elements3D", - static_cast&(Mesh::*)()> (&Mesh::VolumeElements), + static_cast&(Mesh::*)()> (&Mesh::VolumeElements), py::return_value_policy::reference) .def("Elements2D", - static_cast&(Mesh::*)()> (&Mesh::SurfaceElements), + static_cast&(Mesh::*)()> (&Mesh::SurfaceElements), 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& @@ -754,59 +953,244 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) static_cast (&Mesh::Points), py::return_value_policy::reference) + .def("Coordinates", [](Mesh & self) { + return py::array + ( + py::memoryview::from_buffer + (&self.Points()[PointIndex::BASE](0), sizeof(double), + py::format_descriptor::value, + { self.Points().Size(), size_t(self.GetDimension()) }, + { sizeof(self.Points()[PointIndex::BASE]), sizeof(double) } ) + ); + }) + .def("FaceDescriptor", static_cast (&Mesh::GetFaceDescriptor), py::return_value_policy::reference) .def("GetNFaceDescriptors", &Mesh::GetNFD) + + .def("FaceDescriptors", + // static_cast&(Mesh::*)()> (&Mesh::FaceDescriptors), + &Mesh::FaceDescriptors, + py::return_value_policy::reference) + + + .def("GetNDomains", &Mesh::GetNDomains) + + .def("GetVolumeNeighboursOfSurfaceElement", [](Mesh & self, size_t sel) + { + int elnr1, elnr2; + self.GetTopology().GetSurface2VolumeElement(sel+1, elnr1, elnr2); + return py::make_tuple(elnr1, elnr2); + }, "Returns element nrs of volume element connected to surface element, -1 if no volume element") .def("GetNCD2Names", &Mesh::GetNCD2Names) - .def("__getitem__", FunctionPointer ([](const Mesh & self, PointIndex pi) - { - return self[pi]; - })) + .def("__getitem__", [](const Mesh & self, PointIndex id) { return self[id]; }) + .def("__getitem__", [](const Mesh & self, ElementIndex id) { return self[id]; }) + .def("__getitem__", [](const Mesh & self, SurfaceElementIndex id) { return self[id]; }) + .def("__getitem__", [](const Mesh & self, SegmentIndex id) { return self[id]; }) - .def ("Add", FunctionPointer ([](Mesh & self, MeshPoint p) - { - return self.AddPoint (Point3d(p)); - })) - - .def ("Add", FunctionPointer ([](Mesh & self, const Element & el) - { - return self.AddVolumeElement (el); - })) - - .def ("Add", FunctionPointer ([](Mesh & self, const Element2d & el) - { - return self.AddSurfaceElement (el); - })) - - .def ("Add", FunctionPointer ([](Mesh & self, const Segment & el) - { - return self.AddSegment (el); - })) + .def("__setitem__", [](Mesh & self, PointIndex id, const MeshPoint & mp) { return self[id] = mp; }) - .def ("Add", FunctionPointer ([](Mesh & self, const Element0d & el) - { - return self.pointelements.Append (el); - })) + .def ("Add", [](Mesh & self, MeshPoint p) + { + return self.AddPoint (Point3d(p)); + }) + + .def ("Add", [](Mesh & self, const Element & el) + { + return self.AddVolumeElement (el); + }) + + .def ("Add", [](Mesh & self, const Element2d & el) + { + return self.AddSurfaceElement (el); + }) - .def ("Add", FunctionPointer ([](Mesh & self, const FaceDescriptor & fd) - { - return self.AddFaceDescriptor (fd); - })) + .def ("Add", [](Mesh & self, const Segment & el) + { + return self.AddSegment (el); + }) + + .def ("Add", [](Mesh & self, const Element0d & el) + { + return self.pointelements.Append (el); + }) + + .def ("Add", [](Mesh & self, const FaceDescriptor & fd) + { + return self.AddFaceDescriptor (fd); + }) + + .def ("AddPoints", [](Mesh & self, py::buffer b1) + { + static Timer timer("Mesh::AddPoints"); + static Timer timercast("Mesh::AddPoints - casting"); + RegionTimer reg(timer); + + timercast.Start(); + // casting from here: https://github.com/pybind/pybind11/issues/1908 + auto b = b1.cast>(); + timercast.Stop(); + + py::buffer_info info = b.request(); + // cout << "data format = " << info.format << endl; + if (info.ndim != 2) + throw std::runtime_error("AddPoints needs buffer of dimension 2"); + // if (info.format != py::format_descriptor::format()) + // throw std::runtime_error("AddPoints needs buffer of type double"); + if (info.strides[0] != sizeof(double)*info.shape[1]) + throw std::runtime_error("AddPoints needs packed array"); + double * ptr = static_cast (info.ptr); + + self.Points().SetAllocSize(self.Points().Size()+info.shape[0]); + if (info.shape[1]==2) + for (auto i : Range(info.shape[0])) + { + self.AddPoint (Point<3>(ptr[0], ptr[1], 0)); + ptr += 2; + } + if (info.shape[1]==3) + for (auto i : Range(info.shape[0])) + { + self.AddPoint (Point<3>(ptr[0], ptr[1], ptr[2])); + ptr += 3; + } + }) + .def ("AddElements", [](Mesh & self, int dim, int index, py::buffer b1, int base) + { + static Timer timer("Mesh::AddElements"); + static Timer timercast("Mesh::AddElements casting"); + RegionTimer reg(timer); + + timercast.Start(); + auto b = b1.cast>(); + timercast.Stop(); + + py::buffer_info info = b.request(); + if (info.ndim != 2) + throw std::runtime_error("AddElements needs buffer of dimension 2"); + // if (info.format != py::format_descriptor::format()) + // throw std::runtime_error("AddPoints needs buffer of type int"); + + int * ptr = static_cast (info.ptr); + if (dim == 1) + { + ELEMENT_TYPE type; + int np = info.shape[1]; + self.LineSegments().SetAllocSize(self.LineSegments().Size()+info.shape[0]); + for (auto i : Range(info.shape[0])) + { + Segment el; + for (int j = 0; j < np; j++) + el[j] = ptr[j]+PointIndex::BASE-base; + el.si = index; + self.AddSegment(el); + ptr += info.strides[0]/sizeof(int); + } + } + if (dim == 2) + { + ELEMENT_TYPE type; + int np = info.shape[1]; + switch (np) + { + case 3: type = TRIG; break; + case 4: type = QUAD; break; + case 6: type = TRIG6; break; + case 8: type = QUAD8; break; + default: + throw Exception("unsupported 2D element with "+ToString(np)+" points"); + } + self.SurfaceElements().SetAllocSize(self.SurfaceElements().Size()+info.shape[0]); + for (auto i : Range(info.shape[0])) + { + Element2d el(type); + for (int j = 0; j < np; j++) + el[j] = ptr[j]+PointIndex::BASE-base; + el.SetIndex(index); + self.AddSurfaceElement (el); + ptr += info.strides[0]/sizeof(int); + } + } + if (dim == 3) + { + ELEMENT_TYPE type; + int np = info.shape[1]; + switch (np) + { + case 4: type = TET; break; + /* // have to check ordering of points + case 10: type = TET10; break; + case 8: type = HEX; break; + case 6: type = PRISM; break; + */ + default: + throw Exception("unsupported 3D element with "+ToString(np)+" points"); + } + self.VolumeElements().SetAllocSize(self.VolumeElements().Size()+info.shape[0]); + for (auto i : Range(info.shape[0])) + { + Element el(type); + for (int j = 0; j < np;j ++) + el[j] = ptr[j]+PointIndex::BASE-base; + el.SetIndex(index); + self.AddVolumeElement (el); + ptr += info.strides[0]/sizeof(int); + } + } + + }, py::arg("dim"), py::arg("index"), py::arg("data"), py::arg("base")=0) .def ("DeleteSurfaceElement", - FunctionPointer ([](Mesh & self, SurfaceElementIndex i) - { - return self.DeleteSurfaceElement (i); - })) - - .def ("Compress", FunctionPointer ([](Mesh & self) - { - return self.Compress (); - }),py::call_guard()) - + [](Mesh & self, SurfaceElementIndex i) + { + return self.Delete(i); + }) + + .def ("Compress", [](Mesh & self) + { + return self.Compress (); + } ,py::call_guard()) + + .def ("AddRegion", [] (Mesh & self, string name, int dim) -> int + { + auto & regionnames = self.GetRegionNamesCD(self.GetDimension()-dim); + regionnames.Append (new string(name)); + int idx = regionnames.Size(); + if (dim == 2) + { + FaceDescriptor fd; + fd.SetBCName(regionnames.Last()); + fd.SetBCProperty(idx); + self.AddFaceDescriptor(fd); + } + return idx; + }, py::arg("name"), py::arg("dim")) + + .def ("GetRegionNames", [] (Mesh & self, optional optdim, optional optcodim) + { + int codim; + if (optdim) + codim = self.GetDimension() - *optdim; + else if (optcodim) + codim = *optcodim; + else + throw Exception("either 'dim' or 'codim' must be specified"); + + NgArray & codimnames = self.GetRegionNamesCD (codim); + + std::vector names; + for (auto name : codimnames) + { + if (name) + names.push_back(*name); + else + names.push_back(""); + } + return names; + }, py::arg("dim")=nullopt, py::arg("codim")=nullopt) .def ("SetBCName", &Mesh::SetBCName) .def ("GetBCName", FunctionPointer([](Mesh & self, int bc)->string @@ -821,72 +1205,101 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) .def ("GetCD3Name", &Mesh::GetCD3Name) .def ("SetCD3Name", &Mesh::SetCD3Name) - .def ("AddPointIdentification", [](Mesh & self, py::object pindex1, py::object pindex2, int identnr, int type) + .def ("AddPointIdentification", [](Mesh & self, py::object pindex1, py::object pindex2, int identnr, Identifications::ID_TYPE type) { if(py::extract(pindex1).check() && py::extract(pindex2).check()) { self.GetIdentifications().Add (py::extract(pindex1)(), py::extract(pindex2)(), identnr); - self.GetIdentifications().SetType(identnr, Identifications::ID_TYPE(type)); // type = 2 ... periodic + self.GetIdentifications().SetType(identnr, type); // type = 2 ... periodic } }, //py::default_call_policies(), py::arg("pid1"), py::arg("pid2"), py::arg("identnr"), - py::arg("type")) + py::arg("type")=Identifications::PERIODIC) + .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) { - Array maxh; + NgArray maxh; for(auto el : maxhlist) maxh.Append(py::cast(el)); self.SetMaxHDomain(maxh); }) .def ("GenerateVolumeMesh", - [](Mesh & self, py::object pymp) + [](Mesh & self, MeshingParameters* pars, + py::kwargs kwargs) { - cout << "generate vol mesh" << endl; - MeshingParameters mp; + if(pars) mp = *pars; { py::gil_scoped_acquire acquire; - if (py::extract(pymp).check()) - mp = py::extract(pymp)(); - else - { - mp.optsteps3d = 5; - } + CreateMPfromKwargs(mp, kwargs); } MeshVolume (mp, self); OptimizeVolume (mp, self); - }, - py::arg("mp")=NGDummyArgument(),py::call_guard()) + }, py::arg("mp")=nullptr, + meshingparameter_description.c_str(), + py::call_guard()) - .def ("OptimizeVolumeMesh", FunctionPointer - ([](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, MeshingParameters* pars) + { + self.CalcLocalH(0.5); + MeshingParameters mp; + if(pars) mp = *pars; + else mp.optsteps2d = 5; + if(!self.GetGeometry()) + throw Exception("Cannot optimize surface mesh without geometry!"); + Optimize2d (self, mp); + }, py::arg("mp")=nullptr, py::call_guard()) + .def ("Refine", FunctionPointer - ([](Mesh & self) + ([](Mesh & self, bool adaptive) { - if (self.GetGeometry()) - self.GetGeometry()->GetRefinement().Refine(self); + if (!adaptive) + { + self.GetGeometry()->GetRefinement().Refine(self); + self.UpdateTopology(); + } else - Refinement().Refine(self); - self.UpdateTopology(); - }),py::call_guard()) + { + BisectionOptions biopt; + 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) + biopt.refine_hp = 1; + */ + self.GetGeometry()->GetRefinement().Bisect (self, biopt); + self.UpdateTopology(); + self.GetCurvedElements().SetIsHighOrder (false); + } + }), py::arg("adaptive")=false, py::call_guard()) + + .def("ZRefine", &Mesh::ZRefine) .def ("SecondOrder", FunctionPointer ([](Mesh & self) { - if (self.GetGeometry()) - self.GetGeometry()->GetRefinement().MakeSecondOrder(self); - else - Refinement().MakeSecondOrder(self); + self.GetGeometry()->GetRefinement().MakeSecondOrder(self); })) .def ("GetGeometry", [] (Mesh& self) { return self.GetGeometry(); }) @@ -905,79 +1318,260 @@ 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 ("BoundaryLayer2", GenerateBoundaryLayer2, py::arg("domain"), py::arg("thicknesses"), py::arg("make_new_domain")=true, py::arg("boundaries")=Array{}) + .def ("BoundaryLayer", [](Mesh & self, variant boundary, + variant thickness, + variant> material, + variant domain, bool outside, + optional project_boundaries, + bool grow_edges, bool limit_growth_vectors, + bool sides_keep_surfaceindex) { - 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) + boundaries.SetBit(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)) + { + boundaries.SetBit(i); + auto dom_pattern = get_if(&domain); + // only add if adjacent to domain + if(dom_pattern) + { + regex pattern(*dom_pattern); + bool mat1_match = fd.DomainIn() > 0 && regex_match(self.GetMaterial(fd.DomainIn()), pattern); + bool mat2_match = fd.DomainOut() > 0 && regex_match(self.GetMaterial(fd.DomainOut()), pattern); + // if boundary is inner or outer remove from list + if(mat1_match == mat2_match) + boundaries.Clear(i); + // if((fd.DomainIn() > 0 && regex_match(self.GetMaterial(fd.DomainIn()), pattern)) || (fd.DomainOut() > 0 && regex_match(self.GetMaterial(fd.DomainOut()), pattern))) + // boundaries.Clear(i); + // blp.surfid.Append(i); + } + // else + // blp.surfid.Append(i); + } + } + } + for(int i = 1; i<=self.GetNFD(); i++) + if(boundaries.Test(i)) + blp.surfid.Append(i); + if(string* mat = get_if(&material); mat) + blp.new_mat = { { ".*", *mat } }; + else + blp.new_mat = *get_if>(&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 = grow_edges; + blp.limit_growth_vectors = limit_growth_vectors; + blp.sides_keep_surfaceindex = sides_keep_surfaceindex; + 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, py::arg("grow_edges")=true, py::arg("limit_growth_vectors") = true, py::arg("sides_keep_surfaceindex")=false, + 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_static ("EnableTableClass", [] (string name, bool set) + { + MeshTopology::EnableTableStatic(name, set); + }, + py::arg("name"), py::arg("set")=true) .def ("EnableTable", [] (Mesh & self, string name, bool set) { - if (name == "edges") - const_cast(self.GetTopology()).SetBuildEdges(set); - if (name == "faces") - const_cast(self.GetTopology()).SetBuildFaces(set); + const_cast(self.GetTopology()).EnableTable(name, set); }, py::arg("name"), py::arg("set")=true) - .def ("Scale", FunctionPointer([](Mesh & self, double factor) - { - for(auto i = 0; i (); + *m2 = self; + return m2; + }) + .def ("CalcMinMaxAngle", [](Mesh & self, double badel_limit) + { + double values[4]; + self.CalcMinMaxAngle (badel_limit, values); + py::dict res; + res["trig"] = py::make_tuple( values[0], values[1] ); + res["tet"] = py::make_tuple( values[2], values[3] ); + return res; + }, py::arg("badelement_limit")=175.0) + .def ("Update", [](Mesh & self) + { + self.SetNextTimeStamp(); + }) + .def ("CalcTotalBadness", &Mesh::CalcTotalBad) + .def ("GetQualityHistogram", &Mesh::GetQualityHistogram) + .def("Mirror", &Mesh::Mirror) + .def("_getVertices", [](Mesh & self) + { + // std::vector verts(3*self.GetNV()); + Array verts(3*self.GetNV()); + ParallelForRange( self.GetNV(), [&](auto myrange) { + const auto & points = self.Points(); + for(auto i : myrange) + { + auto p = points[PointIndex::BASE+i]; + auto * v = &verts[3*i]; + for(auto k : Range(3)) + v[k] = p[k]; + } }); + return verts; + }) + .def("_getSegments", [](Mesh & self) + { + // std::vector output; + // output.resize(2*self.GetNSeg()); + Array output(2*self.GetNSeg()); + ParallelForRange( self.GetNSeg(), [&](auto myrange) { + const auto & segs = self.LineSegments(); + for(auto i : myrange) + { + const auto & seg = segs[i]; + for(auto k : Range(2)) + output[2*i+k] = seg[k]-PointIndex::BASE; + } }); + return output; + }) + .def("_getWireframe", [](Mesh & self) + { + const auto & topo = self.GetTopology(); + size_t n = topo.GetNEdges(); + /* + std::vector output; + output.resize(2*n); + */ + Array output(2*n); + ParallelForRange( n, [&](auto myrange) { + for(auto i : myrange) + { + PointIndex p0,p1; + topo.GetEdgeVertices(i+1, p0, p1); + output[2*i] = p0-PointIndex::BASE; + output[2*i+1] = p1-PointIndex::BASE; + } }); + return output; + }) + .def("_get2dElementsAsTriangles", [](Mesh & self) + { + /* + std::vector trigs; + trigs.resize(3*self.GetNSE()); + */ + Array trigs(3*self.GetNSE()); + ParallelForRange( self.GetNSE(), [&](auto myrange) { + const auto & surfels = self.SurfaceElements(); + for(auto i : myrange) + { + const auto & sel = surfels[i]; + auto * trig = &trigs[3*i]; + for(auto k : Range(3)) + trig[k] = sel[k]-PointIndex::BASE; + // todo: quads (store the second trig in thread-local extra array, merge them at the end (mutex) + } }); + return trigs; + }) + .def("_get3dElementsAsTets", [](Mesh & self) + { + // std::vector tets; + // tets.resize(4*self.GetNE()); + + Array tets(4*self.GetNE()); + ParallelForRange( self.GetNE(), [&](auto myrange) { + const auto & els = self.VolumeElements(); + for(auto i : myrange) + { + const auto & el = els[i]; + auto * trig = &tets[4*i]; + for(auto k : Range(4)) + trig[k] = el[k]-PointIndex::BASE; + // todo: prisms etc (store the extra tets in thread-local extra array, merge them at the end (mutex) + } }); + return tets; + }) ; m.def("ImportMesh", [](const string& filename) @@ -995,55 +1589,42 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) Pro/ENGINEER format (*.fnf) )delimiter"); py::enum_(m,"MeshingStep") - .value("MESHEDGES",MESHCONST_MESHEDGES) - .value("MESHSURFACE",MESHCONST_OPTSURFACE) - .value("MESHVOLUME",MESHCONST_OPTVOLUME) + .value("ANALYSE", MESHCONST_ANALYSE) + .value("MESHEDGES", MESHCONST_MESHEDGES) + .value("MESHSURFACE", MESHCONST_OPTSURFACE) + .value("MESHVOLUME", MESHCONST_OPTVOLUME) ; typedef MeshingParameters MP; - py::class_ (m, "MeshingParameters") + auto mp = py::class_ (m, "MeshingParameters") .def(py::init<>()) - .def(py::init([](double maxh, bool quad_dominated, int optsteps2d, int optsteps3d, - MESHING_STEP perfstepsend, int only3D_domain, const string & meshsizefilename, - double grading, double curvaturesafety, double segmentsperedge) + .def(py::init([](MeshingParameters* other, py::kwargs kwargs) { - MP * instance = new MeshingParameters; - instance->maxh = maxh; - instance->quad = int(quad_dominated); - instance->optsteps2d = optsteps2d; - instance->optsteps3d = optsteps3d; - instance->only3D_domain_nr = only3D_domain; - instance->perfstepsend = perfstepsend; - instance->meshsizefilename = meshsizefilename; - - instance->grading = grading; - instance->curvaturesafety = curvaturesafety; - instance->segmentsperedge = segmentsperedge; - return instance; - }), - py::arg("maxh")=1000, - py::arg("quad_dominated")=false, - py::arg("optsteps2d") = 3, - py::arg("optsteps3d") = 3, - py::arg("perfstepsend") = MESHCONST_OPTVOLUME, - py::arg("only3D_domain") = 0, - py::arg("meshsizefilename") = "", - py::arg("grading")=0.3, - py::arg("curvaturesafety")=2, - py::arg("segmentsperedge")=1, - "create meshing parameters" - ) + MeshingParameters mp; + if(other) mp = *other; + CreateMPfromKwargs(mp, kwargs, false); + return mp; + }), py::arg("mp")=nullptr, meshingparameter_description.c_str()) .def("__str__", &ToString) - .def_property("maxh", - FunctionPointer ([](const MP & mp ) { return mp.maxh; }), - FunctionPointer ([](MP & mp, double maxh) { return mp.maxh = maxh; })) - .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) @@ -1059,7 +1640,121 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) return old; })); + py::class_ (m, "_DebugParameters") + .def_readwrite("debugoutput", &DebugParameters::debugoutput) + .def_readwrite("slowchecks", &DebugParameters::slowchecks) + .def_readwrite("haltsuccess", &DebugParameters::haltsuccess) + .def_readwrite("haltnosuccess", &DebugParameters::haltnosuccess) + .def_readwrite("haltlargequalclass", &DebugParameters::haltlargequalclass) + .def_readwrite("haltsegment", &DebugParameters::haltsegment) + .def_readwrite("haltnode", &DebugParameters::haltnode) + .def_readwrite("haltsegmentp1", &DebugParameters::haltsegmentp1) + .def_readwrite("haltsegmentp2", &DebugParameters::haltsegmentp2) + .def_readwrite("haltexistingline", &DebugParameters::haltexistingline) + .def_readwrite("haltoverlap", &DebugParameters::haltoverlap) + .def_readwrite("haltface", &DebugParameters::haltface) + .def_readwrite("haltfacenr", &DebugParameters::haltfacenr) + .def_readwrite("write_mesh_on_error", &DebugParameters::write_mesh_on_error) + ; + m.attr("debugparam") = py::cast(&debugparam); + + 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->GenerateStructuredMesh (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; }); + m.def ("_Redraw", + ([](bool blocking, double fr) + { + static auto last_time = std::chrono::system_clock::now()-std::chrono::seconds(10); + auto now = std::chrono::system_clock::now(); + double elapsed = std::chrono::duration(now-last_time).count(); + if (blocking || elapsed * fr > 1) + { + Ng_Redraw(blocking); + last_time = std::chrono::system_clock::now(); + return true; + } + return false; + }), + py::arg("blocking")=false, py::arg("fr") = 25, R"raw_string( + Redraw all + + Parameters: + + blocking : bool + input blocking + + fr : double + input framerate + + )raw_string"); } PYBIND11_MODULE(libmesh, m) { diff --git a/libsrc/meshing/python_mesh.hpp b/libsrc/meshing/python_mesh.hpp new file mode 100644 index 00000000..4a5db879 --- /dev/null +++ b/libsrc/meshing/python_mesh.hpp @@ -0,0 +1,198 @@ +#ifndef NETGEN_MESHING_PYTHON_MESH_HPP +#define NETGEN_MESHING_PYTHON_MESH_HPP + +#include + +#include "meshing.hpp" + +namespace netgen +{ + // TODO: Clarify a lot of these parameters + static string meshingparameter_description = R"delimiter( +Meshing Parameters +------------------- + +maxh: float = 1e10 + Global upper bound for mesh size. + +grading: float = 0.3 + Mesh grading how fast the local mesh size can change. + +meshsizefilename: str = None + Load meshsize from file. Can set local mesh size for points + and along edges. File must have the format: + + nr_points + x1, y1, z1, meshsize + x2, y2, z2, meshsize + ... + xn, yn, zn, meshsize + + nr_edges + x11, y11, z11, x12, y12, z12, meshsize + ... + xn1, yn1, zn1, xn2, yn2, zn2, meshsize + +segmentsperedge: float = 1. + Minimal number of segments per edge. + +quad_dominated: bool = False + Quad-dominated surface meshing. + +blockfill: bool = True + Do fast blockfilling. + +filldist: float = 0.1 + Block fill up to distance + +delaunay: bool = True + Use delaunay meshing. + +delaunay2d : bool = True + Use delaunay meshing for 2d geometries. + +Optimization Parameters +----------------------- + +optimize3d: str = "cmdmustm" + 3d optimization strategy: + m .. move nodes + M .. move nodes, cheap functional + s .. swap faces + c .. combine elements + d .. divide elements + p .. plot, no pause + P .. plot, Pause + h .. Histogramm, no pause + H .. Histogramm, pause + +optsteps3d: int = 3 + Number of 3d optimization steps. + +optimize2d: str = "smcmSmcmSmcm" + 2d optimization strategy: + s .. swap, opt 6 lines/node + S .. swap, optimal elements + m .. move nodes + p .. plot, no pause + P .. plot, pause + c .. combine + +optsteps2d: int = 3 + Number of 2d optimization steps. + +elsizeweight: float = 0.2 + Weight of element size w.r.t. element shape in optimization. + +)delimiter"; + +inline void CreateMPfromKwargs(MeshingParameters& mp, py::kwargs kwargs, bool throw_if_not_all_parsed=true) + { + if(kwargs.contains("optimize3d")) + mp.optimize3d = py::cast(kwargs.attr("pop")("optimize3d")); + if(kwargs.contains("optsteps3d")) + mp.optsteps3d = py::cast(kwargs.attr("pop")("optsteps3d")); + if(kwargs.contains("optimize2d")) + mp.optimize2d = py::cast(kwargs.attr("pop")("optimize2d")); + if(kwargs.contains("optsteps2d")) + mp.optsteps2d = py::cast(kwargs.attr("pop")("optsteps2d")); + if(kwargs.contains("opterrpow")) + mp.opterrpow = py::cast(kwargs.attr("pop")("opterrpow")); + if(kwargs.contains("blockfill")) + mp.blockfill = py::cast(kwargs.attr("pop")("blockfill")); + if(kwargs.contains("filldist")) + mp.filldist = py::cast(kwargs.attr("pop")("filldist")); + if(kwargs.contains("safety")) + mp.safety = py::cast(kwargs.attr("pop")("safety")); + if(kwargs.contains("relinnersafety")) + mp.relinnersafety = py::cast(kwargs.attr("pop")("relinnersafety")); + if(kwargs.contains("uselocalh")) + mp.uselocalh = py::cast(kwargs.attr("pop")("uselocalh")); + if(kwargs.contains("grading")) + 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")) + mp.minh = py::cast(kwargs.attr("pop")("minh")); + if(kwargs.contains("meshsizefilename")) + mp.meshsizefilename = py::cast(kwargs.attr("pop")("meshsizefilename")); + if(kwargs.contains("startinsurface")) + mp.startinsurface = py::cast(kwargs.attr("pop")("startinsurface")); + if(kwargs.contains("checkoverlap")) + mp.checkoverlap = py::cast(kwargs.attr("pop")("checkoverlap")); + if(kwargs.contains("checkoverlappingboundary")) + mp.checkoverlappingboundary = py::cast(kwargs.attr("pop")("checkoverlappingboundary")); + if(kwargs.contains("checkchartboundary")) + mp.checkchartboundary = py::cast(kwargs.attr("pop")("checkchartboundary")); + if(kwargs.contains("curvaturesafety")) + mp.curvaturesafety = py::cast(kwargs.attr("pop")("curvaturesafety")); + if(kwargs.contains("segmentsperedge")) + mp.segmentsperedge = py::cast(kwargs.attr("pop")("segmentsperedge")); + if(kwargs.contains("parthread")) + mp.parthread = py::cast(kwargs.attr("pop")("parthread")); + if(kwargs.contains("elsizeweight")) + mp.elsizeweight = py::cast(kwargs.attr("pop")("elsizeweight")); + if(kwargs.contains("perfstepsstart")) + mp.perfstepsstart = py::cast(kwargs.attr("pop")("perfstepsstart")); + if(kwargs.contains("perfstepsend")) + mp.perfstepsend = py::cast(kwargs.attr("pop")("perfstepsend")); + if(kwargs.contains("giveuptol2d")) + mp.giveuptol2d = py::cast(kwargs.attr("pop")("giveuptol2d")); + if(kwargs.contains("giveuptol")) + mp.giveuptol = py::cast(kwargs.attr("pop")("giveuptol")); + if(kwargs.contains("giveuptolopenquads")) + mp.giveuptolopenquads = py::cast(kwargs.attr("pop")("giveuptolopenquads")); + if(kwargs.contains("maxoutersteps")) + mp.maxoutersteps = py::cast(kwargs.attr("pop")("maxoutersteps")); + if(kwargs.contains("starshapeclass")) + mp.starshapeclass = py::cast(kwargs.attr("pop")("starshapeclass")); + if(kwargs.contains("baseelnp")) + mp.baseelnp = py::cast(kwargs.attr("pop")("baseelnp")); + if(kwargs.contains("sloppy")) + mp.sloppy = py::cast(kwargs.attr("pop")("sloppy")); + if(kwargs.contains("badellimit")) + mp.badellimit = py::cast(kwargs.attr("pop")("badellimit")); + if(kwargs.contains("check_impossible")) + mp.check_impossible = py::cast(kwargs.attr("pop")("check_impossible")); + if(kwargs.contains("only3D_domain_nr")) + mp.only3D_domain_nr = py::cast(kwargs.attr("pop")("only3D_domain_nr")); + if(kwargs.contains("secondorder")) + mp.secondorder = py::cast(kwargs.attr("pop")("secondorder")); + if(kwargs.contains("elementorder")) + mp.elementorder = py::cast(kwargs.attr("pop")("elementorder")); + if(kwargs.contains("quad")) + { + cout << "WARNING: Meshing parameter 'quad' is deprecated, use 'quad_dominated' instead!" << endl; + mp.quad = py::cast(kwargs.attr("pop")("quad")); + } + if(kwargs.contains("quad_dominated")) + mp.quad = py::cast(kwargs.attr("pop")("quad_dominated")); + if(kwargs.contains("try_hexes")) + mp.try_hexes = py::cast(kwargs.attr("pop")("try_hexes")); + if(kwargs.contains("inverttets")) + mp.inverttets = py::cast(kwargs.attr("pop")("inverttets")); + if(kwargs.contains("inverttrigs")) + mp.inverttrigs = py::cast(kwargs.attr("pop")("inverttrigs")); + if(kwargs.contains("autozrefine")) + mp.autozrefine = py::cast(kwargs.attr("pop")("autozrefine")); + if(kwargs.contains("parallel_meshing")) + mp.parallel_meshing = py::cast(kwargs.attr("pop")("parallel_meshing")); + if(kwargs.contains("nthreads")) + mp.nthreads = py::cast(kwargs.attr("pop")("nthreads")); + if(kwargs.contains("closeedgefac")) + mp.closeedgefac = py::cast>(kwargs.attr("pop")("closeedgefac")); + if(kwargs.size()) + { + if(throw_if_not_all_parsed) + throw Exception(string("Not all kwargs given to GenerateMesh could be parsed:") + string(py::str(kwargs))); + mp.geometrySpecificParameters = CreateFlagsFromKwArgs(kwargs); + } + } +} // namespace netgen + +#endif // NETGEN_MESHING_PYTHON_MESH_HPP + diff --git a/libsrc/meshing/quadrls.cpp b/libsrc/meshing/quadrls.cpp deleted file mode 100644 index 1c2cd23b..00000000 --- a/libsrc/meshing/quadrls.cpp +++ /dev/null @@ -1,887 +0,0 @@ -namespace netgen -{ -const char * quadrules[] = { -"rule \"Free Quad (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 1) { 1 X2 } { };\n",\ -"(0, 1) { } { };\n",\ -"\n",\ -"newlines\n",\ -"(3, 2);\n",\ -"(4, 3);\n",\ -"(1, 4);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 1.5) { 1.5 X2 } { };\n",\ -"(-0.5, 1.5) { -0.5 X2 } { };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Free Quad (5)\"\n",\ -"\n",\ -"quality 5\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 1) { 1 X2 } { };\n",\ -"(0, 1) { } { };\n",\ -"\n",\ -"newlines\n",\ -"(3, 2);\n",\ -"(4, 3);\n",\ -"(1, 4);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 1.5) { 1.5 X2 } { };\n",\ -"(-0.5, 1.5) { -0.5 X2 } { };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X2 } { };\n",\ -"(0, 1) { } { };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Quad Right (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0, 1) { } { 1 y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(4, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(-0.5, 1.5) { } { 1.5 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Quad P Right (2)\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0, 1) { -1 X2, 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(4, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.2, 0.5) { 0.7 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(-0.5, 1.5) { -2 X2, 1.5 X3 } { 1.5 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { -1 X2, 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"Quad P Right (150)\"\n",\ -"\n",\ -"quality 150\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 4)\n;",\ -"(4, 3)\n;",\ -"(3, 2)\n;",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.2, 0.5) { 0.7 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(-0.5, 1.5) { -2 X2, 1.5 X3 } { 1.5 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"Quad Right PL (2)\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(4, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0.5, 1.2) { -0.1 X2, 0.6 X3, 0.6 X4 } { -0.1 Y2, 0.6 Y3, 0.6 Y4 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(-0.2, 0.5) { -0.1 X2, -0.1 X3, 0.6 X4 } { -0.1 Y2, -0.1 Y3, 0.6 Y4 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3);\n",\ -"(1, 3, 4);\n",\ -"(1, 2, 4);\n",\ -"(4, 2, 3);\n",\ -"\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left Quad (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left P Quad (2)\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"(-0.2, 0.6) { -0.2 X2, 0.6 X3 } { 0.6 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X2, 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 0.5) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left P Quad (150)\"\n",\ -"\n",\ -"quality 150\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"(-0.2, 0.6) { -0.2 X2, 0.6 X3 } { 0.6 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X2, -1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 0.5) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left Quad RP (2)\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0, 1);\n",\ -"(1, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.2, 0.5) { 0.6 X2, 0.6 X4, -0.1 X3 } { 0.6 Y2, 0.6 Y4, -0.1 Y3 };\n",\ -"(1, 1) { 1 X4 } { 1 Y4 };\n",\ -"(0.5, 1.2) { -0.1 X2, 0.6 X3, 0.6 X4 } { -0.1 Y2, 0.6 Y3, 0.6 Y4 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 0.5 X2, 0.5 X4 } { 0.5 Y2, 0.5 Y4 };\n",\ -"(1, 1) { 1 X4 } { 1 Y4 };\n",\ -"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\ -"(0, 1) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4, 3);\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 4);\n",\ -"(1, 4, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Two left (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 4) del;\n",\ -"(4, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.5) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y3, -0.25 Y4 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Two Right (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"(3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(-0.5, 0.5) { -0.25 X2, -0.25 X3, 0.75 X4 } { -0.25 Y3, 0.75 Y4 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Right 120 (1)\"\n",\ -"\n",\ -"quality 1000\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 1 X3, -1 X2 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(4, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(1, 1.732) { -2 X2, 2 X3 } { 2 Y3 };\n",\ -"(0, 1.732) { -3 X2, 2 X3 } { 2 Y3 };\n",\ -"(-0.5, 0.866) { -2 X2, 1 X3 } {1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4);\n",\ -"(2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left 120 (1)\"\n",\ -"\n",\ -"quality 1000\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(-0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 1 X3, 1 X2 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.866) { 2 X2, 1 X3 } { 1 Y3 };\n",\ -"(1, 1.732) { 2 X2, 2 X3 } { 2 Y3 };\n",\ -"(0, 1.732) { -1 X2, 2 X3 } { 2 Y3 };\n",\ -"(-0.5, 0.866) { 1 X3 } {1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4);\n",\ -"(2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left Right (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"(4, 1) del;\n",\ -"\n",\ -"\n",\ -"newlines\n",\ -"(4, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0.5, 1.5) { -0.25 X2, 0.75 X3, 0.75 X4 } { 0.75 Y3, 0.75 Y4 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0.5, 1) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Fill Quad\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"(3, 4) del;\n",\ -"(4, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { 1 Y2 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Fill Triangle\"\n",\ -"\n",\ -"quality 10\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0.5, 0.86);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { 1 Y2 };\n",\ -"(0.5, 0.86) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Right 60 (1)\"\n",\ -"\n",\ -"quality 10\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 0.5, 0, 1.0 };\n",\ -"(0.5, 0.866) { 0.6, 0, 0.8 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(-0.125, 0.6495) { -0.5 X2, 0.75 X3 } { -0.5 Y2, 0.75 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Vis A Vis (2)\"\n",\ -"\n",\ -"quality 2\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1);\n",\ -"(0, 1);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.5) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y3, -0.25 Y4 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(-0.5, 0.5) { -0.25 X2, -0.25 X3, 0.75 X4 } { -0.25 Y3, 0.75 Y4 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 };\n",\ -"(1, 1) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1) { 1 X4 } { 1 Y4 };\n",\ -"(0, 0.5) { 0.5 X4 } { 0.5 Y4 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"orientations\n",\ -"(1, 3, 4);\n",\ -"(2, 3, 4);\n",\ -"(1, 2, 3);\n",\ -"(1, 2, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Triangle Vis A Vis (200)\"\n",\ -"\n",\ -"quality 200\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.2, 0.693) { 0.8 X2, 0.8 X3 } { 0.8 Y2, 0.8 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(-0.2, 0.693) { -0.6 X2, 0.8 X3 } { -0.6 Y2, 0.8 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"2 h Vis A Vis (1)\"\n",\ -"\n",\ -"quality 3000\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1.732);\n",\ -"(0, 1.732);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 0.25 X3, 0.25 X4 } { 0.25 Y2, 0.25 Y3, 0.25 Y4 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 5);\n",\ -"(5, 4);\n",\ -"(3, 5);\n",\ -"(5, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { 1 Y2 };\n",\ -"(1.5, 0.866) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y2, 0.75 Y3, -0.25 Y4 };\n",\ -"(1, 1.732) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1.732) { 1 X4 } { 1 Y4 };\n",\ -"(-0.5, 0.866) { 0.75 X4, -0.25 X2, -0.25 X3 } { 0.75 Y4, -0.25 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 5);\n",\ -"(3, 4, 5);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/refine.cpp b/libsrc/meshing/refine.cpp index 54c9f592..7f9ed6ad 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,13 +34,16 @@ 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); // new version with consistent ordering across sub-domains - Array parents; + NgArray parents; for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++) { const Segment & el = mesh[si]; @@ -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"); } @@ -109,7 +141,7 @@ namespace netgen PrintMessage (5, "have points"); - Array par_nr(parents.Size()); + NgArray par_nr(parents.Size()); for (int i = 0; i < par_nr.Size(); i++) par_nr[i] = i; QuickSort (parents, par_nr); @@ -119,15 +151,15 @@ 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()); - Array pointset(mesh.GetNP()); + NgArray pointset(mesh.GetNP()); pointset = false; PrintMessage (5, "sorting complete"); // refine edges - Array epgi; + NgArray epgi; int oldns = mesh.GetNSeg(); for (SegmentIndex si = 0; si < oldns; si++) @@ -147,11 +179,11 @@ namespace netgen { pointset[pinew] = true; Point<3> pnew; - PointBetween (mesh.Point (el[0]), - mesh.Point (el[1]), 0.5, - el.surfnr1, el.surfnr2, - el.epgeominfo[0], el.epgeominfo[1], - pnew, ngi); + geo.PointBetweenEdge(mesh.Point (el[0]), + mesh.Point (el[1]), 0.5, + el.surfnr1, el.surfnr2, + el.epgeominfo[0], el.epgeominfo[1], + pnew, ngi); // pinew = mesh.AddPoint (pnew); mesh.Point(pinew) = pnew; @@ -176,7 +208,7 @@ namespace netgen PrintMessage (5, "have 1d elements"); // refine surface elements - Array surfgi (8*mesh.GetNP()); + NgArray surfgi (8*mesh.GetNP()); for (int i = PointIndex::BASE; i < surfgi.Size()+PointIndex::BASE; i++) surfgi[i].trignum = -1; @@ -185,15 +217,15 @@ namespace netgen int oldnf = mesh.GetNSE(); for (SurfaceElementIndex sei = 0; sei < oldnf; sei++) { - const Element2d & el = mesh.SurfaceElement(sei); + const Element2d & el = mesh[sei]; switch (el.GetType()) { case TRIG: case TRIG6: { - ArrayMem pnums(6); - ArrayMem pgis(6); + NgArrayMem pnums(6); + NgArrayMem pgis(6); static int betw[3][3] = { { 2, 3, 4 }, @@ -216,12 +248,12 @@ namespace netgen Point<3> pb; PointGeomInfo pgi; - 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, pgi); + 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, pgi); pgis.Elem(4+j) = pgi; @@ -265,7 +297,7 @@ namespace netgen nel.SetIndex(ind); if (j == 0) - mesh.SurfaceElement(sei) = nel; + mesh[sei] = nel; else mesh.AddSurfaceElement(nel); } @@ -275,75 +307,81 @@ namespace netgen case QUAD6: case QUAD8: { - ArrayMem pnums(9); - ArrayMem 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; - 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); if (j == 0) - mesh.SurfaceElement(sei) = nel; + mesh[sei] = nel; else mesh.AddSurfaceElement(nel); } @@ -361,13 +399,13 @@ namespace netgen mesh.VolumeElements().SetAllocSize(8*oldne); for (ElementIndex ei = 0; ei < oldne; ei++) { - const Element & el = mesh.VolumeElement(ei); + const Element & el = mesh[ei]; switch (el.GetType()) { case TET: case TET10: { - ArrayMem pnums(10); + NgArrayMem pnums(10); static int betw[6][3] = { { 1, 2, 5 }, { 1, 3, 6 }, @@ -376,7 +414,7 @@ namespace netgen { 2, 4, 9 }, { 3, 4, 10 } }; - int elrev = el.flags.reverse; + int elrev = el.Flags().reverse; for (int j = 1; j <= 4; j++) pnums.Elem(j) = el.PNum(j); @@ -442,10 +480,10 @@ namespace netgen for (int k = 1; k <= 4; k++) nel.PNum(k) = pnums.Get(reftab[j][k-1]); nel.SetIndex(ind); - nel.flags.reverse = reverse[j]; + nel.Flags().reverse = reverse[j]; if (elrev) { - nel.flags.reverse = !nel.flags.reverse; + nel.Flags().reverse = !nel.Flags().reverse; swap (nel.PNum(3), nel.PNum(4)); } @@ -458,7 +496,7 @@ namespace netgen } case HEX: { - ArrayMem pnums(27); + NgArrayMem pnums(27); static int betw[13][3] = { { 1, 2, 9 }, { 3, 4, 10 }, @@ -584,7 +622,7 @@ namespace netgen } case PRISM: { - ArrayMem pnums(18); + NgArrayMem pnums(18); static int betw[9][3] = { { 3, 1, 7 }, { 1, 2, 8 }, @@ -625,8 +663,8 @@ namespace netgen // if (elrev) // swap (pnums.Elem(3), pnums.Elem(4)); - for (int j = 0; j < 9; j++) - { + for (int j = 0; j < 9; j++) + { INDEX_2 i2; i2.I1() = pnums.Get(betw[j][0]); i2.I2() = pnums.Get(betw[j][1]); @@ -641,10 +679,10 @@ namespace netgen mesh.Point(i2.I2()))); between.Set (i2, pnums.Elem(7+j)); } - } + } - for (int j = 0; j < 3; j++) - { + for (int j = 0; j < 3; j++) + { INDEX_2 i2a, i2b; i2a.I1() = pnums.Get(fbetw[2*j][0]); i2a.I2() = pnums.Get(fbetw[2*j][1]); @@ -711,7 +749,7 @@ namespace netgen // update identification tables for (int i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++) { - Array identmap; + NgArray identmap; mesh.GetIdentifications().GetMap (i, identmap); for (int j = 1; j <= between.GetNBags(); j++) @@ -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; @@ -744,18 +794,18 @@ namespace netgen if (mesh.VolumeElement(i).Volume(mesh.Points()) < 0) { wrongels++; - mesh.VolumeElement(i).flags.badel = 1; + mesh.VolumeElement(i).Flags().badel = 1; } else - mesh.VolumeElement(i).flags.badel = 0; + mesh.VolumeElement(i).Flags().badel = 0; if (wrongels) { cout << "WARNING: " << wrongels << " with wrong orientation found" << endl; int np = mesh.GetNP(); - Array > should(np); - Array > can(np); + NgArray > should(np); + NgArray > can(np); for (int i = 1; i <= np; i++) { should.Elem(i) = can.Elem(i) = mesh.Point(i); @@ -770,7 +820,7 @@ namespace netgen can.Elem(parent.I2())); } - BitArray boundp(np); + NgBitArray boundp(np); boundp.Clear(); for (auto & sel : mesh.SurfaceElements()) for (auto pi : sel.PNums()) @@ -801,7 +851,7 @@ namespace netgen mesh.Point(i) = can.Get(i); - BitArray free (mesh.GetNP()), fhelp(mesh.GetNP()); + NgBitArray free (mesh.GetNP()), fhelp(mesh.GetNP()); free.Clear(); for (int i = 1; i <= mesh.GetNE(); i++) { @@ -850,14 +900,14 @@ namespace netgen if (mesh.VolumeElement(i).Volume(mesh.Points()) < 0) { wrongels++; - mesh.VolumeElement(i).flags.badel = 1; + mesh.VolumeElement(i).Flags().badel = 1; (*testout) << "wrong el: "; for (int j = 1; j <= 4; j++) (*testout) << mesh.VolumeElement(i).PNum(j) << " "; (*testout) << endl; } else - mesh.VolumeElement(i).flags.badel = 0; + mesh.VolumeElement(i).Flags().badel = 0; } cout << "wrongels = " << wrongels << endl; } diff --git a/libsrc/meshing/ruler2.cpp b/libsrc/meshing/ruler2.cpp index a6d5410f..cafecd90 100644 --- a/libsrc/meshing/ruler2.cpp +++ b/libsrc/meshing/ruler2.cpp @@ -5,13 +5,13 @@ namespace netgen { - static double CalcElementBadness (const Array & points, + static double CalcElementBadness (const NgArray> & points, const Element2d & elem) { // badness = sqrt(3) /36 * circumference^2 / area - 1 + // h / li + li / h - 2 - Vec2d v12, v13, v23; + Vec<2> v12, v13, v23; double l12, l13, l23, cir, area; static const double c = sqrt(3.0) / 36; @@ -24,7 +24,7 @@ namespace netgen l23 = v23.Length(); cir = l12 + l13 + l23; - area = 0.5 * (v12.X() * v13.Y() - v12.Y() * v13.X()); + area = 0.5 * (v12[0] * v13[1] - v12[1] * v13[0]); if (area < 1e-6) { return 1e8; @@ -45,18 +45,16 @@ namespace netgen - int Meshing2 ::ApplyRules (Array & lpoints, - Array & legalpoints, + int Meshing2 ::ApplyRules (NgArray> & lpoints, + NgArray & legalpoints, int maxlegalpoint, - Array & llines1, + NgArray & llines1, int maxlegalline, - Array & elements, - Array & dellines, int tolerance, + NgArray & elements, + NgArray & dellines, int tolerance, const MeshingParameters & mp) { - static int timer = NgProfiler::CreateTimer ("meshing2::ApplyRules"); - NgProfiler::RegionTimer reg (timer); - + // static Timer timer ("meshing2::ApplyRules"); RegionTimer reg (timer); double maxerr = 0.5 + 0.3 * tolerance; @@ -66,16 +64,19 @@ namespace netgen int noldll = llines1.Size(); - ArrayMem pused(maxlegalpoint), lused(maxlegalline); - ArrayMem pnearness(noldlp), lnearness(llines1.Size()); + NgArrayMem pused(maxlegalpoint), lused(maxlegalline); + NgArrayMem pnearness(noldlp), lnearness(llines1.Size()); - ArrayMem pmap, pfixed, lmap; + NgArrayMem pmap, pfixed, lmap; - ArrayMem tempnewpoints; - ArrayMem tempnewlines; - ArrayMem tempdellines; - ArrayMem tempelements; + NgArrayMem,100> tempnewpoints; + NgArrayMem tempnewlines; + NgArrayMem tempdellines; + NgArrayMem tempelements; + // a least 2 * maximal number of old points in rules, + // what is actually 4 now + double oldumem[20]; elements.SetSize (0); dellines.SetSize (0); @@ -135,8 +136,8 @@ namespace netgen // resort lines after lnearness - Array llines(llines1.Size()); - Array sortlines(llines1.Size()); + NgArray llines(llines1.Size()); + NgArray sortlines(llines1.Size()); int lnearness_class[MAX_NEARNESS]; for (int j = 0; j < MAX_NEARNESS; j++) @@ -208,7 +209,7 @@ namespace netgen for (int ri = 1; ri <= rules.Size(); ri++) { // NgProfiler::RegionTimer reg(timers[ri-1]); - netrule * rule = rules.Get(ri); + netrule * rule = rules[ri-1].get(); #ifdef LOCDEBUG if (loctestmode) @@ -262,7 +263,7 @@ namespace netgen ok = 1; INDEX_2 loclin = llines.Get(locli); - Vec2d linevec = lpoints.Get(loclin.I2()) - lpoints.Get(loclin.I1()); + auto linevec = lpoints.Get(loclin.I2()) - lpoints.Get(loclin.I1()); if (rule->CalcLineError (nlok, linevec) > maxerr) { @@ -457,7 +458,8 @@ namespace netgen if (!ok) continue; - Vector oldu (2 * rule->GetNOldP()); + // Vector oldu (2 * rule->GetNOldP()); + Vector oldu (2 * rule->GetNOldP(), &oldumem[0]); for (int i = 1; i <= rule->GetNOldP(); i++) { @@ -590,9 +592,9 @@ namespace netgen int oldnp = rule->GetNOldP(); for (int i = oldnp + 1; i <= rule->GetNP(); i++) { - Point2d np = rule->GetPoint(i); - np.X() += newu (2 * (i-oldnp) - 2); - np.Y() += newu (2 * (i-oldnp) - 1); + auto np = rule->GetPoint(i); + np[0] += newu (2 * (i-oldnp) - 2); + np[1] += newu (2 * (i-oldnp) - 1); lpoints.Append (np); pmap.Elem(i) = lpoints.Size(); diff --git a/libsrc/meshing/ruler2.hpp b/libsrc/meshing/ruler2.hpp index afbe6b98..3d9ca6af 100644 --- a/libsrc/meshing/ruler2.hpp +++ b/libsrc/meshing/ruler2.hpp @@ -21,35 +21,35 @@ private: /// int quality; /// - char * name; + string name; /// - Array points; + NgArray> points; /// - Array lines; + NgArray lines; /// - Array freezone, freezonelimit; + NgArray> freezone, freezonelimit; /// - Array*> freezone_i; + NgArray>> freezone_i; /// - Array transfreezone; + NgArray> transfreezone; /// - Array dellines; + NgArray dellines; /// - Array elements; + NgArray elements; /// - Array tolerances, linetolerances; + NgArray tolerances, linetolerances; /// - Array orientations; + NgArray orientations; /// DenseMatrix oldutonewu, oldutofreearea, oldutofreearealimit; /// - Array oldutofreearea_i; + NgArray oldutofreearea_i; /// MatrixFixWidth<3> freesetinequ; /// - Array linevecs; + NgArray> linevecs; /// int noldp, noldl; @@ -57,7 +57,7 @@ private: float fzminx, fzmaxx, fzminy, fzmaxy; /// topological distance of line to base element - Array lnearness; + NgArray lnearness; public: @@ -86,7 +86,7 @@ public: int GetLNearness (int li) const { return lnearness.Get(li); } /// - const Point2d & GetPoint (int i) const { return points.Get(i); } + const Point<2>& GetPoint (int i) const { return points.Get(i); } /// const INDEX_2 & GetLine (int i) const { return lines.Get(i); } /// @@ -96,55 +96,55 @@ public: /// int GetDelLine (int i) const { return dellines.Get(i); } /// - const Array & GetDelLines() const { return dellines; } + const NgArray & GetDelLines() const { return dellines; } /// - void GetFreeZone (Array & afreearea); + void GetFreeZone (NgArray> & afreearea); /// - double CalcPointDist (int pi, const Point2d & p) const + double CalcPointDist (int pi, const Point<2> & p) const { - double dx = p.X() - points.Get(pi).X(); - double dy = p.Y() - points.Get(pi).Y(); + double dx = p[0] - points.Get(pi)[0]; + double dy = p[1] - points.Get(pi)[1]; const threefloat * tfp = &tolerances.Get(pi); return tfp->f1 * dx * dx + tfp->f2 * dx * dy + tfp->f3 * dy * dy; } /// - float CalcLineError (int li, const Vec2d & v) const; + float CalcLineError (int li, const Vec<2>& v) const; /// void SetFreeZoneTransformation (const Vector & u, int tolclass); /// - bool IsInFreeZone (const Point2d & p) const + bool IsInFreeZone (const Point<2> & p) const { - if (p.X() < fzminx || p.X() > fzmaxx || - p.Y() < fzminy || p.Y() > fzmaxy) return 0; + if (p[0] < fzminx || p[0] > fzmaxx || + p[1] < fzminy || p[1] > fzmaxy) return 0; for (int i = 0; i < transfreezone.Size(); i++) { - if (freesetinequ(i, 0) * p.X() + - freesetinequ(i, 1) * p.Y() + + if (freesetinequ(i, 0) * p[0] + + freesetinequ(i, 1) * p[1] + freesetinequ(i, 2) > 0) return 0; } return 1; } /// - int IsLineInFreeZone (const Point2d & p1, const Point2d & p2) const + int IsLineInFreeZone (const Point<2> & p1, const Point<2> & p2) const { - if ( (p1.X() > fzmaxx && p2.X() > fzmaxx) || - (p1.X() < fzminx && p2.X() < fzminx) || - (p1.Y() > fzmaxy && p2.Y() > fzmaxy) || - (p1.Y() < fzminy && p2.Y() < fzminy) ) return 0; + if ( (p1[0] > fzmaxx && p2[0] > fzmaxx) || + (p1[0] < fzminx && p2[0] < fzminx) || + (p1[1] > fzmaxy && p2[1] > fzmaxy) || + (p1[1] < fzminy && p2[1] < fzminy) ) return 0; return IsLineInFreeZone2 (p1, p2); } /// - int IsLineInFreeZone2 (const Point2d & p1, const Point2d & p2) const; + int IsLineInFreeZone2 (const Point<2> & p1, const Point<2> & p2) const; /// int ConvexFreeZone () const; /// - const Array & GetTransFreeZone () { return transfreezone; } + const NgArray> & GetTransFreeZone () { return transfreezone; } /// int GetPointNr (int ln, int endp) const { return lines.Get(ln).I(endp); } @@ -154,7 +154,7 @@ public: /// const DenseMatrix & GetOldUToFreeArea () const { return oldutofreearea; } /// - const char * Name () const { return name; } + const string & Name () const { return name; } /// void LoadRule (istream & ist); diff --git a/libsrc/meshing/ruler3.cpp b/libsrc/meshing/ruler3.cpp index 10fa5e69..dd70a8ab 100644 --- a/libsrc/meshing/ruler3.cpp +++ b/libsrc/meshing/ruler3.cpp @@ -8,7 +8,7 @@ extern double minother; extern double minwithoutother; - static double CalcElementBadness (const Array & points, + static double CalcElementBadness (const NgArray & points, const Element & elem) { double vol, l, l4, l5, l6; @@ -49,13 +49,13 @@ extern double minwithoutother; int Meshing3 :: ApplyRules ( - Array & lpoints, // in: local points, out: old+new local points - Array & allowpoint, // in: 2 .. it is allowed to use pointi, 1..will be allowed later, 0..no means - Array & lfaces, // in: local faces, out: old+new local faces + NgArray & lpoints, // in: local points, out: old+new local points + NgArray & allowpoint, // in: 2 .. it is allowed to use pointi, 1..will be allowed later, 0..no means + NgArray & lfaces, // in: local faces, out: old+new local faces INDEX lfacesplit, // for local faces in outer radius INDEX_2_HASHTABLE & connectedpairs, // connected pairs for prism-meshing - Array & elements, // out: new elements - Array & delfaces, // out: face indices of faces to delete + NgArray & elements, // out: new elements + NgArray & delfaces, // out: face indices of faces to delete int tolerance, // quality class: 1 best double sloppy, // quality strength int rotind1, // how to rotate base element @@ -63,11 +63,11 @@ int Meshing3 :: ApplyRules ) { - static Timer t("ruler3 - all"); RegionTimer reg(t); - static Timer tstart("ruler3 - rule start"); - static Timer tloop("ruler3 - rule loop"); + // static Timer t("ruler3 - all"); RegionTimer reg(t); + // static Timer tstart("ruler3 - rule start"); + // static Timer tloop("ruler3 - rule loop"); - tstart.Start(); + // tstart.Start(); float err, minerr, teterr, minteterr; char ok, found, hc; // vnetrule * rule; @@ -78,23 +78,23 @@ int Meshing3 :: ApplyRules int loktestmode; - Array pused; // point is already mapped, number of uses - ArrayMem fused; // face is already mapped - ArrayMem pmap; // map of reference point to local point - ArrayMem pfixed; // point mapped by face-map - ArrayMem fmapi; // face in reference is mapped to face nr ... - ArrayMem fmapr; // face in reference is rotated to map - ArrayMem transfreezone; // transformed free-zone + NgArray pused; // point is already mapped, number of uses + NgArrayMem fused; // face is already mapped + NgArrayMem pmap; // map of reference point to local point + NgArrayMem pfixed; // point mapped by face-map + NgArrayMem fmapi; // face in reference is mapped to face nr ... + NgArrayMem fmapr; // face in reference is rotated to map + NgArrayMem transfreezone; // transformed free-zone INDEX_2_CLOSED_HASHTABLE ledges(100); // edges in local environment - ArrayMem tempnewpoints; - Array tempnewfaces; - ArrayMem tempdelfaces; - Array tempelements; - ArrayMem triboxes; // bounding boxes of local faces + NgArrayMem tempnewpoints; + NgArray tempnewfaces; + NgArrayMem tempdelfaces; + NgArray tempelements; + NgArrayMem triboxes; // bounding boxes of local faces - Array pnearness; - Array fnearness; + NgArray pnearness; + NgArray fnearness; static int cnt = 0; cnt++; @@ -224,8 +224,8 @@ int Meshing3 :: ApplyRules // check each rule: - tstart.Stop(); - tloop.Start(); + // tstart.Stop(); + // tloop.Start(); for (int ri = 1; ri <= rules.Size(); ri++) { int base = (lfaces[0].GetNP() == 3) ? 100 : 200; @@ -650,7 +650,7 @@ int Meshing3 :: ApplyRules if (loktestmode) { - const Array & fz = rule->GetTransFreeZone(); + const NgArray & fz = rule->GetTransFreeZone(); (*testout) << "Freezone: " << endl; for (int i = 1; i <= fz.Size(); i++) (*testout) << fz.Get(i) << endl; @@ -685,7 +685,7 @@ int Meshing3 :: ApplyRules for (int i = 1; i <= lfaces.Size() && ok; i++) { - static Array lpi(4); + NgArrayMem lpi(4); if (!fused.Get(i)) { @@ -1115,7 +1115,7 @@ int Meshing3 :: ApplyRules if (loktestmode) (*testout) << "end rule" << endl; } - tloop.Stop(); + // tloop.Stop(); if (found) { diff --git a/libsrc/meshing/ruler3.hpp b/libsrc/meshing/ruler3.hpp index fcbf8f59..91ee4653 100644 --- a/libsrc/meshing/ruler3.hpp +++ b/libsrc/meshing/ruler3.hpp @@ -13,33 +13,33 @@ private: /// name of rule char * name; /// point coordinates in reference position - Array points; + NgArray points; /// old and new faces in reference numbering - Array faces; + NgArray faces; /// additional edges of rule - Array edges; + NgArray edges; /// points of freezone in reference coordinates - Array freezone; + NgArray freezone; /// points of freezone in reference coordinates if tolcalss to infty - Array freezonelimit; + NgArray freezonelimit; /// point index, if point equal to mappoint, otherwise 0 - Array freezonepi; + NgArray freezonepi; /// faces of each convex part of freezone - Array*> freefaces; + NgArray*> freefaces; /// set of points of each convex part of freezone - Array*> freesets; + NgArray*> freesets; /// points of transformed freezone - Array transfreezone; + NgArray transfreezone; /// edges of each convex part of freezone - Array*> freeedges; + NgArray*> freeedges; /// face numbers to be deleted - Array delfaces; + NgArray delfaces; /// elements to be generated - Array elements; + NgArray elements; /// tolerances for points and faces (used ??) - Array tolerances, linetolerances; + NgArray tolerances, linetolerances; /// transformation matrix DenseMatrix oldutonewu; /// transformation matrix: deviation old point to dev. freezone @@ -55,21 +55,21 @@ private: a point is outside of convex part of freezone, iff mat * (point, 1) >= 0 for each component (correct ?) */ - Array freefaceinequ; + NgArray freefaceinequ; /// - Array orientations; + NgArray orientations; /** flags specified in rule-description file: t .. test rule */ - Array flags; + NgArray flags; /** topological distance of face to base element non-connected: > 100 (??) */ - Array fnearness; - Array pnearness; + NgArray fnearness; + NgArray pnearness; int maxpnearness; /// number of old points in rule @@ -144,19 +144,19 @@ public: -1 maybe */ int IsTriangleInFreeZone (const Point3d & p1, const Point3d & p2, - const Point3d & p3, const Array & pi, int newone); + const Point3d & p3, const NgArray & pi, int newone); /// int IsQuadInFreeZone (const Point3d & p1, const Point3d & p2, const Point3d & p3, const Point3d & p4, - const Array & pi, int newone); + const NgArray & pi, int newone); /// int IsTriangleInFreeSet (const Point3d & p1, const Point3d & p2, - const Point3d & p3, int fs, const Array & pi, int newone); + const Point3d & p3, int fs, const NgArray & pi, int newone); /// int IsQuadInFreeSet (const Point3d & p1, const Point3d & p2, const Point3d & p3, const Point3d & p4, - int fs, const Array & pi, int newone); + int fs, const NgArray & pi, int newone); /// int ConvexFreeZone () const; @@ -194,7 +194,7 @@ public: void LoadRule (istream & ist); /// - const Array & GetTransFreeZone () { return transfreezone; } + const NgArray & GetTransFreeZone () { return transfreezone; } /// int TestOk () const; diff --git a/libsrc/meshing/secondorder.cpp b/libsrc/meshing/secondorder.cpp index 49f91662..b69b1474 100644 --- a/libsrc/meshing/secondorder.cpp +++ b/libsrc/meshing/secondorder.cpp @@ -23,6 +23,14 @@ namespace netgen INDEX_2_HASHTABLE between(mesh.GetNP() + 5); + for (SegmentIndex si = 0; si < mesh.GetNSeg(); si++) + { + auto & seg = mesh[si]; + if (seg.GetType() == SEGMENT3) + between.Set(INDEX_2::Sort(seg[0],seg[1]), seg[2]); + } + + for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) { const Element2d & el = mesh[sei]; @@ -92,25 +100,25 @@ namespace netgen { Point<3> pb; EdgePointGeomInfo ngi; - PointBetween (mesh.Point (el[0]), - mesh.Point (el[1]), 0.5, - el.surfnr1, el.surfnr2, - el.epgeominfo[0], el.epgeominfo[1], - pb, ngi); + geo.PointBetweenEdge(mesh.Point (el[0]), + mesh.Point (el[1]), 0.5, + el.surfnr1, el.surfnr2, + el.epgeominfo[0], el.epgeominfo[1], + pb, ngi); el[2] = mesh.AddPoint (pb, mesh.Point(el[0]).GetLayer(), EDGEPOINT); between.Set (i2, el[2]); } + el.SetCurved(true); } // refine surface elements for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) { - int j; - const Element2d & el = mesh.SurfaceElement(sei); + const Element2d & el = mesh[sei]; - int onp(0); + int onp = 0; Element2d newel(TRIG); newel.SetIndex (el.GetIndex()); @@ -160,11 +168,11 @@ namespace netgen PrintSysError ("Unhandled element in secondorder:", int(el.GetType())); } - for (j = 0; j < onp; j++) + for (int j = 0; j < onp; j++) newel[j] = el[j]; int nnp = newel.GetNP(); - for (j = 0; j < nnp-onp; j++) + for (int j = 0; j < nnp-onp; j++) { int pi1 = newel[betw[j][0]]; int pi2 = newel[betw[j][1]]; @@ -177,12 +185,12 @@ namespace netgen { Point<3> pb; PointGeomInfo newgi; - 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, newgi); + 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, newgi); newel[onp+j] = mesh.AddPoint (pb, mesh.Point(pi1).GetLayer(), SURFACEPOINT); @@ -190,7 +198,7 @@ namespace netgen } } - mesh.SurfaceElement(sei) = newel; + mesh[sei] = newel; } @@ -343,7 +351,7 @@ namespace netgen // update identification tables for (int i = 1; i <= mesh.GetIdentifications().GetMaxNr(); i++) { - Array identmap; + NgArray identmap; mesh.GetIdentifications().GetMap (i, identmap); for (INDEX_2_HASHTABLE::Iterator it = between.Begin(); @@ -417,7 +425,7 @@ namespace netgen int np = mesh.GetNP(); int ne = mesh.GetNE(); // int i, j; - Array parents(np); + NgArray parents(np); for (int i = 1; i <= np; i++) parents.Elem(i) = INDEX_2(0,0); @@ -451,7 +459,7 @@ namespace netgen void Refinement :: ValidateRefinedMesh (Mesh & mesh, - Array & parents) + NgArray & parents) { // int i, j, k; @@ -465,15 +473,15 @@ namespace netgen if (mesh.VolumeElement(i).CalcJacobianBadness (mesh.Points()) > 1e10) { wrongels++; - mesh.VolumeElement(i).flags.badel = 1; + mesh.VolumeElement(i).Flags().badel = 1; } else - mesh.VolumeElement(i).flags.badel = 0; + mesh.VolumeElement(i).Flags().badel = 0; double facok = 0; double factry; - BitArray illegalels(ne); + NgBitArray illegalels(ne); illegalels.Clear(); @@ -482,8 +490,8 @@ namespace netgen cout << "WARNING: " << wrongels << " illegal element(s) found" << endl; int np = mesh.GetNP(); - Array > should(np); - Array > can(np); + NgArray > should(np); + NgArray > can(np); for (int i = 1; i <= np; i++) { @@ -497,7 +505,7 @@ namespace netgen can.Elem(parents.Get(i).I2())); } - BitArray boundp(np); + NgBitArray boundp(np); boundp.Clear(); for (int i = 1; i <= mesh.GetNSE(); i++) { @@ -551,7 +559,7 @@ namespace netgen { wrongels++; Element & el = mesh.VolumeElement(i); - el.flags.badel = 1; + el.Flags().badel = 1; if (lam < 1e-4) @@ -566,7 +574,7 @@ namespace netgen */ } else - mesh.VolumeElement(i).flags.badel = 0; + mesh.VolumeElement(i).Flags().badel = 0; } cout << "wrongels = " << wrongels << endl; } @@ -589,10 +597,10 @@ namespace netgen if (illegalels.Test(i)) { cout << "illegal element: " << i << endl; - mesh.VolumeElement(i).flags.badel = 1; + mesh.VolumeElement(i).Flags().badel = 1; } else - mesh.VolumeElement(i).flags.badel = 0; + mesh.VolumeElement(i).Flags().badel = 0; } /* diff --git a/libsrc/meshing/smoothing2.5.cpp b/libsrc/meshing/smoothing2.5.cpp index b09b4e30..587c8d47 100644 --- a/libsrc/meshing/smoothing2.5.cpp +++ b/libsrc/meshing/smoothing2.5.cpp @@ -7,22 +7,22 @@ namespace netgen { - void MeshOptimize2d :: ProjectBoundaryPoints(Array & surfaceindex, - const Array* > & from, Array* > & dest) + void MeshOptimize2d :: ProjectBoundaryPoints(NgArray & surfaceindex, + const NgArray* > & from, NgArray* > & dest) { for(int i=0; i= 0) { *dest[i] = *from[i]; - ProjectPoint(surfaceindex[i],*dest[i]); + geo.ProjectPoint(surfaceindex[i],*dest[i]); } } } - void MeshOptimize2d :: ImproveVolumeMesh (Mesh & mesh) + void MeshOptimize2d :: ImproveVolumeMesh () { if (!faceindex) @@ -31,7 +31,7 @@ namespace netgen for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) { - ImproveVolumeMesh (mesh); + ImproveVolumeMesh (); if (multithread.terminate) throw NgException ("Meshing stopped"); } @@ -73,9 +73,9 @@ namespace netgen Vector x(3); - Array savepoints(mesh.GetNP()); + NgArray savepoints(mesh.GetNP()); - Array nelementsonpoint(mesh.GetNP()); + NgArray nelementsonpoint(mesh.GetNP()); nelementsonpoint = 0; for (i = 0; i < seia.Size(); i++) @@ -110,7 +110,7 @@ namespace netgen int np = mesh.GetNP(); int ne = mesh.GetNE(); - BitArray badnodes(np); + NgBitArray badnodes(np); badnodes.Clear(); for (i = 1; i <= ne; i++) @@ -139,10 +139,11 @@ namespace netgen int cnt = 0; - Array locelements(0); - Array locrots(0); + NgArray locelements(0); + NgArray locrots(0); - for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End(); pi++) + // for (PointIndex pi = mesh.Points().Begin(); pi < mesh.Points().End(); pi++) + for (PointIndex pi : mesh.Points().Range()) { if (mesh[pi].Type() != SURFACEPOINT) continue; @@ -228,7 +229,7 @@ namespace netgen //cout << "origp " << origp << " newp " << mesh[pi]; ngi = gi1; - moveisok = (ProjectPointGI (surfi, mesh[pi], ngi) != 0); + moveisok = (geo.ProjectPointGI(surfi, mesh[pi], ngi) != 0); //cout << " projected " << mesh[pi] << endl; diff --git a/libsrc/meshing/smoothing2.cpp b/libsrc/meshing/smoothing2.cpp index 537ed391..0e6db78a 100644 --- a/libsrc/meshing/smoothing2.cpp +++ b/libsrc/meshing/smoothing2.cpp @@ -186,10 +186,10 @@ namespace netgen MeshPoint sp1; PointGeomInfo gi1; Vec<3> normal, t1, t2; - Array locelements; - Array locrots; - Array lochs; - Array > loc_pnts2, loc_pnts3; + NgArray locelements; + NgArray locrots; + NgArray lochs; + NgArray > loc_pnts2, loc_pnts3; // static int locerr2; double locmetricweight; double loch; @@ -205,22 +205,20 @@ namespace netgen class Opti2SurfaceMinFunction : public MinFunction { - const Mesh & mesh; Opti2dLocalData & ld; + const NetgenGeometry& geo; public: Opti2SurfaceMinFunction (const Mesh & amesh, Opti2dLocalData & ald) - : mesh(amesh), ld(ald) + : ld(ald), geo(*amesh.GetGeometry()) { } ; virtual double Func (const Vector & x) const { - Vec<3> n; - double badness = 0; - ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n); + auto n = geo.GetNormal(ld.surfi, ld.sp1, &ld.gi1); Point<3> pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2; for (int j = 0; j < ld.locelements.Size(); j++) @@ -355,13 +353,13 @@ namespace netgen // static int timer = NgProfiler::CreateTimer ("opti2surface - funcgrad"); // NgProfiler::RegionTimer reg (timer); - Vec<3> n, vgrad; + Vec<3> vgrad; Point<3> pp1; vgrad = 0; double badness = 0; - ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n); + auto n = geo.GetNormal(ld.surfi, ld.sp1, &ld.gi1); pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2; // meshthis -> ProjectPoint (surfi, pp1); @@ -410,13 +408,13 @@ namespace netgen // static int timer = NgProfiler::CreateTimer ("opti2surface - funcderiv"); // NgProfiler::RegionTimer reg (timer); - Vec<3> n, vgrad; + Vec<3> vgrad; Point<3> pp1; vgrad = 0; double badness = 0; - ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n); + auto n = geo.GetNormal(ld.surfi, ld.sp1, &ld.gi1); pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2; for (int j = 0; j < ld.locelements.Size(); j++) @@ -474,11 +472,12 @@ namespace netgen { const Mesh & mesh; Opti2dLocalData & ld; + const NetgenGeometry& geo; public: Opti2EdgeMinFunction (const Mesh & amesh, Opti2dLocalData & ald) - : mesh(amesh), ld(ald) { } ; + : mesh(amesh), ld(ald), geo(*amesh.GetGeometry()) { } ; virtual double FuncGrad (const Vector & x, Vector & g) const; virtual double Func (const Vector & x) const; @@ -493,7 +492,7 @@ namespace netgen double Opti2EdgeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const { int j, rot; - Vec<3> n1, n2, v1, v2, e1, e2, vgrad; + Vec<3> v1, v2, e1, e2, vgrad; Point<3> pp1; Vec<2> g1; double badness, hbadness; @@ -502,7 +501,7 @@ namespace netgen badness = 0; pp1 = ld.sp1 + x(0) * ld.t1; - ld.meshthis -> ProjectPoint2 (ld.surfi, ld.surfi2, pp1); + geo.ProjectPointEdge(ld.surfi, ld.surfi2, pp1); for (j = 0; j < ld.locelements.Size(); j++) { @@ -526,8 +525,8 @@ namespace netgen vgrad += g1(0) * e1 + g1(1) * e2; } - ld.meshthis -> GetNormalVector (ld.surfi, pp1, n1); - ld.meshthis -> GetNormalVector (ld.surfi2, pp1, n2); + auto n1 = geo.GetNormal(ld.surfi, pp1); + auto n2 = geo.GetNormal(ld.surfi2, pp1); v1 = Cross (n1, n2); v1.Normalize(); @@ -544,11 +543,12 @@ namespace netgen { const Mesh & mesh; Opti2dLocalData & ld; + const NetgenGeometry& geo; public: Opti2SurfaceMinFunctionJacobian (const Mesh & amesh, Opti2dLocalData & ald) - : mesh(amesh), ld(ald) + : mesh(amesh), ld(ald), geo(*amesh.GetGeometry()) { } ; virtual double FuncGrad (const Vector & x, Vector & g) const; virtual double FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const; @@ -569,22 +569,22 @@ namespace netgen // from 2d: int lpi, gpi; - Vec<3> n, vgrad; + Vec<3> vgrad; Point<3> pp1; - Vec2d g1, vdir; + Vec<2> g1, vdir; double badness, hbad, hderiv; vgrad = 0; badness = 0; - ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n); + // auto n = geo.GetNormal(ld.surfi, ld.sp1, &ld.gi1); pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2; // meshthis -> ProjectPoint (surfi, pp1); // meshthis -> GetNormalVector (surfi, pp1, n); - static Array pts2d; + static NgArray> pts2d; pts2d.SetSize(mesh.GetNP()); grad = 0; @@ -603,15 +603,15 @@ namespace netgen pts2d.Elem(pi) = Point2d (ld.t1 * (mesh.Point(pi) - ld.sp1), ld.t2 * (mesh.Point(pi) - ld.sp1)); } - pts2d.Elem(gpi) = Point2d (x(0), x(1)); + pts2d.Elem(gpi) = { x(0), x(1) }; for (int k = 1; k <= 2; k++) { if (k == 1) - vdir = Vec2d (1, 0); + vdir = {1., 0.}; else - vdir = Vec2d (0, 1); + vdir = {0., 1.}; hbad = bel. CalcJacobianBadnessDirDeriv (pts2d, lpi, vdir, hderiv); @@ -641,21 +641,19 @@ namespace netgen // from 2d: int j, k, lpi, gpi; - Vec<3> n, vgrad; + Vec<3> vgrad; Point<3> pp1; - Vec2d g1, vdir; + Vec<2> g1, vdir; double badness, hbad, hderiv; vgrad = 0; badness = 0; - ld.meshthis -> GetNormalVector (ld.surfi, ld.sp1, ld.gi1, n); - // pp1 = sp1; // pp1.Add2 (x.Get(1), t1, x.Get(2), t2); pp1 = ld.sp1 + x(0) * ld.t1 + x(1) * ld.t2; - static Array pts2d; + static NgArray> pts2d; pts2d.SetSize(mesh.GetNP()); deriv = 0; @@ -677,7 +675,7 @@ namespace netgen pts2d.Elem(gpi) = Point2d (x(0), x(1)); - vdir = Vec2d (dir(0), dir(1)); + vdir = { dir(0), dir(1) }; hbad = bel. CalcJacobianBadnessDirDeriv (pts2d, lpi, vdir, hderiv); @@ -690,144 +688,104 @@ namespace netgen return badness; } - - - - - - - MeshOptimize2d dummy; - - MeshOptimize2d :: MeshOptimize2d () + void MeshOptimize2d :: ImproveMesh (const MeshingParameters & mp) { - SetFaceIndex (0); - SetImproveEdges (0); - SetMetricWeight (0); - SetWriteStatus (1); - } + static Timer timer("MeshSmoothing 2D"); RegionTimer reg (timer); - - void MeshOptimize2d :: SelectSurfaceOfPoint (const Point<3> & p, - const PointGeomInfo & gi) - { - ; - } - - void MeshOptimize2d :: ImproveMesh (Mesh & mesh, const MeshingParameters & mp) - { - if (!faceindex) - { - PrintMessage (3, "Smoothing"); - - for (faceindex = 1; faceindex <= mesh.GetNFD(); faceindex++) - { - ImproveMesh (mesh, mp); - if (multithread.terminate) - throw NgException ("Meshing stopped"); - } - faceindex = 0; - return; - } - - static int timer = NgProfiler::CreateTimer ("MeshSmoothing 2D"); - static int timer1 = NgProfiler::CreateTimer ("MeshSmoothing 2D start"); - static int timer2 = NgProfiler::CreateTimer ("MeshSmoothing 2D - BFGS"); - - NgProfiler::RegionTimer reg (timer); - NgProfiler::StartTimer (timer1); + PrintMessage (3, "Smoothing"); CheckMeshApproximation (mesh); - Opti2dLocalData ld; + int ncolors; + Array colors; + bool mixed = false; + auto elementsonpoint = mesh.CreatePoint2SurfaceElementTable( faceindex ); + NgArray savepoints(mesh.GetNP()); - - Array seia; - mesh.GetSurfaceElementsOfFace (faceindex, seia); - bool mixed = 0; - for (int i = 0; i < seia.Size(); i++) - if (mesh[seia[i]].GetNP() != 3) - { - mixed = 1; - break; - } - - Vector x(2); - - Array savepoints(mesh.GetNP()); - - ld.uselocalh = mp.uselocalh; - - Array compress(mesh.GetNP()); - Array icompress; - for (int i = 0; i < seia.Size(); i++) + Table color_table; + if(faceindex) { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - compress[el[j]] = -1; - } - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - if (compress[el[j]] == -1) - { - compress[el[j]] = icompress.Size(); - icompress.Append(el[j]); - } - } - Array cnta(icompress.Size()); - cnta = 0; - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - cnta[compress[el[j]]]++; - } - TABLE elementsonpoint(cnta); - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - elementsonpoint.Add (compress[el[j]], seia[i]); - } + Array seia; + mesh.GetSurfaceElementsOfFace (faceindex, seia); + for (auto sei : seia) + if (mesh[sei].GetNP() != 3) + { + mixed = true; + break; + } + Array compress(mesh.GetNP()); + NgArray icompress; + for (int i = 0; i < seia.Size(); i++) + { + const Element2d & el = mesh[seia[i]]; + for (int j = 0; j < el.GetNP(); j++) + compress[el[j]] = -1; + } + for (int i = 0; i < seia.Size(); i++) + { + const Element2d & el = mesh[seia[i]]; + for (int j = 0; j < el.GetNP(); j++) + if (compress[el[j]] == -1) + { + compress[el[j]] = icompress.Size(); + icompress.Append(el[j]); + } + } + + const auto & getDofs = [&] (int i) + { + return elementsonpoint[icompress[i]]; + }; + + colors.SetSize(icompress.Size()); + + ncolors = ngcore::ComputeColoring( colors, mesh.GetNSE(), getDofs ); + + TableCreator creator(ncolors); + for ( ; !creator.Done(); creator++) + ParallelForRange( Range(colors), [&](auto myrange) + { + for(auto i : myrange) + creator.Add(colors[i], icompress[i]); + }); + + color_table = creator.MoveTable(); + } + else + { + for (auto & se : mesh.SurfaceElements()) + if (se.GetNP() != 3) + { + for(auto pi : se.PNums()) + if(mesh[pi].Type() == SURFACEPOINT) + { + mixed = true; + break; + } + if(mixed) + break; + } + + const auto & getDofs = [&] (int i) + { + return elementsonpoint[i+PointIndex::BASE]; + }; + + colors.SetSize(mesh.GetNP()); + ncolors = ngcore::ComputeColoring( colors, mesh.GetNSE(), getDofs ); + + TableCreator creator(ncolors); + for ( ; !creator.Done(); creator++) + ParallelForRange( Range(colors), [&](auto myrange) + { + for(auto i : myrange) + creator.Add(colors[i], PointIndex(i+PointIndex::BASE)); + }); + + color_table = creator.MoveTable(); + } - /* - Array nelementsonpoint(mesh.GetNP()); - nelementsonpoint = 0; - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - nelementsonpoint[el[j]]++; - } - - TABLE elementsonpoint(nelementsonpoint); - - for (int i = 0; i < seia.Size(); i++) - { - const Element2d & el = mesh[seia[i]]; - for (int j = 0; j < el.GetNP(); j++) - elementsonpoint.Add (el[j], seia[i]); - } - */ - - - - ld.loch = mp.maxh; - ld.locmetricweight = metricweight; - ld.meshthis = this; - - - - Opti2SurfaceMinFunction surfminf(mesh, ld); - Opti2EdgeMinFunction edgeminf(mesh, ld); - Opti2SurfaceMinFunctionJacobian surfminfj(mesh, ld); - - OptiParameters par; - par.maxit_linsearch = 8; - par.maxit_bfgs = 5; - /* int i, j, k; Vector xedge(1); @@ -897,55 +855,48 @@ namespace netgen */ - bool printeddot = 0; - char plotchar = '.'; - int modplot = 1; - if (mesh.GetNP() > 1000) - { - plotchar = '+'; - modplot = 100; - } - if (mesh.GetNP() > 10000) - { - plotchar = 'o'; - modplot = 1000; - } - if (mesh.GetNP() > 100000) - { - plotchar = 'O'; - modplot = 10000; - } - int cnt = 0; - - - NgProfiler::StopTimer (timer1); + // NgProfiler::StopTimer (timer1); /* for (PointIndex pi = PointIndex::BASE; pi < mesh.GetNP()+PointIndex::BASE; pi++) if (mesh[pi].Type() == SURFACEPOINT) */ - for (int hi = 0; hi < icompress.Size(); hi++) - { - PointIndex pi = icompress[hi]; + + static Timer tloop("MeshSmooting 2D - loop"); + tloop.Start(); + for (auto icolor : Range(color_table)) + { + if (multithread.terminate) + break; + ParallelForRange( Range(color_table[icolor].Size()), [&](auto myrange) + { + Opti2dLocalData ld; + ld.uselocalh = mp.uselocalh; + ld.loch = mp.maxh; + ld.locmetricweight = metricweight; + ld.meshthis = this; + + Opti2SurfaceMinFunction surfminf(mesh, ld); + Opti2SurfaceMinFunctionJacobian surfminfj(mesh, ld); + + MinFunction & minfunc = mixed ? static_cast(surfminfj) : surfminf; + + OptiParameters par; + par.maxit_linsearch = 8; + par.maxit_bfgs = 5; + for (auto i : myrange) + { + PointIndex pi = color_table[icolor][i]; if (mesh[pi].Type() == SURFACEPOINT) { if (multithread.terminate) - throw NgException ("Meshing stopped"); + return; - cnt++; - if (cnt % modplot == 0 && writestatus) - { - printeddot = 1; - PrintDot (plotchar); - } - - // if (elementsonpoint[pi].Size() == 0) continue; - if (elementsonpoint[hi].Size() == 0) continue; - + if (elementsonpoint[pi].Size() == 0) continue; + ld.sp1 = mesh[pi]; - // Element2d & hel = mesh[elementsonpoint[pi][0]]; - Element2d & hel = mesh[elementsonpoint[hi][0]]; + Element2d & hel = mesh[elementsonpoint[pi][0]]; int hpi = 0; for (int j = 1; j <= hel.GetNP(); j++) @@ -956,17 +907,17 @@ namespace netgen } ld.gi1 = hel.GeomInfoPi(hpi); - SelectSurfaceOfPoint (ld.sp1, ld.gi1); + // SelectSurfaceOfPoint (ld.sp1, ld.gi1); ld.locelements.SetSize(0); ld.locrots.SetSize (0); ld.lochs.SetSize (0); ld.loc_pnts2.SetSize (0); ld.loc_pnts3.SetSize (0); - - for (int j = 0; j < elementsonpoint[hi].Size(); j++) + + for (int j = 0; j < elementsonpoint[pi].Size(); j++) { - SurfaceElementIndex sei = elementsonpoint[hi][j]; + SurfaceElementIndex sei = elementsonpoint[pi][j]; const Element2d & bel = mesh[sei]; ld.surfi = mesh.GetFaceDescriptor(bel.GetIndex()).SurfNr(); @@ -987,61 +938,62 @@ namespace netgen ld.lochs.Append (mesh.GetH(pmid)); } } - - GetNormalVector (ld.surfi, ld.sp1, ld.gi1, ld.normal); + + + ld.normal = geo.GetNormal(ld.surfi, ld.sp1, &ld.gi1); ld.t1 = ld.normal.GetNormal (); ld.t2 = Cross (ld.normal, ld.t1); - // save points, and project to tangential plane - for (int j = 0; j < ld.locelements.Size(); j++) - { - const Element2d & el = mesh[ld.locelements[j]]; - for (int k = 0; k < el.GetNP(); k++) - savepoints[el[k]] = mesh[el[k]]; - } + if(mixed) + { + // save points, and project to tangential plane (only for optimization with Opti2SurfaceMinFunctionJacobian in mixed element meshes) + for (int j = 0; j < ld.locelements.Size(); j++) + { + const Element2d & el = mesh[ld.locelements[j]]; + for (int k = 0; k < el.GetNP(); k++) + savepoints[el[k]] = mesh[el[k]]; + } - for (int j = 0; j < ld.locelements.Size(); j++) - { - const Element2d & el = mesh[ld.locelements[j]]; - for (int k = 0; k < el.GetNP(); k++) - { - PointIndex hhpi = el[k]; - double lam = ld.normal * (mesh[hhpi] - ld.sp1); - mesh[hhpi] -= lam * ld.normal; - } - } + for (int j = 0; j < ld.locelements.Size(); j++) + { + const Element2d & el = mesh[ld.locelements[j]]; + for (int k = 0; k < el.GetNP(); k++) + { + PointIndex hhpi = el[k]; + double lam = ld.normal * (mesh[hhpi] - ld.sp1); + mesh[hhpi] -= lam * ld.normal; + } + } + } + Vector x(2); x = 0; par.typx = 0.3*ld.lochs[0]; - NgProfiler::StartTimer (timer2); + // NgProfiler::StartTimer (timer2); - if (mixed) - { - BFGS (x, surfminfj, par, 1e-6); - } - else - { - BFGS (x, surfminf, par, 1e-6); - } + BFGS (x, minfunc, par, 1e-6); - NgProfiler::StopTimer (timer2); + // NgProfiler::StopTimer (timer2); - Point3d origp = mesh[pi]; + auto origp = mesh[pi]; int loci = 1; double fact = 1; int moveisok = 0; - // restore other points - for (int j = 0; j < ld.locelements.Size(); j++) - { - const Element2d & el = mesh[ld.locelements[j]]; - for (int k = 0; k < el.GetNP(); k++) - { - PointIndex hhpi = el[k]; - if (hhpi != pi) mesh[hhpi] = savepoints[hhpi]; - } - } + if(mixed) + { + // restore other points + for (int j = 0; j < ld.locelements.Size(); j++) + { + const Element2d & el = mesh[ld.locelements[j]]; + for (int k = 0; k < el.GetNP(); k++) + { + PointIndex hhpi = el[k]; + if (hhpi != pi) mesh[hhpi] = savepoints[hhpi]; + } + } + } //optimizer loop (if whole distance is not possible, move only a bit!!!!) @@ -1066,7 +1018,7 @@ namespace netgen PointGeomInfo ngi; ngi = ld.gi1; - moveisok = ProjectPointGI (ld.surfi, mesh[pi], ngi); + moveisok = geo.ProjectPointGI(ld.surfi, mesh[pi], ngi); // point lies on same chart in stlsurface if (moveisok) @@ -1076,26 +1028,17 @@ namespace netgen } else { - mesh[pi] = Point<3> (origp); + mesh[pi] = origp; } } - } - } - if (printeddot) - PrintDot ('\n'); + } + } + }, mixed ? 1 : ngcore::TasksPerThread(4)); // mixed element smoothing not parallel yet + } + tloop.Stop(); CheckMeshApproximation (mesh); mesh.SetNextTimeStamp(); } - - void MeshOptimize2d :: GetNormalVector(INDEX /* surfind */, const Point<3> & p, Vec<3> & nv) const - { - nv = Vec<3> (0, 0, 1); - } - - void MeshOptimize2d :: GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & gi, Vec<3> & n) const - { - GetNormalVector (surfind, p, n); - } } diff --git a/libsrc/meshing/smoothing3.cpp b/libsrc/meshing/smoothing3.cpp index f85af08f..321c6704 100644 --- a/libsrc/meshing/smoothing3.cpp +++ b/libsrc/meshing/smoothing3.cpp @@ -5,10 +5,13 @@ #include #endif #include +#include +#include namespace netgen { + using namespace ngcore; double MinFunctionSum :: Func (const Vector & x) const @@ -88,7 +91,7 @@ namespace netgen } PointFunction1 :: PointFunction1 (Mesh::T_POINTS & apoints, - const Array & afaces, + const NgArray & afaces, const MeshingParameters & amp, double ah) : points(apoints), faces(afaces), mp(amp) @@ -176,12 +179,12 @@ namespace netgen class CheapPointFunction1 : public MinFunction { Mesh::T_POINTS & points; - const Array & faces; + const NgArray & faces; DenseMatrix m; double h; public: CheapPointFunction1 (Mesh::T_POINTS & apoints, - const Array & afaces, + const NgArray & afaces, double ah); virtual double Func (const Vector & x) const; @@ -189,7 +192,7 @@ namespace netgen }; CheapPointFunction1 :: CheapPointFunction1 (Mesh::T_POINTS & apoints, - const Array & afaces, + const NgArray & afaces, double ah) : points(apoints), faces(afaces) { @@ -300,20 +303,21 @@ namespace netgen { public: Mesh::T_POINTS & points; - const Mesh::T_VOLELEMENTS & elements; - TABLE elementsonpoint; + const Array & elements; + Table &elementsonpoint; + bool own_elementsonpoint; const MeshingParameters & mp; PointIndex actpind; double h; public: - PointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements, - const MeshingParameters & amp); - virtual ~PointFunction () { ; } + PointFunction (Mesh & mesh, const MeshingParameters & amp); + PointFunction (const PointFunction & pf); + virtual ~PointFunction () { if(own_elementsonpoint) delete &elementsonpoint; } virtual void SetPointIndex (PointIndex aactpind); void SetLocalH (double ah) { h = ah; } double GetLocalH () const { return h; } + const Table & GetPointToElementTable() { return elementsonpoint; }; virtual double PointFunctionValue (const Point<3> & pp) const; virtual double PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const; virtual double PointFunctionValueDeriv (const Point<3> & pp, const Vec<3> & dir, double & deriv) const; @@ -322,15 +326,26 @@ namespace netgen }; - PointFunction :: PointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements, - const MeshingParameters & amp) - : points(apoints), elements(aelements), elementsonpoint(apoints.Size()), mp(amp) + PointFunction :: PointFunction (const PointFunction & pf) + : points(pf.points), elements(pf.elements), elementsonpoint(pf.elementsonpoint), own_elementsonpoint(false), mp(pf.mp) + { } + + PointFunction :: PointFunction (Mesh & mesh, const MeshingParameters & amp) + : points(mesh.Points()), elements(mesh.VolumeElements()), elementsonpoint(* new Table()), own_elementsonpoint(true), mp(amp) { - for (int i = 0; i < elements.Size(); i++) - if (elements[i].NP() == 4) - for (int j = 0; j < elements[i].NP(); j++) - elementsonpoint.Add (elements[i][j], i); + static Timer tim("PointFunction - build elementsonpoint table"); RegionTimer reg(tim); + + elementsonpoint = ngcore::CreateSortedTable( elements.Range(), + [&](auto & table, ElementIndex ei) + { + const auto & el = elements[ei]; + + if(el.NP()!=4 || (mp.only3D_domain_nr && mp.only3D_domain_nr != el.GetIndex()) ) + return; + + for (PointIndex pi : el.PNums()) + table.Add (pi, ei); + }, points.Size()); } void PointFunction :: SetPointIndex (PointIndex aactpind) @@ -348,9 +363,9 @@ namespace netgen hp = points[actpind]; points[actpind] = Point<3> (pp); - for (int j = 0; j < elementsonpoint[actpind].Size(); j++) + for (auto ei : elementsonpoint[actpind]) { - const Element & el = elements[elementsonpoint[actpind][j]]; + const Element & el = elements[ei]; badness += CalcTetBadness (points[el[0]], points[el[1]], points[el[2]], points[el[3]], -1, mp); } @@ -368,9 +383,9 @@ namespace netgen Vec<3> vgradi, vgrad(0,0,0); points[actpind] = Point<3> (pp); - for (int j = 0; j < elementsonpoint[actpind].Size(); j++) + for (auto ei : elementsonpoint[actpind]) { - const Element & el = elements[elementsonpoint[actpind][j]]; + const Element & el = elements[ei]; for (int k = 0; k < 4; k++) if (el[k] == actpind) { @@ -398,9 +413,9 @@ namespace netgen points[actpind] = pp; double f = 0; - for (int j = 0; j < elementsonpoint[actpind].Size(); j++) + for (auto ei : elementsonpoint[actpind]) { - const Element & el = elements[elementsonpoint[actpind][j]]; + const Element & el = elements[ei]; for (int k = 1; k <= 4; k++) if (el.PNum(k) == actpind) @@ -422,12 +437,11 @@ namespace netgen int PointFunction :: MovePointToInner () { // try point movement - Array faces; + NgArray faces; - for (int j = 0; j < elementsonpoint[actpind].Size(); j++) + for (auto ei : elementsonpoint[actpind]) { - const Element & el = - elements[elementsonpoint[actpind][j]]; + const Element & el = elements[ei]; for (int k = 1; k <= 4; k++) if (el.PNum(k) == actpind) @@ -476,19 +490,15 @@ namespace netgen { DenseMatrix m; public: - CheapPointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements, - const MeshingParameters & amp); + CheapPointFunction (Mesh & mesh, const MeshingParameters & amp); virtual void SetPointIndex (PointIndex aactpind); virtual double PointFunctionValue (const Point<3> & pp) const; virtual double PointFunctionValueGrad (const Point<3> & pp, Vec<3> & grad) const; }; - CheapPointFunction :: CheapPointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements, - const MeshingParameters & amp) - : PointFunction (apoints, aelements, amp) + CheapPointFunction :: CheapPointFunction (Mesh & mesh, const MeshingParameters & amp) + : PointFunction (mesh, amp) { ; } @@ -919,35 +929,6 @@ double Opti3EdgeMinFunction :: FuncGrad (const Vector & x, Vector & grad) const -double CalcTotalBad (const Mesh::T_POINTS & points, - const Mesh::T_VOLELEMENTS & elements, - const MeshingParameters & mp) -{ - static Timer t("CalcTotalBad"); RegionTimer reg(t); - - double sum = 0; - double elbad; - - tets_in_qualclass.SetSize(20); - tets_in_qualclass = 0; - - double teterrpow = mp.opterrpow; - - for (int i = 1; i <= elements.Size(); i++) - { - elbad = pow (max2(CalcBad (points, elements.Get(i), 0, mp),1e-10), - 1/teterrpow); - - int qualclass = int (20 / elbad + 1); - if (qualclass < 1) qualclass = 1; - if (qualclass > 20) qualclass = 20; - tets_in_qualclass.Elem(qualclass)++; - - sum += elbad; - } - return sum; -} - int WrongOrientation (const Mesh::T_POINTS & points, const Element & el) { const Point3d & p1 = points[el.PNum(1)]; @@ -985,13 +966,13 @@ int WrongOrientation (const Mesh::T_POINTS & points, const Element & el) // { // public: // Mesh::T_POINTS & points; -// const Mesh::T_VOLELEMENTS & elements; +// const NgArray & elements; // TABLE elementsonpoint; // PointIndex actpind; // public: // JacobianPointFunction (Mesh::T_POINTS & apoints, -// const Mesh::T_VOLELEMENTS & aelements); +// const NgArray & aelements); // virtual void SetPointIndex (PointIndex aactpind); // virtual double Func (const Vector & x) const; @@ -1002,17 +983,12 @@ int WrongOrientation (const Mesh::T_POINTS & points, const Element & el) JacobianPointFunction :: JacobianPointFunction (Mesh::T_POINTS & apoints, - const Mesh::T_VOLELEMENTS & aelements) + const Array & aelements) : points(apoints), elements(aelements), elementsonpoint(apoints.Size()) { - INDEX i; - int j; - - for (i = 1; i <= elements.Size(); i++) - { - for (j = 1; j <= elements.Get(i).NP(); j++) - elementsonpoint.Add1 (elements.Get(i).PNum(j), i); - } + for (int i = 0; i < elements.Size(); i++) + for (int j = 1; j <= elements[i].NP(); j++) + elementsonpoint.Add1 (elements[i].PNum(j), i+1); onplane = false; } @@ -1028,21 +1004,18 @@ double JacobianPointFunction :: Func (const Vector & v) const int j; double badness = 0; - Point<3> hp = points.Elem(actpind); + Point<3> hp = points[actpind]; - points.Elem(actpind) = hp + Vec<3> (v(0), v(1), v(2)); + points[actpind] = hp + Vec<3> (v(0), v(1), v(2)); if(onplane) - points.Elem(actpind) -= (v(0)*nv(0)+v(1)*nv(1)+v(2)*nv(2)) * nv; + points[actpind] -= (v(0)*nv(0)+v(1)*nv(1)+v(2)*nv(2)) * nv; - for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++) - { - int eli = elementsonpoint.Get(actpind, j); - badness += elements.Get(eli).CalcJacobianBadness (points); - } + for (auto eli : elementsonpoint[actpind]) + badness += elements[eli].CalcJacobianBadness (points); - points.Elem(actpind) = hp; + points[actpind] = hp; return badness; } @@ -1058,21 +1031,20 @@ FuncGrad (const Vector & x, Vector & g) const int lpi; double badness = 0;//, hbad; - Point<3> hp = points.Elem(actpind); - points.Elem(actpind) = hp + Vec<3> (x(0), x(1), x(2)); + Point<3> hp = points[actpind]; + points[actpind] = hp + Vec<3> (x(0), x(1), x(2)); if(onplane) - points.Elem(actpind) -= (x(0)*nv(0)+x(1)*nv(1)+x(2)*nv(2)) * nv; + points[actpind] -= (x(0)*nv(0)+x(1)*nv(1)+x(2)*nv(2)) * nv; Vec<3> hderiv; //Vec3d vdir; g.SetSize(3); g = 0; - for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++) + for (auto ei : elementsonpoint[actpind]) { - int eli = elementsonpoint.Get(actpind, j); - const Element & el = elements.Get(eli); + const Element & el = elements[ei]; lpi = 0; for (k = 1; k <= el.GetNP(); k++) @@ -1080,7 +1052,7 @@ FuncGrad (const Vector & x, Vector & g) const lpi = k; if (!lpi) cerr << "loc point not found" << endl; - badness += elements.Get(eli). + badness += elements[ei]. CalcJacobianBadnessGradient (points, lpi, hderiv); for(k=0; k<3; k++) @@ -1113,7 +1085,7 @@ FuncGrad (const Vector & x, Vector & g) const //(*testout) << "g = " << g << endl; - points.Elem(actpind) = hp; + points[actpind] = hp; return badness; } @@ -1126,11 +1098,11 @@ FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const int lpi; double badness = 0; - Point<3> hp = points.Elem(actpind); - points.Elem(actpind) = Point<3> (hp + Vec3d (x(0), x(1), x(2))); + Point<3> hp = points[actpind]; + points[actpind] = Point<3> (hp + Vec3d (x(0), x(1), x(2))); if(onplane) - points.Elem(actpind) -= (Vec3d (x(0), x(1), x(2))*nv) * nv; + points[actpind] -= (Vec3d (x(0), x(1), x(2))*nv) * nv; double hderiv; deriv = 0; @@ -1142,10 +1114,9 @@ FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const vdir -= scal*nv; } - for (j = 1; j <= elementsonpoint.EntrySize(actpind); j++) + for (auto ei : elementsonpoint[actpind]) { - int eli = elementsonpoint.Get(actpind, j); - const Element & el = elements.Get(eli); + const Element & el = elements[ei]; lpi = 0; for (k = 1; k <= el.GetNP(); k++) @@ -1153,12 +1124,12 @@ FuncDeriv (const Vector & x, const Vector & dir, double & deriv) const lpi = k; if (!lpi) cerr << "loc point not found" << endl; - badness += elements.Get(eli). + badness += elements[ei]. CalcJacobianBadnessDirDeriv (points, lpi, vdir, hderiv); deriv += hderiv; } - points.Elem(actpind) = hp; + points[actpind] = hp; return badness; @@ -1187,7 +1158,7 @@ void Mesh :: ImproveMesh (const CSG eometry & geometry, OPTIMIZEGOAL goal) } const char * savetask = multithread.task; - multithread.task = "Smooth Mesh"; + multithread.task = "Optimize Volume: Smooth Mesh"; TABLE surfelementsonpoint(points.Size()); @@ -1265,8 +1236,8 @@ void Mesh :: ImproveMesh (const CSG eometry & geometry, OPTIMIZEGOAL goal) // if (uselocalh) { - double lh = GetH (points.Get(i)); - pf->SetLocalH (GetH (points.Get(i))); + double lh = GetH(points.Get(i), points.Get(i).GetLayer()); + pf->SetLocalH (lh); par.typx = lh / 10; // (*testout) << "lh(" << points.Get(i) << ") = " << lh << "\n"; } @@ -1356,78 +1327,62 @@ void Mesh :: ImproveMesh (const CSG eometry & geometry, OPTIMIZEGOAL goal) void Mesh :: ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal) { static Timer t("Mesh::ImproveMesh"); RegionTimer reg(t); - - int typ = 1; - + static Timer tcoloring("coloring"); + static Timer tcalcbadmax("Calc badmax"); + static Timer topt("optimize"); + static Timer trange("range"); + static Timer tloch("loch"); + + BuildBoundaryEdges(false); + (*testout) << "Improve Mesh" << "\n"; PrintMessage (3, "ImproveMesh"); int np = GetNP(); int ne = GetNE(); + PointFunction pf_glob(*this, mp); - Array perrs(np); - perrs = 1.0; + auto & elementsonpoint = pf_glob.GetPointToElementTable(); - double bad1 = 0; - double badmax = 0; + const auto & getDofs = [&] (int i) + { + return elementsonpoint[i += PointIndex::BASE]; + }; + + Array colors(points.Size()); + + tcoloring.Start(); + int ncolors = ngcore::ComputeColoring( colors, ne, getDofs ); + auto color_table = CreateTable( points.Size(), + [&] ( auto & table, int i ) + { + PointIndex pi = i+static_cast(PointIndex::BASE); + table.Add(colors[i], pi); + }, ncolors); + + tcoloring.Stop(); if (goal == OPT_QUALITY) { - for (int i = 1; i <= ne; i++) - { - const Element & el = VolumeElement(i); - if (el.GetType() != TET) - continue; - - double hbad = CalcBad (points, el, 0, mp); - for (int j = 0; j < 4; j++) - perrs[el[j]] += hbad; - - bad1 += hbad; - } - - for (int i = perrs.Begin(); i < perrs.End(); i++) - if (perrs[i] > badmax) - badmax = perrs[i]; - badmax = 0; - } - - if (goal == OPT_QUALITY) - { - bad1 = CalcTotalBad (points, volelements, mp); + double bad1 = CalcTotalBad (mp); (*testout) << "Total badness = " << bad1 << endl; PrintMessage (5, "Total badness = ", bad1); } - - Vector x(3); - + + (*testout) << setprecision(8); - - //int uselocalh = mparam.uselocalh; + Array pointh (points.Size()); - PointFunction * pf; - - if (typ == 1) - pf = new PointFunction(points, volelements, mp); - else - pf = new CheapPointFunction(points, volelements, mp); - - // pf->SetLocalH (h); - - Opti3FreeMinFunction freeminf(*pf); - - OptiParameters par; - par.maxit_linsearch = 20; - par.maxit_bfgs = 20; - - Array pointh (points.Size()); - - if(lochfunc) + if(HasLocalHFunction()) { - for (PointIndex pi : points.Range()) - pointh[pi] = GetH(points[pi]); + RegionTimer rt(tloch); + ParallelForRange(points.Range(), [&] (auto myrange) + { + for(auto pi : myrange) + pointh[pi] = GetH(pi); + }); } else { @@ -1440,73 +1395,76 @@ void Mesh :: ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal) pointh[pi] = h; } } - - - int printmod = 1; - char printdot = '.'; - if (points.Size() > 1000) - { - printmod = 10; - printdot = '+'; - } - if (points.Size() > 10000) - { - printmod = 100; - printdot = '*'; - } - const char * savetask = multithread.task; - multithread.task = "Smooth Mesh"; + multithread.task = "Optimize Volume: Smooth Mesh"; - for (PointIndex pi : points.Range()) - if ( (*this)[pi].Type() == INNERPOINT && perrs[pi] > 0.01 * badmax) + topt.Start(); + int counter = 0; + for (auto icolor : Range(ncolors)) + { + if (multithread.terminate) + throw NgException ("Meshing stopped"); + + ParallelForRange( color_table[icolor].Range(), [&](auto myrange) { - if (multithread.terminate) - throw NgException ("Meshing stopped"); + RegionTracer reg(ngcore::TaskManager::GetThreadId(), trange, myrange.Size()); + Vector x(3); - multithread.percent = 100.0 * (pi+1-PointIndex::BASE) / points.Size(); + PointFunction pf{pf_glob}; - if ( (pi+1-PointIndex::BASE) % printmod == 0) PrintDot (printdot); + Opti3FreeMinFunction freeminf(pf); - double lh = pointh[pi]; - pf->SetLocalH (lh); - par.typx = lh; + OptiParameters par; + par.maxit_linsearch = 20; + par.maxit_bfgs = 20; - freeminf.SetPoint (points[pi]); - pf->SetPointIndex (pi); + for (auto i : myrange) + { + PointIndex pi = color_table[icolor][i]; + if ( (*this)[pi].Type() == INNERPOINT ) + { + counter++; - x = 0; - int pok; - pok = freeminf.Func (x) < 1e10; + double lh = pointh[pi]; + pf.SetLocalH (lh); + par.typx = lh; - if (!pok) - { - pok = pf->MovePointToInner (); + freeminf.SetPoint (points[pi]); + pf.SetPointIndex (pi); - freeminf.SetPoint (points[pi]); - pf->SetPointIndex (pi); - } + x = 0; + int pok; + pok = freeminf.Func (x) < 1e10; - if (pok) - { - //*testout << "start BFGS, pok" << endl; - BFGS (x, freeminf, par); - //*testout << "BFGS complete, pok" << endl; - points[pi](0) += x(0); - points[pi](1) += x(1); - points[pi](2) += x(2); - } - } - PrintDot ('\n'); - - delete pf; + if (!pok) + { + pok = pf.MovePointToInner (); + + freeminf.SetPoint (points[pi]); + pf.SetPointIndex (pi); + } + + if (pok) + { + //*testout << "start BFGS, pok" << endl; + BFGS (x, freeminf, par); + //*testout << "BFGS complete, pok" << endl; + points[pi](0) += x(0); + points[pi](1) += x(1); + points[pi](2) += x(2); + } + } + } + }, 4*ngcore::TaskManager::GetNumThreads()); + } + topt.Stop(); multithread.task = savetask; if (goal == OPT_QUALITY) { - bad1 = CalcTotalBad (points, volelements, mp); + double bad1 = CalcTotalBad (mp); (*testout) << "Total badness = " << bad1 << endl; PrintMessage (5, "Total badness = ", bad1); } @@ -1514,12 +1472,11 @@ void Mesh :: ImproveMesh (const MeshingParameters & mp, OPTIMIZEGOAL goal) - // Improve Condition number of Jacobian, any elements void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, - OPTIMIZEGOAL goal, const BitArray * usepoint) + OPTIMIZEGOAL goal, const NgBitArray * usepoint) { - int i, j; + // int i, j; (*testout) << "Improve Mesh Jacobian" << "\n"; PrintMessage (3, "ImproveMesh Jacobian"); @@ -1539,33 +1496,34 @@ void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, par.maxit_linsearch = 20; par.maxit_bfgs = 20; - BitArray badnodes(np); + NgBitArray badnodes(np); badnodes.Clear(); - for (i = 1; i <= ne; i++) + for (int i = 1; i <= ne; i++) { const Element & el = VolumeElement(i); double bad = el.CalcJacobianBadness (Points()); if (bad > 1) - for (j = 1; j <= el.GetNP(); j++) + for (int j = 1; j <= el.GetNP(); j++) badnodes.Set (el.PNum(j)); } - Array pointh (points.Size()); + NgArray pointh (points.Size()); - if(lochfunc) + if(HasLocalHFunction()) { - for(i = 1; i<=points.Size(); i++) - pointh[i] = GetH(points.Get(i)); + // for(i = 1; i<=points.Size(); i++) + for (PointIndex pi : points.Range()) + pointh[pi] = GetH(pi); } else { pointh = 0; - for(i=0; i pointh[el.PNum(j)]) pointh[el.PNum(j)] = h; } @@ -1574,19 +1532,20 @@ void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, const char * savetask = multithread.task; - multithread.task = "Smooth Mesh Jacobian"; + multithread.task = "Optimize Volume: Smooth Mesh Jacobian"; - for (PointIndex pi = points.Begin(); i < points.End(); pi++) + // for (PointIndex pi = points.Begin(); i < points.End(); pi++) + for (PointIndex pi : points.Range()) { if ((*this)[pi].Type() != INNERPOINT) continue; - if(usepoint && !usepoint->Test(i)) + if(usepoint && !usepoint->Test(pi)) continue; //(*testout) << "improvejac, p = " << i << endl; - if (goal == OPT_WORSTCASE && !badnodes.Test(i)) + if (goal == OPT_WORSTCASE && !badnodes.Test(pi)) continue; // (*testout) << "smooth p " << i << endl; @@ -1597,15 +1556,15 @@ void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, if (multithread.terminate) throw NgException ("Meshing stopped"); - multithread.percent = 100.0 * i / points.Size(); + multithread.percent = 100.0 * pi / points.Size(); if (points.Size() < 1000) PrintDot (); else - if (i % 10 == 0) + if (pi % 10 == 0) PrintDot ('+'); - double lh = pointh[i]; + double lh = pointh[pi]; par.typx = lh; pf.SetPointIndex (pi); @@ -1618,9 +1577,9 @@ void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, //*testout << "start BFGS, Jacobian" << endl; BFGS (x, pf, par); //*testout << "end BFGS, Jacobian" << endl; - points.Elem(i)(0) += x(0); - points.Elem(i)(1) += x(1); - points.Elem(i)(2) += x(2); + points[pi](0) += x(0); + points[pi](1) += x(1); + points[pi](2) += x(2); } else { @@ -1638,12 +1597,12 @@ void Mesh :: ImproveMeshJacobian (const MeshingParameters & mp, // Improve Condition number of Jacobian, any elements void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, - const BitArray & usepoint, - const Array< Vec<3>* > & nv, + const NgBitArray & usepoint, + const NgArray< Vec<3>* > & nv, OPTIMIZEGOAL goal, - const Array< Array* > * idmaps) + const NgArray< NgArray* > * idmaps) { - int i, j; + // int i, j; (*testout) << "Improve Mesh Jacobian" << "\n"; PrintMessage (3, "ImproveMesh Jacobian"); @@ -1658,8 +1617,8 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, JacobianPointFunction pf(points, volelements); - Array< Array* > locidmaps; - const Array< Array* > * used_idmaps; + NgArray< NgArray* > locidmaps; + const NgArray< NgArray* > * used_idmaps; if(idmaps) used_idmaps = idmaps; @@ -1667,11 +1626,11 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, { used_idmaps = &locidmaps; - for(i=1; i<=GetIdentifications().GetMaxNr(); i++) + for(int i=1; i<=GetIdentifications().GetMaxNr(); i++) { if(GetIdentifications().GetType(i) == Identifications::PERIODIC) { - locidmaps.Append(new Array); + locidmaps.Append(new NgArray); GetIdentifications().GetMap(i,*locidmaps.Last(),true); } } @@ -1694,33 +1653,34 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, par.maxit_linsearch = 20; par.maxit_bfgs = 20; - BitArray badnodes(np); + NgBitArray badnodes(np); badnodes.Clear(); - for (i = 1; i <= ne; i++) + for (int i = 1; i <= ne; i++) { const Element & el = VolumeElement(i); double bad = el.CalcJacobianBadness (Points()); if (bad > 1) - for (j = 1; j <= el.GetNP(); j++) + for (int j = 1; j <= el.GetNP(); j++) badnodes.Set (el.PNum(j)); } - Array pointh (points.Size()); + NgArray pointh (points.Size()); - if(lochfunc) + if(HasLocalHFunction()) { - for(i=1; i<=points.Size(); i++) - pointh[i] = GetH(points.Get(i)); + // for(i=1; i<=points.Size(); i++) + for (PointIndex pi : points.Range()) + pointh[pi] = GetH(pi); } else { pointh = 0; - for(i=0; i pointh[el.PNum(j)]) pointh[el.PNum(j)] = h; } @@ -1728,14 +1688,15 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, const char * savetask = multithread.task; - multithread.task = "Smooth Mesh Jacobian"; + multithread.task = "Optimize Volume: Smooth Mesh Jacobian"; - for (PointIndex pi = points.Begin(); pi <= points.End(); pi++) - if ( usepoint.Test(i) ) + // for (PointIndex pi = points.Begin(); pi <= points.End(); pi++) + for (PointIndex pi : points.Range()) + if ( usepoint.Test(pi) ) { //(*testout) << "improvejac, p = " << i << endl; - if (goal == OPT_WORSTCASE && !badnodes.Test(i)) + if (goal == OPT_WORSTCASE && !badnodes.Test(pi)) continue; // (*testout) << "smooth p " << i << endl; @@ -1746,15 +1707,15 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, if (multithread.terminate) throw NgException ("Meshing stopped"); - multithread.percent = 100.0 * i / points.Size(); + multithread.percent = 100.0 * pi / points.Size(); if (points.Size() < 1000) PrintDot (); else - if (i % 10 == 0) + if (pi % 10 == 0) PrintDot ('+'); - double lh = pointh[i];//GetH(points.Get(i)); + double lh = pointh[pi];//GetH(points.Get(i)); par.typx = lh; pf.SetPointIndex (pi); @@ -1762,29 +1723,29 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, PointIndex brother (-1); if(usesum) { - for(j=0; brother == -1 && jSize(); j++) + for(int j=0; brother == -1 && jSize(); j++) { - if(i < (*used_idmaps)[j]->Size() + PointIndex::BASE) + if(pi < (*used_idmaps)[j]->Size() + PointIndex::BASE) { - brother = (*(*used_idmaps)[j])[i]; - if(brother == i || brother == 0) + brother = (*(*used_idmaps)[j])[pi]; + if(brother == pi || brother == 0) brother = -1; } } - if(brother >= i) + if(brother >= pi) { pf2ptr->SetPointIndex(brother); pf2ptr->SetNV(*nv[brother-1]); } } - if(usesum && brother < i) + if(usesum && brother < pi) continue; //pf.UnSetNV(); x = 0; //(*testout) << "before " << pf.Func(x); - pf.SetNV(*nv[i-1]); + pf.SetNV(*nv[pi-1]); x = 0; int pok = (brother == -1) ? (pf.Func (x) < 1e10) : (pf_sum.Func (x) < 1e10); @@ -1798,12 +1759,12 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, BFGS (x, pf_sum, par); - for(j=0; j<3; j++) - points.Elem(i)(j) += x(j);// - scal*nv[i-1].X(j); + for(int j=0; j<3; j++) + points[pi](j) += x(j);// - scal*nv[i-1].X(j); if(brother != -1) - for(j=0; j<3; j++) - points.Elem(brother)(j) += x(j);// - scal*nv[brother-1].X(j); + for(int j=0; j<3; j++) + points[brother](j) += x(j);// - scal*nv[brother-1].X(j); } @@ -1821,7 +1782,7 @@ void Mesh :: ImproveMeshJacobianOnSurface (const MeshingParameters & mp, PrintDot ('\n'); delete pf2ptr; - for(i=0; i * 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/meshing/specials.cpp b/libsrc/meshing/specials.cpp index 3d0dbbdd..a391ee94 100644 --- a/libsrc/meshing/specials.cpp +++ b/libsrc/meshing/specials.cpp @@ -20,7 +20,7 @@ void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh) othermesh.GetNP(), " points, ", othermesh.GetNSE(), " surface elements."); - Array otherbounds(nse); + NgArray otherbounds(nse); Box3d otherbox; double maxh = 0; @@ -62,7 +62,7 @@ void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh) } cout << endl; - BitArray connected(mesh.GetNP()); + NgBitArray connected(mesh.GetNP()); connected.Clear(); for (i = 1; i <= mesh.GetNSE(); i++) { @@ -120,7 +120,7 @@ void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh) mesh.Compress(); mesh.FindOpenElements(); - BitArray locked(mesh.GetNP()); + NgBitArray locked(mesh.GetNP()); locked.Set(); for (i = 1; i <= mesh.GetNOpenElements(); i++) for (j = 1; j <= 3; j++) @@ -135,7 +135,7 @@ void CutOffAndCombine (Mesh & mesh, const Mesh & othermesh) - Array pmat(onp); + NgArray pmat(onp); for (i = 1; i <= onp; i++) pmat.Elem(i) = mesh.AddPoint (othermesh.Point(i)); diff --git a/libsrc/meshing/surfacegeom.cpp b/libsrc/meshing/surfacegeom.cpp new file mode 100644 index 00000000..d0f3ceb0 --- /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 :: GenerateStructuredMesh(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..6402f103 --- /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 GenerateStructuredMesh(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/tetrarls.cpp b/libsrc/meshing/tetrarls.cpp deleted file mode 100644 index cb28648b..00000000 --- a/libsrc/meshing/tetrarls.cpp +++ /dev/null @@ -1,1466 +0,0 @@ -namespace netgen -{ -const char * tetrules[] = { -"tolfak 0.5\n",\ -"\n",\ -"rule \"Free Tetrahedron\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0);\n",\ -"(0.5, 0.866, 0);\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.288, -0.816)\n",\ -" { 0.333 X1, 0.333 X2, 0.333 X3 }\n",\ -" { 0.333 Y1, 0.333 Y2, 0.333 Y3 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(4, 1, 2);\n",\ -"(4, 2, 3);\n",\ -"(4, 3, 1);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1.6 P4, -0.2 P1, -0.2 P2, -0.2 P3 };\n",\ -"{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 };\n",\ -"{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 };\n",\ -"{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 60\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"flags c;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 } ;\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0.5, 0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 4, 3);\n",\ -"(4, 2, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\ -"\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.65 P3, 0.35 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 60 with edge(1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"flags c;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.8 };\n",\ -"(0.5, 0.866, 0) { 0.8 };\n",\ -"(0.5, 0.288, -0.816) { 0.8 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"mapedges\n",\ -"(3, 4);\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 4, 3);\n",\ -"(4, 2, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.4 P1, 0.4 P4, 0.4 P3, -0.2 P2 };\n",\ -"{ 0.4 P2, 0.4 P4, 0.4 P3, -0.2 P1 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P4, 0.3334 P3 };\n",\ -"{ 0.3333 P2, 0.3333 P4, 0.3334 P3 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron Vis a Vis Point (1)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0.5, 0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 3, 1);\n",\ -"(4, 2, 3);\n",\ -"(4, 1, 2);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 };\n",\ -"{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 };\n",\ -"{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 };\n",\ -"{ 0.8 P1, -0.1 P2, -0.1 P3, 0.4 P4 };\n",\ -"{ -0.1 P1, 0.8 P2, -0.1 P3, 0.4 P4 };\n",\ -"{ -0.1 P1, -0.1 P2, 0.8 P3, 0.4 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\ -"{ 0.7 P1, 0.3 P4 };\n",\ -"{ 0.7 P2, 0.3 P4 };\n",\ -"{ 0.7 P3, 0.3 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron Vis a Vis Point with edge(1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0.5, 0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"mapedges\n",\ -"(1, 4);\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 3, 1);\n",\ -"(4, 2, 3);\n",\ -"(4, 1, 2);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\ -"{ -0.05 P1, 0.7 P2, -0.05 P3, 0.4 P4 };\n",\ -"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\ -"{ 0.65 P2, 0.35 P4 };\n",\ -"{ 0.65 P3, 0.35 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron Vis a Vis Point with 2 edges (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0.5, 0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"mapedges\n",\ -"(1, 4);\n",\ -"(2, 4);\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 3, 1);\n",\ -"(4, 2, 3);\n",\ -"(4, 1, 2);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\ -"{ -0.05 P1, -0.05 P2, 0.7 P3, 0.4 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\ -"{ 0.65 P3, 0.35 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron Vis a Vis Point with 3 edges (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0.5, 0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"mapedges\n",\ -"(1, 4);\n",\ -"(2, 4);\n",\ -"(3, 4);\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(4, 3, 1);\n",\ -"(4, 2, 3);\n",\ -"(4, 1, 2);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ -0.35 P1, 0.45 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, -0.35 P2, 0.45 P3, 0.45 P4 };\n",\ -"{ 0.45 P1, 0.45 P2, -0.35 P3, 0.45 P4 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P2, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P2, 0.3334 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron Vis a Vis Triangle (1)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 0.866, 0) { 0.5 };\n",\ -"(0, 0, -0.816) { 0.5 };\n",\ -"(1, 0, -0.816) { 0.5 };\n",\ -"(0.5, 0.866, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(4, 6, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 4);\n",\ -"(2, 5, 4);\n",\ -"(2, 3, 6);\n",\ -"(2, 6, 5);\n",\ -"(3, 1, 4);\n",\ -"(3, 4, 6);\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"(4, 2, 3, 6);\n",\ -"(4, 2, 6, 5);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ -0.2 P1, 0.35 P2, 0.35 P3, -0.2 P4, 0.35 P5, 0.35 P6 };\n",\ -"{ 0.35 P1, -0.2 P2, 0.35 P3, 0.35 P4, -0.2 P5, 0.35 P6 };\n",\ -"{ 0.35 P1, 0.35 P2, -0.2 P3, 0.35 P4, 0.35 P5, -0.2 P6 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Octaeder 1\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.95 };\n",\ -"(0.5, 0.866, 0) { 0.95 };\n",\ -"(0.5, -0.288, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"(0, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(2, 3, 5);\n",\ -"(3, 1, 6);\n",\ -"(3, 6, 5);\n",\ -"(2, 5, 4);\n",\ -"(1, 4, 6);\n",\ -"(4, 5, 6);\n",\ -"\n",\ -"elements\n",\ -"(3, 4, 1, 2);\n",\ -"(3, 4, 2, 5);\n",\ -"(3, 4, 5, 6);\n",\ -"(3, 4, 6, 1);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\ -"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 1 Z4 };\n",\ -"( 1.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 1 Z4 };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Octaeder 2\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.95 };\n",\ -"(0.5, 0.866, 0) { 0.95 };\n",\ -"(0.5, -0.288, -0.816) { 0.5 };\n",\ -"(1, 0.578, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(2, 3, 5);\n",\ -"(3, 1, 6);\n",\ -"(3, 6, 5);\n",\ -"(2, 5, 4);\n",\ -"(1, 4, 6);\n",\ -"(4, 5, 6);\n",\ -"\n",\ -"elements\n",\ -"(3, 4, 1, 2);\n",\ -"(3, 4, 2, 5);\n",\ -"(3, 4, 5, 6);\n",\ -"(3, 4, 6, 1);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\ -"(1, 0.578, -0.816) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\ -"\n",\ -"(0.9, 0.097, -0.544) { 0.333 X2, 0.333 X4, 0.333 X5 }\n",\ -" { 0.333 Y2, 0.333 Y4, 0.333 Y5 }\n",\ -" { 0.333 Z2, 0.333 Z4, 0.333 Z5 };\n",\ -"(0.9, 0.481, -0.272) { 0.333 X2, 0.333 X3, 0.333 X5 }\n",\ -" { 0.333 Y2, 0.333 Y3, 0.333 Y5 }\n",\ -" { 0.333 Z2, 0.333 Z3, 0.333 Z5 };\n",\ -"\n",\ -"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 0.5 Z4, 0.5 Z5 };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"Octaeder 2a\"\n",\ -"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.95 };\n",\ -"(0.5, 0.866, 0) { 0.95 };\n",\ -"(0.5, -0.288, -0.816) { 0.5 };\n",\ -"(1, 0.578, -0.816) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(3, 2, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0, 0.578, -0.816)\n",\ -" { -1 X2, 1 X3, 1 X4 }\n",\ -" { -1 Y2, 1 Y3, 1 Y4 }\n",\ -" { -1 Z2, 1 Z3, 1 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 2, 4);\n",\ -"(3, 1, 6);\n",\ -"(3, 6, 5);\n",\ -"(2, 5, 4);\n",\ -"(1, 4, 6);\n",\ -"(4, 5, 6);\n",\ -"\n",\ -"elements\n",\ -"(3, 4, 1, 2);\n",\ -"(3, 4, 2, 5);\n",\ -"(3, 4, 5, 6);\n",\ -"(3, 4, 6, 1);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\ -"(1, 0.578, -0.816) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\ -"\n",\ -"(0.9, 0.097, -0.544) { 0.333 X2, 0.333 X4, 0.333 X5 }\n",\ -" { 0.333 Y2, 0.333 Y4, 0.333 Y5 }\n",\ -" { 0.333 Z2, 0.333 Z4, 0.333 Z5 };\n",\ -"\n",\ -"(0.5, -0.097, -0.272) { 0.333 X2, 0.333 X4, 0.333 X1 }\n",\ -" { 0.333 Y2, 0.333 Y4, 0.333 Y1 }\n",\ -" { 0.333 Z2, 0.333 Z4, 0.333 Z1 };\n",\ -"\n",\ -"(-0.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 } { 0.5 Z4, 0.5 Z5 };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Pyramid 1\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 };\n",\ -"(0.5, 0.866, 0) { 1 };\n",\ -"(0.5, -0.288, -0.816) { 1 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(1, 0.578, -0.816) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 4, 3);\n",\ -"(2, 3, 5);\n",\ -"(2, 5, 4);\n",\ -"(4, 5, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"(4, 2, 3, 5);\n",\ -"\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, -0.288, -0.816) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\ -"(0, 1, -1) { 0.5 X3, 0.5 X4 } { 1 Y3 } { 1 Z4 };\n",\ -"(1.5, 1, -1.5) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 2 times 60\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.3 };\n",\ -"(0.5, 0.866, 0) { 0.3 };\n",\ -"(0.5, 0.288, -0.816) { 0.3 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(2, 4, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"(1, 4, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.4 P1, 0.4 P4, 0.4 P3, -0.2 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 0.3333 P1, 0.3333 P3, 0.3334 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Fill Tetrahedron (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.2 };\n",\ -"(0.5, 0.866, 0) { 0.2 };\n",\ -"(0.5, 0.288, -0.816) { 0.2 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(2, 4, 3) del;\n",\ -"(3, 4, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newfaces\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 120 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 };\n",\ -"(0.5, 0.866, 0) { 1 };\n",\ -"(0.5, -0.674, -0.544) { 1 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.288, -0.816)\n",\ -" { -0.5 X1, -0.5 X2, 1 X3, 1 X4 }\n",\ -" { -0.5 Y1, -0.5 Y2, 1 Y3, 1 Y4}\n",\ -" { -0.5 Z1, -0.5 Z2, 1 Z3, 1 Z4};\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 3);\n",\ -"(3, 5, 2);\n",\ -"(1, 4, 5);\n",\ -"(2, 5, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 4, 2, 5);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.3 P5, -0.3 P1 };\n",\ -"{ 1.3 P5, -0.3 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P5 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 2 times 120 (1)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 };\n",\ -"(0.5, 0.866, 0) { 1 };\n",\ -"(0.5, -0.674, -0.544) { 0.8 };\n",\ -"(1.334, 0.77, -0.544) { 0.8 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(3, 2, 5) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.288, -0.816) { 0.25 X1, -0.5 X2, 0.25 X3, 0.5 X4, 0.5 X5 }\n",\ -" { 0.25 Y1, -0.5 Y2, 0.25 Y3, 0.5 Y4, 0.5 Y5 }\n",\ -" { 0.25 Z1, -0.5 Z2, 0.25 Z3, 0.5 Z4, 0.5 Z5 };\n",\ -"\n",\ -"newfaces\n",\ -"(6, 3, 1);\n",\ -"(6, 1, 4);\n",\ -"(6, 4, 2);\n",\ -"(6, 2, 5);\n",\ -"(6, 5, 3);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 6);\n",\ -"(1, 4, 2, 6);\n",\ -"(2, 5, 3, 6);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1.4 P6, -0.4 P2 };\n",\ -"{ 1.4 P6, -0.4 P1 };\n",\ -"{ 1.4 P6, -0.4 P3 };\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"four Tetrahedron non convex (4)\"\n",\ -"\n",\ -"quality 4\n",\ -"flags l;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.1 };\n",\ -"(0.5, 1, 0) { 0.1 };\n",\ -"(0.5, 0, -1) { 0.1 };\n",\ -"(0.5, 0.3, -0.3) { 0.1 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(1, 5, 4) del;\n",\ -"(1, 3, 5) del;\n",\ -"\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.1, -0.1)\n",\ -" { 0.333 X1, 0.333 X2, 0.334 X5 }\n",\ -" { 0.333 Y1, 0.333 Y2, 0.334 Y5 }\n",\ -" { 0.333 Z1, 0.333 Z2, 0.334 Z5 };\n",\ -"\n",\ -"newfaces\n",\ -"(6, 2, 3) del;\n",\ -"(6, 4, 2) del;\n",\ -"(6, 5, 4) del;\n",\ -"(6, 3, 5) del;\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 6);\n",\ -"(1, 4, 2, 6);\n",\ -"(1, 5, 4, 6);\n",\ -"(1, 3, 5, 6);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1.5 P6, -0.5 P1 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"\n",\ -"\n",\ -"freeset\n",\ -"1 6 2 3;\n",\ -"\n",\ -"freeset\n",\ -"1 6 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 6 5 4;\n",\ -"\n",\ -"freeset\n",\ -"1 6 4 2;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"five Tetrahedron non convex (4)\"\n",\ -"\n",\ -"quality 4\n",\ -"flags l;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0, 0.8, -0.2) { 0.5 };\n",\ -"(0, 0.2, -0.8) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 3, 4) del;\n",\ -"(1, 4, 5) del;\n",\ -"(1, 5, 6) del;\n",\ -"(1, 6, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.1, 0.1, -0.1)\n",\ -" { 0.75 X1, 0.05 X2, 0.05 X3, 0.05 X4, 0.05 X5, 0.05 X6 }\n",\ -" { 0.75 Y1, 0.05 Y2, 0.05 Y3, 0.05 Y4, 0.05 Y5, 0.05 Y6 }\n",\ -" { 0.75 Z1, 0.05 Z2, 0.05 Z3, 0.05 Z4, 0.05 Z5, 0.05 Z6 };\n",\ -"\n",\ -"newfaces\n",\ -"(7, 2, 3);\n",\ -"(7, 3, 4);\n",\ -"(7, 4, 5);\n",\ -"(7, 5, 6);\n",\ -"(7, 6, 2);\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 7);\n",\ -"(1, 3, 4, 7);\n",\ -"(1, 4, 5, 7);\n",\ -"(1, 5, 6, 7);\n",\ -"(1, 6, 2, 7);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1.5 P7, -0.5 P1 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"{ 1 P7 };\n",\ -"\n",\ -"\n",\ -"\n",\ -"freeset\n",\ -"1 7 2 3;\n",\ -"\n",\ -"freeset\n",\ -"1 7 3 4;\n",\ -"\n",\ -"freeset\n",\ -"1 7 4 5;\n",\ -"\n",\ -"freeset\n",\ -"1 7 5 6;\n",\ -"\n",\ -"freeset\n",\ -"1 7 6 2;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"four Tetrahedron non convex (6)\"\n",\ -"\n",\ -"quality 6\n",\ -"flags l;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"(0.5, 0.3, -0.3) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(1, 5, 4) del;\n",\ -"(1, 3, 5) del;\n",\ -"\n",\ -"\n",\ -"newpoints\n",\ -"(0.095, 0.003, -0.003)\n",\ -" { 0.9 X1, 0.09 X2, 0.01 X5 }\n",\ -" { 0.9 Y1, 0.09 Y2, 0.01 Y5 }\n",\ -" { 0.9 Z1, 0.09 Z2, 0.01 Z5 };\n",\ -"\n",\ -"newfaces\n",\ -"(6, 2, 3) del;\n",\ -"(6, 4, 2) del;\n",\ -"(6, 5, 4) del;\n",\ -"(6, 3, 5) del;\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 6);\n",\ -"(1, 4, 2, 6);\n",\ -"(1, 5, 4, 6);\n",\ -"(1, 3, 5, 6);\n",\ -"\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1.499 P6, -0.5 P1, 0.001 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"\n",\ -"\n",\ -"freeset\n",\ -"1 6 2 3;\n",\ -"\n",\ -"freeset\n",\ -"1 6 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 6 5 4;\n",\ -"\n",\ -"freeset\n",\ -"1 6 4 2;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"four Tetrahedron non convex (6)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"(0.5, 0.4, -0.4) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(4, 5, 2) del;\n",\ -"(5, 3, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.925, 0.02, -0.02)\n",\ -" { 0.05 X1, 0.9 X2, 0.05 X5 }\n",\ -" { 0.05 Y1, 0.9 Y2, 0.05 Y5 }\n",\ -" { 0.05 Z1, 0.9 Z2, 0.05 Z5 };\n",\ -"\n",\ -"newfaces\n",\ -"(3, 1, 6);\n",\ -"(1, 4, 6);\n",\ -"(4, 5, 6);\n",\ -"(5, 3, 6);\n",\ -"\n",\ -"elements\n",\ -"(3, 1, 2, 6);\n",\ -"(1, 4, 2, 6);\n",\ -"(4, 5, 2, 6);\n",\ -"(5, 3, 2, 6);\n",\ -"\n",\ -"orientations\n",\ -"(3, 1, 2, 5);\n",\ -"(1, 4, 2, 5);\n",\ -"(2, 4, 5, 1);\n",\ -"(3, 2, 5, 1);\n",\ -"(5, 4, 2, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1.5 P6, -0.5 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"{ 1 P6 };\n",\ -"\n",\ -"freeset\n",\ -"3 1 2 6;\n",\ -"\n",\ -"freeset\n",\ -"1 4 2 6;\n",\ -"\n",\ -"freeset\n",\ -"4 5 2 6;\n",\ -"\n",\ -"freeset\n",\ -"5 3 2 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"three Tetrahedron non convex (4)\"\n",\ -"\n",\ -"quality 4\n",\ -"flags l;\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(1, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.25, -0.25)\n",\ -" { 0.25 X1, 0.25 X2, 0.25 X3, 0.25 X4 }\n",\ -" { 0.25 Y1, 0.25 Y2, 0.25 Y3, 0.25 Y4 }\n",\ -" { 0.25 Z1, 0.25 Z2, 0.25 Z3, 0.25 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3);\n",\ -"(5, 4, 2);\n",\ -"(5, 3, 4);\n",\ -"\n",\ -"elements\n",\ -"(2, 3, 1, 5);\n",\ -"(3, 4, 1, 5);\n",\ -"(4, 2, 1, 5;\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 4, 3);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.5 P5, -0.5 P1 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"freeset\n",\ -"1 4 2 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"three Tetrahedron non convex (6)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(1, 3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.2, 0.1, -0.1)\n",\ -" { 0.7 X1, 0.1 X2, 0.1 X3, 0.1 X4 }\n",\ -" { 0.7 Y1, 0.1 Y2, 0.1 Y3, 0.1 Y4 }\n",\ -" { 0.7 Z1, 0.1 Z2, 0.1 Z3, 0.1 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3);\n",\ -"(5, 4, 2);\n",\ -"(5, 3, 4);\n",\ -"\n",\ -"elements\n",\ -"(2, 3, 1, 5);\n",\ -"(3, 4, 1, 5);\n",\ -"(4, 2, 1, 5;\n",\ -"\n",\ -"orientations\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.5 P5, -0.5 P1 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 3 4 5;\n",\ -"\n",\ -"freeset\n",\ -"1 4 2 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"four Tetrahedron non convex (6)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"(0.5, 0.4, -0.4) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"(4, 5, 2) del;\n",\ -"(5, 3, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.7, 0.08, -0.08) { 0.6 X2, 0.2 X5 } { 0.2 Y5 } { 0.2 Z5 };\n",\ -"\n",\ -"newfaces\n",\ -"(3, 1, 6);\n",\ -"(1, 4, 6);\n",\ -"(4, 5, 6);\n",\ -"(5, 3, 6);\n",\ -"\n",\ -"elements\n",\ -"(3, 1, 2, 6);\n",\ -"(1, 4, 2, 6);\n",\ -"(4, 5, 2, 6);\n",\ -"(5, 3, 2, 6);\n",\ -"\n",\ -"\n",\ -"orientations\n",\ -"(3, 1, 2, 5);\n",\ -"(5, 1, 2, 4);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 1, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, 0, -1) { 1 X4 } { 1 Y4 } { 1 Z4 };\n",\ -"(0.5, 0.4, -0.4) { 1 X5 } { 1 Y5 } { 1 Z5 };\n",\ -"(0.55, 0.12, -0.12) { 0.4 X2, 0.3 X5 } { 0.3 Y5 } { 0.3 Z5 };\n",\ -"\n",\ -"freeset\n",\ -"3 1 2 6;\n",\ -"\n",\ -"freeset\n",\ -"1 4 2 6;\n",\ -"\n",\ -"freeset\n",\ -"4 5 2 6;\n",\ -"\n",\ -"freeset\n",\ -"5 3 2 6;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 2 in 60 (12)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 0.5 };\n",\ -"(0.5, 1, 0) { 0.5 };\n",\ -"(0.5, 0, -1) { 0.5 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.1, -0.1)\n",\ -" { 0.4 X1, 0.4 X2, 0.1 X3, 0.1 X4 }\n",\ -" { 0.4 Y1, 0.4 Y2, 0.1 Y3, 0.1 Y4 }\n",\ -" { 0.4 Z1, 0.4 Z2, 0.1 Z3, 0.1 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(5, 2, 3);\n",\ -"(5, 3, 1);\n",\ -"(5, 4, 2);\n",\ -"(5, 1, 4);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 5);\n",\ -"(1, 2, 5, 4);\n",\ -"\n",\ -"freezone2\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1.5 P5, -0.25 P1, -0.25 P2 };\n",\ -"\n",\ -"freezonelimit\n",\ -"{ 1 P1 };\n",\ -"{ 1 P2 };\n",\ -"{ 1 P3 };\n",\ -"{ 1 P4 };\n",\ -"{ 1 P5 };\n",\ -"\n",\ -"freeset\n",\ -"1 2 3 5;\n",\ -"\n",\ -"freeset\n",\ -"1 2 4 5;\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Tetrahedron 120, but more than 180 (13)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 };\n",\ -"(0.5, 0.866, 0) { 1 };\n",\ -"(0.5, -0.866, 0) { 1 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"(1, 4, 2);\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0, -0.3) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"newfaces\n",\ -"(1, 5, 3);\n",\ -"(3, 5, 2);\n",\ -"(2, 5, 1);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 5);\n",\ -"\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, -0.1, -0.4) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4} { 0.5 Z3, 0.5 Z4 };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Free Tetrahedron (14)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1.0 };\n",\ -"(0.5, 0.866, 0) { 1.0 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.288, -0.2) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(4, 1, 2);\n",\ -"(4, 2, 3);\n",\ -"(4, 3, 1);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, 0.288, -0.25) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Free Tetrahedron (15)\"\n",\ -"\n",\ -"quality 100\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1.0 };\n",\ -"(0.5, 0.866, 0) { 1.0 };\n",\ -"\n",\ -"mapfaces\n",\ -"(1, 2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.288, -0.1) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\ -"\n",\ -"newfaces\n",\ -"(4, 1, 2);\n",\ -"(4, 2, 3);\n",\ -"(4, 3, 1);\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3, 4);\n",\ -"\n",\ -"freezone\n",\ -"(0, 0, 0);\n",\ -"(1, 0, 0) { 1 X2 } { } { };\n",\ -"(0.5, 0.866, 0) { 1 X3 } { 1 Y3 } { };\n",\ -"(0.5, 0.288, -0.15) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { };\n",\ -"\n",\ -"endrule\n", -0}; -} diff --git a/libsrc/meshing/topology.cpp b/libsrc/meshing/topology.cpp index f5881796..00108838 100644 --- a/libsrc/meshing/topology.cpp +++ b/libsrc/meshing/topology.cpp @@ -3,10 +3,14 @@ namespace netgen { + using ngcore::ParallelForRange; + using ngcore::ParallelFor; + using ngcore::INT; + using ngcore::TasksPerThread; - + /* template - void QuickSortRec (FlatArray data, + void QuickSortRec (NgFlatArray data, int left, int right) { int i = left; @@ -30,27 +34,64 @@ namespace netgen } template - void QuickSort (FlatArray data) + void QuickSort (NgFlatArray data) { if (data.Size() > 1) QuickSortRec (data, 0, data.Size()-1); } + */ - - - + MeshTopology :: MeshTopology (const Mesh & amesh) : mesh(&amesh) { - buildedges = true; - buildfaces = true; + buildedges = static_buildedges; + buildfaces = static_buildfaces; + buildvertex2element = static_buildvertex2element; timestamp = -1; } MeshTopology :: ~MeshTopology () { ; } + 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 ("nothing known about table "+name +"\n" + "known are 'edges', 'faces', 'parentedges', 'parentfaces'"); + } + + bool MeshTopology :: static_buildedges = true; + bool MeshTopology :: static_buildfaces = true; + bool MeshTopology :: static_buildvertex2element = true; + + void MeshTopology :: EnableTableStatic (string name, bool set) + { + if (name == "edges") + static_buildedges = set; + else if (name == "faces") + static_buildfaces = set; + else if (name == "vertex2element") + static_buildvertex2element = set; + else + throw Exception ("nothing known about table "+name +"\n" + "known are 'edges', 'faces', 'vertex2element'"); + } + + template void LoopOverEdges (const Mesh & mesh, MeshTopology & top, PointIndex v, FUNC func) @@ -59,38 +100,34 @@ namespace netgen { const Element & el = mesh[elnr]; - int neledges = MeshTopology::GetNEdges (el.GetType()); - const ELEMENT_EDGE * eledges = MeshTopology::GetEdges0 (el.GetType()); - - for (int k = 0; k < neledges; k++) + auto eledges = MeshTopology::GetEdges (el.GetType()); + for (int k = 0; k < eledges.Size(); k++) { INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]); - // edge.Sort(); + int edgedir = (edge.I1() > edge.I2()); if (edgedir) swap (edge.I1(), edge.I2()); if (edge.I1() != v) continue; - func (edge, elnr, k, 3, edgedir); - } + func (edge, elnr, k, 3); + } } for (SurfaceElementIndex elnr : top.GetVertexSurfaceElements(v)) { const Element2d & el = mesh[elnr]; - int neledges = MeshTopology::GetNEdges (el.GetType()); - const ELEMENT_EDGE * eledges = MeshTopology::GetEdges0 (el.GetType()); - - for (int k = 0; k < neledges; k++) + auto eledges = MeshTopology::GetEdges (el.GetType()); + for (int k = 0; k < eledges.Size(); k++) { INDEX_2 edge(el[eledges[k][0]], el[eledges[k][1]]); - // edge.Sort(); + int edgedir = (edge.I1() > edge.I2()); if (edgedir) swap (edge.I1(), edge.I2()); if (edge.I1() != v) continue; - func (edge, elnr, k, 2, edgedir); + func (edge, elnr, k, 2); } } @@ -104,7 +141,7 @@ namespace netgen edge.Sort(); if (edge.I1() != v) continue; - func (edge, elnr, 0, 1, edgedir); + func (edge, elnr, 0, 1); } } @@ -136,7 +173,7 @@ namespace netgen if (face.I1() != v) continue; - func (face, elnr, j, true, facedir); + func (face, elnr, j, true); } /* if (pass == 1) @@ -186,7 +223,7 @@ namespace netgen if (face4.I1() != v) continue; - func(face4, elnr, j, true, facedir); + func(face4, elnr, j, true); /* INDEX_3 face(face4.I1(), face4.I2(), face4.I3()); @@ -216,7 +253,7 @@ namespace netgen for (SurfaceElementIndex elnr : top.GetVertexSurfaceElements(v)) { - const Element2d & el = mesh.SurfaceElement (elnr); + const Element2d & el = mesh[elnr]; const ELEMENT_FACE * elfaces = MeshTopology::GetFaces1 (el.GetType()); @@ -250,7 +287,7 @@ namespace netgen if (face.I1() != v) continue; - func(face, elnr, 0, false, facedir); + func(face, elnr, 0, false); /* if (vert2face.Used (face)) facenum = vert2face.Get(face); @@ -303,7 +340,7 @@ namespace netgen } if (face4.I1() != v) continue; - func(face4, elnr, 0, false, facedir); + func(face4, elnr, 0, false); /* INDEX_3 face(face4.I1(), face4.I2(), face4.I3()); @@ -329,10 +366,11 @@ 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"); + static Timer timer_tables("Build vertex to element table"); + RegionTimer reg (timer); #ifdef PARALLEL // ParallelMeshTopology & paralleltop = mesh.GetParallelTopology(); @@ -360,8 +398,7 @@ namespace netgen (*testout) << "nv = " << nv << endl; (*tracer) ("Topology::Update setup tables", false); - Array cnt(nv); - Array vnums; + NgArray cnt(nv); /* generate: @@ -369,130 +406,38 @@ namespace netgen vertex to surface element vertex to segment */ - cnt = 0; - /* - for (ElementIndex ei = 0; ei < ne; ei++) - { - const Element & el = (*mesh)[ei]; - for (int j = 0; j < el.GetNV(); j++) - cnt[el[j]]++; - } - */ - ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) - { - for (ElementIndex ei = begin; ei < end; ei++) - { - const Element & el = (*mesh)[ei]; - for (int j = 0; j < el.GetNV(); j++) - AsAtomic(cnt[el[j]])++; - } - }); - - vert2element = TABLE (cnt); - /* - for (ElementIndex ei = 0; ei < ne; ei++) - { - const Element & el = (*mesh)[ei]; - for (int j = 0; j < el.GetNV(); j++) - vert2element.AddSave (el[j], ei); - } - */ - ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) - { - for (ElementIndex ei = begin; ei < end; ei++) - { - const Element & el = (*mesh)[ei]; - for (int j = 0; j < el.GetNV(); j++) - vert2element.ParallelAdd (el[j], ei); - } - }); - - cnt = 0; - /* - for (SurfaceElementIndex sei = 0; sei < nse; sei++) - { - const Element2d & el = (*mesh)[sei]; - for (int j = 0; j < el.GetNV(); j++) - cnt[el[j]]++; - } - */ - ParallelForRange - (tm, nse, - [&] (size_t begin, size_t end) - { - for (SurfaceElementIndex ei = begin; ei < end; ei++) - { - const Element2d & el = (*mesh)[ei]; - for (int j = 0; j < el.GetNV(); j++) - AsAtomic(cnt[el[j]])++; - } - }); - - - vert2surfelement = TABLE (cnt); - /* - for (SurfaceElementIndex sei = 0; sei < nse; sei++) + if (buildvertex2element) { - const Element2d & el = (*mesh)[sei]; - for (int j = 0; j < el.GetNV(); j++) - vert2surfelement.AddSave (el[j], sei); - } - */ - ParallelForRange - (tm, nse, - [&] (size_t begin, size_t end) - { - for (SurfaceElementIndex sei = begin; sei < end; sei++) - { - const Element2d & el = (*mesh)[sei]; - for (int j = 0; j < el.GetNV(); j++) - vert2surfelement.ParallelAdd (el[j], sei); - } - }); + timer_tables.Start(); + vert2element = mesh->CreatePoint2ElementTable(); + vert2surfelement = mesh->CreatePoint2SurfaceElementTable(0); - - cnt = 0; - for (SegmentIndex si = 0; si < nseg; si++) - { - const Segment & seg = mesh->LineSegment(si); - cnt[seg[0]]++; - cnt[seg[1]]++; - } - - vert2segment = TABLE (cnt); - for (SegmentIndex si = 0; si < nseg; si++) - { - const Segment & seg = mesh->LineSegment(si); - vert2segment.AddSave (seg[0], si); - vert2segment.AddSave (seg[1], si); + vert2segment = ngcore::CreateSortedTable( mesh->LineSegments().Range(), + [&](auto & table, SegmentIndex segi) + { + const Segment & seg = (*mesh)[segi]; + table.Add (seg[0], segi); + table.Add (seg[1], segi); + }, np); + + vert2pointelement = ngcore::CreateSortedTable( mesh->pointelements.Range(), + [&](auto & table, int pei) + { + const Element0d & pointel = mesh->pointelements[pei]; + table.Add(pointel.pnum, pei); + }, np); + timer_tables.Stop(); } - cnt = 0; - for (int pei = 0; pei < mesh->pointelements.Size(); pei++) - { - const Element0d & pointel = mesh->pointelements[pei]; - cnt[pointel.pnum]++; - } - - vert2pointelement = TABLE (cnt); - for (int pei = 0; pei < mesh->pointelements.Size(); pei++) - { - const Element0d & pointel = mesh->pointelements[pei]; - vert2pointelement.AddSave (pointel.pnum, pei); - } (*tracer) ("Topology::Update setup tables", true); if (buildedges) { - static int timer1 = NgProfiler::CreateTimer ("topology::buildedges"); - NgProfiler::RegionTimer reg1 (timer1); + static Timer timer1("topology::buildedges"); + RegionTimer reg1(timer1); if (id == 0) PrintMessage (5, "Update edges "); @@ -501,13 +446,27 @@ namespace netgen surfedges.SetSize(nse); segedges.SetSize(nseg); + /* for (int i = 0; i < ne; i++) for (int j = 0; j < 12; j++) edges[i][j].nr = -1; for (int i = 0; i < nse; i++) for (int j = 0; j < 4; j++) surfedges[i][j].nr = -1; + */ + ParallelFor (ne, [this](auto i) + { + for (auto & e : edges[i]) + e = -1; + }); + ParallelFor (nse, [this](auto i) + { + for (auto & e : surfedges[i]) + e = -1; + }); + + // keep existing edges cnt = 0; for (int i = 0; i < edge2vert.Size(); i++) @@ -518,16 +477,18 @@ namespace netgen // ensure all coarse grid and intermediate level edges cnt = 0; - for (int i = mesh->mlbetweennodes.Begin(); i < mesh->mlbetweennodes.End(); i++) + // for (int i = mesh->mlbetweennodes.Begin(); i < mesh->mlbetweennodes.End(); i++) + for (int i : mesh->mlbetweennodes.Range()) { INDEX_2 parents = Sort (mesh->mlbetweennodes[i]); if (parents[0] >= PointIndex::BASE) cnt[parents[0]]++; } TABLE vert2vertcoarse (cnt); - for (int i = mesh->mlbetweennodes.Begin(); i < mesh->mlbetweennodes.End(); i++) + // for (int i = mesh->mlbetweennodes.Begin(); i < mesh->mlbetweennodes.End(); i++) + for (int i : mesh->mlbetweennodes.Range()) { INDEX_2 parents = Sort (mesh->mlbetweennodes[i]); - if (parents[0] > PointIndex::BASE) vert2vertcoarse.AddSave (parents[0], parents[1]); + if (parents[0] >= PointIndex::BASE) vert2vertcoarse.AddSave (parents[0], parents[1]); } @@ -545,10 +506,13 @@ namespace netgen cnt = 0; ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { - INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); + auto begin = r.First(); + auto end = r.Next(); + // INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); + ngcore::ClosedHashTable v2eht(2*max_edge_on_vertex+10); for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) { @@ -559,32 +523,24 @@ namespace netgen v2eht.Set (v2, ednr); } - int cnti = 0; - + size_t usedold = v2eht.UsedElements(); + for (int v2 : vert2vertcoarse[v]) - if (!v2eht.Used(v2)) - { - cnti++; - v2eht.Set (v2, 33); // some value - } + v2eht.Set (v2, 33); // some value LoopOverEdges (*mesh, *this, v, - [&] (INDEX_2 edge, int elnr, int loc_edge, int element_dim, int edgedir) + [&] (INDEX_2 edge, int elnr, int loc_edge, int element_dim) { - if (!v2eht.Used (edge.I2())) - { - cnti++; - v2eht.Set (edge.I2(), 33); // something - } + v2eht.Set (edge[1], 33); // something }); - cnt[v] = cnti; + + cnt[v] = v2eht.UsedElements()-usedold; } - } ); + }, TasksPerThread(4) ); // accumulate number of edges int ned = edge2vert.Size(); - // for (size_t v = 0; v < mesh->GetNV(); v++) for (size_t v : cnt.Range()) { auto hv = cnt[v]; @@ -592,24 +548,29 @@ namespace netgen ned += hv; } edge2vert.SetSize(ned); - + edge2segment.SetSize(ned); + edge2segment = -1; // INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); - // Array vertex2; + // 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) { - INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); + auto begin = r.First(); + auto end = r.Next(); + // INDEX_CLOSED_HASHTABLE v2eht(2*max_edge_on_vertex+10); + ngcore::ClosedHashTable v2eht(2*max_edge_on_vertex+10); + Array vertex2; for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) { int ned = cnt[v]; v2eht.DeleteData(); - vertex2.SetSize (0); + vertex2.SetSize0 (); for (int ednr : vert2edge[v]) { @@ -625,68 +586,357 @@ namespace netgen } LoopOverEdges (*mesh, *this, v, - [&](INDEX_2 edge, int elnr, int loc_edge, int element_dim, int edgedir) + [&](INDEX_2 edge, int elnr, int loc_edge, int element_dim) { + size_t pos; + if (v2eht.PositionCreate(edge[1], pos)) + { + vertex2.Append (edge[1]); + v2eht.SetData (pos, 33); + } + /* if (!v2eht.Used(edge.I2())) { vertex2.Append (edge.I2()); v2eht.Set (edge.I2(), 33); } + */ }); QuickSort (vertex2); - + + /* for (int j = 0; j < vertex2.Size(); j++) { v2eht.Set (vertex2[j], ned); - edge2vert[ned] = INDEX_2 (v, vertex2[j]); + edge2vert[ned] = { v, vertex2[j] }; + ned++; + } + */ + for (auto v2 : vertex2) + { + v2eht.Set (v2, ned); + edge2vert[ned] = { v, v2 }; ned++; } LoopOverEdges (*mesh, *this, v, - [&](INDEX_2 edge, int elnr, int loc_edge, int element_dim, int edgedir) + [&](INDEX_2 edge, int elnr, int loc_edge, int element_dim) { - int edgenum = v2eht.Get(edge.I2()); + int edgenum = v2eht.Get(edge[1]); switch (element_dim) { case 3: - edges[elnr][loc_edge].nr = edgenum; - // edges[elnr][loc_edge].orient = edgedir; + edges[elnr][loc_edge] = edgenum; break; case 2: - surfedges[elnr][loc_edge].nr = edgenum; - // surfedges[elnr][loc_edge].orient = edgedir; + surfedges[elnr][loc_edge] = edgenum; break; case 1: - segedges[elnr].nr = edgenum; - // segedges[elnr].orient = edgedir; + segedges[elnr] = edgenum; + edge2segment[edgenum] = elnr; break; } }); } - } ); + }, TasksPerThread(4) ); + + + 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 third 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) { - static int timer2 = NgProfiler::CreateTimer ("topology::buildfaces"); - static int timer2a = NgProfiler::CreateTimer ("topology::buildfacesa"); - static int timer2b = NgProfiler::CreateTimer ("topology::buildfacesb"); - static int timer2b1 = NgProfiler::CreateTimer ("topology::buildfacesb1"); - static int timer2c = NgProfiler::CreateTimer ("topology::buildfacesc"); - NgProfiler::RegionTimer reg2 (timer2); + static Timer timer2("topology::buildfaces"); + // static int timer2a = NgProfiler::CreateTimer ("topology::buildfacesa"); + // static int timer2b = NgProfiler::CreateTimer ("topology::buildfacesb"); + // static int timer2b1 = NgProfiler::CreateTimer ("topology::buildfacesb1"); + // static int timer2c = NgProfiler::CreateTimer ("topology::buildfacesc"); + RegionTimer reg2 (timer2); if (id == 0) PrintMessage (5, "Update faces "); - NgProfiler::StartTimer (timer2a); + // NgProfiler::StartTimer (timer2a); faces.SetSize(ne); surffaces.SetSize(nse); - + cnt = 0; for (int i = 0; i < face2vert.Size(); i++) @@ -695,10 +945,57 @@ 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; + faces[elnr][j] = -1; int max_face_on_vertex = 0; @@ -711,40 +1008,58 @@ namespace netgen - NgProfiler::StopTimer (timer2a); - NgProfiler::StartTimer (timer2b); + // 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(); // count faces associated with vertices cnt = 0; // for (auto v : mesh.Points().Range()) - NgProfiler::StartTimer (timer2b1); + // NgProfiler::StartTimer (timer2b1); ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), // Points().Size(), + [&] (IntRange r) { - INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); - for (PointIndex v = begin+PointIndex::BASE; - v < end+PointIndex::BASE; v++) + // auto begin = r.First(); + // auto end = r.Next(); + // INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); + ClosedHashTable vert2face(2*max_face_on_vertex+10); + // for (PointIndex v = begin+PointIndex::BASE; + // v < end+PointIndex::BASE; v++) + for (PointIndex v : r+PointIndex::BASE) { vert2face.DeleteData(); for (int j = 0; j < vert2oldface[v].Size(); j++) { int fnr = vert2oldface[v][j]; - INDEX_3 face (face2vert[fnr].I1(), - face2vert[fnr].I2(), - face2vert[fnr].I3()); + INDEX_3 face (face2vert[fnr][0], + face2vert[fnr][1], + face2vert[fnr][2]); 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) + [&] (INDEX_4 i4, int elnr, int j, bool volume) { - INDEX_3 face(i4.I1(), i4.I2(), i4.I3()); + INDEX_3 face(i4[0], i4[1], i4[2]); if (!vert2face.Used (face)) { cnti++; @@ -753,8 +1068,8 @@ namespace netgen }); cnt[v] = cnti; } - } ); - NgProfiler::StopTimer (timer2b1); + }, TasksPerThread(4) ); + // NgProfiler::StopTimer (timer2b1); // accumulate number of faces int nfa = oldnfa; @@ -768,15 +1083,20 @@ namespace netgen } face2vert.SetSize(nfa); - // for (auto v : mesh.Points().Range()) ParallelForRange - (tm, mesh->GetNV(), // Points().Size(), - [&] (size_t begin, size_t end) + (mesh->GetNV(), + [&] (IntRange r) { - INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); + // auto begin = r.First(); + // auto end = r.Next(); + // INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); + ClosedHashTable vert2face(2*max_face_on_vertex+10); + /* for (PointIndex v = begin+PointIndex::BASE; v < end+PointIndex::BASE; v++) + */ + for (PointIndex v : r+PointIndex::BASE) { int first_fa = cnt[v]; int nfa = first_fa; @@ -785,22 +1105,56 @@ namespace netgen for (int j = 0; j < vert2oldface[v].Size(); j++) { int fnr = vert2oldface[v][j]; - INDEX_3 face (face2vert[fnr].I1(), - face2vert[fnr].I2(), - face2vert[fnr].I3()); + INDEX_3 face (face2vert[fnr][0], + face2vert[fnr][1], + face2vert[fnr][2]); 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)) + { + face2vert[nfa] = { face[0], face[1], face[2], 0 }; // i4; + vert2face.Set (face, nfa); + nfa++; + } + */ + size_t pos; + if (vert2face.PositionCreate(face, pos)) + { + face2vert[nfa] = { face[0], face[1], face[2], 0 }; // i4; + vert2face.SetData (pos, face, nfa); + nfa++; + } + + } + LoopOverFaces (*mesh, *this, v, - [&] (INDEX_4 i4, int elnr, int j, bool volume, int facedir) + [&] (INDEX_4 i4, int elnr, int j, bool volume) { INDEX_3 face(i4.I1(), i4.I2(), i4.I3()); + /* if (!vert2face.Used (face)) { - face2vert[nfa] = i4; + face2vert[nfa] = { i4[0], i4[1], i4[2], i4[3] }; // i4; vert2face.Set (face, nfa); nfa++; } + */ + size_t pos; + if (vert2face.PositionCreate(face, pos)) + { + face2vert[nfa] = { i4[0], i4[1], i4[2], i4[3] }; // i4; + vert2face.SetData (pos, face, nfa); + nfa++; + } }); @@ -810,9 +1164,9 @@ namespace netgen { if (face2vert[j][0] == v) { - INDEX_3 face (face2vert[j].I1(), - face2vert[j].I2(), - face2vert[j].I3()); + INDEX_3 face (face2vert[j][0], + face2vert[j][1], + face2vert[j][2]); vert2face.Set (face, j); } else @@ -821,277 +1175,23 @@ namespace netgen LoopOverFaces (*mesh, *this, v, - [&] (INDEX_4 i4, int elnr, int j, bool volume, int facedir) + [&] (INDEX_4 i4, int elnr, int j, bool volume) { INDEX_3 face(i4.I1(), i4.I2(), i4.I3()); int facenum = vert2face.Get(face); if (volume) - { - faces[elnr][j].fnr = facenum; - // faces[elnr][j].forient = facedir; - } + faces[elnr][j] = facenum; else - { - surffaces[elnr].fnr = facenum; - // surffaces[elnr].forient = facedir; - } + surffaces[elnr] = facenum; }); } - }); + }, TasksPerThread(4) ); - /* - int oldnfa = face2vert.Size(); - int nfa = oldnfa; - INDEX_3_CLOSED_HASHTABLE vert2face(2*max_face_on_vertex+10); - - for (auto v : mesh.Points().Range()) - { - int first_fa = nfa; - - vert2face.DeleteData(); - - for (int j = 0; j < vert2oldface[v].Size(); j++) - { - int fnr = vert2oldface[v][j]; - INDEX_3 face (face2vert[fnr].I1(), - face2vert[fnr].I2(), - face2vert[fnr].I3()); - vert2face.Set (face, fnr+1); - } - - - for (int pass = 1; pass <= 2; pass++) - { - - for (ElementIndex elnr : (*vert2element)[v]) - { - const Element & el = mesh[elnr]; - - int nelfaces = GetNFaces (el.GetType()); - const ELEMENT_FACE * elfaces = GetFaces0 (el.GetType()); - - for (int j = 0; j < nelfaces; j++) - if (elfaces[j][3] < 0) - - { // triangle - INDEX_3 face(el[elfaces[j][0]], el[elfaces[j][1]], - el[elfaces[j][2]]); - - int facedir = 0; - if (face.I1() > face.I2()) - { swap (face.I1(), face.I2()); facedir += 1; } - if (face.I2() > face.I3()) - { swap (face.I2(), face.I3()); facedir += 2; } - if (face.I1() > face.I2()) - { swap (face.I1(), face.I2()); facedir += 4; } - - if (face.I1() != v) continue; - - if (pass == 1) - { - if (!vert2face.Used (face)) - { - nfa++; - vert2face.Set (face, nfa); - INDEX_4 hface(face.I1(),face.I2(),face.I3(),0); - face2vert.Append (hface); - } - } - else - { - int facenum = vert2face.Get(face); - faces[elnr][j].fnr = facenum-1; - faces[elnr][j].forient = facedir; - } - } - - else - - { - // quad - int facenum; - INDEX_4Q face4(el[elfaces[j][0]], el[elfaces[j][1]], - el[elfaces[j][2]], el[elfaces[j][3]]); - - int facedir = 0; - if (min2 (face4.I1(), face4.I2()) > - min2 (face4.I4(), face4.I3())) - { // z - flip - facedir += 1; - swap (face4.I1(), face4.I4()); - swap (face4.I2(), face4.I3()); - } - if (min2 (face4.I1(), face4.I4()) > - min2 (face4.I2(), face4.I3())) - { // x - flip - facedir += 2; - swap (face4.I1(), face4.I2()); - swap (face4.I3(), face4.I4()); - } - if (face4.I2() > face4.I4()) - { // diagonal flip - facedir += 4; - swap (face4.I2(), face4.I4()); - } - - - INDEX_3 face(face4.I1(), face4.I2(), face4.I3()); - - if (face.I1() != v) continue; - - if (vert2face.Used (face)) - { - facenum = vert2face.Get(face); - } - else - { - if (pass == 2) cout << "hier in pass 2" << endl; - nfa++; - vert2face.Set (face, nfa); - facenum = nfa; - - INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I4()); - face2vert.Append (hface); - } - - faces[elnr][j].fnr = facenum-1; - faces[elnr][j].forient = facedir; - } - } - - for (int j = 0; j < (*vert2surfelement)[v].Size(); j++) - { - SurfaceElementIndex elnr = (*vert2surfelement)[v][j]; - const Element2d & el = mesh.SurfaceElement (elnr); - - const ELEMENT_FACE * elfaces = GetFaces1 (el.GetType()); - - if (elfaces[0][3] == 0) - - { // triangle - - int facenum; - int facedir; - - INDEX_3 face(el.PNum(elfaces[0][0]), - el.PNum(elfaces[0][1]), - el.PNum(elfaces[0][2])); - - facedir = 0; - if (face.I1() > face.I2()) - { - swap (face.I1(), face.I2()); - facedir += 1; - } - if (face.I2() > face.I3()) - { - swap (face.I2(), face.I3()); - facedir += 2; - } - if (face.I1() > face.I2()) - { - swap (face.I1(), face.I2()); - facedir += 4; - } - - if (face.I1() != v) continue; - - if (vert2face.Used (face)) - facenum = vert2face.Get(face); - else - { - nfa++; - vert2face.Set (face, nfa); - facenum = nfa; - - INDEX_4 hface(face.I1(),face.I2(),face.I3(),0); - face2vert.Append (hface); - } - - surffaces[elnr].fnr = facenum-1; - surffaces[elnr].forient = facedir; - } - - else - - { - // quad - int facenum; - int facedir; - - INDEX_4Q face4(el.PNum(elfaces[0][0]), - el.PNum(elfaces[0][1]), - el.PNum(elfaces[0][2]), - el.PNum(elfaces[0][3])); - - facedir = 0; - if (min2 (face4.I1(), face4.I2()) > - min2 (face4.I4(), face4.I3())) - { // z - orientation - facedir += 1; - swap (face4.I1(), face4.I4()); - swap (face4.I2(), face4.I3()); - } - if (min2 (face4.I1(), face4.I4()) > - min2 (face4.I2(), face4.I3())) - { // x - orientation - facedir += 2; - swap (face4.I1(), face4.I2()); - swap (face4.I3(), face4.I4()); - } - if (face4.I2() > face4.I4()) - { - facedir += 4; - swap (face4.I2(), face4.I4()); - } - - INDEX_3 face(face4.I1(), face4.I2(), face4.I3()); - if (face.I1() != v) continue; - - if (vert2face.Used (face)) - facenum = vert2face.Get(face); - else - { - nfa++; - vert2face.Set (face, nfa); - facenum = nfa; - - INDEX_4 hface(face4.I1(),face4.I2(),face4.I3(),face4.I4()); - face2vert.Append (hface); - } - - surffaces[elnr].fnr = facenum-1; - surffaces[elnr].forient = facedir; - } - } - - // sort faces - if (pass == 1) - { - QuickSort (face2vert.Range(first_fa, nfa)); - - for (int j = first_fa; j < face2vert.Size(); j++) - { - if (face2vert[j][0] == v) - { - INDEX_3 face (face2vert[j].I1(), - face2vert[j].I2(), - face2vert[j].I3()); - vert2face.Set (face, j+1); - } - else - break; - } - } - } - } - face2vert.SetAllocSize (nfa); - */ // *testout << "face2vert = " << endl << face2vert << endl; - NgProfiler::StopTimer (timer2b); - NgProfiler::StartTimer (timer2c); + // NgProfiler::StopTimer (timer2b); + // NgProfiler::StartTimer (timer2c); face2surfel.SetSize (nfa); @@ -1117,19 +1217,21 @@ namespace netgen surf2volelement.Elem(i)[1] = 0; } (*tracer) ("Topology::Update build surf2vol", false); - for (int i = 1; i <= ne; i++) - for (int j = 0; j < 6; j++) - { - // int fnum = (faces.Get(i)[j]+7) / 8; - int fnum = faces.Get(i)[j].fnr+1; - if (fnum > 0 && face2surfel.Elem(fnum)) - { - int sel = face2surfel.Elem(fnum); - surf2volelement.Elem(sel)[1] = - surf2volelement.Elem(sel)[0]; - surf2volelement.Elem(sel)[0] = i; - } - } + // for (int i = 0; i < ne; i++) + ParallelFor (ne, [this](auto i) + { + for (int j = 0; j < 6; j++) + { + // int fnum = (faces.Get(i)[j]+7) / 8; + int fnum = faces[i][j]+1; + if (fnum > 0 && face2surfel.Elem(fnum)) + { + int sel = face2surfel.Elem(fnum); + surf2volelement.Elem(sel)[1] = + surf2volelement.Elem(sel)[0]; + surf2volelement.Elem(sel)[0] = i+1; + } + }}); (*tracer) ("Topology::Update build surf2vol", true); face2vert.SetAllocSize (face2vert.Size()); @@ -1143,30 +1245,28 @@ namespace netgen #endif (*tracer) ("Topology::Update count face_els", false); - Array face_els(nfa), face_surfels(nfa); + NgArray face_els(nfa), face_surfels(nfa); face_els = 0; face_surfels = 0; - /* - Array hfaces; - for (int i = 1; i <= ne; i++) - { - GetElementFaces (i, hfaces); - for (int j = 0; j < hfaces.Size(); j++) - face_els[hfaces[j]-1]++; - } - */ + ParallelForRange - (tm, ne, - [&] (size_t begin, size_t end) + (ne, + [&] (IntRange r) { - Array hfaces; - for (ElementIndex ei = begin; ei < end; ei++) - { + /* + NgArray hfaces; + for (ElementIndex ei : r) + { GetElementFaces (ei+1, hfaces); for (auto f : hfaces) AsAtomic(face_els[f-1])++; } - }); + */ + for (ElementIndex ei : r) + for (auto f : GetFaces(ei)) + AsAtomic(face_els[f])++; + + }, TasksPerThread(4)); for (int i = 1; i <= nse; i++) face_surfels[GetSurfaceElementFace (i)-1]++; (*tracer) ("Topology::Update count face_els", true); @@ -1195,14 +1295,19 @@ namespace netgen #endif { (*testout) << "illegal face : " << i << endl; - (*testout) << "points = " << face2vert[i] << endl; + (*testout) << "points = " + << face2vert[i][0] << "," + << face2vert[i][1] << "," + << face2vert[i][2] << "," + << face2vert[i][3] + << endl; (*testout) << "pos = "; for (int j = 0; j < 4; j++) - if (face2vert[i].I(j+1) >= 1) - (*testout) << (*mesh)[(PointIndex)face2vert[i].I(j+1)] << " "; + if (face2vert[i][j] >= 1) + (*testout) << (*mesh)[(PointIndex)face2vert[i][j]] << " "; (*testout) << endl; - FlatArray vertels = GetVertexElements (face2vert[i].I(1)); + FlatArray vertels = GetVertexElements (face2vert[i][0]); for (int k = 0; k < vertels.Size(); k++) { int elfaces[10], orient[10]; @@ -1215,7 +1320,7 @@ namespace netgen if (mesh->coarsemesh && mesh->hpelements->Size() == mesh->GetNE() ) { const HPRefElement & hpref_el = - (*mesh->hpelements) [ (*mesh)[vertels[k]].hp_elnr]; + (*mesh->hpelements) [ (*mesh)[vertels[k]].GetHpElnr()]; (*testout) << "coarse eleme = " << hpref_el.coarse_elnr << endl; } @@ -1226,9 +1331,309 @@ namespace netgen } if (cnt_err && ntasks == 1) - cout << cnt_err << " elements are not matching !!!" << endl; + cout << IM(5) << cnt_err << " elements are not matching !!!" << endl; } - NgProfiler::StopTimer (timer2c); + // 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 "< & eledges) const + void MeshTopology :: GetElementEdges (int elnr, NgArray & eledges) const { int ned = GetNEdges (mesh->VolumeElement(elnr).GetType()); eledges.SetSize (ned); for (int i = 0; i < ned; i++) - eledges[i] = edges.Get(elnr)[i].nr+1; + eledges[i] = edges.Get(elnr)[i]+1; // eledges[i] = abs (edges.Get(elnr)[i]); } - void MeshTopology :: GetElementFaces (int elnr, Array & elfaces, bool withorientation) const + void MeshTopology :: GetElementFaces (int elnr, NgArray & elfaces, bool withorientation) const { int nfa = GetNFaces (mesh->VolumeElement(elnr).GetType()); elfaces.SetSize (nfa); - if (!withorientation) - - for (int i = 1; i <= nfa; i++) - { - // elfaces.Elem(i) = (faces.Get(elnr)[i-1]-1) / 8 + 1; - elfaces.Elem(i) = faces.Get(elnr)[i-1].fnr+1; - } + for (auto i : Range(nfa)) + elfaces[i] = faces.Get(elnr)[i]+1; - else - { - cerr << "GetElementFaces with orientation currently not supported" << endl; - /* - for (int i = 1; i <= nfa; i++) - { - elfaces.Elem(i) = (faces.Get(elnr)[i-1]-1) / 8 + 1; - int orient = (faces.Get(elnr)[i-1]-1) % 8; - if(orient == 1 || orient == 2 || orient == 4 || orient == 7) - elfaces.Elem(i) *= -1; - } - */ - } + if(withorientation) + { + for(auto & face : elfaces) + { + auto v = face2vert[face-1]; + if(v[3]!=0) + cerr << "GetElementFaces with orientation currently not supported for quads" << endl; + + int classnr = 0; + if (v[0] > v[1]) { classnr++; } + if (v[1] > v[2]) { classnr++; } + if (v[2] > v[0]) { classnr++; } + + if(classnr==1) + face = -face; + } + } } - void MeshTopology :: GetElementEdgeOrientations (int elnr, Array & eorient) const + void MeshTopology :: GetElementEdgeOrientations (int elnr, NgArray & eorient) const { int ned = GetNEdges (mesh->VolumeElement(elnr).GetType()); eorient.SetSize (ned); @@ -1413,7 +1817,7 @@ namespace netgen eorient.Elem(i) = GetElementEdgeOrientation (elnr, i-1) ? -1 : 1; } - void MeshTopology :: GetElementFaceOrientations (int elnr, Array & forient) const + void MeshTopology :: GetElementFaceOrientations (int elnr, NgArray & forient) const { int nfa = GetNFaces (mesh->VolumeElement(elnr).GetType()); forient.SetSize (nfa); @@ -1424,7 +1828,7 @@ namespace netgen } - + int MeshTopology :: GetElementEdges (int elnr, int * eledges, int * orient) const { // int ned = GetNEdges (mesh.VolumeElement(elnr).GetType()); @@ -1440,8 +1844,8 @@ namespace netgen eledges[i] = abs (edges.Get(elnr)[i]); orient[i] = (edges.Get(elnr)[i] > 0 ) ? 1 : -1; */ - if (edges.Get(elnr)[i].nr == -1) return i; - eledges[i] = edges.Get(elnr)[i].nr+1; + if (edges.Get(elnr)[i] == -1) return i; + eledges[i] = edges.Get(elnr)[i]+1; // orient[i] = edges.Get(elnr)[i].orient ? -1 : 1; orient[i] = GetElementEdgeOrientation(elnr, i) ? -1 : 1; } @@ -1452,8 +1856,8 @@ namespace netgen { // if (!edges.Get(elnr)[i]) return i; // eledges[i] = abs (edges.Get(elnr)[i]); - if (edges.Get(elnr)[i].nr == -1) return i; - eledges[i] = edges.Get(elnr)[i].nr+1; + if (edges.Get(elnr)[i] == -1) return i; + eledges[i] = edges.Get(elnr)[i]+1; } } @@ -1496,8 +1900,8 @@ namespace netgen elfaces[i] = (faces.Get(elnr)[i]-1) / 8 + 1; orient[i] = (faces.Get(elnr)[i]-1) % 8; */ - if (faces.Get(elnr)[i].fnr == -1) return i; - elfaces[i] = faces.Get(elnr)[i].fnr+1; + if (faces.Get(elnr)[i] == -1) return i; + elfaces[i] = faces.Get(elnr)[i]+1; // orient[i] = faces.Get(elnr)[i].forient; orient[i] = GetElementFaceOrientation (elnr, i); } @@ -1508,34 +1912,51 @@ namespace netgen { // if (!faces.Get(elnr)[i]) return i; // elfaces[i] = (faces.Get(elnr)[i]-1) / 8 + 1; - if (faces.Get(elnr)[i].fnr == -1) return i; - elfaces[i] = faces.Get(elnr)[i].fnr+1; + if (faces.Get(elnr)[i] == -1) return i; + elfaces[i] = faces.Get(elnr)[i]+1; } } return 6; } - void MeshTopology :: GetSurfaceElementEdges (int elnr, Array & eledges) const + + void MeshTopology :: GetSurfaceElementEdges (int elnr, NgArray & eledges) const { int ned = GetNEdges (mesh->SurfaceElement(elnr).GetType()); eledges.SetSize (ned); for (int i = 0; i < ned; i++) - // eledges[i] = abs (surfedges.Get(elnr)[i]); - eledges[i] = surfedges.Get(elnr)[i].nr+1; + eledges[i] = surfedges.Get(elnr)[i]+1; } - void MeshTopology :: GetEdges (SurfaceElementIndex elnr, Array & eledges) const + void MeshTopology :: GetEdges (SurfaceElementIndex elnr, NgArray & eledges) const { int ned = GetNEdges ( (*mesh)[elnr].GetType()); eledges.SetSize (ned); for (int i = 0; i < ned; i++) - // eledges[i] = abs (surfedges[elnr][i])-1; - eledges[i] = surfedges[elnr][i].nr; + eledges[i] = surfedges[elnr][i]; } + /* + FlatArray MeshTopology :: GetEdges (SurfaceElementIndex elnr) const + { + return FlatArray(GetNEdges ( (*mesh)[elnr].GetType()), &surfedges[elnr][0]); + } + + FlatArray MeshTopology :: GetEdges (ElementIndex elnr) const + { + return FlatArray(GetNEdges ( (*mesh)[elnr].GetType()), &edges[elnr][0]); + } + + FlatArray MeshTopology :: GetFaces (ElementIndex elnr) const + { + return FlatArray(GetNFaces ( (*mesh)[elnr].GetType()), &faces[elnr][0]); + } + */ + + int MeshTopology :: GetSurfaceElementFace (int elnr) const { - return surffaces.Get(elnr).fnr+1; + return surffaces.Get(elnr)+1; } /* @@ -1547,7 +1968,7 @@ namespace netgen void MeshTopology :: - GetSurfaceElementEdgeOrientations (int elnr, Array & eorient) const + GetSurfaceElementEdgeOrientations (int elnr, NgArray & eorient) const { int ned = GetNEdges (mesh->SurfaceElement(elnr).GetType()); eorient.SetSize (ned); @@ -1578,10 +1999,11 @@ namespace netgen eledges[i] = abs (surfedges.Get(elnr)[i]); orient[i] = (surfedges.Get(elnr)[i] > 0 ) ? 1 : -1; */ - if (surfedges.Get(elnr)[i].nr == -1) return i; - eledges[i] = surfedges.Get(elnr)[i].nr+1; + if (surfedges.Get(elnr)[i] == -1) return i; + eledges[i] = surfedges.Get(elnr)[i]+1; // orient[i] = (surfedges.Get(elnr)[i].orient) ? -1 : 1; - orient[i] = GetSurfaceElementEdgeOrientation(elnr, i) ? -1 : 1; + // orient[i] = GetSurfaceElementEdgeOrientation(elnr, i) ? -1 : 1; + orient[i] = 1; } } @@ -1593,8 +2015,8 @@ namespace netgen if (!surfedges.Get(elnr)[i]) return i; eledges[i] = abs (surfedges.Get(elnr)[i]); */ - if (surfedges.Get(elnr)[i].nr == -1) return i; - eledges[i] = surfedges.Get(elnr)[i].nr+1; + if (surfedges.Get(elnr)[i] == -1) return i; + eledges[i] = surfedges.Get(elnr)[i]+1; } } return 4; @@ -1606,10 +2028,11 @@ namespace netgen if (orient) orient[0] = segedges.Get(elnr) > 0 ? 1 : -1; */ - eledges[0] = segedges.Get(elnr).nr+1; + eledges[0] = segedges.Get(elnr)+1; if (orient) // orient[0] = segedges.Get(elnr).orient ? -1 : 1; - orient[0] = GetSegmentEdgeOrientation(elnr) ? -1 : 1; + // orient[0] = GetSegmentEdgeOrientation(elnr) ? -1 : 1; + orient[0] = 1; } return 1; } @@ -1747,6 +2170,13 @@ namespace netgen return facedir; } } + + void MeshTopology :: GetSegmentEdge (int segnr, int & enr, int & orient) const + { + enr = segedges.Get(segnr)+1; + orient = GetSegmentEdgeOrientation(segnr); + } + int MeshTopology :: GetSegmentEdgeOrientation (int elnr) const { @@ -1761,11 +2191,11 @@ namespace netgen - void MeshTopology :: GetFaceVertices (int fnr, Array & vertices) const + void MeshTopology :: GetFaceVertices (int fnr, NgArray & vertices) const { vertices.SetSize(4); for (int i = 0; i < 4; i++) - vertices[i] = face2vert.Get(fnr)[i]; + vertices[i] = face2vert[fnr-1][i]; if (vertices[3] == 0) vertices.SetSize(3); } @@ -1773,7 +2203,7 @@ namespace netgen void MeshTopology :: GetFaceVertices (int fnr, int * vertices) const { for (int i = 0; i <= 3; i++) - vertices[i] = face2vert.Get(fnr)[i]; + vertices[i] = face2vert[fnr-1][i]; } @@ -1784,21 +2214,21 @@ namespace netgen cerr << "illegal edge nr: " << ednr << ", numedges = " << edge2vert.Size() << " id = " << id << endl; - v1 = edge2vert.Get(ednr)[0]; - v2 = edge2vert.Get(ednr)[1]; + v1 = edge2vert[ednr-1][0]; + v2 = edge2vert[ednr-1][1]; } void MeshTopology :: GetEdgeVertices (int ednr, PointIndex & v1, PointIndex & v2) const { - v1 = edge2vert.Get(ednr)[0]; - v2 = edge2vert.Get(ednr)[1]; + v1 = edge2vert[ednr-1][0]; + v2 = edge2vert[ednr-1][1]; } - void MeshTopology :: GetFaceEdges (int fnr, Array & fedges, bool withorientation) const + void MeshTopology :: GetFaceEdges (int fnr, NgArray & fedges, bool withorientation) const { - ArrayMem pi(4); - ArrayMem eledges; + NgArrayMem pi(4); + NgArrayMem eledges; fedges.SetSize (0); GetFaceVertices(fnr, pi); @@ -1809,7 +2239,7 @@ namespace netgen // e3 = op e1(f2,f3) // e4 = op e2(f1,f3) - /* ArrayMem fp; + /* NgArrayMem fp; fp[0] = pi[0]; for(int k=1;kfp[0]) swap(fp[k],fp[0]); @@ -1902,75 +2332,36 @@ namespace netgen } } - /* - ELEMENT_TYPE MeshTopology :: GetFaceType (int fnr) const - { - if (face2vert.Get(fnr)[3] == 0) return TRIG; else return QUAD; - } - */ - void MeshTopology :: GetVertexElements (int vnr, Array & elements) const { if (vert2element.Size()) - { - int ne = vert2element.EntrySize(vnr); - elements.SetSize(ne); - for (int i = 1; i <= ne; i++) - elements.Elem(i) = vert2element.Get(vnr, i); - } + elements = vert2element[vnr]; } - /* - FlatArray MeshTopology :: GetVertexElements (int vnr) const - { - if (vert2element) - return (*vert2element)[vnr]; - return FlatArray (0,0); - } - - FlatArray MeshTopology :: GetVertexSurfaceElements (int vnr) const - { - if (vert2surfelement) - return (*vert2surfelement)[vnr]; - return FlatArray (0,0); - } - - FlatArray MeshTopology :: GetVertexSegments (int vnr) const - { - if (vert2segment) - return (*vert2segment)[vnr]; - return FlatArray (0,0); - } - */ - void MeshTopology :: GetVertexSurfaceElements( int vnr, Array & elements ) const { if (vert2surfelement.Size()) - { - int i; - int ne = vert2surfelement.EntrySize(vnr); - elements.SetSize(ne); - for (i = 1; i <= ne; i++) - elements.Elem(i) = vert2surfelement.Get(vnr, i); - } + elements = vert2surfelement[vnr]; } int MeshTopology :: GetVerticesEdge ( int v1, int v2 ) const { - Array elements_v1; - Array elementedges; - GetVertexElements ( v1, elements_v1); + // NgArray elementedges; + // Array elements_v1; + // GetVertexElements ( v1, elements_v1); + auto elements_v1 = GetVertexElements ( v1 ); int edv1, edv2; for ( int i = 0; i < elements_v1.Size(); i++ ) { - GetElementEdges( elements_v1[i]+1, elementedges ); + // GetElementEdges( elements_v1[i]+1, elementedges ); + auto elementedges = GetEdges(ElementIndex(elements_v1[i])); for ( int ed = 0; ed < elementedges.Size(); ed ++) { - GetEdgeVertices( elementedges[ed], edv1, edv2 ); + GetEdgeVertices( elementedges[ed]+1, edv1, edv2 ); if ( ( edv1 == v1 && edv2 == v2 ) || ( edv1 == v2 && edv2 == v1 ) ) return elementedges[ed]; } @@ -1982,33 +2373,36 @@ namespace netgen void MeshTopology :: - GetSegmentVolumeElements ( int segnr, Array & volels ) const + GetSegmentVolumeElements ( int segnr, NgArray & volels ) const { + /* int v1, v2; - GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 ); - Array volels1, volels2; - GetVertexElements ( v1, volels1 ); - GetVertexElements ( v2, volels2 ); + // GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 ); + GetEdgeVertices ( GetEdge (segnr-1)+1, v1, v2 ); + */ + auto [v1,v2] = GetEdgeVertices ( GetEdge (segnr-1) ); + auto volels1 = GetVertexElements ( v1 ); + auto volels2 = GetVertexElements ( v2 ); volels.SetSize(0); - for ( int eli1=1; eli1 <= volels1.Size(); eli1++) - if ( volels2.Contains( volels1.Elem(eli1) ) ) - volels.Append ( volels1.Elem(eli1) ); + for ( auto volel1 : volels1 ) + if ( volels2.Contains( volel1 ) ) + volels.Append ( volel1 ); } void MeshTopology :: - GetSegmentSurfaceElements (int segnr, Array & els) const + GetSegmentSurfaceElements (int segnr, NgArray & els) const { int v1, v2; - GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 ); - Array els1, els2; - GetVertexSurfaceElements ( v1, els1 ); - GetVertexSurfaceElements ( v2, els2 ); + // GetEdgeVertices ( GetSegmentEdge (segnr), v1, v2 ); + GetEdgeVertices ( GetEdge (segnr-1)+1, v1, v2 ); + auto els1 = GetVertexSurfaceElements ( v1 ); + auto els2 = GetVertexSurfaceElements ( v2 ); els.SetSize(0); - for ( int eli1=1; eli1 <= els1.Size(); eli1++) - if ( els2.Contains( els1.Elem(eli1) ) ) - els.Append ( els1.Elem(eli1) ); + for ( auto el1 : els1 ) + if ( els2.Contains( el1 ) ) + els.Append ( el1 ); } diff --git a/libsrc/meshing/topology.hpp b/libsrc/meshing/topology.hpp index b4e646d2..c3cc3ed2 100644 --- a/libsrc/meshing/topology.hpp +++ b/libsrc/meshing/topology.hpp @@ -15,75 +15,61 @@ namespace netgen { -struct T_EDGE -{ - // int orient:1; - int nr; // 0-based -}; + typedef int T_EDGE; + typedef int T_FACE; -struct T_FACE -{ - // int forient:3; - int fnr; // 0-based -}; - - template - struct FixArray - { - T vals[S]; - T & operator[] (size_t i) { return vals[i]; } - T operator[] (size_t i) const { return vals[i]; } - }; - class MeshTopology { const Mesh * mesh; + bool buildvertex2element; 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 + static bool static_buildedges, static_buildfaces, static_buildvertex2element; - Array edge2vert; - Array face2vert; - /* - Array edges; - Array faces; - Array surfedges; - */ - Array> edges; - Array> faces; - Array> surfedges; + Array> edge2vert; + Array> face2vert; + + NgArray> edges; + NgArray> faces; + NgArray> surfedges; - Array segedges; - Array surffaces; - Array surf2volelement; - Array face2surfel; - TABLE vert2element; - TABLE vert2surfelement; - TABLE vert2segment; - TABLE vert2pointelement; + NgArray segedges; + NgArray surffaces; + NgArray surf2volelement; + NgArray face2surfel; + Array edge2segment; + Table vert2element; + Table vert2surfelement; + Table vert2segment; + Table vert2pointelement; int timestamp; public: int GetNSurfedges() const {return surfedges.Size();} MeshTopology () = default; - MeshTopology (const MeshTopology & top) = default; MeshTopology (MeshTopology && top) = default; MeshTopology (const Mesh & amesh); ~MeshTopology (); - MeshTopology & operator= (const MeshTopology & top) = default; MeshTopology & operator= (MeshTopology && top) = default; - - void SetBuildEdges (bool be) - { buildedges = be; } - void SetBuildFaces (bool bf) - { buildfaces = bf; } - bool HasEdges () const - { return buildedges; } - bool HasFaces () const - { return buildfaces; } + void SetBuildVertex2Element (bool bv2e) { buildvertex2element = bv2e; } + void SetBuildEdges (bool be) { buildedges = be; } + void SetBuildFaces (bool bf) { buildfaces = bf; } + void SetBuildParentEdges (bool bh) { build_parent_edges = bh; } + void SetBuildParentFaces (bool bh) { build_parent_faces = bh; } - void Update(TaskManager tm = &DummyTaskManager, Tracer tracer = &DummyTracer); + void EnableTable (string name, bool set); + static void EnableTableStatic (string name, bool set); + + 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; int GetNEdges () const { return edge2vert.Size(); } @@ -94,55 +80,89 @@ public: static inline short int GetNEdges (ELEMENT_TYPE et); static inline short int GetNFaces (ELEMENT_TYPE et); - static const Point3d * GetVertices (ELEMENT_TYPE et); + DLL_HEADER static const Point3d * GetVertices (ELEMENT_TYPE et); inline static const ELEMENT_EDGE * GetEdges1 (ELEMENT_TYPE et); inline static const ELEMENT_EDGE * GetEdges0 (ELEMENT_TYPE et); + inline static FlatArray GetEdges (ELEMENT_TYPE et); inline static const ELEMENT_FACE * GetFaces1 (ELEMENT_TYPE et); inline static const ELEMENT_FACE * GetFaces0 (ELEMENT_TYPE et); - - int GetSegmentEdge (int segnr) const { return segedges[segnr-1].nr+1; } - int GetEdge (SegmentIndex segnr) const { return segedges[segnr].nr; } - void GetSegmentEdge (int segnr, int & enr, int & orient) const + [[deprecated("use GetEdge(SegmentIndex) instead")]] + int GetSegmentEdge (int segnr) const { return segedges[segnr-1]+1; } + + int GetEdge (SegmentIndex segnr) const { return segedges[segnr]; } + + [[deprecated("use GetEdge(SegmentIndex) instead")]] + void GetSegmentEdge (int segnr, int & enr, int & orient) const; + /* { - enr = segedges.Get(segnr).nr+1; + enr = segedges.Get(segnr)+1; // orient = segedges.Get(segnr).orient; orient = GetSegmentEdgeOrientation(segnr); - } + } + */ - void GetElementEdges (int elnr, Array & edges) const; - void GetElementFaces (int elnr, Array & faces, bool withorientation = false) const; - void GetElementEdgeOrientations (int elnr, Array & eorient) const; - void GetElementFaceOrientations (int elnr, Array & forient) const; + [[deprecated("use GetEdges (ElementIndex) -> FlatArray")]] + void GetElementEdges (int elnr, NgArray & edges) const; + [[deprecated("use GetFaces (ElementIndex) -> FlatArray")]] + void GetElementFaces (int elnr, NgArray & faces, bool withorientation = false) const; + // definition in meshclass.hpp + inline FlatArray GetEdges (ElementIndex elnr) const; + inline FlatArray GetFaces (ElementIndex elnr) const; + + + // [[deprecated("use GetElementEdge instead")]] + void GetElementEdgeOrientations (int elnr, NgArray & eorient) const; + // [[deprecated("use GetElementEdge instead")]] + void GetElementFaceOrientations (int elnr, NgArray & forient) const; + + [[deprecated("use GetEdges (ElementIndex) -> FlatArray")]] int GetElementEdges (int elnr, int * edges, int * orient) const; + + // [[deprecated("use GetFaces (ElementIndex) -> FlatArray")]] int GetElementFaces (int elnr, int * faces, int * orient) const; + // [[deprecated("use GetElementEdge instead")]] int GetElementEdgeOrientation (int elnr, int locedgenr) const; // old style + // [[deprecated("use GetElementEdge instead")]] int GetElementFaceOrientation (int elnr, int locfacenr) const; // old style + // [[deprecated("use GetElementEdge instead")]] int GetSurfaceElementEdgeOrientation (int elnr, int locedgenr) const; // old style + // [[deprecated("use GetElementEdge instead")]] int GetSurfaceElementFaceOrientation2 (int elnr) const; // old style + // [[deprecated("use GetElementEdge instead")]] int GetSegmentEdgeOrientation (int elnr) const; // old style - void GetFaceVertices (int fnr, Array & vertices) const; - void GetFaceVertices (int fnr, int * vertices) const; - void GetEdgeVertices (int enr, int & v1, int & v2) const; - void GetEdgeVertices (int enr, PointIndex & v1, PointIndex & v2) const; - const int * GetEdgeVerticesPtr (int enr) const { return &edge2vert[enr][0]; } - const int * GetFaceVerticesPtr (int fnr) const { return &face2vert[fnr][0]; } - void GetFaceEdges (int fnr, Array & edges, bool withorientation = false) const; + DLL_HEADER void GetFaceVertices (int fnr, NgArray & vertices) const; + DLL_HEADER void GetFaceVertices (int fnr, int * vertices) const; + DLL_HEADER void GetEdgeVertices (int enr, int & v1, int & v2) const; + DLL_HEADER void GetEdgeVertices (int enr, PointIndex & v1, PointIndex & v2) const; + auto GetEdgeVertices (int enr) const { return tuple(edge2vert[enr][0], edge2vert[enr][1]); } + auto GetEdgeVerticesPtr (int enr) const { return &edge2vert[enr][0]; } + auto GetFaceVerticesPtr (int fnr) const { return &face2vert[fnr][0]; } + DLL_HEADER void GetFaceEdges (int fnr, NgArray & edges, bool withorientation = false) const; ELEMENT_TYPE GetFaceType (int fnr) const - { return (face2vert.Get(fnr)[3] == 0) ? TRIG : QUAD; } + // { return (face2vert.Get(fnr)[3] == 0) ? TRIG : QUAD; } + { return (face2vert[fnr-1][3] == 0) ? TRIG : QUAD; } - void GetSurfaceElementEdges (int elnr, Array & edges) const; + void GetSurfaceElementEdges (int elnr, NgArray & edges) const; int GetSurfaceElementFace (int elnr) const; - void GetSurfaceElementEdgeOrientations (int elnr, Array & eorient) const; + [[deprecated("orientation is outdated")]] + void GetSurfaceElementEdgeOrientations (int elnr, NgArray & eorient) const; + // [[deprecated("orientation is outdated")]] int GetSurfaceElementFaceOrientation (int elnr) const; - void GetEdges (SurfaceElementIndex elnr, Array & edges) const; + + [[deprecated("use GetEdge -> FlatArray instead")]] + void GetEdges (SurfaceElementIndex elnr, NgArray & edges) const; + + inline FlatArray GetEdges (SurfaceElementIndex elnr) const; + // { return FlatArray(GetNEdges ( (*mesh)[elnr].GetType()), &surfedges[elnr][0]); } + int GetFace (SurfaceElementIndex elnr) const - { return surffaces[elnr].fnr; } + { return surffaces[elnr]; } int GetSurfaceElementEdges (int elnr, int * edges, int * orient) const; @@ -161,24 +181,44 @@ public: } int GetFace2SurfaceElement (int fnr) const { return face2surfel[fnr-1]; } - + + SegmentIndex GetSegmentOfEdge(int edgenr) const { return edge2segment[edgenr-1]; } + + [[deprecated("use GetVertexElements -> FlatArray instead")]] void GetVertexElements (int vnr, Array & elements) const; - FlatArray GetVertexElements (int vnr) const + + FlatArray GetVertexElements (PointIndex vnr) const { return vert2element[vnr]; } + [[deprecated("use GetVertexSurfaceElements -> FlatArray instead")]] void GetVertexSurfaceElements( int vnr, Array& elements ) const; - FlatArray GetVertexSurfaceElements (int vnr) const + const auto & GetVertexSurfaceElements( ) const { return vert2surfelement; } + + FlatArray GetVertexSurfaceElements(PointIndex vnr) const { return vert2surfelement[vnr]; } - FlatArray GetVertexSegments (int vnr) const + FlatArray GetVertexSegments (PointIndex vnr) const { return vert2segment[vnr]; } - FlatArray GetVertexPointElements (int vnr) const + FlatArray GetVertexPointElements (PointIndex vnr) const { return vert2pointelement[vnr]; } int GetVerticesEdge ( int v1, int v2) const; - void GetSegmentVolumeElements ( int segnr, Array & els ) const; - void GetSegmentSurfaceElements ( int segnr, Array & els ) const; + void GetSegmentVolumeElements ( int segnr, NgArray & els ) const; + void GetSegmentSurfaceElements ( int segnr, NgArray & els ) const; + + // Call this before Update() to discard old edges + void ClearEdges() { edge2vert.SetSize0(); } + +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]; } }; @@ -573,6 +613,104 @@ const ELEMENT_EDGE * MeshTopology :: GetEdges0 (ELEMENT_TYPE et) } +FlatArray MeshTopology :: GetEdges (ELEMENT_TYPE et) +{ + static ELEMENT_EDGE segm_edges[1] = + { { 0, 1 }}; + + static ELEMENT_EDGE trig_edges[3] = + { { 2, 0 }, + { 1, 2 }, + { 0, 1 }}; + + static ELEMENT_EDGE quad_edges[4] = + { { 0, 1 }, + { 2, 3 }, + { 3, 0 }, + { 1, 2 }}; + + + static ELEMENT_EDGE tet_edges[6] = + { { 3, 0 }, + { 3, 1 }, + { 3, 2 }, + { 0, 1 }, + { 0, 2 }, + { 1, 2 }}; + + static ELEMENT_EDGE prism_edges[9] = + { { 2, 0 }, + { 0, 1 }, + { 2, 1 }, + { 5, 3 }, + { 3, 4 }, + { 5, 4 }, + { 2, 5 }, + { 0, 3 }, + { 1, 4 }}; + + static ELEMENT_EDGE pyramid_edges[8] = + { { 0, 1 }, + { 1, 2 }, + { 0, 3 }, + { 3, 2 }, + { 0, 4 }, + { 1, 4 }, + { 2, 4 }, + { 3, 4 }}; + + static ELEMENT_EDGE hex_edges[12] = + { + { 0, 1 }, + { 2, 3 }, + { 3, 0 }, + { 1, 2 }, + { 4, 5 }, + { 6, 7 }, + { 7, 4 }, + { 5, 6 }, + { 0, 4 }, + { 1, 5 }, + { 2, 6 }, + { 3, 7 }, + }; + + switch (et) + { + case SEGMENT: + case SEGMENT3: + return { 1, segm_edges }; + + case TRIG: + case TRIG6: + return { 3, trig_edges }; + + case QUAD: + case QUAD6: + case QUAD8: + return { 4, quad_edges }; + + case TET: + case TET10: + return { 6, tet_edges }; + + case PYRAMID: + case PYRAMID13: + return { 8, pyramid_edges }; + + case PRISM: + case PRISM12: + case PRISM15: + return { 9, prism_edges }; + + case HEX: + case HEX20: + return { 12, hex_edges }; + // default: + // cerr << "Ng_ME_GetEdges, illegal element type " << et << endl; + } + return { 0, nullptr }; +} diff --git a/libsrc/meshing/triarls.cpp b/libsrc/meshing/triarls.cpp deleted file mode 100644 index e87433e7..00000000 --- a/libsrc/meshing/triarls.cpp +++ /dev/null @@ -1,468 +0,0 @@ -namespace netgen -{ -const char * triarules[] = { -"rule \"Free Triangle (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 1.0, 0, 1.0 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 0.5 X2 } { };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.7) { 0.5 X2 } { };\n",\ -"(0.5, 1.5) { 0.5 X2 } { };\n",\ -"(-0.5, 0.7) { 0.5 X2 } { };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.866) { 0.5 X2 } { };\n",\ -"(0.5, 0.866) { 0.5 X2 } { };\n",\ -"(0.5, 0.866) { 0.5 X2 } { };\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"Free Triangle (5)\"\n",\ -"\n",\ -"quality 5\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 1.0, 0, 1.0 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.5) { 0.5 X2 } { };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.7) { 1 X2 } { };\n",\ -"(0, 0.7) { } { };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.5) { 0.5 X2 } { };\n",\ -"(0.5, 0.5) { 0.5 X2 } { };\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Free Triangle (10)\"\n",\ -"\n",\ -"quality 10\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 1.0, 0, 1.0 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.3) { 0.5 X2 } { };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.5) { 1 X2 } { };\n",\ -"(0, 0.5) { } { };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.3) { 0.5 X2 } { };\n",\ -"(0.5, 0.3) { 0.5 X2 } { };\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Free Triangle (20)\"\n",\ -"\n",\ -"quality 20\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 1.0, 0, 1.0 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.1) { 0.5 X2 } { };\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1, 0.2) { 1 X2 } { };\n",\ -"(0, 0.2) { } { };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.1) { 0.5 X2 } { };\n",\ -"(0.5, 0.1) { 0.5 X2 } { };\n",\ -"\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Right 60 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0) { 0.5, 0, 1.0 };\n",\ -"(0.5, 0.866) { 0.6, 0, 0.8 };\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(-0.125, 0.6495) { -0.5 X2, 0.75 X3 } { -0.5 Y2, 0.75 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left 60 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.125, 0.6495) { 0.75 X2, 0.75 X3 } { 0.75 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Right 120 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 1 X3, -1 X2 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 4);\n",\ -"(4, 3);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(1, 1.732) { -2 X2, 2 X3 } { 2 Y3 };\n",\ -"(0, 1.732) { -3 X2, 2 X3 } { 2 Y3 };\n",\ -"(-0.5, 0.866) { -2 X2, 1 X3 } {1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4);\n",\ -"(2, 3, 4);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left 120 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(-0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 1 X3, 1 X2 } { 1 Y3 };\n",\ -"\n",\ -"newlines\n",\ -"(3, 4);\n",\ -"(4, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.866) { 2 X2, 1 X3 } { 1 Y3 };\n",\ -"(1, 1.732) { 2 X2, 2 X3 } { 2 Y3 };\n",\ -"(0, 1.732) { 1 X2, 2 X3 } { 2 Y3 };\n",\ -"(-0.5, 0.866) { 1 X3 } {1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 4);\n",\ -"(1, 4, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Left Right 120 (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(-0.5, 0.866);\n",\ -"(1.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 1) del;\n",\ -"(2, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 0.5 X3, 0.5 X4 } { 0.5 Y3, 0.5 Y4 };\n",\ -"\n",\ -"newlines\n",\ -"(3, 5);\n",\ -"(5, 4);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.5, 0.866) { 1 X4 } { 1 Y4 };\n",\ -"(1, 1.299) { -0.5 X2, 0.375 X3, 1.125 X4 } { -0.5 Y2, 0.375 Y3, 1.125 Y4 };\n",\ -"(0, 1.299) { 1.125 X3, 0.375 X4 } { 1.125 Y3, 0.375 Y4 };\n",\ -"(-0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 5);\n",\ -"(3, 1, 5);\n",\ -"(2, 4, 5);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"rule \"Fill Triangle\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(2, 3) del;\n",\ -"(3, 1) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { 1 Y2 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"Vis A Vis (1)\"\n",\ -"\n",\ -"quality 1\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(0.5, 0.866);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"\n",\ -"newpoints\n",\ -"\n",\ -"newlines\n",\ -"(1, 3);\n",\ -"(3, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(1.2, 0.693) { 0.8 X2, 0.8 X3 } { 0.8 Y2, 0.8 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(-0.2, 0.693) { -0.6 X2, 0.8 X3 } { -0.6 Y2, 0.8 Y3 };\n",\ -"\n",\ -"freearea2\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { };\n",\ -"(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 };\n",\ -"(0.5, 0.866) { 1 X3 } { 1 Y3 };\n",\ -"(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 3);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"rule \"2 h Vis A Vis (1)\"\n",\ -"\n",\ -"quality 3\n",\ -"\n",\ -"mappoints\n",\ -"(0, 0);\n",\ -"(1, 0);\n",\ -"(1, 1.732);\n",\ -"(0, 1.732);\n",\ -"\n",\ -"maplines\n",\ -"(1, 2) del;\n",\ -"(3, 4) del;\n",\ -"\n",\ -"newpoints\n",\ -"(0.5, 0.866) { 0.25 X2, 0.25 X3, 0.25 X4 } { 0.25 Y2, 0.25 Y3, 0.25 Y4 };\n",\ -"\n",\ -"newlines\n",\ -"(1, 5);\n",\ -"(5, 4);\n",\ -"(3, 5);\n",\ -"(5, 2);\n",\ -"\n",\ -"freearea\n",\ -"(0, 0);\n",\ -"(1, 0) { 1 X2 } { 1 Y2 };\n",\ -"(1.5, 0.866) { 0.75 X2, 0.75 X3, -0.25 X4 } { 0.75 Y2, 0.75 Y3, -0.25 Y4 };\n",\ -"(1, 1.732) { 1 X3 } { 1 Y3 };\n",\ -"(0, 1.732) { 1 X4 } { 1 Y4 };\n",\ -"(-0.5, 0.866) { 0.75 X4, -0.25 X2, -0.25 X3 } { 0.75 Y4, -0.25 Y3 };\n",\ -"\n",\ -"elements\n",\ -"(1, 2, 5);\n",\ -"(3, 4, 5);\n",\ -"\n",\ -"endrule\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -"\n",\ -0}; -} diff --git a/libsrc/meshing/validate.cpp b/libsrc/meshing/validate.cpp index 44b983cc..f5fbff75 100644 --- a/libsrc/meshing/validate.cpp +++ b/libsrc/meshing/validate.cpp @@ -5,8 +5,8 @@ namespace netgen { - void GetPureBadness(Mesh & mesh, Array & pure_badness, - const BitArray & isnewpoint) + void GetPureBadness(Mesh & mesh, NgArray & pure_badness, + const NgBitArray & isnewpoint) { //const int ne = mesh.GetNE(); const int np = mesh.GetNP(); @@ -14,7 +14,7 @@ namespace netgen pure_badness.SetSize(np+PointIndex::BASE+1); pure_badness = -1; - Array< Point<3>* > backup(np); + NgArray< Point<3>* > backup(np); for(int i=0; i & bad_elements, - const Array & pure_badness, + double Validate(const Mesh & mesh, NgArray & bad_elements, + const NgArray & pure_badness, double max_worsening, const bool uselocalworsening, - Array * quality_loss) + NgArray * quality_loss) { PrintMessage(3,"!!!! Validating !!!!"); //if(max_worsening > 0) @@ -104,8 +104,8 @@ namespace netgen } - void GetWorkingArea(BitArray & working_elements, BitArray & working_points, - const Mesh & mesh, const Array & bad_elements, + void GetWorkingArea(NgBitArray & working_elements, NgBitArray & working_points, + const Mesh & mesh, const NgArray & bad_elements, const int width) { working_elements.Clear(); @@ -151,11 +151,11 @@ namespace netgen - void RepairBisection(Mesh & mesh, Array & bad_elements, - const BitArray & isnewpoint, const Refinement & refinement, - const Array & pure_badness, + void RepairBisection(Mesh & mesh, NgArray & bad_elements, + const NgBitArray & isnewpoint, const Refinement & refinement, + const NgArray & pure_badness, double max_worsening, const bool uselocalworsening, - const Array< Array* > & idmaps) + const NgArray< NgArray* > & idmaps) { ostringstream ostrstr; @@ -175,9 +175,9 @@ namespace netgen PushStatus("Repair Bisection"); - Array* > should(np); - Array* > can(np); - Array* > nv(np); + NgArray* > should(np); + NgArray* > can(np); + NgArray* > nv(np); for(int i=0; i; @@ -185,7 +185,7 @@ namespace netgen can[i] = new Point<3>; } - BitArray isboundarypoint(np),isedgepoint(np); + NgBitArray isboundarypoint(np),isedgepoint(np); isboundarypoint.Clear(); isedgepoint.Clear(); @@ -196,7 +196,7 @@ namespace netgen isedgepoint.Set(seg[1]); } - Array surfaceindex(np); + NgArray surfaceindex(np); surfaceindex = -1; for (int i = 1; i <= mesh.GetNSE(); i++) @@ -216,8 +216,8 @@ namespace netgen Validate(mesh,bad_elements,pure_badness, ((uselocalworsening) ? (0.8*(max_worsening-1.) + 1.) : (0.1*(max_worsening-1.) + 1.)), uselocalworsening); // -> larger working area - BitArray working_elements(ne); - BitArray working_points(np); + NgBitArray working_elements(ne); + NgBitArray working_points(np); GetWorkingArea(working_elements,working_points,mesh,bad_elements,numbadneighbours); //working_elements.Set(); @@ -240,7 +240,7 @@ namespace netgen PrintMessage(5,ostrstr.str()); - BitArray isworkingboundary(np); + NgBitArray isworkingboundary(np); for(int i=1; i<=np; i++) if(working_points.Test(i) && isboundarypoint.Test(i)) isworkingboundary.Set(i); @@ -276,8 +276,8 @@ namespace netgen double oldlamedge,oldlamface; - MeshOptimize2d * optimizer2d = refinement.Get2dOptimizer(); - if(!optimizer2d) + auto geo = mesh.GetGeometry(); + if(!geo) { cerr << "No 2D Optimizer!" << endl; return; @@ -382,8 +382,15 @@ namespace netgen for (int i = 1; i <= np; i++) *can.Elem(i) = mesh.Point(i); - if(optimizer2d) - optimizer2d->ProjectBoundaryPoints(surfaceindex,can,should); + if(geo) + for(int i=0; i= 0) + { + *should[i] = *can[i]; + geo->ProjectPoint(surfaceindex[i],*should[i]); + } + } } diff --git a/libsrc/meshing/validate.hpp b/libsrc/meshing/validate.hpp index 1cc01bec..5e646d5d 100644 --- a/libsrc/meshing/validate.hpp +++ b/libsrc/meshing/validate.hpp @@ -4,17 +4,17 @@ namespace netgen { - void GetPureBadness(Mesh & mesh, Array & pure_badness, - const BitArray & isnewpoint); - double Validate(const Mesh & mesh, Array & bad_elements, - const Array & pure_badness, + void GetPureBadness(Mesh & mesh, NgArray & pure_badness, + const NgBitArray & isnewpoint); + double Validate(const Mesh & mesh, NgArray & bad_elements, + const NgArray & pure_badness, double max_worsening, const bool uselocalworsening, - Array * quality_loss = NULL); - void RepairBisection(Mesh & mesh, Array & bad_elements, - const BitArray & isnewpoint, const Refinement & refinement, - const Array & pure_badness, + NgArray * quality_loss = NULL); + void RepairBisection(Mesh & mesh, NgArray & bad_elements, + const NgBitArray & isnewpoint, const Refinement & refinement, + const NgArray & pure_badness, double max_worsening, const bool uselocalworsening, - const Array< Array* > & idmaps); + const NgArray< NgArray* > & idmaps); } diff --git a/libsrc/meshing/visual_interface.cpp b/libsrc/meshing/visual_interface.cpp new file mode 100644 index 00000000..6a2ca929 --- /dev/null +++ b/libsrc/meshing/visual_interface.cpp @@ -0,0 +1,26 @@ +#include "visual_interface.hpp" +#include "../include/nginterface.h" + +void (*Ptr_Ng_ClearSolutionData) () = nullptr; +void (*Ptr_Ng_InitSolutionData) (Ng_SolutionData*) = nullptr; +void (*Ptr_Ng_SetSolutionData) (Ng_SolutionData*) = nullptr; +void (*Ptr_Ng_Redraw) (bool blocking) = nullptr; + +void Ng_ClearSolutionData () { if(Ptr_Ng_ClearSolutionData) Ptr_Ng_ClearSolutionData(); } +void Ng_InitSolutionData (Ng_SolutionData * soldata) { if(Ptr_Ng_InitSolutionData) Ptr_Ng_InitSolutionData(soldata); } +void Ng_SetSolutionData (Ng_SolutionData * soldata) { if(Ptr_Ng_SetSolutionData) Ptr_Ng_SetSolutionData(soldata); } +void Ng_Redraw (bool blocking) { if(Ptr_Ng_Redraw) Ptr_Ng_Redraw(blocking); } + +namespace netgen +{ + void (*Ptr_Ng_Tcl_SetResult)(Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc) = nullptr; + void (*Ptr_Ng_Tcl_CreateCommand)(Tcl_Interp *interp, + const char *cmdName, Tcl_CmdProc *proc) = nullptr; + void (*Ptr_Render)(bool) = nullptr; + void (*Ptr_UpdateVisSurfaceMeshData)(int, + shared_ptr>>, + shared_ptr>, + shared_ptr>> + ) = nullptr; +} // namespace netgen + diff --git a/libsrc/meshing/visual_interface.hpp b/libsrc/meshing/visual_interface.hpp new file mode 100644 index 00000000..5f856a24 --- /dev/null +++ b/libsrc/meshing/visual_interface.hpp @@ -0,0 +1,71 @@ +#ifndef VISUAL_INTERFACE_HPP_INCLUDED +#define VISUAL_INTERFACE_HPP_INCLUDED + +#include +#include +#include + +class Ng_SolutionData; + +// Function pointers for visualization purposed, all set to nullptr by default and initialized correctly when the GUI library is loaded + +DLL_HEADER extern void (*Ptr_Ng_ClearSolutionData) (); +DLL_HEADER extern void (*Ptr_Ng_InitSolutionData) (Ng_SolutionData * soldata); +DLL_HEADER extern void (*Ptr_Ng_SetSolutionData) (Ng_SolutionData * soldata); +DLL_HEADER extern void (*Ptr_Ng_Redraw) (bool blocking); + +// Tcl wrapper functions +class Tcl_Interp; +typedef int (Tcl_CmdProc) (void * clientData, Tcl_Interp *interp, + int argc, const char *argv[]); +typedef void (Tcl_FreeProc) (char *blockPtr); + +namespace netgen { + /* + inline constexpr int NG_TCL_VOLATILE = 1; + inline constexpr int NG_TCL_STATIC = 0; + inline constexpr int NG_TCL_DYNAMIC = 3; + */ + +#define NG_TCL_VOLATILE ((Tcl_FreeProc *) 1) +#define NG_TCL_STATIC ((Tcl_FreeProc *) 0) +#define NG_TCL_DYNAMIC ((Tcl_FreeProc *) 3) + + inline constexpr int NG_TCL_OK = 0; + inline constexpr int NG_TCL_ERROR = 1; + inline constexpr int NG_TCL_RETURN = 2; + inline constexpr int NG_TCL_BREAK = 3; + inline constexpr int NG_TCL_CONTINUE = 4; + DLL_HEADER extern void (*Ptr_Ng_Tcl_SetResult)(Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc); + DLL_HEADER extern void (*Ptr_Ng_Tcl_CreateCommand)(Tcl_Interp *interp, + const char *cmdName, Tcl_CmdProc *proc); + + DLL_HEADER extern void (*Ptr_Render)(bool); + DLL_HEADER extern void (*Ptr_UpdateVisSurfaceMeshData)(int, + shared_ptr>>, + shared_ptr>, + shared_ptr>> + ); + + inline void Render(bool blocking = false) { if(Ptr_Render) Ptr_Render(blocking); } + inline void UpdateVisSurfaceMeshData(int oldnl, + shared_ptr>> locpointsptr = nullptr, + shared_ptr> loclinesptr = nullptr, + shared_ptr>> plainpointsptr = nullptr + ) { + if(Ptr_UpdateVisSurfaceMeshData) Ptr_UpdateVisSurfaceMeshData(oldnl, locpointsptr, loclinesptr, plainpointsptr); + } + + inline void Ng_Tcl_SetResult(Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc) + { + if(Ptr_Ng_Tcl_SetResult) + Ptr_Ng_Tcl_SetResult(interp, result, freeProc); + } + inline void Ng_Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc) + { + if(Ptr_Ng_Tcl_CreateCommand) + Ptr_Ng_Tcl_CreateCommand(interp, cmdName, proc); + } +} + +#endif // VISUAL_INTERFACE_HPP_INCLUDED diff --git a/libsrc/occ/CMakeLists.txt b/libsrc/occ/CMakeLists.txt index 1f093ad8..aeec0d85 100644 --- a/libsrc/occ/CMakeLists.txt +++ b/libsrc/occ/CMakeLists.txt @@ -1,25 +1,17 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -add_library(occ ${NG_LIB_TYPE} +target_sources(nglib PRIVATE 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 + python_occ_basic.cpp python_occ_shapes.cpp + occ_face.cpp occ_edge.cpp occ_vertex.cpp occ_utils.cpp +) + if(USE_GUI) - add_library(occvis ${NG_LIB_TYPE} vsocc.cpp) + target_sources(nggui PRIVATE vsocc.cpp occpkg.cpp) endif(USE_GUI) -target_link_libraries(occ PUBLIC ngcore) - -if(NOT WIN32) - target_link_libraries( occ PUBLIC ${OCC_LIBRARIES} ${PYTHON_LIBRARIES}) - install( TARGETS occ ${NG_INSTALL_DIR}) - if (USE_GUI) - target_link_libraries( occvis PUBLIC occ ) - install( TARGETS occvis ${NG_INSTALL_DIR}) - endif(USE_GUI) -endif(NOT WIN32) - install(FILES - occgeom.hpp occmeshsurf.hpp vsocc.hpp + occgeom.hpp occmeshsurf.hpp vsocc.hpp occ_utils.hpp + occ_vertex.hpp occ_edge.hpp occ_face.hpp occ_solid.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/occ COMPONENT netgen_devel ) diff --git a/libsrc/occ/Partition_Inter2d.cxx b/libsrc/occ/Partition_Inter2d.cxx index 70095527..7fed573b 100644 --- a/libsrc/occ/Partition_Inter2d.cxx +++ b/libsrc/occ/Partition_Inter2d.cxx @@ -157,7 +157,7 @@ static Standard_Boolean findVOnE(const TopoDS_Vertex & theV, // Check that vertex equal to V already exists on one // of edges, in such a case, V is not added but // existing vertex is updated to be on E1 and E2 and -// is returned insead of V. +// is returned instead of V. //======================================================================= TopoDS_Vertex Partition_Inter2d::AddVonE(const TopoDS_Vertex& theV, @@ -170,7 +170,7 @@ TopoDS_Vertex Partition_Inter2d::AddVonE(const TopoDS_Vertex& theV, //------------------------------------------------------------- // test if the points of intersection already exist. If not, // add as descendants of the edges. - // nb: theses points are only vertices of intersection. + // nb: these points are only vertices of intersection. //------------------------------------------------------------- const TopTools_ListOfShape& VOnE1 = AsDes->Descendant(E1); const TopTools_ListOfShape& VOnE2 = AsDes->Descendant(E2); @@ -247,7 +247,7 @@ TopoDS_Vertex Partition_Inter2d::AddVonE(const TopoDS_Vertex& theV, // if 3 faces intersects each others, 3 new edges on them must pass // through one vertex but real intersection points of each // pair of edges are sometimes more far than a tolerance. - // Try to analitically find vertices that E1 and E2 must pass trough + // Try to analytically find vertices that E1 and E2 must pass through TopoDS_Shape F1 = getOtherShape( theF, AsDes->Ascendant( E1 )); TopoDS_Shape F2 = getOtherShape( theF, AsDes->Ascendant( E2 )); @@ -405,7 +405,7 @@ static void EdgesPartition(const TopoDS_Face& F, // if E1 and E2 are results of intersection of F and two connex faces then // no need to intersect edges, they can contact by vertices only - // (encounted an exception in TopOpeBRep_EdgesIntersector in such a case) + // (encountered an exception in TopOpeBRep_EdgesIntersector in such a case) Standard_Boolean intersect = Standard_True; TopTools_IndexedMapOfShape ME; TopExp::MapShapes(F, TopAbs_EDGE, ME); diff --git a/libsrc/occ/Partition_Inter3d.cxx b/libsrc/occ/Partition_Inter3d.cxx index 38787fbe..3fc97b43 100644 --- a/libsrc/occ/Partition_Inter3d.cxx +++ b/libsrc/occ/Partition_Inter3d.cxx @@ -206,7 +206,7 @@ static void PutInBounds (const TopoDS_Face& F, Handle (Geom_Surface) S = BRep_Tool::Surface(F,L); if (S->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface))) { - S = (*(Handle_Geom_RectangularTrimmedSurface*)&S)->BasisSurface(); + S = Handle(Geom_RectangularTrimmedSurface)::DownCast(S)->BasisSurface(); } if (!S->IsUPeriodic() && !S->IsVPeriodic()) return; @@ -218,7 +218,7 @@ static void PutInBounds (const TopoDS_Face& F, const Standard_Real Um = 0.34*f + 0.66*l; gp_Pnt2d Pm = C2d->Value( Um ); - // sometimes on shpere, pcurve is out of domain by V though S is + // sometimes on sphere, pcurve is out of domain by V though S is // UPeriodic, sometimes it is in domain but nonetheless it has // wrong position. // Check pcurve position by 3D point @@ -499,7 +499,7 @@ void Partition_Inter3d::Inter3D(const TopoDS_Face& F1, const TopoDS_Face& F = (ancRank == 1) ? F2 : F1; - // add se to face but dont add twice + // add se to face but don't add twice TopTools_ListIteratorOfListOfShape itE( myAsDes->Descendant( F )); if (myAsDes->HasDescendant( F )) { for ( ; itE.More(); itE.Next()) @@ -702,7 +702,7 @@ TopTools_MapOfShape& Partition_Inter3d::TouchedFaces() //purpose : //======================================================================= -Handle_BRepAlgo_AsDes Partition_Inter3d::AsDes() const +Handle(BRepAlgo_AsDes) Partition_Inter3d::AsDes() const { return myAsDes; } @@ -829,7 +829,7 @@ TopoDS_Vertex Partition_Inter3d::ReplaceSameDomainV(const TopoDS_Vertex& V, //purpose : //======================================================================= -Handle_BRepAlgo_AsDes Partition_Inter3d::SectionEdgesAD() const +Handle(BRepAlgo_AsDes) Partition_Inter3d::SectionEdgesAD() const { return mySectionEdgesAD; } diff --git a/libsrc/occ/Partition_Inter3d.hxx b/libsrc/occ/Partition_Inter3d.hxx index 93af0773..f2133c24 100644 --- a/libsrc/occ/Partition_Inter3d.hxx +++ b/libsrc/occ/Partition_Inter3d.hxx @@ -96,13 +96,13 @@ public: void FacesPartition(const TopoDS_Face& F1,const TopoDS_Face& F2) ; Standard_Boolean IsDone(const TopoDS_Face& F1,const TopoDS_Face& F2) const; TopTools_MapOfShape& TouchedFaces() ; - Handle_BRepAlgo_AsDes AsDes() const; + Handle(BRepAlgo_AsDes) AsDes() const; TopTools_MapOfShape& NewEdges() ; Standard_Boolean HasSameDomainF(const TopoDS_Shape& F) const; Standard_Boolean IsSameDomainF(const TopoDS_Shape& F1,const TopoDS_Shape& F2) const; const TopTools_ListOfShape& SameDomain(const TopoDS_Face& F) const; TopoDS_Vertex ReplaceSameDomainV(const TopoDS_Vertex& V,const TopoDS_Edge& E) const; - Handle_BRepAlgo_AsDes SectionEdgesAD() const; + Handle(BRepAlgo_AsDes) SectionEdgesAD() const; Standard_Boolean IsSectionEdge(const TopoDS_Edge& E) const; Standard_Boolean HasSectionEdge(const TopoDS_Face& F) const; Standard_Boolean IsSplitOn(const TopoDS_Edge& NewE,const TopoDS_Edge& OldE,const TopoDS_Face& F) const; @@ -134,11 +134,11 @@ private: // Fields PRIVATE // - Handle_BRepAlgo_AsDes myAsDes; + Handle(BRepAlgo_AsDes) myAsDes; TopTools_DataMapOfShapeListOfShape myDone; TopTools_MapOfShape myTouched; TopTools_MapOfShape myNewEdges; - Handle_BRepAlgo_AsDes mySectionEdgesAD; + Handle(BRepAlgo_AsDes) mySectionEdgesAD; TopTools_DataMapOfShapeListOfShape mySameDomainFM; TopTools_DataMapOfShapeShape mySameDomainVM; 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/Partition_Spliter.cxx b/libsrc/occ/Partition_Spliter.cxx index eb52af9d..1263909e 100644 --- a/libsrc/occ/Partition_Spliter.cxx +++ b/libsrc/occ/Partition_Spliter.cxx @@ -1065,7 +1065,7 @@ void Partition_Spliter::MakeShells(const TopoDS_Shape& S, NS = ShellMaker.MakeShells( myAddedFacesMap ); // Add faces added to new shell to myAddedFacesMap: - // avoid rebuilding twice commont part of 2 solids. + // avoid rebuilding twice common part of 2 solids. TopTools_ListIteratorOfListOfShape itS(NS); while ( itS.More()) { TopExp_Explorer expF (itS.Value(), TopAbs_FACE); @@ -1080,7 +1080,7 @@ void Partition_Spliter::MakeShells(const TopoDS_Shape& S, //function : findEqual //purpose : compare edges of EL1 against edges of EL2, // Result is in EMM binding EL1 edges to list of equal edges. -// Edges are considered equall only if they have same vertices. +// Edges are considered equal only if they have same vertices. // ==True makes consider same edges as equal // Put in all equal edges //======================================================================= @@ -1486,7 +1486,7 @@ void Partition_Spliter::MakeEdges (const TopoDS_Edge& E, //---------------------------------------------------------------- - // Construction of the new edges . + // Construction of the new edges. //---------------------------------------------------------------- if (VF.IsSame(VL)) { // closed edge @@ -1501,7 +1501,7 @@ void Partition_Spliter::MakeEdges (const TopoDS_Edge& E, else if (endV.IsSame(SV.Last())) SV.Prepend(endV); else - MESSAGE ("END VERTEX IS IN SEQUNCE MIDDLE"); + MESSAGE ("END VERTEX IS IN SEQUENCE MIDDLE"); } NbVer = SV.Length(); } @@ -1664,7 +1664,7 @@ void Partition_Spliter::MergeEqualEdges (const TopTools_ListOfShape& LSE) //======================================================================= //function : KeepShapesInside -//purpose : remove shapes that are outside of S from resul +//purpose : remove shapes that are outside of S from result //======================================================================= void Partition_Spliter::KeepShapesInside (const TopoDS_Shape& S) @@ -1735,7 +1735,7 @@ void Partition_Spliter::KeepShapesInside (const TopoDS_Shape& S) //======================================================================= //function : RemoveShapesInside -//purpose : remove shapes that are inside S from resul +//purpose : remove shapes that are inside S from result //======================================================================= void Partition_Spliter::RemoveShapesInside (const TopoDS_Shape& S) @@ -1815,7 +1815,7 @@ void Partition_Spliter::RemoveShapesInside (const TopoDS_Shape& S) TopoDS_Shell Shell; myBuilder.MakeShell( Shell ); - // exclude redundant internal face with edges encounterd only once + // exclude redundant internal face with edges encountered only once TopTools_IndexedDataMapOfShapeListOfShape MEF; TopTools_MapIteratorOfMapOfShape itF (RFM); for ( ; itF.More(); itF.Next()) diff --git a/libsrc/occ/Partition_Spliter.hxx b/libsrc/occ/Partition_Spliter.hxx index e75e9893..1cb6c571 100644 --- a/libsrc/occ/Partition_Spliter.hxx +++ b/libsrc/occ/Partition_Spliter.hxx @@ -143,7 +143,7 @@ private: TopTools_DataMapOfShapeShape myFaceShapeMap; TopTools_DataMapOfShapeShape myInternalFaces; TopTools_DataMapOfShapeShape myIntNotClFaces; - Handle_BRepAlgo_AsDes myAsDes; + Handle(BRepAlgo_AsDes) myAsDes; BRepAlgo_Image myImagesFaces; BRepAlgo_Image myImagesEdges; BRepAlgo_Image myImageShape; diff --git a/libsrc/occ/occ_edge.cpp b/libsrc/occ/occ_edge.cpp new file mode 100644 index 00000000..a3a2b06e --- /dev/null +++ b/libsrc/occ/occ_edge.cpp @@ -0,0 +1,75 @@ +#include +#include +#include + +#include "occ_edge.hpp" +#include "occgeom.hpp" + +namespace netgen +{ + OCCEdge::OCCEdge(TopoDS_Shape edge_, GeometryVertex & start_, GeometryVertex & end_) + : GeometryEdge(start_, end_), + edge(TopoDS::Edge(edge_)) + { + curve = BRep_Tool::Curve(edge, s0, s1); + BRepGProp::LinearProperties(edge, props); + + auto verts = GetVertices(edge); + if(verts.size() != 2) + throw Exception("OCC edge does not have 2 vertices"); + + if(start != end) + { + // swap start/end if necessary + double d00 = Dist(GetPoint(0), start->GetPoint()); + double d01 = Dist(GetPoint(0), end->GetPoint()); + if(d01 < d00) + swap(start, end); + } + } + + double OCCEdge::GetLength() const + { + return props.Mass(); + } + + Point<3> OCCEdge::GetCenter() const + { + return occ2ng( props.CentreOfMass() ); + } + + Point<3> OCCEdge::GetPoint(double t) const + { + return occ2ng( curve->Value(s0+t*(s1-s0)) ); + } + + double OCCEdge::CalcStep(double t, double sag) const + { + throw Exception(ToString("not implemented") + __FILE__ + ":" + ToString(__LINE__)); + } + + size_t OCCEdge::GetHash() const + { + return edge.HashCode(std::numeric_limits::max()); + } + + void OCCEdge::ProjectPoint(Point<3>& p, EdgePointGeomInfo* gi) const + { + auto pnt = ng2occ(p); + GeomAPI_ProjectPointOnCurve proj(pnt, curve, s0, s1); + pnt = proj.NearestPoint(); + if(gi) + gi->dist = (proj.LowerDistanceParameter() - s0)/(s1-s0); + p = occ2ng(pnt); + } + + Vec<3> OCCEdge::GetTangent(double t) const + { + t = s0 + t*(s1-s0); + gp_Pnt p; + gp_Vec v; + curve->D1(t, p, v); + return occ2ng(v) * (s1-s0); + } + +} diff --git a/libsrc/occ/occ_edge.hpp b/libsrc/occ/occ_edge.hpp new file mode 100644 index 00000000..1770e56a --- /dev/null +++ b/libsrc/occ/occ_edge.hpp @@ -0,0 +1,42 @@ +#ifndef FILE_OCC_EDGE_INCLUDED +#define FILE_OCC_EDGE_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include "occ_vertex.hpp" +#include "meshing.hpp" + +namespace netgen +{ + class OCCEdge : public GeometryEdge + { + public: + TopoDS_Edge edge; + Handle(Geom_Curve) curve; + double s0, s1; + GProp_GProps props; + + public: + OCCEdge(TopoDS_Shape edge_, GeometryVertex & start_, GeometryVertex & end_); + + auto Shape() const { return edge; } + + double GetLength() const override; + Point<3> GetCenter() const override; + Point<3> GetPoint(double t) const override; + double CalcStep(double t, double sag) const override; + size_t GetHash() const override; + void ProjectPoint(Point<3>& p, EdgePointGeomInfo* gi) const override; + Vec<3> GetTangent(double t) const override; + bool IsDegenerated(double) const override { + return BRep_Tool::Degenerated(edge); + } + }; +} + +#endif // FILE_OCCEDGE_INCLUDED diff --git a/libsrc/occ/occ_face.cpp b/libsrc/occ/occ_face.cpp new file mode 100644 index 00000000..2e60469a --- /dev/null +++ b/libsrc/occ/occ_face.cpp @@ -0,0 +1,278 @@ +#include +#include +#include +#include + +#include "occ_edge.hpp" +#include "occ_face.hpp" +#include "occgeom.hpp" + +namespace netgen +{ + OCCFace::OCCFace(TopoDS_Shape dshape) + : face(TopoDS::Face(dshape)) + { + BRepGProp::SurfaceProperties (dshape, props); + bbox = ::netgen::GetBoundingBox(face); + + surface = BRep_Tool::Surface(face); + shape_analysis = new ShapeAnalysis_Surface( surface ); + tolerance = BRep_Tool::Tolerance( face ); + } + + size_t OCCFace::GetNBoundaries() const + { + return 0; + } + + size_t OCCFace::GetHash() const + { + return face.HashCode(std::numeric_limits::max()); + } + + Point<3> OCCFace::GetCenter() const + { + return occ2ng( props.CentreOfMass() ); + } + + Array OCCFace::GetBoundary(const Mesh& mesh) const + { + auto & geom = dynamic_cast(*mesh.GetGeometry()); + + auto n_edges = geom.GetNEdges(); + constexpr int UNUSED = 0; + constexpr int FORWARD = 1; + constexpr int REVERSED = 2; + constexpr int BOTH = 3; + + Array edge_orientation(n_edges); + edge_orientation = UNUSED; + + Array curve_on_face[BOTH]; + curve_on_face[FORWARD].SetSize(n_edges); + curve_on_face[REVERSED].SetSize(n_edges); + + Array edge_on_face[BOTH]; + edge_on_face[FORWARD].SetSize(n_edges); + edge_on_face[REVERSED].SetSize(n_edges); + + for(auto edge_ : GetEdges(face)) + { + auto edge = TopoDS::Edge(edge_); + auto edgenr = geom.GetEdge(edge).nr; + auto & orientation = edge_orientation[edgenr]; + double s0, s1; + auto cof = BRep_Tool::CurveOnSurface (edge, face, s0, s1); + if(edge.Orientation() == TopAbs_FORWARD) + { + curve_on_face[FORWARD][edgenr] = cof; + orientation += FORWARD; + edge_on_face[FORWARD][edgenr] = edge; + } + if(edge.Orientation() == TopAbs_REVERSED) + { + curve_on_face[REVERSED][edgenr] = cof; + orientation += REVERSED; + edge_on_face[REVERSED][edgenr] = edge; + } + + if(orientation > BOTH) + throw Exception("have edge more than twice in face " + ToString(nr) + " " + properties.GetName() + ", orientation: " + ToString(orientation)); + } + + Array boundary; + for (auto seg : mesh.LineSegments()) + { + auto edgenr = seg.epgeominfo[0].edgenr; + auto orientation = edge_orientation[edgenr]; + + if(orientation == UNUSED) + continue; + + for(const auto ORIENTATION : {FORWARD, REVERSED}) + { + if((orientation & ORIENTATION) == 0) + continue; + + // auto cof = curve_on_face[ORIENTATION][edgenr]; + auto edge = edge_on_face[ORIENTATION][edgenr]; + double s0, s1; + auto cof = BRep_Tool::CurveOnSurface (edge, face, s0, s1); + + double s[2] = { seg.epgeominfo[0].dist, seg.epgeominfo[1].dist }; + + // dist is in [0,1], map parametrization to [s0, s1] + s[0] = s0 + s[0]*(s1-s0); + s[1] = s0 + s[1]*(s1-s0); + + // fixes normal-vector roundoff problem when endpoint is cone-tip + double delta = s[1]-s[0]; + s[0] += 1e-10*delta; + s[1] -= 1e-10*delta; + + for(auto i : Range(2)) + { + // take uv from CurveOnSurface as start value but project again for better accuracy + // (cof->Value yields wrong values (outside of surface) for complicated faces + auto uv = cof->Value(s[i]); + PointGeomInfo gi; + gi.u = uv.X(); + gi.v = uv.Y(); + Point<3> pproject = mesh[seg[i]]; + ProjectPointGI(pproject, gi); + seg.epgeominfo[i].u = gi.u; + seg.epgeominfo[i].v = gi.v; + } + + bool do_swap = ORIENTATION == REVERSED; + if(seg.epgeominfo[1].dist < seg.epgeominfo[0].dist) + do_swap = !do_swap; + + if(do_swap) + { + swap(seg[0], seg[1]); + swap(seg.epgeominfo[0].dist, seg.epgeominfo[1].dist); + swap(seg.epgeominfo[0].u, seg.epgeominfo[1].u); + swap(seg.epgeominfo[0].v, seg.epgeominfo[1].v); + } + + boundary.Append(seg); + } + } + return boundary; + } + + PointGeomInfo OCCFace::Project(Point<3>& p) const + { + auto suval = shape_analysis->ValueOfUV(ng2occ(p), tolerance); + double u,v; + suval.Coord(u, v); + p = occ2ng(surface->Value( u, v )); + + PointGeomInfo gi; + gi.trignum = nr+1; + gi.u = u; + gi.v = v; + return gi; + } + + bool OCCFace::ProjectPointGI(Point<3>& p_, PointGeomInfo& gi) const + { + /* + static Timer t("OCCFace::ProjectPointGI"); + RegionTimer rt(t); + // *testout << "input, uv = " << gi.u << ", " << gi.v << endl; + auto suval = shape_analysis->NextValueOfUV({gi.u, gi.v}, ng2occ(p_), tolerance); + gi.trignum = nr+1; + suval.Coord(gi.u, gi.v); + // *testout << "result, uv = " << gi.u << ", " << gi.v << endl; + p_ = occ2ng(surface->Value( gi.u, gi.v )); + return true; + */ + // Old code: do newton iterations manually + double u = gi.u; + double v = gi.v; + auto p = ng2occ(p_); + auto x = surface->Value (u,v); + + if (p.SquareDistance(x) <= sqr(PROJECTION_TOLERANCE)) return true; + + gp_Vec du, dv; + surface->D1(u,v,x,du,dv); + + int count = 0; + gp_Pnt xold; + gp_Vec n; + double det, lambda, mu; + + do { + count++; + + n = du^dv; + + det = Det3 (n.X(), du.X(), dv.X(), + n.Y(), du.Y(), dv.Y(), + n.Z(), du.Z(), dv.Z()); + + if (det < 1e-15) return false; + + lambda = Det3 (n.X(), p.X()-x.X(), dv.X(), + n.Y(), p.Y()-x.Y(), dv.Y(), + n.Z(), p.Z()-x.Z(), dv.Z())/det; + + mu = Det3 (n.X(), du.X(), p.X()-x.X(), + n.Y(), du.Y(), p.Y()-x.Y(), + n.Z(), du.Z(), p.Z()-x.Z())/det; + + u += lambda; + v += mu; + + xold = x; + surface->D1(u,v,x,du,dv); + + } while (xold.SquareDistance(x) > sqr(PROJECTION_TOLERANCE) && count < 50); + + // (*testout) << "FastProject count: " << count << endl; + + if (count == 50) return false; + + p_ = occ2ng(x); + gi.u = u; gi.v = v; + + return true; + } + + Point<3> OCCFace::GetPoint(const PointGeomInfo& gi) const + { + return occ2ng(surface->Value( gi.u, gi.v )); + } + + void OCCFace::CalcEdgePointGI(const GeometryEdge& edge, + double t, + EdgePointGeomInfo& egi) const + { + throw Exception(ToString("not implemented") + __FILE__ + ":" + ToString(__LINE__)); + } + + Box<3> OCCFace::GetBoundingBox() const + { + return bbox; + } + + + double OCCFace::GetCurvature(const PointGeomInfo& gi) const + { + BRepAdaptor_Surface sf(face, Standard_True); + BRepLProp_SLProps prop2(sf, 2, 1e-5); + prop2.SetParameters (gi.u, gi.v); + return max(fabs(prop2.MinCurvature()), + fabs(prop2.MaxCurvature())); + } + + void OCCFace::RestrictH(Mesh& mesh, const MeshingParameters& mparam) const + { + throw Exception(ToString("not implemented") + __FILE__ + ":" + ToString(__LINE__)); + } + + Vec<3> OCCFace::GetNormal(const Point<3>& p, const PointGeomInfo* gi) const + { + PointGeomInfo gi_; + if(gi==nullptr) + { + auto p_ = p; + gi_ = Project(p_); + gi = &gi_; + } + + gp_Pnt pnt; + gp_Vec du, dv; + surface->D1(gi->u,gi->v,pnt,du,dv); + auto n = Cross (occ2ng(du), occ2ng(dv)); + n.Normalize(); + if (face.Orientation() == TopAbs_REVERSED) + n *= -1; + return n; + } + + +} diff --git a/libsrc/occ/occ_face.hpp b/libsrc/occ/occ_face.hpp new file mode 100644 index 00000000..43e66bb6 --- /dev/null +++ b/libsrc/occ/occ_face.hpp @@ -0,0 +1,48 @@ +#ifndef FILE_OCC_FACE_INCLUDED +#define FILE_OCC_FACE_INCLUDED + +#include +#include +#include +#include + +#include "occ_vertex.hpp" +#include "meshing.hpp" + +namespace netgen +{ + class OCCFace : public GeometryFace + { + TopoDS_Face face; + GProp_GProps props; + Box<3> bbox; + + Handle( Geom_Surface ) surface; + Handle( ShapeAnalysis_Surface ) shape_analysis; + double tolerance; + + public: + OCCFace(TopoDS_Shape dshape); + + const TopoDS_Face Shape() const { return face; } + + size_t GetHash() const override; + Point<3> GetCenter() const override; + virtual size_t GetNBoundaries() const override; + virtual Array GetBoundary(const Mesh& mesh) const override; + virtual PointGeomInfo Project(Point<3>& p) const override; + virtual bool ProjectPointGI(Point<3>& p, PointGeomInfo& gi) const override; + virtual Point<3> GetPoint(const PointGeomInfo& gi) const override; + virtual void CalcEdgePointGI(const GeometryEdge& edge, + double t, + EdgePointGeomInfo& egi) const override; + virtual Box<3> GetBoundingBox() const override; + + virtual double GetCurvature(const PointGeomInfo& gi) const override; + + virtual void RestrictH(Mesh& mesh, const MeshingParameters& mparam) const override; + virtual Vec<3> GetNormal(const Point<3>& p, const PointGeomInfo* gi = nullptr) const override; + }; +} + +#endif // FILE_OCC_FACE_INCLUDED diff --git a/libsrc/occ/occ_solid.hpp b/libsrc/occ/occ_solid.hpp new file mode 100644 index 00000000..d598de4a --- /dev/null +++ b/libsrc/occ/occ_solid.hpp @@ -0,0 +1,24 @@ +#ifndef FILE_OCC_SOLID_INCLUDED +#define FILE_OCC_SOLID_INCLUDED + +#include +#include + +#include "meshing.hpp" + +namespace netgen +{ + class OCCSolid : public GeometrySolid + { + TopoDS_Solid solid; + + public: + OCCSolid(TopoDS_Shape dshape) + : solid(TopoDS::Solid(dshape)) + { } + + size_t GetHash() const override { return solid.HashCode(std::numeric_limits::max()); } + }; +} + +#endif // FILE_OCC_SOLID_INCLUDED diff --git a/libsrc/occ/occ_utils.cpp b/libsrc/occ/occ_utils.cpp new file mode 100644 index 00000000..97c6f836 --- /dev/null +++ b/libsrc/occ/occ_utils.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "occ_utils.hpp" + +namespace netgen +{ + Point<3> occ2ng (const TopoDS_Shape& shape) + { + if(shape.ShapeType() != TopAbs_VERTEX) + throw Exception("Try to convert non vertex to point!"); + return occ2ng( BRep_Tool::Pnt(TopoDS::Vertex(shape)) ); + } + + Transformation<3> occ2ng (const gp_Trsf & occ_trafo) + { + Transformation<3> trafo; + auto v = occ_trafo.TranslationPart(); + auto m = occ_trafo.VectorialPart(); + auto & tv = trafo.GetVector(); + auto & tm = trafo.GetMatrix(); + for(auto i : Range(3)) + { + tv[i] = v.Coord(i+1); + for(auto k : Range(3)) + tm(i,k) = m(i+1,k+1); + } + return trafo; + } + + Transformation<3> occ2ng (const gp_GTrsf & occ_trafo) + { + Transformation<3> trafo; + auto v = occ_trafo.TranslationPart(); + auto m = occ_trafo.VectorialPart(); + auto & tv = trafo.GetVector(); + auto & tm = trafo.GetMatrix(); + for(auto i : Range(3)) + { + tv[i] = v.Coord(i+1); + for(auto k : Range(3)) + tm(i,k) = m(i+1,k+1); + } + return trafo; + } + + Box<3> GetBoundingBox( const TopoDS_Shape & shape ) + { + Bnd_Box bb; +#if OCC_VERSION_HEX < 0x070000 + BRepBndLib::Add (shape, bb); +#else + BRepBndLib::Add (shape, bb, true); +#endif + return {occ2ng(bb.CornerMin()), occ2ng(bb.CornerMax())}; + } +} diff --git a/libsrc/occ/occ_utils.hpp b/libsrc/occ/occ_utils.hpp new file mode 100644 index 00000000..2f55d405 --- /dev/null +++ b/libsrc/occ/occ_utils.hpp @@ -0,0 +1,315 @@ +#ifndef FILE_OCC_UTILS_INCLUDED +#define FILE_OCC_UTILS_INCLUDED + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "meshing.hpp" + +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4 +#define OCC_HAVE_DUMP_JSON +#endif + +namespace netgen +{ + inline Point<3> occ2ng (const gp_Pnt & p) + { + return Point<3> (p.X(), p.Y(), p.Z()); + } + + inline Point<2> occ2ng (const gp_Pnt2d & p) + { + return Point<2> (p.X(), p.Y()); + } + + inline Vec<3> occ2ng (const gp_Vec & v) + { + return Vec<3> (v.X(), v.Y(), v.Z()); + } + + DLL_HEADER Point<3> occ2ng (const TopoDS_Shape & s); + + inline Point<3> occ2ng (const TopoDS_Vertex & v) + { + return occ2ng (BRep_Tool::Pnt (v)); + } + + DLL_HEADER Transformation<3> occ2ng (const gp_Trsf & t); + DLL_HEADER Transformation<3> occ2ng (const gp_GTrsf & t); + inline Transformation<3> occ2ng (const variant & t) + { + if(auto t1 = get_if(&t)) + return occ2ng(*t1); + return occ2ng(get(t)); + } + + inline gp_Pnt ng2occ (const Point<3> & p) + { + return gp_Pnt(p(0), p(1), p(2)); + } + + DLL_HEADER Box<3> GetBoundingBox( const TopoDS_Shape & shape ); + + class OCCIdentification + { + public: + TopoDS_Shape from; + TopoDS_Shape to; + Transformation<3> trafo; + string name; + Identifications::ID_TYPE type; + bool opposite_direction; + }; + + + class MyExplorer + { + class Iterator + { + TopExp_Explorer exp; + public: + Iterator (TopoDS_Shape ashape, TopAbs_ShapeEnum atoFind, TopAbs_ShapeEnum atoAvoid) + : exp(ashape, atoFind, atoAvoid) { } + auto operator*() { return exp.Current(); } + Iterator & operator++() { exp.Next(); return *this; } + bool operator!= (nullptr_t nu) { return exp.More(); } + }; + + public: + TopoDS_Shape shape; + TopAbs_ShapeEnum toFind; + TopAbs_ShapeEnum toAvoid; + MyExplorer (TopoDS_Shape ashape, TopAbs_ShapeEnum atoFind, TopAbs_ShapeEnum atoAvoid = TopAbs_SHAPE) + : shape(ashape), toFind(atoFind), toAvoid(atoAvoid) { ; } + Iterator begin() { return Iterator(shape, toFind, toAvoid); } + auto end() { return nullptr; } + }; + + inline auto Explore (TopoDS_Shape shape, TopAbs_ShapeEnum toFind, TopAbs_ShapeEnum toAvoid = TopAbs_SHAPE) + { + return MyExplorer (shape, toFind, toAvoid); + } + + + class IndexMapIterator + { + class Iterator + { + const TopTools_IndexedMapOfShape & indmap; + int i; + public: + Iterator (const TopTools_IndexedMapOfShape & aindmap, int ai) + : indmap(aindmap), i(ai) { ; } + auto operator*() { return tuple(i, indmap(i)); } + Iterator & operator++() { i++; return *this; } + bool operator!= (const Iterator & i2) { return i != i2.i; } + }; + + public: + const TopTools_IndexedMapOfShape & indmap; + IndexMapIterator (const TopTools_IndexedMapOfShape & aindmap) : indmap(aindmap) { } + Iterator begin() { return Iterator(indmap, 1); } + Iterator end() { return Iterator(indmap, indmap.Extent()+1); } + }; + + inline auto Enumerate (const TopTools_IndexedMapOfShape & indmap) + { + return IndexMapIterator(indmap); + } + + class ListOfShapes : public std::vector + { + public: + DLL_HEADER TopoDS_Shape Max(gp_Vec dir); + DLL_HEADER TopoDS_Shape Nearest(gp_Pnt pnt); + DLL_HEADER ListOfShapes SubShapes(TopAbs_ShapeEnum type) const; + + ListOfShapes Solids() const + { + return SubShapes(TopAbs_SOLID); + } + ListOfShapes Faces() const + { + return SubShapes(TopAbs_FACE); + } + ListOfShapes Wires() const + { + return SubShapes(TopAbs_WIRE); + } + ListOfShapes Edges() const + { + return SubShapes(TopAbs_EDGE); + } + ListOfShapes Vertices() const + { + return SubShapes(TopAbs_VERTEX); + } + + ListOfShapes operator*(const ListOfShapes& other) const + { + ListOfShapes common; + for(const auto& shape : (*this)) + for(const auto& shape_o : other) + if(shape.IsSame(shape_o)) + common.push_back(shape); + return common; + } + }; + + inline ListOfShapes GetSolids(const TopoDS_Shape & shape) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, TopAbs_SOLID); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + } + + inline ListOfShapes GetFaces(const TopoDS_Shape & shape) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + } + + inline ListOfShapes GetWires(const TopoDS_Shape & shape) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, TopAbs_WIRE); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + } + + inline ListOfShapes GetEdges(const TopoDS_Shape & shape) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, TopAbs_EDGE); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + } + + inline ListOfShapes GetVertices(const TopoDS_Shape & shape) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, TopAbs_VERTEX); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + } + + class DirectionalInterval + { + public: + gp_Vec dir; + double minval = -1e99; + double maxval = 1e99; + bool openmin = false, openmax = false; + + DirectionalInterval (gp_Vec adir) : dir(adir) { ; } + DirectionalInterval (const DirectionalInterval & i2) = default; + + DirectionalInterval operator< (double val) const + { + DirectionalInterval i2 = *this; + i2.maxval = val; + i2.openmax = true; + return i2; + } + + DirectionalInterval operator> (double val) const + { + DirectionalInterval i2 = *this; + i2.minval = val; + i2.openmin = true; + return i2; + } + + DirectionalInterval operator<= (double val) const + { + DirectionalInterval i2 = *this; + i2.maxval = val; + i2.openmax = false; + return i2; + } + + DirectionalInterval operator>= (double val) const + { + DirectionalInterval i2 = *this; + i2.minval = val; + i2.openmin = false; + return i2; + } + + DirectionalInterval Intersect (const DirectionalInterval & i2) + { + DirectionalInterval res = *this; + res.minval = max(res.minval, i2.minval); + res.maxval = min(res.maxval, i2.maxval); + return res; + } + + bool Contains (gp_Pnt p, double eps = 1e-8) + { + // cout << "Contains point " << p.X() << "," << p.Y() << "," << p.Z() << " ? " << endl; + double val = dir.X()*p.X() + dir.Y()*p.Y() + dir.Z() * p.Z(); + // cout << "minval = " << minval << ", val = " << val << " maxval = " << maxval << endl; + if (openmin) { + if (val < minval+eps) return false; + } else { + if (val < minval-eps) return false; + } + if (openmax) { + if (val > maxval-eps) return false; + } else { + if (val > maxval+eps) return false; + } + return true; + } + }; + + inline auto Properties (TopoDS_Shape shape) + { + GProp_GProps props; + double tol; + switch (shape.ShapeType()) + { + case TopAbs_SOLID: + case TopAbs_COMPOUND: + case TopAbs_COMPSOLID: + tol = 1e-2 * BRep_Tool::MaxTolerance(shape, TopAbs_FACE); + BRepGProp::VolumeProperties (shape, props, tol); break; + case TopAbs_FACE: + case TopAbs_SHELL: + tol = 1e-2 * BRep_Tool::MaxTolerance(shape, TopAbs_FACE); + BRepGProp::SurfaceProperties (shape, props, tol); break; + case TopAbs_WIRE: + case TopAbs_EDGE: + tol = 1e-2 * BRep_Tool::MaxTolerance(shape, TopAbs_EDGE); + BRepGProp::LinearProperties(shape, props, tol); break; + default: + BRepGProp::LinearProperties(shape, props); + } + return props; + } + + inline gp_Pnt Center (TopoDS_Shape shape) + { + return Properties(shape).CentreOfMass(); + } + + inline double Mass (TopoDS_Shape shape) + { + return Properties(shape).Mass(); + } + +} +#endif // FILE_OCC_UTILS_INCLUDED diff --git a/libsrc/occ/occ_vertex.cpp b/libsrc/occ/occ_vertex.cpp new file mode 100644 index 00000000..6e83c894 --- /dev/null +++ b/libsrc/occ/occ_vertex.cpp @@ -0,0 +1,24 @@ +#include +#include + +#include "occ_vertex.hpp" + +namespace netgen +{ + + OCCVertex::OCCVertex( TopoDS_Shape s ) + : vertex(TopoDS::Vertex(s)) + { + p = occ2ng(vertex); + } + + Point<3> OCCVertex::GetPoint() const + { + return p; + } + + size_t OCCVertex::GetHash() const + { + return vertex.HashCode(std::numeric_limits::max()); + } +} diff --git a/libsrc/occ/occ_vertex.hpp b/libsrc/occ/occ_vertex.hpp new file mode 100644 index 00000000..9ec3a4e7 --- /dev/null +++ b/libsrc/occ/occ_vertex.hpp @@ -0,0 +1,26 @@ +#ifndef FILE_OCC_VERTEX_INCLUDED +#define FILE_OCC_VERTEX_INCLUDED + +#include +#include + +#include "meshing.hpp" +#include "occ_utils.hpp" + +namespace netgen +{ + class OCCVertex : public GeometryVertex + { + TopoDS_Vertex vertex; + Point<3> p; + + public: + OCCVertex( ) = default; + OCCVertex( TopoDS_Shape s ); + ~OCCVertex() {} + Point<3> GetPoint() const override; + size_t GetHash() const override; + }; +} + +#endif // FILE_OCC_VERTEX_INCLUDED diff --git a/libsrc/occ/occgenmesh.cpp b/libsrc/occ/occgenmesh.cpp index 14fa5374..517e1ebb 100644 --- a/libsrc/occ/occgenmesh.cpp +++ b/libsrc/occ/occgenmesh.cpp @@ -1,1105 +1,611 @@ #ifdef OCCGEOMETRY #include -#include #include +#include "occgeom.hpp" +#include "occ_face.hpp" +#include "occmeshsurf.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace netgen { -#include "occmeshsurf.hpp" #define TCL_OK 0 #define TCL_ERROR 1 -#define DIVIDEEDGESECTIONS 1000 -#define IGNORECURVELENGTH 1e-4 +#define DIVIDEEDGESECTIONS 10000 // better solution to come soon +#define IGNORECURVELENGTH 0 #define VSMALL 1e-10 - DLL_HEADER bool merge_solids = 1; + DLL_HEADER bool merge_solids = false; // can you please explain what you intend to compute here (JS) !!! - double Line :: Dist (Line l) - { - Vec<3> n = p1-p0; - Vec<3> q = l.p1-l.p0; - double nq = n*q; + double Line :: Dist (Line l) + { + Vec<3> n = p1-p0; + Vec<3> q = l.p1-l.p0; + double nq = n*q; - Point<3> p = p0 + 0.5*n; - double lambda = (p-l.p0)*n / (nq + VSMALL); + Point<3> p = p0 + 0.5*n; + double lambda = (p-l.p0)*n / (nq + VSMALL); - if (lambda >= 0 && lambda <= 1) + if (lambda >= 0 && lambda <= 1) { - double d = (p-l.p0-lambda*q).Length(); - // if (d < 1e-3) d = 1e99; - return d; + double d = (p-l.p0-lambda*q).Length(); + // if (d < 1e-3) d = 1e99; + return d; } - else - return 1e99; - } + else + return 1e99; + } + + + double ComputeH (double kappa, const MeshingParameters & mparam) + { + kappa *= mparam.curvaturesafety; + /* + double hret; + + if (mparam.maxh * kappa < 1) + hret = mparam.maxh; + else + hret = 1 / (kappa + VSMALL); + + if (mparam.maxh < hret) + hret = mparam.maxh; + + return hret; + */ + // return min(mparam.maxh, 1/kappa); + return (mparam.maxh*kappa < 1) ? mparam.maxh : 1/kappa; + } - double Line :: Length () - { - return (p1-p0).Length(); - } + void RestrictHTriangle (gp_Pnt2d & par0, gp_Pnt2d & par1, gp_Pnt2d & par2, + BRepLProp_SLProps * prop, BRepLProp_SLProps * prop2, Mesh & mesh, int depth, double h, int layer, const MeshingParameters & mparam) + { + int ls = -1; + gp_Pnt pnt0,pnt1,pnt2; + prop->SetParameters (par0.X(), par0.Y()); + pnt0 = prop->Value(); - inline Point<3> occ2ng (const gp_Pnt & p) - { - return Point<3> (p.X(), p.Y(), p.Z()); - } + prop->SetParameters (par1.X(), par1.Y()); + pnt1 = prop->Value(); + prop->SetParameters (par2.X(), par2.Y()); + pnt2 = prop->Value(); - - double ComputeH (double kappa) - { - double hret; - kappa *= mparam.curvaturesafety; - - if (mparam.maxh * kappa < 1) - hret = mparam.maxh; - else - hret = 1 / (kappa + VSMALL); - - if (mparam.maxh < hret) - hret = mparam.maxh; - - return (hret); - } - - - - - void RestrictHTriangle (gp_Pnt2d & par0, gp_Pnt2d & par1, gp_Pnt2d & par2, - BRepLProp_SLProps * prop, Mesh & mesh, int depth, double h = 0) - { - int ls = -1; - - gp_Pnt pnt0,pnt1,pnt2; - - prop->SetParameters (par0.X(), par0.Y()); - pnt0 = prop->Value(); - - prop->SetParameters (par1.X(), par1.Y()); - pnt1 = prop->Value(); - - prop->SetParameters (par2.X(), par2.Y()); - pnt2 = prop->Value(); - - double aux; - double maxside = pnt0.Distance(pnt1); - ls = 2; - aux = pnt1.Distance(pnt2); - if(aux > maxside) + double aux; + double maxside = pnt0.Distance(pnt1); + ls = 2; + aux = pnt1.Distance(pnt2); + if(aux > maxside) { - maxside = aux; - ls = 0; + maxside = aux; + ls = 0; } - aux = pnt2.Distance(pnt0); - if(aux > maxside) + aux = pnt2.Distance(pnt0); + if(aux > maxside) { - maxside = aux; - ls = 1; + maxside = aux; + ls = 1; } - gp_Pnt2d parmid; + gp_Pnt2d parmid; - parmid.SetX( (par0.X()+par1.X()+par2.X()) / 3 ); - parmid.SetY( (par0.Y()+par1.Y()+par2.Y()) / 3 ); + parmid.SetX( (par0.X()+par1.X()+par2.X()) / 3 ); + parmid.SetY( (par0.Y()+par1.Y()+par2.Y()) / 3 ); - if (depth%3 == 0) + if (depth%3 == 0) { - double curvature = 0; + double curvature = 0; - prop->SetParameters (parmid.X(), parmid.Y()); - if (!prop->IsCurvatureDefined()) - { + prop2->SetParameters (parmid.X(), parmid.Y()); + if (!prop2->IsCurvatureDefined()) + { (*testout) << "curvature not defined!" << endl; return; - } - curvature = max(fabs(prop->MinCurvature()), - fabs(prop->MaxCurvature())); + } + curvature = max(fabs(prop2->MinCurvature()), + fabs(prop2->MaxCurvature())); - prop->SetParameters (par0.X(), par0.Y()); - if (!prop->IsCurvatureDefined()) - { + prop2->SetParameters (par0.X(), par0.Y()); + if (!prop2->IsCurvatureDefined()) + { (*testout) << "curvature not defined!" << endl; return; - } - curvature = max(curvature,max(fabs(prop->MinCurvature()), - fabs(prop->MaxCurvature()))); + } + curvature = max(curvature,max(fabs(prop2->MinCurvature()), + fabs(prop2->MaxCurvature()))); - prop->SetParameters (par1.X(), par1.Y()); - if (!prop->IsCurvatureDefined()) - { + prop2->SetParameters (par1.X(), par1.Y()); + if (!prop2->IsCurvatureDefined()) + { (*testout) << "curvature not defined!" << endl; return; - } - curvature = max(curvature,max(fabs(prop->MinCurvature()), - fabs(prop->MaxCurvature()))); + } + curvature = max(curvature,max(fabs(prop2->MinCurvature()), + fabs(prop2->MaxCurvature()))); - prop->SetParameters (par2.X(), par2.Y()); - if (!prop->IsCurvatureDefined()) - { + prop2->SetParameters (par2.X(), par2.Y()); + if (!prop2->IsCurvatureDefined()) + { (*testout) << "curvature not defined!" << endl; return; - } - curvature = max(curvature,max(fabs(prop->MinCurvature()), - fabs(prop->MaxCurvature()))); + } + curvature = max(curvature,max(fabs(prop2->MinCurvature()), + fabs(prop2->MaxCurvature()))); - //(*testout) << "curvature " << curvature << endl; + //(*testout) << "curvature " << curvature << endl; - if (curvature < 1e-3) - { + if (curvature < 1e-3) + { //(*testout) << "curvature too small (" << curvature << ")!" << endl; return; // return war bis 10.2.05 auskommentiert - } + } - h = ComputeH (curvature+1e-10); + h = ComputeH (curvature+1e-10, mparam); - if(h < 1e-4*maxside) - return; + if(h < 1e-4*maxside) + return; - if (h > 30) return; + // if (h > 30) return; } - if (h < maxside && depth < 10) + if (h < maxside && depth < 10) { - //cout << "\r h " << h << flush; - gp_Pnt2d pm; + //cout << "\r h " << h << flush; + gp_Pnt2d pm; - //cout << "h " << h << " maxside " << maxside << " depth " << depth << endl; - //cout << "par0 " << par0.X() << " " << par0.Y() - //<< " par1 " << par1.X() << " " << par1.Y() - // << " par2 " << par2.X() << " " << par2.Y()<< endl; + //cout << "h " << h << " maxside " << maxside << " depth " << depth << endl; + //cout << "par0 " << par0.X() << " " << par0.Y() + //<< " par1 " << par1.X() << " " << par1.Y() + // << " par2 " << par2.X() << " " << par2.Y()<< endl; - if(ls == 0) - { + if(ls == 0) + { pm.SetX(0.5*(par1.X()+par2.X())); pm.SetY(0.5*(par1.Y()+par2.Y())); - RestrictHTriangle(pm, par2, par0, prop, mesh, depth+1, h); - RestrictHTriangle(pm, par0, par1, prop, mesh, depth+1, h); - } - else if(ls == 1) - { + RestrictHTriangle(pm, par2, par0, prop, prop2, mesh, depth+1, h, layer, mparam); + RestrictHTriangle(pm, par0, par1, prop, prop2, mesh, depth+1, h, layer, mparam); + } + else if(ls == 1) + { pm.SetX(0.5*(par0.X()+par2.X())); pm.SetY(0.5*(par0.Y()+par2.Y())); - RestrictHTriangle(pm, par1, par2, prop, mesh, depth+1, h); - RestrictHTriangle(pm, par0, par1, prop, mesh, depth+1, h); - } - else if(ls == 2) - { + RestrictHTriangle(pm, par1, par2, prop, prop2, mesh, depth+1, h, layer, mparam); + RestrictHTriangle(pm, par0, par1, prop, prop2, mesh, depth+1, h, layer, mparam); + } + else if(ls == 2) + { pm.SetX(0.5*(par0.X()+par1.X())); pm.SetY(0.5*(par0.Y()+par1.Y())); - RestrictHTriangle(pm, par1, par2, prop, mesh, depth+1, h); - RestrictHTriangle(pm, par2, par0, prop, mesh, depth+1, h); - } + RestrictHTriangle(pm, par1, par2, prop, prop2, mesh, depth+1, h, layer, mparam); + RestrictHTriangle(pm, par2, par0, prop, prop2, mesh, depth+1, h, layer, mparam); + } } - else + else { - gp_Pnt pnt; - Point3d p3d; + gp_Pnt pnt; + Point3d p3d; - prop->SetParameters (parmid.X(), parmid.Y()); - pnt = prop->Value(); - p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z()); - mesh.RestrictLocalH (p3d, h); + prop->SetParameters (parmid.X(), parmid.Y()); + pnt = prop->Value(); + p3d = Point3d(pnt.X(), pnt.Y(), pnt.Z()); + mesh.RestrictLocalH (p3d, h, layer); - p3d = Point3d(pnt0.X(), pnt0.Y(), pnt0.Z()); - mesh.RestrictLocalH (p3d, h); + p3d = Point3d(pnt0.X(), pnt0.Y(), pnt0.Z()); + mesh.RestrictLocalH (p3d, h, layer); - p3d = Point3d(pnt1.X(), pnt1.Y(), pnt1.Z()); - mesh.RestrictLocalH (p3d, h); + p3d = Point3d(pnt1.X(), pnt1.Y(), pnt1.Z()); + mesh.RestrictLocalH (p3d, h, layer); - p3d = Point3d(pnt2.X(), pnt2.Y(), pnt2.Z()); - mesh.RestrictLocalH (p3d, h); + p3d = Point3d(pnt2.X(), pnt2.Y(), pnt2.Z()); + mesh.RestrictLocalH (p3d, h, layer); - //(*testout) << "p = " << p3d << ", h = " << h << ", maxside = " << maxside << endl; + //(*testout) << "p = " << p3d << ", h = " << h << ", maxside = " << maxside << endl; } - } + } - - - void DivideEdge (TopoDS_Edge & edge, Array & ps, - Array & params, Mesh & mesh) - { - double s0, s1; - double maxh = mparam.maxh; - int nsubedges = 1; - gp_Pnt pnt, oldpnt; - double svalue[DIVIDEEDGESECTIONS]; - - GProp_GProps system; - BRepGProp::LinearProperties(edge, system); - double L = system.Mass(); - - Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); - - double hvalue[DIVIDEEDGESECTIONS+1]; - hvalue[0] = 0; - pnt = c->Value(s0); - - double olddist = 0; - double dist = 0; - - int tmpVal = (int)(DIVIDEEDGESECTIONS); - - for (int i = 1; i <= tmpVal; i++) + bool OCCMeshFace (const OCCGeometry & geom, Mesh & mesh, FlatArray glob2loc, + const MeshingParameters & mparam, int nr, int projecttype, bool delete_on_failure) + { + auto k = nr+1; + if(1==0 && !geom.fvispar[k-1].IsDrawable()) { - oldpnt = pnt; - pnt = c->Value(s0+(i/double(DIVIDEEDGESECTIONS))*(s1-s0)); - hvalue[i] = hvalue[i-1] + - 1.0/mesh.GetH(Point3d(pnt.X(), pnt.Y(), pnt.Z()))* - pnt.Distance(oldpnt); - - //(*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); + (*testout) << "ignoring face " << k << endl; + cout << "ignoring face " << k << endl; + return true; } - // nsubedges = int(ceil(hvalue[DIVIDEEDGESECTIONS])); - nsubedges = max (1, int(floor(hvalue[DIVIDEEDGESECTIONS]+0.5))); + // if(master_faces[k]!=k) + // continue; - ps.SetSize(nsubedges-1); - params.SetSize(nsubedges+1); + (*testout) << "mesh face " << k << endl; + multithread.percent = 100 * k / (mesh.GetNFD() + VSMALL); + geom.facemeshstatus[k-1] = -1; - int i = 1; - int i1 = 0; - do + FaceDescriptor & fd = mesh.GetFaceDescriptor(k); + auto face = TopoDS::Face(geom.fmap(k)); + const auto& occface = dynamic_cast(geom.GetFace(k-1)); + + int oldnf = mesh.GetNSE(); + + Box<3> bb = geom.GetBoundingBox(); + + // int projecttype = PLANESPACE; + // int projecttype = PARAMETERSPACE; + + static Timer tinit("init"); + tinit.Start(); + Meshing2OCCSurfaces meshing(geom, face, bb, projecttype, mparam); + tinit.Stop(); + + + static Timer tprint("print"); + tprint.Start(); + if (meshing.GetProjectionType() == PLANESPACE) + PrintMessage (2, "Face ", k, " / ", geom.GetNFaces(), " (plane space projection)"); + else + PrintMessage (2, "Face ", k, " / ", geom.GetNFaces(), " (parameter space projection)"); + tprint.Stop(); + + // Meshing2OCCSurfaces meshing(f2, bb); + // meshing.SetStartTime (starttime); + //(*testout) << "Face " << k << endl << endl; + + + auto segments = geom.GetFace(k-1).GetBoundary(mesh); + + if (meshing.GetProjectionType() == PLANESPACE) { - if (hvalue[i1]/hvalue[DIVIDEEDGESECTIONS]*nsubedges >= i) - { - params[i] = s0+(i1/double(DIVIDEEDGESECTIONS))*(s1-s0); - pnt = c->Value(params[i]); - ps[i-1] = MeshPoint (Point3d(pnt.X(), pnt.Y(), pnt.Z())); - i++; - } - i1++; - if (i1 > DIVIDEEDGESECTIONS) - { - nsubedges = i; - ps.SetSize(nsubedges-1); - params.SetSize(nsubedges+1); - cout << "divide edge: local h too small" << endl; - } - } while (i < nsubedges); + static Timer t("MeshSurface: Find edges and points - Physical"); RegionTimer r(t); + int cntp = 0; + glob2loc = 0; - params[0] = s0; - params[nsubedges] = s1; - - if (params[nsubedges] <= params[nsubedges-1]) - { - cout << "CORRECTED" << endl; - ps.SetSize (nsubedges-2); - params.SetSize (nsubedges); - params[nsubedges] = s1; - } - } - - - - - void OCCFindEdges (OCCGeometry & geom, Mesh & mesh) - { - const char * savetask = multithread.task; - multithread.task = "Edge meshing"; - - (*testout) << "edge meshing" << endl; - - int nvertices = geom.vmap.Extent(); - int nedges = geom.emap.Extent(); - - (*testout) << "nvertices = " << nvertices << endl; - (*testout) << "nedges = " << nedges << endl; - - double eps = 1e-6 * geom.GetBoundingBox().Diam(); - - for (int i = 1; i <= nvertices; i++) - { - gp_Pnt pnt = BRep_Tool::Pnt (TopoDS::Vertex(geom.vmap(i))); - MeshPoint mp( Point<3>(pnt.X(), pnt.Y(), pnt.Z()) ); - - bool exists = 0; - if (merge_solids) - for (PointIndex pi = 1; pi <= mesh.GetNP(); pi++) - if ( Dist2 (mesh[pi], Point<3>(mp)) < eps*eps) - { - exists = 1; - break; - } - - if (!exists) - mesh.AddPoint (mp); - } - - (*testout) << "different vertices = " << mesh.GetNP() << endl; - - - int first_ep = mesh.GetNP()+1; - - Array face2solid[2]; - for (int i = 0; i<2; i++) - { - face2solid[i].SetSize (geom.fmap.Extent()); - face2solid[i] = 0; - } - - int solidnr = 0; - for (TopExp_Explorer exp0(geom.shape, TopAbs_SOLID); exp0.More(); exp0.Next()) - { - solidnr++; - for (TopExp_Explorer exp1(exp0.Current(), TopAbs_FACE); exp1.More(); exp1.Next()) - { - TopoDS_Face face = TopoDS::Face(exp1.Current()); - int facenr = geom.fmap.FindIndex(face); - - if (face2solid[0][facenr-1] == 0) - face2solid[0][facenr-1] = solidnr; - else - face2solid[1][facenr-1] = solidnr; - } - } - - - int total = 0; - for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++) - for (TopExp_Explorer exp2(geom.fmap(i3), TopAbs_WIRE); exp2.More(); exp2.Next()) - for (TopExp_Explorer exp3(exp2.Current(), TopAbs_EDGE); exp3.More(); exp3.Next()) - total++; - - - int facenr = 0; - int edgenr = 0; - - - (*testout) << "faces = " << geom.fmap.Extent() << endl; - int curr = 0; - - for (int i3 = 1; i3 <= geom.fmap.Extent(); i3++) - { - TopoDS_Face face = TopoDS::Face(geom.fmap(i3)); - facenr = geom.fmap.FindIndex (face); // sollte doch immer == i3 sein ??? JS - - int solidnr0 = face2solid[0][i3-1]; - int solidnr1 = face2solid[1][i3-1]; - - /* auskommentiert am 3.3.05 von robert - for (exp2.Init (geom.somap(solidnr0), TopAbs_FACE); exp2.More(); exp2.Next()) - { - TopoDS_Face face2 = TopoDS::Face(exp2.Current()); - if (geom.fmap.FindIndex(face2) == facenr) - { - // if (face.Orientation() != face2.Orientation()) swap (solidnr0, solidnr1); - } - } - */ - - mesh.AddFaceDescriptor (FaceDescriptor(facenr, solidnr0, solidnr1, 0)); - - // Philippose - 06/07/2009 - // Add the face colour to the mesh data - Quantity_Color face_colour; - - if(!(geom.face_colours.IsNull()) - && (geom.face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour))) - { - mesh.GetFaceDescriptor(facenr).SetSurfColour(Vec3d(face_colour.Red(),face_colour.Green(),face_colour.Blue())); - } - else - { - mesh.GetFaceDescriptor(facenr).SetSurfColour(Vec3d(0.0,1.0,0.0)); - } - - if(geom.fnames.Size()>=facenr) - mesh.GetFaceDescriptor(facenr).SetBCName(&geom.fnames[facenr-1]); - mesh.GetFaceDescriptor(facenr).SetBCProperty(facenr); - // ACHTUNG! STIMMT NICHT ALLGEMEIN (RG) - - - Handle(Geom_Surface) occface = BRep_Tool::Surface(face); - - for (TopExp_Explorer exp2 (face, TopAbs_WIRE); exp2.More(); exp2.Next()) - { - TopoDS_Shape wire = exp2.Current(); - - for (TopExp_Explorer exp3 (wire, TopAbs_EDGE); exp3.More(); exp3.Next()) - { - curr++; - (*testout) << "edge nr " << curr << endl; - - multithread.percent = 100 * curr / double (total); - if (multithread.terminate) return; - - TopoDS_Edge edge = TopoDS::Edge (exp3.Current()); - if (BRep_Tool::Degenerated(edge)) - { - //(*testout) << "ignoring degenerated edge" << endl; - continue; - } - - if (geom.vmap.FindIndex(TopExp::FirstVertex (edge)) == - geom.vmap.FindIndex(TopExp::LastVertex (edge))) - { - GProp_GProps system; - BRepGProp::LinearProperties(edge, system); - - if (system.Mass() < eps) + for (Segment & seg : segments) + // if (seg.si == k) + for (int j = 0; j < 2; j++) + { + PointIndex pi = seg[j]; + if (glob2loc[pi] == 0) { - cout << "ignoring edge " << geom.emap.FindIndex (edge) - << ". closed edge with length < " << eps << endl; - continue; + meshing.AddPoint (mesh.Point(pi), pi); + cntp++; + glob2loc[pi] = cntp; } - } + } + for(const auto& vert : geom.GetFaceVertices(geom.GetFace(k-1))) + { + PointIndex pi = vert->nr + 1; + if(glob2loc[pi] == 0) + { + auto gi = occface.Project(mesh[pi]); + MultiPointGeomInfo mgi; + mgi.AddPointGeomInfo(gi); + meshing.AddPoint(mesh[pi], pi, &mgi); + cntp++; + glob2loc[pi] = cntp; + } + } - Handle(Geom2d_Curve) cof; - double s0, s1; - cof = BRep_Tool::CurveOnSurface (edge, face, s0, s1); + /* + for (int i = 1; i <= mesh.GetNSeg(); i++) + { + Segment & seg = mesh.LineSegment(i); + */ + // for (Segment & seg : mesh.LineSegments()) + for (Segment & seg : segments) + //if (seg.si == k) + { + PointGeomInfo gi0, gi1; + gi0.trignum = gi1.trignum = k; + gi0.u = seg.epgeominfo[0].u; + gi0.v = seg.epgeominfo[0].v; + gi1.u = seg.epgeominfo[1].u; + gi1.v = seg.epgeominfo[1].v; + + //if(orientation & 1) + meshing.AddBoundaryElement (glob2loc[seg[0]], glob2loc[seg[1]], gi0, gi1); - int geomedgenr = geom.emap.FindIndex(edge); - - Array mp; - Array params; - - DivideEdge (edge, mp, params, mesh); - - Array pnums; - pnums.SetSize (mp.Size()+2); - - if (!merge_solids) - { - pnums[0] = geom.vmap.FindIndex (TopExp::FirstVertex (edge)); - pnums[pnums.Size()-1] = geom.vmap.FindIndex (TopExp::LastVertex (edge)); - } - else - { - Point<3> fp = occ2ng (BRep_Tool::Pnt (TopExp::FirstVertex (edge))); - Point<3> lp = occ2ng (BRep_Tool::Pnt (TopExp::LastVertex (edge))); - - pnums[0] = -1; - pnums.Last() = -1; - for (PointIndex pi = 1; pi < first_ep; pi++) - { - if (Dist2 (mesh[pi], fp) < eps*eps) pnums[0] = pi; - if (Dist2 (mesh[pi], lp) < eps*eps) pnums.Last() = pi; - } - } - - - for (int i = 1; i <= mp.Size(); i++) - { - bool exists = 0; - int j; - for (j = first_ep; j <= mesh.GetNP(); j++) - if ((mesh.Point(j)-Point<3>(mp[i-1])).Length() < eps) - { - exists = 1; - break; - } - - if (exists) - pnums[i] = j; - else - { - mesh.AddPoint (mp[i-1]); - (*testout) << "add meshpoint " << mp[i-1] << endl; - pnums[i] = mesh.GetNP(); - } - } - (*testout) << "NP = " << mesh.GetNP() << endl; - - //(*testout) << pnums[pnums.Size()-1] << endl; - - for (int i = 1; i <= mp.Size()+1; i++) - { - edgenr++; - Segment seg; - - seg[0] = pnums[i-1]; - seg[1] = pnums[i]; - seg.edgenr = edgenr; - seg.si = facenr; - seg.epgeominfo[0].dist = params[i-1]; - seg.epgeominfo[1].dist = params[i]; - seg.epgeominfo[0].edgenr = geomedgenr; - seg.epgeominfo[1].edgenr = geomedgenr; - - gp_Pnt2d p2d; - p2d = cof->Value(params[i-1]); - // if (i == 1) p2d = cof->Value(s0); - seg.epgeominfo[0].u = p2d.X(); - seg.epgeominfo[0].v = p2d.Y(); - p2d = cof->Value(params[i]); - // if (i == mp.Size()+1) p2d = cof -> Value(s1); - seg.epgeominfo[1].u = p2d.X(); - seg.epgeominfo[1].v = p2d.Y(); - - /* - if (occface->IsUPeriodic()) - { - cout << "U Periodic" << endl; - if (fabs(seg.epgeominfo[1].u-seg.epgeominfo[0].u) > - fabs(seg.epgeominfo[1].u- - (seg.epgeominfo[0].u-occface->UPeriod()))) - seg.epgeominfo[0].u = p2d.X()+occface->UPeriod(); - - if (fabs(seg.epgeominfo[1].u-seg.epgeominfo[0].u) > - fabs(seg.epgeominfo[1].u- - (seg.epgeominfo[0].u+occface->UPeriod()))) - seg.epgeominfo[0].u = p2d.X()-occface->UPeriod(); - } - - if (occface->IsVPeriodic()) - { - cout << "V Periodic" << endl; - if (fabs(seg.epgeominfo[1].v-seg.epgeominfo[0].v) > - fabs(seg.epgeominfo[1].v- - (seg.epgeominfo[0].v-occface->VPeriod()))) - seg.epgeominfo[0].v = p2d.Y()+occface->VPeriod(); - - if (fabs(seg.epgeominfo[1].v-seg.epgeominfo[0].v) > - fabs(seg.epgeominfo[1].v- - (seg.epgeominfo[0].v+occface->VPeriod()))) - seg.epgeominfo[0].v = p2d.Y()-occface->VPeriod(); - } - */ - - if (edge.Orientation() == TopAbs_REVERSED) - { - swap (seg[0], seg[1]); - swap (seg.epgeominfo[0].dist, seg.epgeominfo[1].dist); - swap (seg.epgeominfo[0].u, seg.epgeominfo[1].u); - swap (seg.epgeominfo[0].v, seg.epgeominfo[1].v); - } - - mesh.AddSegment (seg); - - //edgesegments[geomedgenr-1]->Append(mesh.GetNSeg()); - - } } - } } - - // for(i=1; i<=mesh.GetNSeg(); i++) - // (*testout) << "edge " << mesh.LineSegment(i).edgenr << " face " << mesh.LineSegment(i).si - // << " p1 " << mesh.LineSegment(i)[0] << " p2 " << mesh.LineSegment(i)[1] << endl; - // exit(10); - - mesh.CalcSurfacesOfNode(); - multithread.task = savetask; - } - - - - - void OCCMeshSurface (OCCGeometry & geom, Mesh & mesh, int perfstepsend) - { - int i, j, k; - int changed; - - const char * savetask = multithread.task; - multithread.task = "Surface meshing"; - - geom.facemeshstatus = 0; - - int noldp = mesh.GetNP(); - - double starttime = GetTime(); - - Array glob2loc(noldp); - - //int projecttype = PARAMETERSPACE; - - int projecttype = PARAMETERSPACE; - - int notrys = 1; - - int surfmesherror = 0; - - for (k = 1; k <= mesh.GetNFD(); k++) + else { - if(1==0 && !geom.fvispar[k-1].IsDrawable()) - { - (*testout) << "ignoring face " << k << endl; - cout << "ignoring face " << k << endl; - continue; - } + static Timer t("MeshSurface: Find edges and points - Parameter"); RegionTimer r(t); - (*testout) << "mesh face " << k << endl; - multithread.percent = 100 * k / (mesh.GetNFD() + VSMALL); - geom.facemeshstatus[k-1] = -1; + Array gis(2*segments.Size()); + gis.SetSize (0); + glob2loc = 0; + int cntpt = 0; + Box<2> uv_box(Box<2>::EMPTY_BOX); + for(auto & seg : segments) + for(auto i : Range(2)) + uv_box.Add( {seg.epgeominfo[i].u, seg.epgeominfo[i].v } ); - /* - if (k != 42) - { - cout << "skipped" << endl; - continue; - } - */ + BoxTree<2> uv_tree(uv_box); + double tol = 1e99; + for(auto& seg : segments) + { + Point<2> p1 = { seg.epgeominfo[0].u, seg.epgeominfo[0].v }; + Point<2> p2 = { seg.epgeominfo[1].u, seg.epgeominfo[1].v }; + tol = min2(tol, Dist(p1, p2)); + } + uv_tree.SetTolerance(0.9 * tol); + Array found_points; + for(auto & seg : segments) + { + PointGeomInfo gi[2]; + gi[0].trignum = gi[1].trignum = k; + gi[0].u = seg.epgeominfo[0].u; + gi[0].v = seg.epgeominfo[0].v; + gi[1].u = seg.epgeominfo[1].u; + gi[1].v = seg.epgeominfo[1].v; - FaceDescriptor & fd = mesh.GetFaceDescriptor(k); + int locpnum[2] = {0, 0}; - int oldnf = mesh.GetNSE(); - - Box<3> bb = geom.GetBoundingBox(); - - // int projecttype = PLANESPACE; - - Meshing2OCCSurfaces meshing(TopoDS::Face(geom.fmap(k)), bb, projecttype); - - if (meshing.GetProjectionType() == PLANESPACE) - PrintMessage (2, "Face ", k, " / ", mesh.GetNFD(), " (plane space projection)"); - else - PrintMessage (2, "Face ", k, " / ", mesh.GetNFD(), " (parameter space projection)"); - - if (surfmesherror) - cout << "Surface meshing error occurred before (in " << surfmesherror << " faces)" << endl; - - // Meshing2OCCSurfaces meshing(f2, bb); - meshing.SetStartTime (starttime); - - //(*testout) << "Face " << k << endl << endl; - - - if (meshing.GetProjectionType() == PLANESPACE) - { - int cntp = 0; - glob2loc = 0; - for (i = 1; i <= mesh.GetNSeg(); i++) + for (int j = 0; j < 2; j++) { - Segment & seg = mesh.LineSegment(i); - if (seg.si == k) - { - for (j = 1; j <= 2; j++) + Point<2> uv = {gi[j].u, gi[j].v}; + uv_tree.GetIntersecting(uv, uv, found_points); + + bool found = false; + for(auto& fp : found_points) { - int pi = (j == 1) ? seg[0] : seg[1]; - if (!glob2loc.Get(pi)) - { - meshing.AddPoint (mesh.Point(pi), pi); - cntp++; - glob2loc.Elem(pi) = cntp; - } + if(meshing.GetGlobalIndex(fp - 1) == seg[j]) + { + locpnum[j] = fp; + found = true; + } } - } + if(!found) + { + PointIndex pi = seg[j]; + locpnum[j] = meshing.AddPoint (mesh.Point(pi), pi) + 1; + glob2loc[pi] = locpnum[j]; + gis.Append (gi[j]); + uv_tree.Insert(uv, locpnum[j]); + } } - for (i = 1; i <= mesh.GetNSeg(); i++) - { - Segment & seg = mesh.LineSegment(i); - if (seg.si == k) - { - PointGeomInfo gi0, gi1; - gi0.trignum = gi1.trignum = k; - gi0.u = seg.epgeominfo[0].u; - gi0.v = seg.epgeominfo[0].v; - gi1.u = seg.epgeominfo[1].u; - gi1.v = seg.epgeominfo[1].v; - - meshing.AddBoundaryElement (glob2loc.Get(seg[0]), glob2loc.Get(seg[1]), gi0, gi1); - //(*testout) << gi0.u << " " << gi0.v << endl; - //(*testout) << gi1.u << " " << gi1.v << endl; - } - } - } - else - { - int cntp = 0; - - for (i = 1; i <= mesh.GetNSeg(); i++) - if (mesh.LineSegment(i).si == k) - cntp+=2; - - - Array< PointGeomInfo > gis; - - gis.SetAllocSize (cntp); - gis.SetSize (0); - - for (i = 1; i <= mesh.GetNSeg(); i++) - { - Segment & seg = mesh.LineSegment(i); - if (seg.si == k) - { - PointGeomInfo gi0, gi1; - gi0.trignum = gi1.trignum = k; - gi0.u = seg.epgeominfo[0].u; - gi0.v = seg.epgeominfo[0].v; - gi1.u = seg.epgeominfo[1].u; - gi1.v = seg.epgeominfo[1].v; - - int locpnum[2] = {0, 0}; - - for (j = 0; j < 2; j++) - { - PointGeomInfo gi = (j == 0) ? gi0 : gi1; - - int l; - for (l = 0; l < gis.Size() && locpnum[j] == 0; l++) - { - double dist = sqr (gis[l].u-gi.u)+sqr(gis[l].v-gi.v); - - if (dist < 1e-10) - locpnum[j] = l+1; - } - - if (locpnum[j] == 0) - { - int pi = (j == 0) ? seg[0] : seg[1]; - meshing.AddPoint (mesh.Point(pi), pi); - - gis.SetSize (gis.Size()+1); - gis[l] = gi; - locpnum[j] = l+1; - } - } - - meshing.AddBoundaryElement (locpnum[0], locpnum[1], gi0, gi1); - //(*testout) << gi0.u << " " << gi0.v << endl; - //(*testout) << gi1.u << " " << gi1.v << endl; - - } - } - } - - - - - - // Philippose - 15/01/2009 - double maxh = geom.face_maxh[k-1]; - //double maxh = mparam.maxh; - mparam.checkoverlap = 0; - // int noldpoints = mesh->GetNP(); - int noldsurfel = mesh.GetNSE(); - - GProp_GProps sprops; - BRepGProp::SurfaceProperties(TopoDS::Face(geom.fmap(k)),sprops); - meshing.SetMaxArea(2.*sprops.Mass()); - - MESHING2_RESULT res; - - try { - res = meshing.GenerateMesh (mesh, mparam, maxh, k); - } - - catch (SingularMatrixException) - { - (*myerr) << "Singular Matrix" << endl; - res = MESHING2_GIVEUP; - } - - catch (UVBoundsException) - { - (*myerr) << "UV bounds exceeded" << endl; - res = MESHING2_GIVEUP; - } - - projecttype = PARAMETERSPACE; - - if (res != MESHING2_OK) - { - if (notrys == 1) - { - for (int i = noldsurfel+1; i <= mesh.GetNSE(); i++) - mesh.DeleteSurfaceElement (i); - - mesh.Compress(); - - cout << "retry Surface " << k << endl; - - k--; - projecttype*=-1; - notrys++; - continue; - } - else - { - geom.facemeshstatus[k-1] = -1; - PrintError ("Problem in Surface mesh generation"); - surfmesherror++; - // throw NgException ("Problem in Surface mesh generation"); - } - } - else - { - geom.facemeshstatus[k-1] = 1; - } - - notrys = 1; - - for (i = oldnf+1; i <= mesh.GetNSE(); i++) - mesh.SurfaceElement(i).SetIndex (k); - - } - -// ofstream problemfile("occmesh.rep"); - -// problemfile << "SURFACEMESHING" << endl << endl; - - if (surfmesherror) - { - cout << "WARNING! NOT ALL FACES HAVE BEEN MESHED" << endl; - cout << "SURFACE MESHING ERROR OCCURRED IN " << surfmesherror << " FACES:" << endl; - for (int i = 1; i <= geom.fmap.Extent(); i++) - if (geom.facemeshstatus[i-1] == -1) - { - cout << "Face " << i << endl; -// problemfile << "problem with face " << i << endl; -// problemfile << "vertices: " << endl; - TopExp_Explorer exp0,exp1,exp2; - for ( exp0.Init(TopoDS::Face (geom.fmap(i)), TopAbs_WIRE); exp0.More(); exp0.Next() ) - { - TopoDS_Wire wire = TopoDS::Wire(exp0.Current()); - for ( exp1.Init(wire,TopAbs_EDGE); exp1.More(); exp1.Next() ) - { - TopoDS_Edge edge = TopoDS::Edge(exp1.Current()); - for ( exp2.Init(edge,TopAbs_VERTEX); exp2.More(); exp2.Next() ) - { - TopoDS_Vertex vertex = TopoDS::Vertex(exp2.Current()); - gp_Pnt point = BRep_Tool::Pnt(vertex); -// problemfile << point.X() << " " << point.Y() << " " << point.Z() << endl; - } - } - } -// problemfile << endl; - - } - cout << endl << endl; - cout << "for more information open IGES/STEP Topology Explorer" << endl; -// problemfile.close(); - throw NgException ("Problem in Surface mesh generation"); - } - else - { -// problemfile << "OK" << endl << endl; -// problemfile.close(); + meshing.AddBoundaryElement (locpnum[0], locpnum[1], gi[0], gi[1]); + } + for(const auto& vert : geom.GetFaceVertices(geom.GetFace(k-1))) + { + PointIndex pi = vert->nr + 1; + if(glob2loc[pi] == 0) + { + auto gi = occface.Project(mesh[pi]); + MultiPointGeomInfo mgi; + mgi.AddPointGeomInfo(gi); + glob2loc[pi] = meshing.AddPoint(mesh[pi], pi, &mgi) + 1; + gis.Append(gi); + Point<2> uv = { gi.u, gi.v }; + uv_tree.Insert(uv, glob2loc[pi]); + } + } } + // Philippose - 15/01/2009 + auto& props = OCCGeometry::GetProperties(geom.fmap(k)); + double maxh = min2(geom.face_maxh[k-1], props.maxh); + //double maxh = mparam.maxh; + // int noldpoints = mesh->GetNP(); + int noldsurfel = mesh.GetNSE(); + int layer = props.layer; + static Timer tsurfprop("surfprop"); + tsurfprop.Start(); + GProp_GProps sprops; + BRepGProp::SurfaceProperties(TopoDS::Face(geom.fmap(k)),sprops); + tsurfprop.Stop(); + meshing.SetMaxArea(2.*sprops.Mass()); - if (multithread.terminate || perfstepsend < MESHCONST_OPTSURFACE) - return; + MESHING2_RESULT res; - multithread.task = "Optimizing surface"; + // TODO: check overlap not correctly working here + MeshingParameters mparam_without_overlap = mparam; + mparam_without_overlap.checkoverlap = false; + + try { + static Timer t("GenerateMesh"); RegionTimer reg(t); + res = meshing.GenerateMesh (mesh, mparam_without_overlap, maxh, k, layer); + } - static int timer_opt2d = NgProfiler::CreateTimer ("Optimization 2D"); - NgProfiler::StartTimer (timer_opt2d); - - for (k = 1; k <= mesh.GetNFD(); k++) + catch (SingularMatrixException) { - // if (k != 42) continue; - // if (k != 36) continue; - - // (*testout) << "optimize face " << k << endl; - multithread.percent = 100 * k / (mesh.GetNFD() + VSMALL); - - FaceDescriptor & fd = mesh.GetFaceDescriptor(k); - - PrintMessage (1, "Optimize Surface ", k); - for (i = 1; i <= mparam.optsteps2d; i++) - { - // (*testout) << "optstep " << i << endl; - if (multithread.terminate) return; - - { - MeshOptimize2dOCCSurfaces meshopt(geom); - meshopt.SetFaceIndex (k); - meshopt.SetImproveEdges (0); - meshopt.SetMetricWeight (mparam.elsizeweight); - //meshopt.SetMetricWeight (0.2); - meshopt.SetWriteStatus (0); - - // (*testout) << "EdgeSwapping (mesh, (i > mparam.optsteps2d/2))" << endl; - meshopt.EdgeSwapping (mesh, (i > mparam.optsteps2d/2)); - } - - if (multithread.terminate) return; - { - MeshOptimize2dOCCSurfaces meshopt(geom); - meshopt.SetFaceIndex (k); - meshopt.SetImproveEdges (0); - //meshopt.SetMetricWeight (0.2); - meshopt.SetMetricWeight (mparam.elsizeweight); - meshopt.SetWriteStatus (0); - - // (*testout) << "ImproveMesh (mesh)" << endl; - meshopt.ImproveMesh (mesh, mparam); - } - - { - MeshOptimize2dOCCSurfaces meshopt(geom); - meshopt.SetFaceIndex (k); - meshopt.SetImproveEdges (0); - //meshopt.SetMetricWeight (0.2); - meshopt.SetMetricWeight (mparam.elsizeweight); - meshopt.SetWriteStatus (0); - - // (*testout) << "CombineImprove (mesh)" << endl; - meshopt.CombineImprove (mesh); - } - - if (multithread.terminate) return; - { - MeshOptimize2dOCCSurfaces meshopt(geom); - meshopt.SetFaceIndex (k); - meshopt.SetImproveEdges (0); - //meshopt.SetMetricWeight (0.2); - meshopt.SetMetricWeight (mparam.elsizeweight); - meshopt.SetWriteStatus (0); - - // (*testout) << "ImproveMesh (mesh)" << endl; - meshopt.ImproveMesh (mesh, mparam); - } - } - + // (*myerr) << "Singular Matrix" << endl; + res = MESHING2_GIVEUP; } - - mesh.CalcSurfacesOfNode(); - mesh.Compress(); - - NgProfiler::StopTimer (timer_opt2d); - - multithread.task = savetask; - - // Gerhard BEGIN - for(int i = 0; i maxhdom; - maxhdom.SetSize (geom.NrSolids()); - maxhdom = mparam.maxh; - - mesh.SetMaxHDomain (maxhdom); - - Box<3> bb = geom.GetBoundingBox(); - bb.Increase (bb.Diam()/10); - - mesh.SetLocalH (bb.PMin(), bb.PMax(), 0.5); - - if (mparam.uselocalh) + catch (UVBoundsException) { - const char * savetask = multithread.task; - multithread.percent = 0; + // (*myerr) << "UV bounds exceeded" << endl; + res = MESHING2_GIVEUP; + } - mesh.SetLocalH (bb.PMin(), bb.PMax(), mparam.grading); + static Timer t1("rest of loop"); RegionTimer reg1(t1); + + bool meshing_failed = res != MESHING2_OK; + if(meshing_failed && delete_on_failure) + { + for (SurfaceElementIndex sei = noldsurfel; sei < mesh.GetNSE(); sei++) + mesh.Delete(sei); - int nedges = geom.emap.Extent(); + mesh.Compress(); + } - double mincurvelength = IGNORECURVELENGTH; - double maxedgelen = 0; - double minedgelen = 1e99; + for (SurfaceElementIndex sei = oldnf; sei < mesh.GetNSE(); sei++) + mesh[sei].SetIndex (k); - if(occparam.resthminedgelenenable) - { - mincurvelength = occparam.resthminedgelen; - if(mincurvelength < IGNORECURVELENGTH) mincurvelength = IGNORECURVELENGTH; - } + auto n_illegal_trigs = mesh.FindIllegalTrigs(); + PrintMessage (3, n_illegal_trigs, " illegal triangles"); + return meshing_failed; + } - multithread.task = "Setting local mesh size (elements per edge)"; - // setting elements per edge + void OCCSetLocalMeshSize(const OCCGeometry & geom, Mesh & mesh, + const MeshingParameters & mparam, const OCCParameters& occparam) + { + static Timer t1("OCCSetLocalMeshSize"); + RegionTimer regt(t1); + mesh.SetGlobalH (mparam.maxh); + mesh.SetMinimalH (mparam.minh); - for (int i = 1; i <= nedges && !multithread.terminate; i++) - { + NgArray maxhdom; + maxhdom.SetSize (geom.NrSolids()); + maxhdom = mparam.maxh; + int maxlayer = 1; + + int dom = 0; + for (TopExp_Explorer e(geom.GetShape(), TopAbs_SOLID); e.More(); e.Next(), dom++) + { + auto& props = OCCGeometry::GetProperties(e.Current()); + maxhdom[dom] = min2(maxhdom[dom], props.maxh); + maxlayer = max2(maxlayer, props.layer); + } + + + mesh.SetMaxHDomain (maxhdom); + + Box<3> bb = geom.GetBoundingBox(); + bb.Increase (bb.Diam()/10); + + if (mparam.uselocalh) + { + const char * savetask = multithread.task; + multithread.percent = 0; + + for(auto layer : Range(1, maxlayer+1)) + mesh.SetLocalH (bb.PMin(), bb.PMax(), mparam.grading, layer); + + int nedges = geom.emap.Extent(); + + double mincurvelength = IGNORECURVELENGTH; + double maxedgelen = 0; + double minedgelen = 1e99; + + if(occparam.resthminedgelenenable) + { + mincurvelength = occparam.resthminedgelen; + if(mincurvelength < IGNORECURVELENGTH) mincurvelength = IGNORECURVELENGTH; + } + + multithread.task = "Setting local mesh size (elements per edge)"; + + // Philippose - 23/01/2009 + // Find all the parent faces of a given edge + // and limit the mesh size of the edge based on the + // mesh size limit of the face + TopTools_IndexedDataMapOfShapeListOfShape edge_face_map; + edge_face_map.Clear(); + TopExp::MapShapesAndAncestors(geom.shape, TopAbs_EDGE, TopAbs_FACE, edge_face_map); + + // setting elements per edge + for (int i = 1; i <= nedges && !multithread.terminate; i++) + { TopoDS_Edge e = TopoDS::Edge (geom.emap(i)); + int layer = OCCGeometry::GetProperties(e).layer; multithread.percent = 100 * (i-1)/double(nedges); if (BRep_Tool::Degenerated(e)) continue; - GProp_GProps system; - BRepGProp::LinearProperties(e, system); - double len = system.Mass(); + double len = Mass(e); if (len < mincurvelength) + { + (*testout) << "ignored" << endl; + continue; + } + + bool is_identified_edge = false; + // TODO: change to use hash value + const auto& gedge = geom.GetEdge(e); + auto& v0 = gedge.GetStartVertex(); + auto& v1 = gedge.GetEndVertex(); + for(auto & ident : v0.identifications) { - (*testout) << "ignored" << endl; - continue; + auto other = ident.from == &v0 ? ident.to : ident.from; + if(other->nr == v1.nr && ident.type == Identifications::CLOSESURFACES) + { + is_identified_edge = true; + break; + } } + if(is_identified_edge) + continue; + double localh = len/mparam.segmentsperedge; double s0, s1; - // Philippose - 23/01/2009 - // Find all the parent faces of a given edge - // and limit the mesh size of the edge based on the - // mesh size limit of the face - TopTools_IndexedDataMapOfShapeListOfShape edge_face_map; - edge_face_map.Clear(); - - TopExp::MapShapesAndAncestors(geom.shape, TopAbs_EDGE, TopAbs_FACE, edge_face_map); const TopTools_ListOfShape& parent_faces = edge_face_map.FindFromKey(e); TopTools_ListIteratorOfListOfShape parent_face_list; for(parent_face_list.Initialize(parent_faces); parent_face_list.More(); parent_face_list.Next()) - { - TopoDS_Face parent_face = TopoDS::Face(parent_face_list.Value()); + { + TopoDS_Face parent_face = TopoDS::Face(parent_face_list.Value()); - int face_index = geom.fmap.FindIndex(parent_face); + int face_index = geom.fmap.FindIndex(parent_face); - if(face_index >= 1) localh = min(localh,geom.face_maxh[face_index - 1]); - } + if(face_index >= 1) localh = min(localh,geom.face_maxh[face_index - 1]); + localh = min2(localh, OCCGeometry::GetProperties(parent_face).maxh); + } Handle(Geom_Curve) c = BRep_Tool::Curve(e, s0, s1); + localh = min2(localh, OCCGeometry::GetProperties(e).maxh); maxedgelen = max (maxedgelen, len); minedgelen = min (minedgelen, len); - - // Philippose - 23/01/2009 - // Modified the calculation of maxj, because the - // method used so far always results in maxj = 2, - // which causes the localh to be set only at the - // starting, mid and end of the edge. - // Old Algorithm: - // int maxj = 2 * (int) ceil (localh/len); - int maxj = max((int) ceil(len/localh), 2); + int maxj = max((int) ceil(len/localh)*2, 2); for (int j = 0; j <= maxj; j++) - { - gp_Pnt pnt = c->Value (s0+double(j)/maxj*(s1-s0)); - mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), localh); - } - } + { + gp_Pnt pnt = c->Value (s0+double(j)/maxj*(s1-s0)); + mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), localh, layer); + } + } - multithread.task = "Setting local mesh size (edge curvature)"; + multithread.task = "Setting local mesh size (edge curvature)"; - // setting edge curvature + // setting edge curvature - int nsections = 20; + int nsections = 20; - for (int i = 1; i <= nedges && !multithread.terminate; i++) - { + for (int i = 1; i <= nedges && !multithread.terminate; i++) + { double maxcur = 0; multithread.percent = 100 * (i-1)/double(nedges); TopoDS_Edge edge = TopoDS::Edge (geom.emap(i)); + int layer = OCCGeometry::GetProperties(edge).layer; if (BRep_Tool::Degenerated(edge)) continue; double s0, s1; Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); @@ -1107,32 +613,34 @@ namespace netgen BRepLProp_CLProps prop(brepc, 2, 1e-5); for (int j = 1; j <= nsections; j++) - { - double s = s0 + j/(double) nsections * (s1-s0); - prop.SetParameter (s); - double curvature = prop.Curvature(); - if(curvature> maxcur) maxcur = curvature; + { + double s = s0 + j/(double) nsections * (s1-s0); + prop.SetParameter (s); + double curvature = 0; + if(prop.IsTangentDefined()) + curvature = prop.Curvature(); + if(curvature> maxcur) maxcur = curvature; - if (curvature >= 1e99) + if (curvature >= 1e99) continue; - gp_Pnt pnt = c->Value (s); + gp_Pnt pnt = c->Value (s); - mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), ComputeH (fabs(curvature))); - } - // (*testout) << "edge " << i << " max. curvature: " << maxcur << endl; - } + mesh.RestrictLocalH (Point3d(pnt.X(), pnt.Y(), pnt.Z()), ComputeH (fabs(curvature), mparam), layer); + } + } - multithread.task = "Setting local mesh size (face curvature)"; + multithread.task = "Setting local mesh size (face curvature)"; - // setting face curvature + // setting face curvature - int nfaces = geom.fmap.Extent(); + int nfaces = geom.fmap.Extent(); - for (int i = 1; i <= nfaces && !multithread.terminate; i++) - { + for (int i = 1; i <= nfaces && !multithread.terminate; i++) + { multithread.percent = 100 * (i-1)/double(nfaces); TopoDS_Face face = TopoDS::Face(geom.fmap(i)); + int layer = OCCGeometry::GetProperties(face).layer; TopLoc_Location loc; Handle(Geom_Surface) surf = BRep_Tool::Surface (face); Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc); @@ -1145,341 +653,170 @@ namespace netgen } BRepAdaptor_Surface sf(face, Standard_True); - BRepLProp_SLProps prop(sf, 2, 1e-5); + // one prop for evaluating and one for derivatives + BRepLProp_SLProps prop(sf, 0, 1e-5); + BRepLProp_SLProps prop2(sf, 2, 1e-5); int ntriangles = triangulation -> NbTriangles(); for (int j = 1; j <= ntriangles; j++) - { - gp_Pnt p[3]; - gp_Pnt2d par[3]; + { + gp_Pnt p[3]; + gp_Pnt2d par[3]; - for (int k = 1; k <=3; k++) - { - int n = triangulation->Triangles()(j)(k); - p[k-1] = triangulation->Nodes()(n).Transformed(loc); - par[k-1] = triangulation->UVNodes()(n); - } + for (int k = 1; k <=3; k++) + { + // int n = triangulation->Triangles()(j)(k); + // p[k-1] = triangulation->Nodes()(n).Transformed(loc); + // par[k-1] = triangulation->UVNodes()(n); + // fix for OCC7.6.0-dev + int n = triangulation->Triangle(j)(k); + p[k-1] = triangulation->Node(n).Transformed(loc); + par[k-1] = triangulation->UVNode(n); + } - //double maxside = 0; - //maxside = max (maxside, p[0].Distance(p[1])); - //maxside = max (maxside, p[0].Distance(p[2])); - //maxside = max (maxside, p[1].Distance(p[2])); - //cout << "\rFace " << i << " pos11 ntriangles " << ntriangles << " maxside " << maxside << flush; + //double maxside = 0; + //maxside = max (maxside, p[0].Distance(p[1])); + //maxside = max (maxside, p[0].Distance(p[2])); + //maxside = max (maxside, p[1].Distance(p[2])); + //cout << "\rFace " << i << " pos11 ntriangles " << ntriangles << " maxside " << maxside << flush; - RestrictHTriangle (par[0], par[1], par[2], &prop, mesh, 0); - //cout << "\rFace " << i << " pos12 ntriangles " << ntriangles << flush; - } - } + RestrictHTriangle (par[0], par[1], par[2], &prop, &prop2, mesh, 0, 0, layer, mparam); + //cout << "\rFace " << i << " pos12 ntriangles " << ntriangles << flush; + } + } - // setting close edges + // setting close edges - if (occparam.resthcloseedgeenable) - { + if (mparam.closeedgefac.has_value()) + { multithread.task = "Setting local mesh size (close edges)"; int sections = 100; - Array lines(sections*nedges); + NgArray lines(sections*nedges); + /* BoxTree<3> * searchtree = new BoxTree<3> (bb.PMin(), bb.PMax()); - + */ + BoxTree<3> searchtree(bb.PMin(), bb.PMax()); + int nlines = 0; + Array edgenumber; for (int i = 1; i <= nedges && !multithread.terminate; i++) - { - TopoDS_Edge edge = TopoDS::Edge (geom.emap(i)); - if (BRep_Tool::Degenerated(edge)) continue; + { + TopoDS_Edge edge = TopoDS::Edge (geom.emap(i)); + int layer = OCCGeometry::GetProperties(edge).layer; + if (BRep_Tool::Degenerated(edge)) continue; - double s0, s1; - Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); - BRepAdaptor_Curve brepc(edge); - BRepLProp_CLProps prop(brepc, 1, 1e-5); - prop.SetParameter (s0); + double s0, s1; + Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); + BRepAdaptor_Curve brepc(edge); + BRepLProp_CLProps prop(brepc, 1, 1e-5); + prop.SetParameter (s0); - gp_Vec d0 = prop.D1().Normalized(); - double s_start = s0; - int count = 0; - for (int j = 1; j <= sections; j++) - { - double s = s0 + (s1-s0)*(double)j/(double)sections; - prop.SetParameter (s); - gp_Vec d1 = prop.D1().Normalized(); - double cosalpha = fabs(d0*d1); - if ((j == sections) || (cosalpha < cos(10.0/180.0*M_PI))) + gp_Vec d0 = prop.D1().Normalized(); + double s_start = s0; + int count = 0; + for (int j = 1; j <= sections; j++) { - count++; - gp_Pnt p0 = c->Value (s_start); - gp_Pnt p1 = c->Value (s); - lines[nlines].p0 = Point<3> (p0.X(), p0.Y(), p0.Z()); - lines[nlines].p1 = Point<3> (p1.X(), p1.Y(), p1.Z()); + double s = s0 + (s1-s0)*(double)j/(double)sections; + prop.SetParameter (s); + gp_Vec d1 = prop.D1().Normalized(); + double cosalpha = fabs(d0*d1); + if ((j == sections) || (cosalpha < cos(10.0/180.0*M_PI))) + { + count++; + gp_Pnt p0 = c->Value (s_start); + gp_Pnt p1 = c->Value (s); + lines[nlines].p0 = Point<3> (p0.X(), p0.Y(), p0.Z()); + lines[nlines].p1 = Point<3> (p1.X(), p1.Y(), p1.Z()); + lines[nlines].layer = layer; - Box3d box; - box.SetPoint (Point3d(lines[nlines].p0)); - box.AddPoint (Point3d(lines[nlines].p1)); + Box3d box; + box.SetPoint (Point3d(lines[nlines].p0)); + box.AddPoint (Point3d(lines[nlines].p1)); - searchtree->Insert (box.PMin(), box.PMax(), nlines+1); - nlines++; + searchtree.Insert (box.PMin(), box.PMax(), nlines+1); + nlines++; + edgenumber.Append(i); - s_start = s; - d0 = d1; + s_start = s; + d0 = d1; + } } - } - } + } - Array linenums; + NgArray linenums; + auto is_identified_edge = [&](int e0, int e1) { + const auto& edge0 = geom.GetEdge(e0-1); + const auto& edge1 = geom.GetEdge(e1-1); + + if(edge0.primary == edge1.primary) + return true; + + Array v0 = { &edge0.GetStartVertex(), &edge0.GetEndVertex() }; + Array v1 = { &edge1.GetStartVertex(), &edge1.GetEndVertex() }; + for(auto i : Range(2)) + for(auto j : Range(2)) + if(v0[i]->primary == v1[j]->primary) + return true; + + return false; + }; for (int i = 0; i < nlines; i++) - { - multithread.percent = (100*i)/double(nlines); - Line & line = lines[i]; + { + multithread.percent = (100*i)/double(nlines); + Line & line = lines[i]; - Box3d box; - box.SetPoint (Point3d(line.p0)); - box.AddPoint (Point3d(line.p1)); - double maxhline = max (mesh.GetH(box.PMin()), - mesh.GetH(box.PMax())); - box.Increase(maxhline); + Box3d box; + box.SetPoint (Point3d(line.p0)); + box.AddPoint (Point3d(line.p1)); + double maxhline = max (mesh.GetH(box.PMin(), line.layer), + mesh.GetH(box.PMax(), line.layer)); + box.Increase(maxhline); - double mindist = 1e99; - linenums.SetSize(0); - searchtree->GetIntersecting(box.PMin(),box.PMax(),linenums); + double mindist = 1e99; + linenums.SetSize(0); + searchtree.GetIntersecting(box.PMin(),box.PMax(),linenums); - for (int j = 0; j < linenums.Size(); j++) - { - int num = linenums[j]-1; - if (i == num) continue; - if ((line.p0-lines[num].p0).Length2() < 1e-15) continue; - if ((line.p0-lines[num].p1).Length2() < 1e-15) continue; - if ((line.p1-lines[num].p0).Length2() < 1e-15) continue; - if ((line.p1-lines[num].p1).Length2() < 1e-15) continue; - mindist = min (mindist, line.Dist(lines[num])); - } + for (int j = 0; j < linenums.Size(); j++) + { + int num = linenums[j]-1; + if (i == num) continue; + if (line.layer != lines[num].layer) continue; + if( is_identified_edge(edgenumber[i], edgenumber[num]) ) continue; + if ((line.p0-lines[num].p0).Length2() < 1e-15) continue; + if ((line.p0-lines[num].p1).Length2() < 1e-15) continue; + if ((line.p1-lines[num].p0).Length2() < 1e-15) continue; + if ((line.p1-lines[num].p1).Length2() < 1e-15) continue; + mindist = min (mindist, line.Dist(lines[num])); + } - mindist /= (occparam.resthcloseedgefac + VSMALL); + mindist /= (*mparam.closeedgefac + VSMALL); - if (mindist < 1e-3) - { - (*testout) << "extremely small local h: " << mindist - << " --> setting to 1e-3" << endl; - (*testout) << "somewhere near " << line.p0 << " - " << line.p1 << endl; - mindist = 1e-3; - } + if (mindist < 1e-3 * bb.Diam()) + { + (*testout) << "extremely small local h: " << mindist + << " --> setting to " << 1e-3 * bb.Diam() << endl; + (*testout) << "somewhere near " << line.p0 << " - " << line.p1 << endl; + mindist = 1e-3 * bb.Diam(); + } - mesh.RestrictLocalHLine(line.p0, line.p1, mindist); - } - } + mesh.RestrictLocalHLine(line.p0, line.p1, mindist, line.layer); + } + } - multithread.task = savetask; + for (auto mspnt : mparam.meshsize_points) + mesh.RestrictLocalH(mspnt.pnt, mspnt.h, mspnt.layer); + + multithread.task = savetask; } - // Philippose - 09/03/2009 - // Added the capability to load the mesh size from a - // file also for OpenCascade Geometry - // Note: - // ** If the "uselocalh" option is ticked in - // the "mesh options...insider" menu, the mesh - // size will be further modified by the topology - // analysis routines. - // ** To use the mesh size file as the sole source - // for defining the mesh size, uncheck the "uselocalh" - // option. - mesh.LoadLocalMeshSize (mparam.meshsizefilename); - } - - - - int OCCGenerateMesh (OCCGeometry & geom, shared_ptr & mesh, MeshingParameters & mparam) - { - multithread.percent = 0; - - if (mparam.perfstepsstart <= MESHCONST_ANALYSE) - { - if(mesh.get() == nullptr) - mesh = make_shared(); - mesh->geomtype = Mesh::GEOM_OCC; - - OCCSetLocalMeshSize(geom,*mesh); - } - - if (multithread.terminate || mparam.perfstepsend <= MESHCONST_ANALYSE) - return TCL_OK; - - if (mparam.perfstepsstart <= MESHCONST_MESHEDGES) - { - OCCFindEdges (geom, *mesh); - - /* - cout << "Removing redundant points" << endl; - - int i, j; - int np = mesh->GetNP(); - Array equalto; - - equalto.SetSize (np); - equalto = 0; - - for (i = 1; i <= np; i++) - { - for (j = i+1; j <= np; j++) - { - if (!equalto[j-1] && (Dist2 (mesh->Point(i), mesh->Point(j)) < 1e-12)) - equalto[j-1] = i; - } - } - - for (i = 1; i <= np; i++) - if (equalto[i-1]) - { - cout << "Point " << i << " is equal to Point " << equalto[i-1] << endl; - for (j = 1; j <= mesh->GetNSeg(); j++) - { - Segment & seg = mesh->LineSegment(j); - if (seg[0] == i) seg[0] = equalto[i-1]; - if (seg[1] == i) seg[1] = equalto[i-1]; - } - } - - cout << "Removing degenerated segments" << endl; - for (j = 1; j <= mesh->GetNSeg(); j++) - { - Segment & seg = mesh->LineSegment(j); - if (seg[0] == seg[1]) - { - mesh->DeleteSegment(j); - cout << "Deleting Segment " << j << endl; - } - } - - mesh->Compress(); - */ - - /* - for (int i = 1; i <= geom.fmap.Extent(); i++) - { - Handle(Geom_Surface) hf1 = - BRep_Tool::Surface(TopoDS::Face(geom.fmap(i))); - for (int j = i+1; j <= geom.fmap.Extent(); j++) - { - Handle(Geom_Surface) hf2 = - BRep_Tool::Surface(TopoDS::Face(geom.fmap(j))); - if (hf1 == hf2) cout << "face " << i << " and face " << j << " lie on same surface" << endl; - } - } - */ - -#ifdef LOG_STREAM - (*logout) << "Edges meshed" << endl - << "time = " << GetTime() << " sec" << endl - << "points: " << mesh->GetNP() << endl; -#endif - } - - if (multithread.terminate || mparam.perfstepsend <= MESHCONST_MESHEDGES) - return TCL_OK; - - if (mparam.perfstepsstart <= MESHCONST_MESHSURFACE) - { - OCCMeshSurface (geom, *mesh, mparam.perfstepsend); - if (multithread.terminate) return TCL_OK; - -#ifdef LOG_STREAM - (*logout) << "Surfaces meshed" << endl - << "time = " << GetTime() << " sec" << endl - << "points: " << mesh->GetNP() << endl; -#endif - -#ifdef STAT_STREAM - (*statout) << mesh->GetNSeg() << " & " - << mesh->GetNSE() << " & - &" - << GetTime() << " & " << endl; -#endif - - // MeshQuality2d (*mesh); - mesh->CalcSurfacesOfNode(); - } - - if (multithread.terminate || mparam.perfstepsend <= MESHCONST_OPTSURFACE) - return TCL_OK; - - if (mparam.perfstepsstart <= MESHCONST_MESHVOLUME) - { - multithread.task = "Volume meshing"; - - MESHING3_RESULT res = MeshVolume (mparam, *mesh); - -/* - ofstream problemfile("occmesh.rep",ios_base::app); - - problemfile << "VOLUMEMESHING" << endl << endl; - if(res != MESHING3_OK) - problemfile << "ERROR" << endl << endl; - else - problemfile << "OK" << endl - << mesh->GetNE() << " elements" << endl << endl; - - problemfile.close(); -*/ - - if (res != MESHING3_OK) return TCL_ERROR; - - if (multithread.terminate) return TCL_OK; - - RemoveIllegalElements (*mesh); - if (multithread.terminate) return TCL_OK; - - MeshQuality3d (*mesh); - -#ifdef STAT_STREAM - (*statout) << GetTime() << " & "; -#endif - -#ifdef LOG_STREAM - (*logout) << "Volume meshed" << endl - << "time = " << GetTime() << " sec" << endl - << "points: " << mesh->GetNP() << endl; -#endif - } - - if (multithread.terminate || mparam.perfstepsend <= MESHCONST_MESHVOLUME) - return TCL_OK; - - if (mparam.perfstepsstart <= MESHCONST_OPTVOLUME) - { - multithread.task = "Volume optimization"; - - OptimizeVolume (mparam, *mesh); - if (multithread.terminate) return TCL_OK; - -#ifdef STAT_STREAM - (*statout) << GetTime() << " & " - << mesh->GetNE() << " & " - << mesh->GetNP() << " " << '\\' << '\\' << " \\" << "hline" << endl; -#endif - -#ifdef LOG_STREAM - (*logout) << "Volume optimized" << endl - << "time = " << GetTime() << " sec" << endl - << "points: " << mesh->GetNP() << endl; -#endif - - // cout << "Optimization complete" << endl; - - } - - (*testout) << "NP: " << mesh->GetNP() << endl; - for (int i = 1; i <= mesh->GetNP(); i++) - (*testout) << mesh->Point(i) << endl; - - (*testout) << endl << "NSegments: " << mesh->GetNSeg() << endl; - for (int i = 1; i <= mesh->GetNSeg(); i++) - (*testout) << mesh->LineSegment(i) << endl; - - for (int i = 0; i < mesh->GetNDomains(); i++) - if(geom.snames.Size()) - mesh->SetMaterial( i+1, geom.snames[i] ); - return TCL_OK; - } + mesh.LoadLocalMeshSize (mparam.meshsizefilename); + } } #endif diff --git a/libsrc/occ/occgeom.cpp b/libsrc/occ/occgeom.cpp index cc721e57..d53dbf16 100644 --- a/libsrc/occ/occgeom.cpp +++ b/libsrc/occ/occgeom.cpp @@ -1,79 +1,258 @@ #ifdef OCCGEOMETRY -#include -#include #include -#include "ShapeAnalysis_ShapeTolerance.hxx" -#include "ShapeAnalysis_ShapeContents.hxx" -#include "ShapeAnalysis_CheckSmallFace.hxx" -#include "ShapeAnalysis_DataMapOfShapeListOfReal.hxx" -#include "ShapeAnalysis_Surface.hxx" +#include -#include "BRepCheck_Analyzer.hxx" -#include "BRepLib.hxx" -#include "ShapeBuild_ReShape.hxx" -#include "ShapeFix.hxx" -#include "ShapeFix_FixSmallFace.hxx" +#include +#include + +#include "occ_vertex.hpp" +#include "occ_edge.hpp" +#include "occ_face.hpp" +#include "occ_solid.hpp" +#include "occgeom.hpp" #include "Partition_Spliter.hxx" -#include "BRepAlgoAPI_Fuse.hxx" -#include "XSControl_WorkSession.hxx" -#include "XSControl_TransferReader.hxx" -#include "StepRepr_RepresentationItem.hxx" - -#ifndef _Standard_Version_HeaderFile -#include -#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #if OCC_VERSION_HEX < 0x070000 // pass #elif OCC_VERSION_HEX < 0x070200 - #include "StlTransfer.hxx" - #include "TopoDS_Iterator.hxx" + #include + #include #else - #include "TopoDS_Iterator.hxx" + #include #endif namespace netgen { -void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * aReader, char * acName) -{ - const Handle(XSControl_WorkSession)& theSession = aReader->Reader().WS(); - const Handle(XSControl_TransferReader)& aTransferReader = - theSession->TransferReader(); + void LoadOCCInto(OCCGeometry* occgeo, const filesystem::path & filename); + void PrintContents (OCCGeometry * geom); - Handle(Standard_Transient) anEntity = - aTransferReader->EntityFromShapeResult(theShape, 1); + TopTools_IndexedMapOfShape OCCGeometry::global_shape_property_indices; + std::vector OCCGeometry::global_shape_properties; + TopTools_IndexedMapOfShape OCCGeometry::global_identification_indices; + std::vector> OCCGeometry::global_identifications; - if (anEntity.IsNull()) { - // as just mapped - anEntity = aTransferReader->EntityFromShapeResult (theShape,-1); - } + TopoDS_Shape ListOfShapes::Max(gp_Vec dir) + { + double maxval = -1e99; + TopoDS_Shape maxshape; + for (auto shape : *this) + { + GProp_GProps props; + gp_Pnt center; - if (anEntity.IsNull()) { - // as anything - anEntity = aTransferReader->EntityFromShapeResult (theShape,4); - } + switch (shape.ShapeType()) + { + case TopAbs_VERTEX: + center = BRep_Tool::Pnt (TopoDS::Vertex(shape)); break; + case TopAbs_FACE: + BRepGProp::SurfaceProperties (shape, props); + center = props.CentreOfMass(); + break; + default: + BRepGProp::LinearProperties(shape, props); + center = props.CentreOfMass(); + } - if (anEntity.IsNull()) { - cout<<"Warning: XSInterVertex_STEPReader::ReadAttributes()\nentity not found"< maxval) + { + maxval = val; + maxshape = shape; + } } - else - strcpy(acName, aReprItem->Name()->ToCString()); - } -} + return maxshape; + } + TopoDS_Shape ListOfShapes::Nearest(gp_Pnt pnt) + { + double mindist = 1e99; + TopoDS_Shape nearestshape; + auto vertex = BRepBuilderAPI_MakeVertex (pnt).Vertex(); + + for (auto shape : *this) + { + double dist = BRepExtrema_DistShapeShape(shape, vertex).Value(); + if (dist < mindist) + { + nearestshape = shape; + mindist = dist; + } + } + return nearestshape; + } + ListOfShapes ListOfShapes::SubShapes(TopAbs_ShapeEnum type) const + { + TopTools_MapOfShape check_unique; + ListOfShapes sub; + for(const auto& shape : *this) + for(TopExp_Explorer e(shape, type); e.More(); e.Next()) + if(const auto& s = e.Current(); !check_unique.Contains(s)) + { + check_unique.Add(s); + sub.push_back(s); + } + return sub; + } + + OCCGeometry::OCCGeometry(const TopoDS_Shape& _shape, int aoccdim, bool copy) + { + if(copy) + { + auto filename = GetTempFilename(); + step_utils::WriteSTEP(_shape, filename); + LoadOCCInto(this, filename); + dimension = aoccdim; + filesystem::remove(filename); + } + else + { + shape = _shape; + changed = 1; + dimension = aoccdim; + BuildFMap(); + CalcBoundingBox(); + PrintContents (this); + } + } + + const GeometryShape & OCCGeometry :: GetShape(const TopoDS_Shape & shape) const + { + switch (shape.ShapeType()) + { + case TopAbs_VERTEX: + return GetVertex(shape); + case TopAbs_EDGE: + return GetEdge(shape); + case TopAbs_FACE: + return GetFace(shape); + default: + throw Exception("unknown shape type"); + } + } + + const GeometryVertex & OCCGeometry :: GetVertex(const TopoDS_Shape & shape) const + { + return *vertices[vmap.FindIndex(shape)-1]; + } + + const GeometryEdge & OCCGeometry :: GetEdge(const TopoDS_Shape & shape) const + { + return *edges[emap.FindIndex(shape)-1]; + } + + const GeometryFace & OCCGeometry :: GetFace(const TopoDS_Shape & shape) const + { + return *faces[fmap.FindIndex(shape)-1]; + } + + + string STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * aReader) + { + const Handle(XSControl_WorkSession)& theSession = aReader->Reader().WS(); + const Handle(XSControl_TransferReader)& aTransferReader = + theSession->TransferReader(); + + Handle(Standard_Transient) anEntity = + aTransferReader->EntityFromShapeResult(theShape, 1); + + if (anEntity.IsNull()) // as just mapped + anEntity = aTransferReader->EntityFromShapeResult (theShape,-1); + + if (anEntity.IsNull()) // as anything + anEntity = aTransferReader->EntityFromShapeResult (theShape,4); + + if (anEntity.IsNull()) + { + cout<<"Warning: cannot get entity from shape" <Name()->ToCString();; + + auto bReprItem = Handle(StepBasic_ProductDefinitionRelationship)::DownCast(anEntity); + if (!bReprItem.IsNull()) + return bReprItem->Description()->ToCString(); + + cout<<"Warning: unknown entity type " << anEntity->DynamicType() << endl; + return "none"; + } + + void OCCGeometry :: Analyse(Mesh& mesh, + const MeshingParameters& mparam) const + { + OCCSetLocalMeshSize(*this, mesh, mparam, occparam); + } + + bool OCCGeometry :: MeshFace(Mesh& mesh, + const MeshingParameters& mparam, int nr, FlatArray glob2loc) const + { + MeshingParameters local_mp = mparam; + auto face = TopoDS::Face(fmap(nr+1)); + if(auto quad_dominated = OCCGeometry::GetProperties(face).quad_dominated; quad_dominated.has_value()) + local_mp.quad = *quad_dominated; + + bool failed = OCCMeshFace(*this, mesh, glob2loc, local_mp, nr, PARAMETERSPACE, true); + if(failed) + failed = OCCMeshFace(*this, mesh, glob2loc, local_mp, nr, PLANESPACE, false); + + if(failed) + { + facemeshstatus[nr] = -1; + PrintError ("Problem in Surface mesh generation"); + } + else + { + facemeshstatus[nr] = 1; + } + return failed; + } void OCCGeometry :: PrintNrShapes () { @@ -120,34 +299,129 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a (*testout) << endl; - cout << "Highest entry in topology hierarchy: " << endl; + cout << IM(3) << "Highest entry in topology hierarchy: " << endl; if (count) - cout << count << " composite solid(s)" << endl; + cout << IM(3) << count << " composite solid(s)" << endl; else if (geom->somap.Extent()) - cout << geom->somap.Extent() << " solid(s)" << endl; + cout << IM(3) << geom->somap.Extent() << " solid(s)" << endl; else if (geom->shmap.Extent()) - cout << geom->shmap.Extent() << " shells(s)" << endl; + cout << IM(3) << geom->shmap.Extent() << " shells(s)" << endl; else if (geom->fmap.Extent()) - cout << geom->fmap.Extent() << " face(s)" << endl; + cout << IM(3) << geom->fmap.Extent() << " face(s)" << endl; else if (geom->wmap.Extent()) - cout << geom->wmap.Extent() << " wire(s)" << endl; + cout << IM(3) << geom->wmap.Extent() << " wire(s)" << endl; else if (geom->emap.Extent()) - cout << geom->emap.Extent() << " edge(s)" << endl; + cout << IM(3) << geom->emap.Extent() << " edge(s)" << endl; else if (geom->vmap.Extent()) - cout << geom->vmap.Extent() << " vertices(s)" << endl; + cout << IM(3) << geom->vmap.Extent() << " vertices(s)" << endl; else - cout << "no entities" << endl; + cout << IM(3) << "no entities" << endl; } + 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 + +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = aBuilder.History (); + + for (TopExp_Explorer e(shape, TopAbs_SOLID); e.More(); e.Next()) + { + if (auto name = OCCGeometry::GetProperties(e.Current()).name) + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).name = *name; + } +#endif // OCC_HAVE_HISTORY + + // result of the operation + shape = aBuilder.Shape(); + BuildFMap(); + } + void OCCGeometry :: HealGeometry () { int nrc = 0, nrcs = 0, @@ -166,9 +440,9 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a for (exp0.Init(shape, TopAbs_COMPSOLID); exp0.More(); exp0.Next()) nrcs++; 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()) { @@ -199,25 +473,13 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a 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); - for (exp0.Init (shape, TopAbs_FACE); exp0.More(); exp0.Next()) { - // Variable to hold the colour (if there exists one) of - // the current face being processed - Quantity_Color face_colour; - TopoDS_Face face = TopoDS::Face (exp0.Current()); - - if(face_colours.IsNull() - || (!(face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour)))) - { - // Set the default face colour to green (Netgen Standard) - // if no colour has been defined for the face - face_colour = Quantity_Color(0.0,1.0,0.0,Quantity_TOC_RGB); - } + auto props = GetProperties(face); sff = new ShapeFix_Face (face); sff->FixAddNaturalBoundMode() = Standard_True; @@ -246,17 +508,16 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a rebuild->Replace(face, newface); } - // Set the original colour of the face to the newly created + // Set the original properties of the face to the newly created // face (after the healing process) - face = TopoDS::Face (exp0.Current()); - face_colours->SetColor(face,face_colour,XCAFDoc_ColorSurf); + // GetProperties(face); } shape = rebuild->Apply(shape); } { - 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()) { @@ -273,7 +534,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a 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); @@ -340,7 +601,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a { 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()) @@ -368,7 +629,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a { - 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()) { @@ -494,7 +755,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a { - 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()) { @@ -539,7 +800,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a 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); @@ -630,13 +891,12 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a cout << "Edges : " << nnre << " (" << nre << ")" << endl; cout << "Vertices : " << nnrv << " (" << nrv << ")" << endl; cout << endl; - cout << "Totol surface area : " << newsurfacecont << " (" << surfacecont << ")" << endl; + cout << "Total surface area : " << newsurfacecont << " (" << surfacecont << ")" << endl; cout << endl; } - void OCCGeometry :: BuildFMap() { somap.Clear(); @@ -738,7 +998,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a TopoDS_Face face = TopoDS::Face(exp2.Current()); if (fmap.FindIndex(face) < 1) { - fmap.Add (face); + fmap.Add (face); for (exp3.Init(face, TopAbs_WIRE); exp3.More(); exp3.Next()) { @@ -770,39 +1030,24 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // Free Faces - - for (exp2.Init(shape, TopAbs_FACE, TopAbs_SHELL); exp2.More(); exp2.Next()) - { - TopoDS_Face face = TopoDS::Face(exp2.Current()); - if (fmap.FindIndex(face) < 1) - { + for (auto face : MyExplorer(shape, TopAbs_FACE, TopAbs_SHELL)) + if (!fmap.Contains(face)) + { fmap.Add (face); - - for (exp3.Init(exp2.Current(), TopAbs_WIRE); exp3.More(); exp3.Next()) - { - TopoDS_Wire wire = TopoDS::Wire (exp3.Current()); - if (wmap.FindIndex(wire) < 1) - { + for (auto wire : MyExplorer(face, TopAbs_WIRE)) + if (!wmap.Contains(wire)) + { wmap.Add (wire); - - for (exp4.Init(exp3.Current(), TopAbs_EDGE); exp4.More(); exp4.Next()) - { - TopoDS_Edge edge = TopoDS::Edge(exp4.Current()); - if (emap.FindIndex(edge) < 1) - { + for (auto edge : MyExplorer(wire, TopAbs_EDGE)) + if (!emap.Contains(edge)) + { emap.Add (edge); - for (exp5.Init(exp4.Current(), TopAbs_VERTEX); exp5.More(); exp5.Next()) - { - TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current()); - if (vmap.FindIndex(vertex) < 1) - vmap.Add (vertex); - } - } - } - } - } - } - } + for (auto vertex : MyExplorer(edge, TopAbs_VERTEX)) + if (!vmap.Contains(vertex)) + vmap.Add (vertex); + } + } + } // Free Wires @@ -833,7 +1078,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // Free Edges - + /* for (exp4.Init(shape, TopAbs_EDGE, TopAbs_WIRE); exp4.More(); exp4.Next()) { TopoDS_Edge edge = TopoDS::Edge(exp4.Current()); @@ -848,19 +1093,29 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a } } } + */ + for (auto edge : MyExplorer(shape, TopAbs_EDGE, TopAbs_WIRE)) + if (!emap.Contains(edge)) + { + emap.Add (edge); + for (auto vertex : MyExplorer(edge, TopAbs_VERTEX)) + if (!vmap.Contains(vertex)) + vmap.Add (vertex); + } - + // Free Vertices - + /* for (exp5.Init(shape, TopAbs_VERTEX, TopAbs_EDGE); exp5.More(); exp5.Next()) { TopoDS_Vertex vertex = TopoDS::Vertex(exp5.Current()); if (vmap.FindIndex(vertex) < 1) vmap.Add (vertex); } - - - + */ + for (auto vertex : MyExplorer(shape, TopAbs_VERTEX, TopAbs_EDGE)) + if (!vmap.Contains(vertex)) + vmap.Add (vertex); facemeshstatus.DeleteAll(); facemeshstatus.SetSize (fmap.Extent()); @@ -869,7 +1124,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // Philippose - 15/01/2009 face_maxh.DeleteAll(); face_maxh.SetSize (fmap.Extent()); - face_maxh = mparam.maxh; + face_maxh = 1e99; // mparam.maxh; // Philippose - 15/01/2010 face_maxh_modified.DeleteAll(); @@ -891,6 +1146,104 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a vsingular.SetSize (vmap.Extent()); fsingular = esingular = vsingular = false; + + NetgenGeometry::Clear(); + + // Add shapes + for(auto i1 : Range(1, vmap.Extent()+1)) + { + auto v = vmap(i1); + auto occ_vertex = make_unique(TopoDS::Vertex(v)); + occ_vertex->nr = vertices.Size(); + + if(HaveProperties(v)) + occ_vertex->properties = GetProperties(v); + vertices.Append(std::move(occ_vertex)); + } + + for(auto i1 : Range(1, emap.Extent()+1)) + { + auto e = emap(i1); + auto edge = TopoDS::Edge(e); + auto verts = GetVertices(e); + auto occ_edge = make_unique(edge, GetVertex(verts[0]), GetVertex(verts[1]) ); + occ_edge->properties = GetProperties(e); + edges.Append(std::move(occ_edge)); + } + + for(auto i1 : Range(1, fmap.Extent()+1)) + { + auto f = fmap(i1); + + auto k = faces.Size(); + auto occ_face = make_unique(f); + + for(auto e : GetEdges(f)) + occ_face->edges.Append( &GetEdge(e) ); + + if(HaveProperties(f)) + occ_face->properties = GetProperties(f); + faces.Append(std::move(occ_face)); + + if(dimension==2) + for(auto e : GetEdges(f)) + { + auto & edge = GetEdge(e); + if(e.Orientation() == TopAbs_REVERSED) + edge.domout = k; + else + edge.domin = k; + } + } + + + for(auto i1 : Range(1, somap.Extent()+1)) + { + auto s = somap(i1); + int k = solids.Size(); + auto occ_solid = make_unique(s); + if(HaveProperties(s)) + occ_solid->properties = GetProperties(s); + solids.Append(std::move(occ_solid)); + + for(auto f : GetFaces(s)) + { + auto & face = GetFace(f); + if(face.domin==-1) + face.domin = k; + else + face.domout = k; + } + } + + // Add identifications + auto add_identifications = [&](auto & shapes, auto & shape_map) + { + for(auto i1 : Range(1, shape_map.Extent()+1)) + { + auto shape = shape_map(i1); + if(HaveIdentifications(shape)) + for(auto & ident : GetIdentifications(shape)) + { + if(!shape_map.Contains(ident.from) || !shape_map.Contains(ident.to)) + continue; + ShapeIdentification si{ + &GetShape(ident.from), + &GetShape(ident.to), + ident.trafo, + ident.type, + ident.name + }; + shapes[i1-1]->identifications.Append(si); + } + } + }; + add_identifications( vertices, vmap ); + add_identifications( edges, emap ); + add_identifications( faces, fmap ); + + bounding_box = ::netgen::GetBoundingBox( shape ); + ProcessIdentifications(); } @@ -962,7 +1315,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a 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); @@ -976,6 +1329,14 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a } + Array OCCGeometry :: GetFaceVertices(const GeometryFace& face) const + { + Array verts; + const auto& occface = dynamic_cast(face); + for(auto& vert : GetVertices(occface.Shape())) + verts.Append(&GetVertex(vert)); + return std::move(verts); + } void OCCGeometry :: BuildVisualizationMesh (double deflection) @@ -993,107 +1354,12 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a void OCCGeometry :: CalcBoundingBox () { - Bnd_Box bb; -#if OCC_VERSION_HEX < 0x070000 - BRepBndLib::Add (shape, bb); -#else - BRepBndLib::Add ((const TopoDS_Shape) shape, bb,(Standard_Boolean)true); -#endif - - double x1,y1,z1,x2,y2,z2; - bb.Get (x1,y1,z1,x2,y2,z2); - Point<3> p1 = Point<3> (x1,y1,z1); - Point<3> p2 = Point<3> (x2,y2,z2); - - (*testout) << "Bounding Box = [" << p1 << " - " << p2 << "]" << endl; - boundingbox = Box<3> (p1,p2); + boundingbox = ::netgen::GetBoundingBox(shape); + (*testout) << "Bounding Box = [" << boundingbox.PMin() << " - " << boundingbox.PMax() << "]" << endl; SetCenter(); } - - - void OCCGeometry :: Project (int surfi, Point<3> & p) const - { - static int cnt = 0; - if (++cnt % 1000 == 0) cout << "Project cnt = " << cnt << endl; - - gp_Pnt pnt(p(0), p(1), p(2)); - - double u,v; - Handle( Geom_Surface ) thesurf = BRep_Tool::Surface(TopoDS::Face(fmap(surfi))); - Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( thesurf ); - gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(fmap(surfi)) ) ); - suval.Coord( u, v); - pnt = thesurf->Value( u, v ); - - - p = Point<3> (pnt.X(), pnt.Y(), pnt.Z()); - - } - - - - - bool OCCGeometry :: FastProject (int surfi, Point<3> & ap, double& u, double& v) const - { - gp_Pnt p(ap(0), ap(1), ap(2)); - - Handle(Geom_Surface) surface = BRep_Tool::Surface(TopoDS::Face(fmap(surfi))); - - gp_Pnt x = surface->Value (u,v); - - if (p.SquareDistance(x) <= sqr(PROJECTION_TOLERANCE)) return true; - - gp_Vec du, dv; - - surface->D1(u,v,x,du,dv); - - int count = 0; - - gp_Pnt xold; - gp_Vec n; - double det, lambda, mu; - - do { - count++; - - n = du^dv; - - det = Det3 (n.X(), du.X(), dv.X(), - n.Y(), du.Y(), dv.Y(), - n.Z(), du.Z(), dv.Z()); - - if (det < 1e-15) return false; - - lambda = Det3 (n.X(), p.X()-x.X(), dv.X(), - n.Y(), p.Y()-x.Y(), dv.Y(), - n.Z(), p.Z()-x.Z(), dv.Z())/det; - - mu = Det3 (n.X(), du.X(), p.X()-x.X(), - n.Y(), du.Y(), p.Y()-x.Y(), - n.Z(), du.Z(), p.Z()-x.Z())/det; - - u += lambda; - v += mu; - - xold = x; - surface->D1(u,v,x,du,dv); - - } while (xold.SquareDistance(x) > sqr(PROJECTION_TOLERANCE) && count < 50); - - // (*testout) << "FastProject count: " << count << endl; - - if (count == 50) return false; - - ap = Point<3> (x.X(), x.Y(), x.Z()); - - return true; - } - - - - // void OCCGeometry :: WriteOCC_STL(char * filename) // { // cout << "writing stl..."; cout.flush(); @@ -1107,14 +1373,18 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // } - void LoadOCCInto(OCCGeometry* occgeo, const char* filename) + void LoadOCCInto(OCCGeometry* occgeo, const filesystem::path & filename) { + static Timer timer_all("LoadOCC"); RegionTimer rtall(timer_all); + static Timer timer_readfile("LoadOCC-ReadFile"); + static Timer timer_transfer("LoadOCC-Transfer"); + 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 @@ -1125,82 +1395,41 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a } dummy_app->NewDocument ("STEP-XCAF",step_doc); + timer_readfile.Start(); STEPCAFControl_Reader reader; // Enable transfer of colours reader.SetColorMode(Standard_True); reader.SetNameMode(Standard_True); - Standard_Integer stat = reader.ReadFile((char*)filename); + Standard_Integer stat = reader.ReadFile(filename.string().c_str()); + timer_readfile.Stop(); + timer_transfer.Start(); if(stat != IFSelect_RetDone) { throw NgException("Couldn't load OCC geometry"); } reader.Transfer(step_doc); + 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()); + auto step_shape_contents = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main()); TDF_LabelSequence step_shapes; step_shape_contents->GetShapes(step_shapes); - // List out the available colours in the STEP File as Colour Names - TDF_LabelSequence all_colours; - step_colour_contents->GetColors(all_colours); - PrintMessage(1,"Number of colours in STEP File: ",all_colours.Length()); - for(int i = 1; i <= all_colours.Length(); i++) - { - Quantity_Color col; - stringstream col_rgb; - step_colour_contents->GetColor(all_colours.Value(i),col); - col_rgb << " : (" << col.Red() << "," << col.Green() << "," << col.Blue() << ")"; - PrintMessage(1, "Colour [", i, "] = ",col.StringName(col.Name()),col_rgb.str()); - } - - // For the STEP File Reader in OCC, the 1st Shape contains the entire // compound geometry as one shape - occgeo->shape = step_shape_contents->GetShape(step_shapes.Value(1)); - occgeo->face_colours = step_colour_contents; + auto main_shape = step_shape_contents->GetShape(step_shapes.Value(1)); + + step_utils::LoadProperties(main_shape, reader, step_doc); + + occgeo->shape = main_shape; occgeo->changed = 1; occgeo->BuildFMap(); - occgeo->CalcBoundingBox(); PrintContents (occgeo); - char * name = new char(50); - //string name; - STEP_GetEntityName(occgeo->shape,&reader,name); - occgeo->snames.Append(name); - TopExp_Explorer exp0,exp1; - - for (exp0.Init(occgeo->shape, TopAbs_FACE); exp0.More(); exp0.Next()) - { - TopoDS_Face face = TopoDS::Face(exp0.Current()); - STEP_GetEntityName(face,&reader,name); - occgeo->fnames.Append(name); - for (exp1.Init(face, TopAbs_EDGE); exp1.More(); exp1.Next()) - { - TopoDS_Edge edge = TopoDS::Edge(exp1.Current()); - STEP_GetEntityName(edge,&reader,name); - occgeo->enames.Append(name); - } - } - // 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); @@ -1265,7 +1494,6 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // For the IGES Reader, all the shapes can be exported as one compound shape // using the "OneShape" member occgeo->shape = reader.OneShape(); - occgeo->face_colours = iges_colour_contents; occgeo->changed = 1; occgeo->BuildFMap(); @@ -1283,7 +1511,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a to extract individual surface colours via the extended OpenCascade XDE and XCAF Feature set. */ - OCCGeometry * LoadOCC_STEP (const char * filename) + OCCGeometry * LoadOCC_STEP (const filesystem::path & filename) { OCCGeometry * occgeo; occgeo = new OCCGeometry; @@ -1295,13 +1523,13 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a - OCCGeometry *LoadOCC_BREP (const char *filename) + OCCGeometry *LoadOCC_BREP (const filesystem::path & filename) { OCCGeometry * occgeo; occgeo = new OCCGeometry; BRep_Builder aBuilder; - Standard_Boolean result = BRepTools::Read(occgeo->shape, const_cast (filename),aBuilder); + Standard_Boolean result = BRepTools::Read(occgeo->shape, filename.string().c_str(), aBuilder); if(!result) { @@ -1309,12 +1537,6 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a return NULL; } - // Philippose - 23/02/2009 - // 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.Nullify(); occgeo->changed = 1; occgeo->BuildFMap(); @@ -1325,63 +1547,141 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a } - void OCCGeometry :: Save (string sfilename) const + void OCCGeometry :: Save (const filesystem::path & filename) const { - const char * filename = sfilename.c_str(); - if (strlen(filename) < 4) - throw NgException ("illegal filename"); - - if (strcmp (&filename[strlen(filename)-3], "igs") == 0) + string ext = ToLower(filename.extension()); + auto s_filename = filename.string(); + auto c_filename = s_filename.c_str(); + + if (ext == ".igs") { IGESControl_Writer writer("millimeters", 1); writer.AddShape (shape); - writer.Write (filename); + writer.Write (c_filename); } - else if (strcmp (&filename[strlen(filename)-3], "stp") == 0) + else if (ext == ".stp") { - STEPControl_Writer writer; - writer.Transfer (shape, STEPControl_AsIs); - writer.Write (filename); + step_utils::WriteSTEP(*this, filename); } - else if (strcmp (&filename[strlen(filename)-3], "stl") == 0) + else if (ext == ".stl") { StlAPI_Writer writer; writer.ASCIIMode() = Standard_True; - writer.Write (shape, filename); + writer.Write (shape, c_filename); } - else if (strcmp (&filename[strlen(filename)-4], "stlb") == 0) + else if (ext == ".stlb") { StlAPI_Writer writer; writer.ASCIIMode() = Standard_False; - writer.Write (shape, filename); + writer.Write (shape, c_filename); } + + throw NgException ("Unknown target format: " + filename); + } + + void OCCGeometry :: SaveToMeshFile (ostream & ost) const + { + auto ss = make_shared(); + TextOutArchive out(ss); + NetgenGeometry *geo = const_cast(this); + out & geo; + + ost << "TextOutArchive" << endl; + ost << ss->str().size() << endl; + ost << ss->str(); } void OCCGeometry :: DoArchive(Archive& ar) { + constexpr int current_format_version = 0; + + int format_version = current_format_version; + auto netgen_version = GetLibraryVersion("netgen"); + ar & netgen_version & format_version; + if(ar.Output()) { std::stringstream ss; - STEPControl_Writer writer; - writer.Transfer(shape, STEPControl_AsIs); - auto filename = ".tmpfile_out.step"; - writer.Write(filename); - std::ifstream is(filename); - ss << is.rdbuf(); +#if OCC_VERSION_HEX < 0x070600 + BRepTools::Write(shape, ss); +#else + BRepTools::Write(shape, ss, false, false, TopTools_FormatVersion_VERSION_1); +#endif ar << ss.str(); - std::remove(filename); } else { + if(format_version>current_format_version) + throw Exception("Loading OCCGeometry from archive: unknown format version " + + ToString(format_version) + + ", written by netgen version " + + ToString(netgen_version)); std::string str; ar & str; + stringstream ss(str); + BRep_Builder builder; + BRepTools::Read(shape, ss, builder); + } - auto filename = ".tmpfile.step"; - auto tmpfile = std::fopen(filename, "w"); - std::fputs(str.c_str(), tmpfile); - std::fclose(tmpfile); - LoadOCCInto(this, filename); - std::remove(filename); + // enumerate shapes and archive only integers + auto my_hash = [](const TopoDS_Shape & key) { + auto occ_hash = key.HashCode(1<<31UL); + return std::hash()(occ_hash); + }; + TopTools_IndexedMapOfShape shape_map; + Array shape_list; + + ar & dimension; + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto ds = e.Current(); + if(shape_map.FindIndex(ds) == 0) + { + shape_map.Add(ds); + shape_list.Append(ds); + } + } + + for (auto s : shape_list) + { + bool has_properties = HaveProperties(s); + ar & has_properties; + if(has_properties) + ar & GetProperties(s); + + bool has_identifications = HaveIdentifications(s); + ar & has_identifications; + if(has_identifications) + { + auto & idents = GetIdentifications(s); + auto n_idents = idents.size(); + ar & n_idents; + idents.resize(n_idents); + for(auto i : Range(n_idents)) + { + auto & id = idents[i]; + int id_from, id_to; + if(ar.Output()) + { + id_from = shape_map.FindIndex(id.from)-1; + id_to = shape_map.FindIndex(id.to)-1; + } + ar & id_from & id_to & id.trafo & id.name; + if(ar.Input()) + { + id.from = shape_list[id_from]; + id.to = shape_list[id_to]; + } + } + } + } + + if(ar.Input()) + { + changed = 1; + BuildFMap(); + CalcBoundingBox(); } } @@ -1440,7 +1740,7 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a case TopAbs_VERTEX: count2 = vmap.FindIndex(TopoDS::Vertex(e.Current())); break; default: - cout << "RecursiveTopologyTree: Case " << e.Current().ShapeType() << " not handeled" << endl; + cout << "RecursiveTopologyTree: Case " << e.Current().ShapeType() << " not handled" << endl; } int nrsubshapes = 0; @@ -1614,8 +1914,8 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a // double dmax; // int cnt = 0; - Array edgeLengths; - Array order; + NgArray edgeLengths; + NgArray order; edgeLengths.SetSize (emap.Extent()); order.SetSize (emap.Extent()); @@ -1679,50 +1979,361 @@ void STEP_GetEntityName(const TopoDS_Shape & theShape, STEPCAFControl_Reader * a return false; } + bool IsMappedShape(const Transformation<3> & trafo, const TopoDS_Shape & me, const TopoDS_Shape & you) + { + if(me.ShapeType() != you.ShapeType()) return false; + Bnd_Box bbox; + BRepBndLib::Add(me, bbox); + BRepBndLib::Add(you, bbox); + BoxTree<3> tree( occ2ng(bbox.CornerMin()), occ2ng(bbox.CornerMax()) ); + Point<3> c_me = occ2ng(Center(me)); + Point<3> c_you = occ2ng(Center(you)); + if(tree.GetTolerance() < Dist(trafo(c_me), c_you)) + return false; - int OCCGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) - { - return OCCGenerateMesh (*this, mesh, mparam); - } + TopTools_IndexedMapOfShape vmap; + std::vector> verts; + auto verts_me = GetVertices(me); + auto verts_you = GetVertices(you); + if(verts_me.size() != verts_you.size()) + return false; + for (auto i : Range(verts_me.size())) + { + auto s = verts_me[i]; + if(vmap.FindIndex(s) > 0) + continue; + auto p = trafo(occ2ng(s)); + tree.Insert( p, i ); + vmap.Add(s); + verts.push_back(nullopt); + } + + for (auto vert : verts_you) + { + auto s = vert; + auto p = occ2ng(s); + bool vert_mapped = false; + tree.GetFirstIntersecting( p, p, [&](size_t i ) { + vmap.Add(verts_me[i]); + verts[vmap.FindIndex(verts_me[i])-1] = s; + vert_mapped = true; + return true; + }); + if(!vert_mapped) + return false; + } + return true; + } - const Refinement & OCCGeometry :: GetRefinement () const - { - return * new OCCRefinementSurfaces (*this); - } + void Identify(const TopoDS_Shape & me, const TopoDS_Shape & you, string name, Identifications::ID_TYPE type, std::optional> opt_trafo) + { + Transformation<3> trafo; + if(opt_trafo) + { + trafo = occ2ng(*opt_trafo); + } + else + { + auto v = occ2ng(Center(you)) - occ2ng(Center(me)); + trafo = Transformation<3>(v); + } + ListOfShapes list_me, list_you; + list_me.push_back(me); + list_you.push_back(you); + Identify(list_me, list_you, name, type, trafo); + } + void Identify(const ListOfShapes & me, const ListOfShapes & you, string name, Identifications::ID_TYPE type, Transformation<3> trafo) + { + ListOfShapes id_me; + ListOfShapes id_you; + if(auto faces_me = me.Faces(); faces_me.size()>0) + { + id_me = faces_me; + id_you = you.Faces(); + } + else if(auto edges_me = me.Edges(); edges_me.size()>0) + { + id_me = edges_me; + id_you = you.Edges(); + } + else + { + id_me = me.Vertices(); + id_you = you.Vertices(); + } - OCCParameters :: OCCParameters() - { - resthcloseedgefac = 1; - resthcloseedgeenable = 1; - resthminedgelen = 0.001; - resthminedgelenenable = 1; - } + for(auto shape_me : id_me) + for(auto shape_you : id_you) + { + if(!IsMappedShape(trafo, shape_me, shape_you)) + continue; + OCCGeometry::GetIdentifications(shape_me).push_back + (OCCIdentification { shape_me, shape_you, trafo, name, type }); + } + } - - - void OCCParameters :: Print(ostream & ost) const + void OCCParameters :: Print(ostream & ost) const { ost << "OCC Parameters:" << endl - << "close edges: " << resthcloseedgeenable - << ", fac = " << resthcloseedgefac << endl << "minimum edge length: " << resthminedgelenenable << ", min len = " << resthminedgelen << endl; } + DLL_HEADER extern OCCParameters occparam; + OCCParameters occparam; - OCCParameters occparam; + + + + + + + + + + // int OCCGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) + // { + // return OCCGenerateMesh (*this, mesh, mparam, occparam); + // } + static RegisterClassForArchive regnggeo; + + namespace step_utils + { + void LoadProperties(const TopoDS_Shape & shape, + const STEPCAFControl_Reader & reader, + const Handle(TDocStd_Document) step_doc) + { + static Timer t("step_utils::LoadProperties"); RegionTimer rt(t); + + auto workSession = reader.Reader().WS(); + auto model = workSession->Model(); + auto transferReader = workSession->TransferReader(); + auto transProc = transferReader->TransientProcess(); + auto shapeTool = XCAFDoc_DocumentTool::ShapeTool(step_doc->Main()); + + // load colors + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + TDF_Label label; + shapeTool->Search(e.Current(), label); + + if(label.IsNull()) + continue; + + XCAFPrs_IndexedDataMapOfShapeStyle set; + TopLoc_Location loc; + XCAFPrs::CollectStyleSettings(label, loc, set); + XCAFPrs_Style aStyle; + set.FindFromKey(e.Current(), aStyle); + + auto & prop = OCCGeometry::GetProperties(e.Current()); + if(aStyle.IsSetColorSurf()) + prop.col = step_utils::ReadColor(aStyle.GetColorSurfRGBA()); + } + + // load names + Standard_Integer nb = model->NbEntities(); + for (Standard_Integer i = 1; i <= nb; i++) + { + Handle(Standard_Transient) entity = model->Value(i); + auto item = Handle(StepRepr_RepresentationItem)::DownCast(entity); + + if(item.IsNull()) + continue; + + TopoDS_Shape shape = TransferBRep::ShapeResult(transProc->Find(item)); + string name = item->Name()->ToCString(); + if (!transProc->IsBound(item)) + continue; + + OCCGeometry::GetProperties(shape).name = name; + } + + + // load custom data (maxh etc.) + for (Standard_Integer i = 1; i <= nb; i++) + { + Handle(Standard_Transient) entity = model->Value(i); + + auto item = Handle(StepRepr_CompoundRepresentationItem)::DownCast(entity); + + if(item.IsNull()) + continue; + + auto shape_item = item->ItemElementValue(1); + TopoDS_Shape shape = TransferBRep::ShapeResult(transProc->Find(shape_item)); + string name = item->Name()->ToCString(); + + if(name == "netgen_geometry_identification") + ReadIdentifications(item, transProc); + + if(name != "netgen_geometry_properties") + continue; + + auto & prop = OCCGeometry::GetProperties(shape); + + auto nprops = item->NbItemElement(); + + for(auto i : Range(2, nprops+1)) + { + auto prop_item = item->ItemElementValue(i); + string prop_name = prop_item->Name()->ToCString(); + + if(prop_name=="maxh") + prop.maxh = Handle(StepRepr_ValueRepresentationItem)::DownCast(prop_item) + ->ValueComponentMember()->Real(); + + if(prop_name=="hpref") + prop.hpref = Handle(StepRepr_ValueRepresentationItem)::DownCast(prop_item) + ->ValueComponentMember()->Real(); + } + } + } + + void WriteProperties(const Handle(Interface_InterfaceModel) model, const Handle(Transfer_FinderProcess) finder, const TopoDS_Shape & shape) + { + static const ShapeProperties default_props; + Handle(StepRepr_RepresentationItem) item = STEPConstruct::FindEntity(finder, shape); + if(!item) + return; + auto prop = OCCGeometry::GetProperties(shape); + + if(auto n = prop.name) + item->SetName(MakeName(*n)); + + Array props; + props.Append(item); + + if(auto maxh = prop.maxh; maxh != default_props.maxh) + props.Append( MakeReal(maxh, "maxh") ); + + if(auto hpref = prop.hpref; hpref != default_props.hpref) + props.Append( MakeReal(hpref, "hpref") ); + + if(props.Size()>1) + { + for(auto & item : props.Range(1, props.Size())) + model->AddEntity(item); + + auto compound = MakeCompound(props, "netgen_geometry_properties"); + model->AddEntity(compound); + } + + WriteIdentifications(model, shape, finder); + } + + void WriteIdentifications(const Handle(Interface_InterfaceModel) model, const TopoDS_Shape & shape, const Handle(Transfer_FinderProcess) finder) + { + Handle(StepRepr_RepresentationItem) item = STEPConstruct::FindEntity(finder, shape); + if(!OCCGeometry::HaveIdentifications(shape)) + return; + auto & identifications = OCCGeometry::GetIdentifications(shape); + if(identifications.size()==0) + return; + auto n = identifications.size(); + Array ident_items; + ident_items.Append(item); + + for(auto & ident : identifications) + { + Array items; + // items.Append(STEPConstruct::FindEntity(finder, ident.other)); // TODO! + auto & m = ident.trafo.GetMatrix(); + for(auto i : Range(9)) + items.Append(MakeReal(m(i))); + auto & v = ident.trafo.GetVector(); + for(auto i : Range(3)) + items.Append(MakeReal(v(i))); + for(auto & item : items.Range(1,items.Size())) + model->AddEntity(item); + ident_items.Append(MakeCompound(items, ident.name)); + } + + for(auto & item : ident_items.Range(1,ident_items.Size())) + model->AddEntity(item); + auto comp = MakeCompound(ident_items, "netgen_geometry_identification"); + model->AddEntity(comp); + } + + void ReadIdentifications(Handle(StepRepr_RepresentationItem) item, Handle(Transfer_TransientProcess) transProc) + { + auto idents = Handle(StepRepr_CompoundRepresentationItem)::DownCast(item); + auto n = idents->NbItemElement(); + std::vector result; + auto shape_origin = TransferBRep::ShapeResult(transProc->Find(idents->ItemElementValue(1))); + + for(auto i : Range(2,n+1)) + { + auto id_item = Handle(StepRepr_CompoundRepresentationItem)::DownCast(idents->ItemElementValue(i)); + OCCIdentification ident; + ident.name = id_item->Name()->ToCString(); + // ident.other = TransferBRep::ShapeResult(transProc->Find(id_item->ItemElementValue(1))); /TODO! + + auto & m = ident.trafo.GetMatrix(); + for(auto i : Range(9)) + m(i) = ReadReal(id_item->ItemElementValue(3+i)); + auto & v = ident.trafo.GetVector(); + for(auto i : Range(3)) + v(i) = ReadReal(id_item->ItemElementValue(12+i)); + + result.push_back(ident); + } + OCCGeometry::GetIdentifications(shape_origin) = result; + } + + void WriteSTEP(const TopoDS_Shape & shape, const filesystem::path & filename) + { + Interface_Static::SetCVal("write.step.schema", "AP242IS"); + Interface_Static::SetIVal("write.step.assembly",1); + Handle(XCAFApp_Application) app = XCAFApp_Application::GetApplication(); + Handle(TDocStd_Document) doc; + + app->NewDocument("STEP-XCAF", doc); + Handle(XCAFDoc_ShapeTool) shapetool = XCAFDoc_DocumentTool::ShapeTool(doc->Main()); + Handle(XCAFDoc_ColorTool) colortool = XCAFDoc_DocumentTool::ColorTool(doc->Main()); + TDF_Label label = shapetool->NewShape(); + shapetool->SetShape(label, shape); + + Handle(XSControl_WorkSession) session = new XSControl_WorkSession; + STEPCAFControl_Writer writer(session); + const Handle(Interface_InterfaceModel) model = session->Model(); + + // Set colors (BEFORE transferring shape into step data structures) + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + if(auto col = prop.col) + colortool->SetColor(e.Current(), step_utils::MakeColor(*col), XCAFDoc_ColorGen); + } + + // Transfer shape into step data structures -> now we can manipulate/add step representation items + writer.Transfer(doc, STEPControl_AsIs); + + // Write all other properties + auto finder = session->TransferWriter()->FinderProcess(); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + WriteProperties(model, finder, e.Current()); + + writer.Write(filename.string().c_str()); + } + + } // namespace step_utils } diff --git a/libsrc/occ/occgeom.hpp b/libsrc/occ/occgeom.hpp index 8c93ff1f..1162fab3 100644 --- a/libsrc/occ/occgeom.hpp +++ b/libsrc/occ/occgeom.hpp @@ -9,112 +9,32 @@ #ifdef OCCGEOMETRY +#include + #include +#include "occ_utils.hpp" +#include "occmeshsurf.hpp" -#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 "BRepTools.hxx" -#include "TopExp.hxx" -#include "BRepBuilderAPI_MakeVertex.hxx" -#include "BRepBuilderAPI_MakeShell.hxx" -#include "BRepBuilderAPI_MakeSolid.hxx" -#include "BRepOffsetAPI_Sewing.hxx" -#include "BRepLProp_SLProps.hxx" -#include "BRepAdaptor_Surface.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 "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" -#include "Interface_Static.hxx" -#include "GeomAPI_ExtremaCurveCurve.hxx" -#include "Standard_ErrorHandler.hxx" -#include "Standard_Failure.hxx" -#include "ShapeUpgrade_ShellSewing.hxx" -#include "ShapeFix_Shape.hxx" -#include "ShapeFix_Wireframe.hxx" -#include "BRepMesh.hxx" -#include "BRepMesh_IncrementalMesh.hxx" -#include "BRepBndLib.hxx" -#include "Bnd_Box.hxx" -#include "ShapeAnalysis.hxx" -#include "ShapeBuild_ReShape.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include - -// Philippose - 29/01/2009 -// OpenCascade XDE Support -// Include support for OpenCascade XDE Features -#include "TDocStd_Document.hxx" -#include "Quantity_Color.hxx" -#include "XCAFApp_Application.hxx" -#include "XCAFDoc_ShapeTool.hxx" -#include "XCAFDoc_Color.hxx" -#include "XCAFDoc_ColorTool.hxx" -#include "XCAFDoc_ColorType.hxx" -#include "XCAFDoc_LayerTool.hxx" -#include "XCAFDoc_DimTolTool.hxx" -#include "XCAFDoc_MaterialTool.hxx" -#include "XCAFDoc_DocumentTool.hxx" -#include "TDF_Label.hxx" -#include "TDF_LabelSequence.hxx" -#include "STEPCAFControl_Reader.hxx" -#include "STEPCAFControl_Writer.hxx" -#include "IGESCAFControl_Reader.hxx" -#include "IGESCAFControl_Writer.hxx" - -#include "IGESControl_Reader.hxx" -#include "STEPControl_Reader.hxx" -#include "IGESControl_Writer.hxx" -#include "STEPControl_Writer.hxx" - -#include "StlAPI_Writer.hxx" -#include "STEPControl_StepModelType.hxx" +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4 +#define OCC_HAVE_HISTORY +#endif namespace netgen { -#include "occmeshsurf.hpp" - extern DLL_HEADER MeshingParameters mparam; + // extern DLL_HEADER MeshingParameters mparam; #define PROJECTION_TOLERANCE 1e-10 @@ -126,329 +46,582 @@ namespace netgen #define OCCGEOMETRYVISUALIZATIONFULLCHANGE 1 // Compute transformation matrices and redraw #define OCCGEOMETRYVISUALIZATIONHALFCHANGE 2 // Redraw + bool IsMappedShape(const Transformation<3> & trafo, const TopoDS_Shape & me, const TopoDS_Shape & you); + class EntityVisualizationCode + { + int code; - class EntityVisualizationCode - { - int code; + public: - public: + EntityVisualizationCode() + { code = ENTITYISVISIBLE + !ENTITYISHIGHLIGHTED + ENTITYISDRAWABLE;} - EntityVisualizationCode() - { code = ENTITYISVISIBLE + !ENTITYISHIGHLIGHTED + ENTITYISDRAWABLE;} + int IsVisible () + { return code & ENTITYISVISIBLE;} - int IsVisible () - { return code & ENTITYISVISIBLE;} + int IsHighlighted () + { return code & ENTITYISHIGHLIGHTED;} - int IsHighlighted () - { return code & ENTITYISHIGHLIGHTED;} + int IsDrawable () + { return code & ENTITYISDRAWABLE;} - int IsDrawable () - { return code & ENTITYISDRAWABLE;} + void Show () + { code |= ENTITYISVISIBLE;} - void Show () - { code |= ENTITYISVISIBLE;} + void Hide () + { code &= ~ENTITYISVISIBLE;} - void Hide () - { code &= ~ENTITYISVISIBLE;} + void Highlight () + { code |= ENTITYISHIGHLIGHTED;} - void Highlight () - { code |= ENTITYISHIGHLIGHTED;} + void Lowlight () + { code &= ~ENTITYISHIGHLIGHTED;} - void Lowlight () - { code &= ~ENTITYISHIGHLIGHTED;} + void SetDrawable () + { code |= ENTITYISDRAWABLE;} - void SetDrawable () - { code |= ENTITYISDRAWABLE;} - - void SetNotDrawable () - { code &= ~ENTITYISDRAWABLE;} - }; + void SetNotDrawable () + { code &= ~ENTITYISDRAWABLE;} + }; - class Line - { - public: - Point<3> p0, p1; - - double Dist (Line l); - - double Length (); - }; + class Line + { + public: + Point<3> p0, p1; + int layer = 1; + double Dist (Line l); + double Length () { return (p1-p0).Length(); } + }; + + inline double Det3 (double a00, double a01, double a02, + double a10, double a11, double a12, + double a20, double a21, double a22) + { + return a00*a11*a22 + a01*a12*a20 + a10*a21*a02 - a20*a11*a02 - a10*a01*a22 - a21*a12*a00; + } + + class DLL_HEADER OCCParameters + { + public: - inline double Det3 (double a00, double a01, double a02, - double a10, double a11, double a12, - double a20, double a21, double a22) - { - return a00*a11*a22 + a01*a12*a20 + a10*a21*a02 - a20*a11*a02 - a10*a01*a22 - a21*a12*a00; - } + /// Factor for meshing close edges, moved to meshingparameters + // double resthcloseedgefac = 2.; + + /// Enable / Disable detection of close edges + // int resthcloseedgeenable = true; + + /// Minimum edge length to be used for dividing edges to mesh points + // double resthminedgelen = 0.001; + double resthminedgelen = 1e-4; + + /// Enable / Disable use of the minimum edge length (by default use 1e-4) + int resthminedgelenenable = false; + + /*! + Dump all the OpenCascade specific meshing parameters + to console + */ + void Print (ostream & ost) const; + }; + class DLL_HEADER OCCGeometry : public NetgenGeometry + { + Point<3> center; + OCCParameters occparam; + public: + static TopTools_IndexedMapOfShape global_shape_property_indices; + static std::vector global_shape_properties; + static TopTools_IndexedMapOfShape global_identification_indices; + static std::vector> global_identifications; + static ShapeProperties& GetProperties(const TopoDS_Shape& shape) + { + auto index = OCCGeometry::global_shape_property_indices.FindIndex(shape); + if(index > 0) + return OCCGeometry::global_shape_properties + [index-1]; + OCCGeometry::global_shape_property_indices.Add(shape); + OCCGeometry::global_shape_properties.push_back({}); + return OCCGeometry::global_shape_properties.back(); + } + static bool HaveProperties(const TopoDS_Shape& shape) + { + return OCCGeometry::global_shape_property_indices.FindIndex(shape) > 0; + } + static std::vector& GetIdentifications(const TopoDS_Shape& shape) + { + auto index = OCCGeometry::global_identification_indices.FindIndex(shape); + if(index > 0) + return OCCGeometry::global_identifications[index-1]; + OCCGeometry::global_identification_indices.Add(shape); + OCCGeometry::global_identifications.push_back({}); + return OCCGeometry::global_identifications.back(); + } + static bool HaveIdentifications(const TopoDS_Shape& shape) + { + return OCCGeometry::global_identification_indices.FindIndex(shape) > 0; + } - class OCCGeometry : public NetgenGeometry - { - Point<3> center; + TopoDS_Shape shape; + TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap; + NgArray fsingular, esingular, vsingular; + Box<3> boundingbox; - public: - TopoDS_Shape shape; - TopTools_IndexedMapOfShape fmap, emap, vmap, somap, shmap, wmap; - Array fsingular, esingular, vsingular; - Box<3> boundingbox; - Array 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; + mutable int changed; + mutable NgArray facemeshstatus; - mutable int changed; - Array facemeshstatus; + // Philippose - 15/01/2009 + // Maximum mesh size for a given face + // (Used to explicitly define mesh size limits on individual faces) + NgArray face_maxh; + + // Philippose - 14/01/2010 + // Boolean array to detect whether a face has been explicitly modified + // by the user or not + NgArray face_maxh_modified; + + // Philippose - 15/01/2009 + // Indicates which faces have been selected by the user in geometry mode + // (Currently handles only selection of one face at a time, but an array would + // help to extend this to multiple faces) + NgArray face_sel_status; + + NgArray fvispar, evispar, vvispar; + + double tolerance; + bool fixsmalledges; + bool fixspotstripfaces; + bool sewfaces; + bool makesolids; + bool splitpartitions; - // Philippose - 15/01/2009 - // Maximum mesh size for a given face - // (Used to explicitly define mesh size limits on individual faces) - Array face_maxh; - - // Philippose - 14/01/2010 - // Boolean array to detect whether a face has been explicitly modified - // by the user or not - Array face_maxh_modified; + OCCGeometry() + { + somap.Clear(); + shmap.Clear(); + fmap.Clear(); + wmap.Clear(); + emap.Clear(); + vmap.Clear(); + } - // Philippose - 15/01/2009 - // Indicates which faces have been selected by the user in geometry mode - // (Currently handles only selection of one face at a time, but an array would - // help to extend this to multiple faces) - Array face_sel_status; + OCCGeometry(const TopoDS_Shape& _shape, int aoccdim = 3, bool copy = false); - Array fvispar, evispar, vvispar; + Mesh::GEOM_TYPE GetGeomType() const override + { return Mesh::GEOM_OCC; } - double tolerance; - bool fixsmalledges; - bool fixspotstripfaces; - bool sewfaces; - bool makesolids; - bool splitpartitions; + void SetDimension(int dim) + { + dimension = dim; + BuildFMap(); + } - OCCGeometry() - { - somap.Clear(); - shmap.Clear(); - fmap.Clear(); - wmap.Clear(); - emap.Clear(); - vmap.Clear(); - } + void SetOCCParameters(const OCCParameters& par) + { occparam = par; } + using NetgenGeometry::GetVertex; + using NetgenGeometry::GetEdge; + using NetgenGeometry::GetFace; - DLL_HEADER virtual void Save (string filename) const; + GeometryShape & GetShape(const TopoDS_Shape & shape) + { + return const_cast(as_const(*this).GetShape(shape)); + } + GeometryVertex & GetVertex(const TopoDS_Shape & shape) + { + return const_cast(as_const(*this).GetVertex(shape)); + } - void DoArchive(Archive& ar); + GeometryEdge & GetEdge(const TopoDS_Shape & shape) + { + return const_cast(as_const(*this).GetEdge(shape)); + } - DLL_HEADER void BuildFMap(); + GeometryFace & GetFace(const TopoDS_Shape & shape) + { + return const_cast(as_const(*this).GetFace(shape)); + } - Box<3> GetBoundingBox() - { return boundingbox;} + const GeometryShape & GetShape(const TopoDS_Shape & shape) const; + const GeometryVertex & GetVertex(const TopoDS_Shape & shape) const; + const GeometryEdge & GetEdge(const TopoDS_Shape & shape) const; + const GeometryFace & GetFace(const TopoDS_Shape & shape) const; - int NrSolids() - { return somap.Extent();} + void Analyse(Mesh& mesh, + const MeshingParameters& mparam) const override; + bool MeshFace(Mesh& mesh, const MeshingParameters& mparam, + int nr, FlatArray glob2loc) const override; + // void OptimizeSurface(Mesh& mesh, const MeshingParameters& mparam) const override {} + + void Save (const filesystem::path & filename) const override; + void SaveToMeshFile (ostream & /* ost */) const override; + + void DoArchive(Archive& ar) override; - // Philippose - 17/01/2009 - // Total number of faces in the geometry - int NrFaces() - { return fmap.Extent();} + void BuildFMap(); - void SetCenter() - { center = boundingbox.Center();} + auto GetShape() const { return shape; } + Box<3> GetBoundingBox() const + { return boundingbox; } - Point<3> Center() - { return center;} + int NrSolids() const + { return somap.Extent(); } - void Project (int surfi, Point<3> & p) const; - bool FastProject (int surfi, Point<3> & ap, double& u, double& v) const; + // Philippose - 17/01/2009 + // Total number of faces in the geometry + int NrFaces() const + { return fmap.Extent(); } - OCCSurface GetSurface (int surfi) - { - cout << "OCCGeometry::GetSurface using PLANESPACE" << endl; - return OCCSurface (TopoDS::Face(fmap(surfi)), PLANESPACE); - } + void SetCenter() + { center = boundingbox.Center(); } - DLL_HEADER void CalcBoundingBox (); - DLL_HEADER void BuildVisualizationMesh (double deflection); + Point<3> Center() const + { return center; } - void RecursiveTopologyTree (const TopoDS_Shape & sh, - stringstream & str, - TopAbs_ShapeEnum l, - bool free, - const char * lname); + OCCSurface GetSurface (int surfi) + { + cout << "OCCGeometry::GetSurface using PLANESPACE" << endl; + return OCCSurface (TopoDS::Face(fmap(surfi)), PLANESPACE); + } - DLL_HEADER void GetTopologyTree (stringstream & str); + void CalcBoundingBox (); + void BuildVisualizationMesh (double deflection); + + void RecursiveTopologyTree (const TopoDS_Shape & sh, + stringstream & str, + TopAbs_ShapeEnum l, + bool free, + const char * lname); - DLL_HEADER void PrintNrShapes (); + void GetTopologyTree (stringstream & str); - DLL_HEADER void CheckIrregularEntities (stringstream & str); + void PrintNrShapes (); - DLL_HEADER void SewFaces(); + void CheckIrregularEntities (stringstream & str); - DLL_HEADER void MakeSolid(); + void SewFaces(); - DLL_HEADER void HealGeometry(); + void MakeSolid(); - // Philippose - 15/01/2009 - // Sets the maximum mesh size for a given face - // (Note: Local mesh size limited by the global max mesh size) - void SetFaceMaxH(int facenr, double faceh) - { - if((facenr> 0) && (facenr <= fmap.Extent())) - { - face_maxh[facenr-1] = min(mparam.maxh,faceh); + Array GetFaceVertices(const GeometryFace& face) const override; + + void HealGeometry(); + void GlueGeometry(); + + // Philippose - 15/01/2009 + // Sets the maximum mesh size for a given face + // (Note: Local mesh size limited by the global max mesh size) + void SetFaceMaxH(int facenr, double faceh, const MeshingParameters & mparam) + { + if((facenr> 0) && (facenr <= fmap.Extent())) + { + face_maxh[facenr-1] = min(mparam.maxh,faceh); - // Philippose - 14/01/2010 - // If the face maxh is greater than or equal to the - // current global maximum, then identify the face as - // not explicitly controlled by the user any more - if(faceh >= mparam.maxh) + // Philippose - 14/01/2010 + // If the face maxh is greater than or equal to the + // current global maximum, then identify the face as + // not explicitly controlled by the user any more + if(faceh >= mparam.maxh) { - face_maxh_modified[facenr-1] = 0; + face_maxh_modified[facenr-1] = 0; } - else + else { - face_maxh_modified[facenr-1] = 1; + face_maxh_modified[facenr-1] = 1; } - } - } + } + } - // Philippose - 15/01/2009 - // Returns the local mesh size of a given face - double GetFaceMaxH(int facenr) - { - if((facenr> 0) && (facenr <= fmap.Extent())) - { - return face_maxh[facenr-1]; - } - else - { - return 0.0; - } - } + void SetFaceMaxH(size_t facenr, double faceh) + { + if(facenr >= fmap.Extent()) + throw RangeException("OCCGeometry faces", facenr, 0, fmap.Extent()); + face_maxh[facenr] = faceh; + face_maxh_modified[facenr] = true; + } + + // Philippose - 15/01/2009 + // Returns the local mesh size of a given face + double GetFaceMaxH(int facenr) + { + if((facenr> 0) && (facenr <= fmap.Extent())) + { + return face_maxh[facenr-1]; + } + else + { + return 0.0; + } + } - // Philippose - 14/01/2010 - // Returns the flag whether the given face - // has a mesh size controlled by the user or not - bool GetFaceMaxhModified(int facenr) - { - return face_maxh_modified[facenr-1]; - } + // Philippose - 14/01/2010 + // Returns the flag whether the given face + // has a mesh size controlled by the user or not + bool GetFaceMaxhModified(int facenr) + { + return face_maxh_modified[facenr-1]; + } - // Philippose - 17/01/2009 - // Returns the index of the currently selected face - int SelectedFace() - { - int i; - - for(i = 1; i <= fmap.Extent(); i++) - { - if(face_sel_status[i-1]) + // Philippose - 17/01/2009 + // Returns the index of the currently selected face + int SelectedFace() + { + for(int i = 1; i <= fmap.Extent(); i++) + { + if(face_sel_status[i-1]) { - return i; + return i; } - } + } - return 0; - } + return 0; + } - // Philippose - 17/01/2009 - // Sets the currently selected face - void SetSelectedFace(int facenr) - { - face_sel_status = 0; + // Philippose - 17/01/2009 + // Sets the currently selected face + void SetSelectedFace(int facenr) + { + face_sel_status = 0; - if((facenr >= 1) && (facenr <= fmap.Extent())) - { - face_sel_status[facenr-1] = 1; - } - } + if((facenr >= 1) && (facenr <= fmap.Extent())) + { + face_sel_status[facenr-1] = 1; + } + } - void LowLightAll() - { - for (int i = 1; i <= fmap.Extent(); i++) - fvispar[i-1].Lowlight(); - for (int i = 1; i <= emap.Extent(); i++) - evispar[i-1].Lowlight(); - for (int i = 1; i <= vmap.Extent(); i++) - vvispar[i-1].Lowlight(); - } + void LowLightAll() + { + for (int i = 1; i <= fmap.Extent(); i++) + fvispar[i-1].Lowlight(); + for (int i = 1; i <= emap.Extent(); i++) + evispar[i-1].Lowlight(); + for (int i = 1; i <= vmap.Extent(); i++) + 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); + // 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; + }; - DLL_HEADER virtual const Refinement & GetRefinement () const; - }; - - - - class DLL_HEADER OCCParameters - { - public: - - /// Factor for meshing close edges - double resthcloseedgefac; - - - /// Enable / Disable detection of close edges - int resthcloseedgeenable; - - - /// Minimum edge length to be used for dividing edges to mesh points - double resthminedgelen; - - - /// Enable / Disable use of the minimum edge length (by default use 1e-4) - int resthminedgelenenable; - - /*! - Default Constructor for the OpenCascade - Mesh generation parameter set - */ - OCCParameters(); - - - /*! - Dump all the OpenCascade specific meshing parameters - to console - */ - void Print (ostream & ost) const; - }; + void Identify(const ListOfShapes & me, const ListOfShapes & you, string name, Identifications::ID_TYPE type, Transformation<3> trafo); + void Identify(const TopoDS_Shape & me, const TopoDS_Shape & you, string name, Identifications::ID_TYPE type, std::optional> opt_trafo); - void PrintContents (OCCGeometry * geom); + void PrintContents (OCCGeometry * geom); - DLL_HEADER OCCGeometry * LoadOCC_IGES (const char * filename); - DLL_HEADER OCCGeometry * LoadOCC_STEP (const char * filename); - DLL_HEADER OCCGeometry * LoadOCC_BREP (const char * filename); + DLL_HEADER OCCGeometry * LoadOCC_IGES (const filesystem::path & filename); + DLL_HEADER OCCGeometry * LoadOCC_STEP (const filesystem::path & filename); + DLL_HEADER OCCGeometry * LoadOCC_BREP (const filesystem::path & filename); - DLL_HEADER extern OCCParameters occparam; + // Philippose - 31.09.2009 + // External access to the mesh generation functions within the OCC + // subsystem (Not sure if this is the best way to implement this....!!) + DLL_HEADER extern void OCCSetLocalMeshSize(const OCCGeometry & geom, Mesh & mesh, const MeshingParameters & mparam, + const OCCParameters& occparam); + + DLL_HEADER extern bool OCCMeshFace (const OCCGeometry & geom, Mesh & mesh, FlatArray glob2loc, + const MeshingParameters & mparam, int nr, int projecttype, bool delete_on_failure); - // Philippose - 31.09.2009 - // External access to the mesh generation functions within the OCC - // subsystem (Not sure if this is the best way to implement this....!!) - DLL_HEADER extern int OCCGenerateMesh (OCCGeometry & occgeometry, shared_ptr & mesh, - MeshingParameters & mparam); + template + void PropagateIdentifications (TBuilder & builder, TopoDS_Shape shape, std::optional> trafo = nullopt) + { + TopTools_IndexedMapOfShape mod_indices; + Array modifications; + TopTools_MapOfShape shape_handled; - DLL_HEADER extern void OCCSetLocalMeshSize(OCCGeometry & geom, Mesh & mesh); + Transformation<3> trafo_inv; + if(trafo) + trafo_inv = trafo->CalcInverse(); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto s = e.Current(); + mod_indices.Add(s); + modifications.Append(TopTools_IndexedMapOfShape()); + modifications.Last().Add(s); + } + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto s = e.Current(); + for (auto mods : builder.Modified(s)) + { + auto index = mod_indices.FindIndex(s)-1; + modifications[index].Add(mods); + } + } + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto s = e.Current(); + + if(shape_handled.Contains(s)) + continue; + shape_handled.Add(s); - DLL_HEADER extern void OCCMeshSurface (OCCGeometry & geom, Mesh & mesh, int perfstepsend); + if(!OCCGeometry::HaveIdentifications(s)) + continue; + auto& identifications = OCCGeometry::GetIdentifications(s); - DLL_HEADER extern void OCCFindEdges (OCCGeometry & geom, Mesh & mesh); + auto& shape_mapped = modifications[mod_indices.FindIndex(s)-1]; + + for(auto ident : identifications) + { + auto i1 = mod_indices.FindIndex(ident.to); + auto i2 = mod_indices.FindIndex(ident.from); + if(i1 == 0 || i2 == 0) // not in geometry + continue; + auto& mods_to = modifications[i1-1]; + auto& mods_from = modifications[i2-1]; + if(mods_to.Extent()==1 && mods_from.Extent() ==1) + continue; + + auto from = ident.from; + auto to = ident.to; + + for(auto it = mods_from.cbegin(); it != mods_from.cend(); it++) + for(auto it2 = mods_to.cbegin(); it2 != mods_to.cend(); it2++) + { + auto& from_mapped = it.Iterator().Value(); + auto& to_mapped = it2.Iterator().Value(); + if(from.IsSame(from_mapped) && to.IsSame(to_mapped)) + continue; + + Transformation<3> trafo_mapped = ident.trafo; + if(trafo) + { + Transformation<3> trafo_temp; + trafo_temp.Combine(ident.trafo, trafo_inv); + trafo_mapped.Combine(*trafo, trafo_temp); + } + + if(!IsMappedShape(trafo_mapped, from_mapped, to_mapped)) + continue; + + OCCIdentification id_new = ident; + id_new.to = to_mapped; + id_new.from = from_mapped; + id_new.trafo = trafo_mapped; + auto id_owner = from.IsSame(s) ? from_mapped : to_mapped; + OCCGeometry::GetIdentifications(id_owner).push_back(id_new); + } + } + } + } + + template + void PropagateProperties (TBuilder & builder, TopoDS_Shape shape, std::optional> trafo = nullopt) + { + bool have_identifications = false; + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto s = e.Current(); + have_identifications |= OCCGeometry::HaveIdentifications(s); + if(!OCCGeometry::HaveProperties(s)) + continue; + auto prop = OCCGeometry::GetProperties(s); + for (auto mods : builder.Modified(s)) + OCCGeometry::GetProperties(mods).Merge(prop); + } + if(have_identifications) + PropagateIdentifications(builder, shape, trafo); + } + + namespace step_utils + { + inline Handle(TCollection_HAsciiString) MakeName (string s) + { + return new TCollection_HAsciiString(s.c_str()); + }; + + inline Handle(StepRepr_RepresentationItem) MakeInt (int n, string name = "") + { + Handle(StepRepr_IntegerRepresentationItem) int_obj = new StepRepr_IntegerRepresentationItem; + int_obj->Init(MakeName(name), n); + return int_obj; + } + + inline int ReadInt (Handle(StepRepr_RepresentationItem) item) + { + return Handle(StepRepr_IntegerRepresentationItem)::DownCast(item)->Value(); + } + + inline Handle(StepRepr_RepresentationItem) MakeReal (double val, string name = "") + { + Handle(StepBasic_MeasureValueMember) value_member = new StepBasic_MeasureValueMember; + value_member->SetReal(val); + Handle(StepRepr_ValueRepresentationItem) value_repr = new StepRepr_ValueRepresentationItem; + value_repr->Init(MakeName(name), value_member); + return value_repr; + } + + inline double ReadReal (Handle(StepRepr_RepresentationItem) item) + { + return Handle(StepRepr_ValueRepresentationItem)::DownCast(item) + ->ValueComponentMember()->Real(); + } + + + inline Handle(StepRepr_RepresentationItem) MakeCompound( FlatArray items, string name = "" ) + { + Handle(StepRepr_HArray1OfRepresentationItem) array_repr = new StepRepr_HArray1OfRepresentationItem(1,items.Size()); + + for(auto i : Range(items)) + array_repr->SetValue(i+1, items[i]); + + Handle(StepRepr_CompoundRepresentationItem) comp = new StepRepr_CompoundRepresentationItem; + comp->Init( MakeName(name), array_repr ); + return comp; + } + + void WriteIdentifications(const Handle(Interface_InterfaceModel) model, const TopoDS_Shape & shape, const Handle(Transfer_FinderProcess) finder); + void ReadIdentifications(Handle(StepRepr_RepresentationItem) item, Handle(Transfer_TransientProcess) transProc); + + inline Quantity_ColorRGBA MakeColor(const Vec<4> & c) + { + return Quantity_ColorRGBA (c[0], c[1], c[2], c[3]); + } + + inline Vec<4> ReadColor (const Quantity_ColorRGBA & c) + { + auto rgb = c.GetRGB(); + return {rgb.Red(), rgb.Green(), rgb.Blue(), c.Alpha()}; + } + + + void LoadProperties(const TopoDS_Shape & shape, + const STEPCAFControl_Reader & reader, + const Handle(TDocStd_Document) step_doc); + void WriteProperties(const Handle(Interface_InterfaceModel) model, const Handle(Transfer_FinderProcess) finder, const TopoDS_Shape & shape); + + void WriteSTEP(const TopoDS_Shape & shape, const filesystem::path & filename); + + inline void WriteSTEP(const OCCGeometry & geo, const filesystem::path & filename) + { + WriteSTEP(geo.GetShape(), filename); + } + + // deep copy, also ensures consistent shape ordering (face numbers etc.) + TopoDS_Shape WriteAndRead(const TopoDS_Shape shape); + } // namespace step_utils } #endif diff --git a/libsrc/occ/occmeshsurf.cpp b/libsrc/occ/occmeshsurf.cpp index 56fbf07b..a4eaff83 100644 --- a/libsrc/occ/occmeshsurf.cpp +++ b/libsrc/occ/occmeshsurf.cpp @@ -2,16 +2,16 @@ #include -#include #include +#include "occgeom.hpp" + #include #include +#include "occmeshsurf.hpp" namespace netgen { -#include "occmeshsurf.hpp" - bool glob_testout(false); @@ -19,34 +19,18 @@ namespace netgen const PointGeomInfo & geominfo, Vec<3> & n) const { - gp_Pnt pnt; - gp_Vec du, dv; + GeomLProp_SLProps lprop(occface,geominfo.u,geominfo.v,1,1e-8); - /* - double gu = geominfo.u; - double gv = geominfo.v; - - if (fabs (gu) < 1e-3) gu = 0; - if (fabs (gv) < 1e-3) gv = 0; - - occface->D1(gu,gv,pnt,du,dv); - */ - - /* - occface->D1(geominfo.u,geominfo.v,pnt,du,dv); - - n = Cross (Vec<3>(du.X(), du.Y(), du.Z()), - Vec<3>(dv.X(), dv.Y(), dv.Z())); - n.Normalize(); - */ - - - - GeomLProp_SLProps lprop(occface,geominfo.u,geominfo.v,1,1e-5); - double setu=geominfo.u,setv=geominfo.v; - - if(lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5) + if (lprop.IsNormalDefined()) { + n = occ2ng(lprop.Normal()); + } + else + { + gp_Pnt pnt; + gp_Vec du, dv; + + double setu=geominfo.u,setv=geominfo.v; double ustep = 0.01*(umax-umin); double vstep = 0.01*(vmax-vmin); @@ -57,9 +41,12 @@ namespace netgen if(setu < umax) { lprop.SetParameters(setu,setv); + /* n(0)+=lprop.Normal().X(); n(1)+=lprop.Normal().Y(); n(2)+=lprop.Normal().Z(); + */ + n += occ2ng(lprop.Normal()); } setu = geominfo.u; while(setu > umin && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5)) @@ -67,9 +54,12 @@ namespace netgen if(setu > umin) { lprop.SetParameters(setu,setv); + /* n(0)+=lprop.Normal().X(); n(1)+=lprop.Normal().Y(); n(2)+=lprop.Normal().Z(); + */ + n += occ2ng(lprop.Normal()); } setu = geominfo.u; @@ -78,9 +68,12 @@ namespace netgen if(setv < vmax) { lprop.SetParameters(setu,setv); + /* n(0)+=lprop.Normal().X(); n(1)+=lprop.Normal().Y(); n(2)+=lprop.Normal().Z(); + */ + n += occ2ng(lprop.Normal()); } setv = geominfo.v; while(setv > vmin && (lprop.D1U().Magnitude() < 1e-5 || lprop.D1V().Magnitude() < 1e-5)) @@ -88,20 +81,17 @@ namespace netgen if(setv > vmin) { lprop.SetParameters(setu,setv); + /* n(0)+=lprop.Normal().X(); n(1)+=lprop.Normal().Y(); n(2)+=lprop.Normal().Z(); + */ + n += occ2ng(lprop.Normal()); } setv = geominfo.v; n.Normalize(); } - else - { - n(0)=lprop.Normal().X(); - n(1)=lprop.Normal().Y(); - n(2)=lprop.Normal().Z(); - } if(glob_testout) { @@ -112,7 +102,7 @@ namespace netgen - if (orient == TopAbs_REVERSED) n = -1*n; + if (orient == TopAbs_REVERSED) n = -n; // (*testout) << "GetNormalVector" << endl; } @@ -173,6 +163,50 @@ namespace netgen gp_Vec du, dv; occface->D1 (geominfo1.u, geominfo1.v, pnt, du, dv); + // static Timer t("occ-defintangplane calculations"); + // RegionTimer reg(t); + + Mat<3,2> D1_; + D1_(0,0) = du.X(); D1_(1,0) = du.Y(); D1_(2,0) = du.Z(); + D1_(0,1) = dv.X(); D1_(1,1) = dv.Y(); D1_(2,1) = dv.Z(); + auto D1T_ = Trans(D1_); + auto D1TD1_ = D1T_*D1_; + if (Det (D1TD1_) == 0) throw SingularMatrixException(); + Mat<2,2> DDTinv_; + CalcInverse (D1TD1_, DDTinv_); + + Mat<3,2> Y_; + Vec<3> y1_ = (ap2-ap1).Normalize(); + Vec<3> y2_ = Cross(n, y1_).Normalize(); + for (int i = 0; i < 3; i++) + { + Y_(i,0) = y1_(i); + Y_(i,1) = y2_(i); + } + + auto A_ = DDTinv_ * D1T_ * Y_; + Mat<2,2> Ainv_; + if (Det(A_) == 0) throw SingularMatrixException(); + CalcInverse (A_, Ainv_); + + Vec<2> temp_ = Ainv_ * (psp2-psp1); + double r_ = temp_.Length(); + Mat<2,2> R_; + R_(0,0) = temp_(0)/r_; + R_(1,0) = temp_(1)/r_; + R_(0,1) = -R_(1,0); + R_(1,1) = R_(0,0); + + A_ = A_ * R_; + Ainv_ = Trans(R_) * Ainv_; + + Amat = A_; + Amatinv = Ainv_; + + // temp = Amatinv * (psp2-psp1); + + +#ifdef OLD DenseMatrix D1(3,2), D1T(2,3), DDTinv(2,2); D1(0,0) = du.X(); D1(1,0) = du.Y(); D1(2,0) = du.Z(); D1(0,1) = dv.X(); D1(1,1) = dv.Y(); D1(2,1) = dv.Z(); @@ -190,6 +224,8 @@ namespace netgen if (D1TD1.Det() == 0) throw SingularMatrixException(); CalcInverse (D1TD1, DDTinv); + // cout << " =?= inv = " << DDTinv << endl; + DenseMatrix Y(3,2); Vec<3> y1 = (ap2-ap1).Normalize(); Vec<3> y2 = Cross(n, y1).Normalize(); @@ -226,6 +262,7 @@ namespace netgen R(0,1) = sin (alpha); R(1,1) = cos (alpha); + // cout << "=?= R = " << R << endl; A = A*R; @@ -240,9 +277,10 @@ namespace netgen Amat(i,j) = A(i,j); Amatinv(i,j) = Ainv(i,j); } - + // cout << "=?= Ainv = " << endl << Ainv << endl; temp = Amatinv * (psp2-psp1); - + cout << " =?= Amatinv = " << Amatinv << endl; +#endif }; } @@ -294,7 +332,9 @@ namespace netgen Point<3> & p3d, PointGeomInfo & gi, double h) - { + { + static Timer t("FromPlane"); RegionTimer reg(t); + if (projecttype == PLANESPACE) { // cout << "2d : " << pplane << endl; @@ -311,19 +351,85 @@ namespace netgen gi.u = pspnew(0); gi.v = pspnew(1); gi.trignum = 1; - gp_Pnt val = occface->Value (gi.u, gi.v); - p3d = Point<3> (val.X(), val.Y(), val.Z()); + p3d = occ2ng(occface->Value (gi.u, gi.v)); }; } - void OCCSurface :: Project (Point<3> & p, PointGeomInfo & gi) + void OCCSurface :: Project (Point<3> & ap, PointGeomInfo & gi) { + static Timer t("OccSurface::Project"); RegionTimer reg(t); + static Timer t2("OccSurface::Project actual"); + + + // try Newton's method ... + + gp_Pnt p = ng2occ(ap); + + double u = gi.u; + double v = gi.v; +#ifdef OLD + // was a problem for pheres: got u-v parameters outside range of definition + + gp_Pnt x = occface->Value (u,v); + + if (p.SquareDistance(x) <= sqr(PROJECTION_TOLERANCE)) return; + + gp_Vec du, dv; + occface->D1(u,v,x,du,dv); + + int count = 0; + + gp_Pnt xold; + gp_Vec n; + double det, lambda, mu; + + do + { + count++; + + n = du^dv; + + det = Det3 (n.X(), du.X(), dv.X(), + n.Y(), du.Y(), dv.Y(), + n.Z(), du.Z(), dv.Z()); + + if (det < 1e-15) + break; + + lambda = Det3 (n.X(), p.X()-x.X(), dv.X(), + n.Y(), p.Y()-x.Y(), dv.Y(), + n.Z(), p.Z()-x.Z(), dv.Z())/det; + + mu = Det3 (n.X(), du.X(), p.X()-x.X(), + n.Y(), du.Y(), p.Y()-x.Y(), + n.Z(), du.Z(), p.Z()-x.Z())/det; + + u += lambda; + v += mu; + + xold = x; + occface->D1(u,v,x,du,dv); + + if (xold.SquareDistance(x) < sqr(PROJECTION_TOLERANCE)) + { + ap = Point<3> (x.X(), x.Y(), x.Z()); + gi.u = u; + gi.v = v; + return; + } + } + while (count < 20); +#endif + + // Newton did not converge, use OCC projection + + // static int cnt = 0; // if (cnt++ % 1000 == 0) cout << "********************************************** OCCSurfce :: Project, cnt = " << cnt << endl; - gp_Pnt pnt(p(0), p(1), p(2)); + gp_Pnt pnt = p; // (p(0), p(1), p(2)); //(*testout) << "pnt = " << pnt.X() << ", " << pnt.Y() << ", " << pnt.Z() << endl; @@ -358,250 +464,68 @@ namespace netgen proj.LowerDistanceParameters (gi.u, gi.v); */ - double u,v; + // double u,v; Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface ); - gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( topods_face ) ); + auto toltool = BRep_Tool::Tolerance( topods_face ); + + // gp_Pnt2d suval = su->ValueOfUV ( pnt, toltool); + t2.Start(); + gp_Pnt2d suval = su->NextValueOfUV (gp_Pnt2d(u,v), pnt, toltool); + t2.Stop(); suval.Coord( u, v); pnt = occface->Value( u, v ); //(*testout) << "pnt(proj) = " << pnt.X() << ", " << pnt.Y() << ", " << pnt.Z() << endl; gi.u = u; gi.v = v; - - gi.trignum = 1; - p = Point<3> (pnt.X(), pnt.Y(), pnt.Z()); + ap = occ2ng(pnt); // Point<3> (pnt.X(), pnt.Y(), pnt.Z()); } - Meshing2OCCSurfaces :: Meshing2OCCSurfaces (const TopoDS_Shape & asurf, - const Box<3> & abb, int aprojecttype) - : Meshing2(mparam, Box<3>(abb.PMin(), abb.PMax())), surface(TopoDS::Face(asurf), aprojecttype) + Meshing2OCCSurfaces :: Meshing2OCCSurfaces (const NetgenGeometry& geo, + const TopoDS_Shape & asurf, + const Box<3> & abb, int aprojecttype, + const MeshingParameters & mparam) + : Meshing2(geo, mparam, Box<3>(abb.PMin(), abb.PMax())), + surface(TopoDS::Face(asurf), aprojecttype) { ; } - void Meshing2OCCSurfaces :: DefineTransformation (const Point3d & p1, const Point3d & p2, + void Meshing2OCCSurfaces :: DefineTransformation (const Point<3> & p1, const Point<3> & p2, const PointGeomInfo * geominfo1, const PointGeomInfo * geominfo2) { ((OCCSurface&)surface).DefineTangentialPlane (p1, *geominfo1, p2, *geominfo2); } - void Meshing2OCCSurfaces :: TransformToPlain (const Point3d & locpoint, + void Meshing2OCCSurfaces :: TransformToPlain (const Point<3>& locpoint, const MultiPointGeomInfo & geominfo, - Point2d & planepoint, + Point<2> & planepoint, double h, int & zone) { - Point<2> hp; - surface.ToPlane (locpoint, geominfo.GetPGI(1), hp, h, zone); - planepoint.X() = hp(0); - planepoint.Y() = hp(1); + surface.ToPlane (locpoint, geominfo.GetPGI(1), planepoint, h, zone); } - int Meshing2OCCSurfaces :: TransformFromPlain (Point2d & planepoint, - Point3d & locpoint, + int Meshing2OCCSurfaces :: TransformFromPlain (const Point<2> & planepoint, + Point<3> & locpoint, PointGeomInfo & gi, double h) { - Point<3> hp; - Point<2> hp2 (planepoint.X(), planepoint.Y()); - surface.FromPlane (hp2, hp, gi, h); - locpoint = hp; + surface.FromPlane (planepoint, locpoint, gi, h); return 0; } - double Meshing2OCCSurfaces :: CalcLocalH (const Point3d & p, double gh) const + double Meshing2OCCSurfaces :: CalcLocalH (const Point<3> & p, double gh) const { return gh; } - - - - - - MeshOptimize2dOCCSurfaces :: MeshOptimize2dOCCSurfaces (const OCCGeometry & ageometry) - : MeshOptimize2d(), geometry(ageometry) - { - ; - } - - - void MeshOptimize2dOCCSurfaces :: ProjectPoint (INDEX surfind, Point<3> & p) const - { - geometry.Project (surfind, p); - } - - - int MeshOptimize2dOCCSurfaces :: ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const - { - double u = gi.u; - double v = gi.v; - - Point<3> hp = p; - if (geometry.FastProject (surfind, hp, u, v)) - { - p = hp; - return 1; - } - ProjectPoint (surfind, p); - return CalcPointGeomInfo (surfind, gi, p); - } - - - void MeshOptimize2dOCCSurfaces :: ProjectPoint2 (INDEX surfind, INDEX surfind2, - Point<3> & p) const - { - TopExp_Explorer exp0, exp1; - bool done = false; - Handle(Geom_Curve) c; - - for (exp0.Init(geometry.fmap(surfind), TopAbs_EDGE); !done && exp0.More(); exp0.Next()) - for (exp1.Init(geometry.fmap(surfind2), TopAbs_EDGE); !done && exp1.More(); exp1.Next()) - { - if (TopoDS::Edge(exp0.Current()).IsSame(TopoDS::Edge(exp1.Current()))) - { - done = true; - double s0, s1; - c = BRep_Tool::Curve(TopoDS::Edge(exp0.Current()), s0, s1); - } - } - - gp_Pnt pnt(p(0), p(1), p(2)); - GeomAPI_ProjectPointOnCurve proj(pnt, c); - pnt = proj.NearestPoint(); - p(0) = pnt.X(); - p(1) = pnt.Y(); - p(2) = pnt.Z(); - - } - - void MeshOptimize2dOCCSurfaces :: - GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & geominfo, Vec<3> & n) const - { - gp_Pnt pnt; - gp_Vec du, dv; - - Handle(Geom_Surface) occface; - occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind))); - - occface->D1(geominfo.u,geominfo.v,pnt,du,dv); - - n = Cross (Vec<3>(du.X(), du.Y(), du.Z()), - Vec<3>(dv.X(), dv.Y(), dv.Z())); - n.Normalize(); - - if (geometry.fmap(surfind).Orientation() == TopAbs_REVERSED) n = -1*n; - - // GetNormalVector (surfind, p, n); - } - - - void MeshOptimize2dOCCSurfaces :: - GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const - { - // static int cnt = 0; - // if (cnt++ % 1000 == 0) cout << "GetNV cnt = " << cnt << endl; - Standard_Real u,v; - - gp_Pnt pnt(p(0), p(1), p(2)); - - Handle(Geom_Surface) occface; - occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind))); - - /* - GeomAPI_ProjectPointOnSurf proj(pnt, occface); - - if (proj.NbPoints() < 1) - { - cout << "ERROR: OCCSurface :: GetNormalVector: GeomAPI_ProjectPointOnSurf failed!" - << endl; - cout << p << endl; - return; - } - - proj.LowerDistanceParameters (u, v); - */ - - Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface ); - gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(geometry.fmap(surfind)) ) ); - suval.Coord( u, v); - pnt = occface->Value( u, v ); - - - - gp_Vec du, dv; - occface->D1(u,v,pnt,du,dv); - - /* - if (!occface->IsCNu (1) || !occface->IsCNv (1)) - (*testout) << "SurfOpt: Differentiation FAIL" << endl; - */ - - n = Cross (Vec3d(du.X(), du.Y(), du.Z()), - Vec3d(dv.X(), dv.Y(), dv.Z())); - n.Normalize(); - - if (geometry.fmap(surfind).Orientation() == TopAbs_REVERSED) n = -1*n; - } - - - int MeshOptimize2dOCCSurfaces :: - CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p) const - { - Standard_Real u,v; - - gp_Pnt pnt(p(0), p(1), p(2)); - - Handle(Geom_Surface) occface; - occface = BRep_Tool::Surface(TopoDS::Face(geometry.fmap(surfind))); - - /* - GeomAPI_ProjectPointOnSurf proj(pnt, occface); - - if (proj.NbPoints() < 1) - { - cout << "ERROR: OCCSurface :: GetNormalVector: GeomAPI_ProjectPointOnSurf failed!" - << endl; - cout << p << endl; - return 0; - } - - proj.LowerDistanceParameters (u, v); - */ - - Handle( ShapeAnalysis_Surface ) su = new ShapeAnalysis_Surface( occface ); - gp_Pnt2d suval = su->ValueOfUV ( pnt, BRep_Tool::Tolerance( TopoDS::Face(geometry.fmap(surfind)) ) ); - suval.Coord( u, v); - //pnt = occface->Value( u, v ); - - - gi.u = u; - gi.v = v; - return 1; - } - - - - - - - OCCRefinementSurfaces :: OCCRefinementSurfaces (const OCCGeometry & ageometry) - : Refinement(), geometry(ageometry) - { - ; - } - - OCCRefinementSurfaces :: ~OCCRefinementSurfaces () - { - ; - } - /* inline double Det3 (double a00, double a01, double a02, double a10, double a11, double a12, @@ -660,76 +584,6 @@ namespace netgen return true; } */ - - void OCCRefinementSurfaces :: - 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 - { - Point<3> hnewp; - hnewp = p1+secpoint*(p2-p1); - - if (surfi > 0) - { - double u = gi1.u+secpoint*(gi2.u-gi1.u); - double v = gi1.v+secpoint*(gi2.v-gi1.v); - - auto savept = hnewp; - if (!geometry.FastProject (surfi, hnewp, u, v) || Dist(hnewp, savept) > Dist(p1,p2)) - { - // cout << "Fast projection to surface fails! Using OCC projection" << endl; - hnewp = savept; - geometry.Project (surfi, hnewp); - } - - newgi.trignum = 1; - newgi.u = u; - newgi.v = v; - } - - newp = hnewp; - } - - - void OCCRefinementSurfaces :: - PointBetween (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 - { - double s0, s1; - - Point<3> hnewp = p1+secpoint*(p2-p1); - gp_Pnt pnt(hnewp(0), hnewp(1), hnewp(2)); - GeomAPI_ProjectPointOnCurve proj(pnt, BRep_Tool::Curve(TopoDS::Edge(geometry.emap(ap1.edgenr)), s0, s1)); - pnt = proj.NearestPoint(); - hnewp = Point<3> (pnt.X(), pnt.Y(), pnt.Z()); - newp = hnewp; - newgi = ap1; - }; - - - void OCCRefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi) const - { - if (surfi > 0) - geometry.Project (surfi, p); - }; - - void OCCRefinementSurfaces :: ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const - { - if (surfi > 0) - if (!geometry.FastProject (surfi, p, gi.u, gi.v)) - { - cout << "Fast projection to surface fails! Using OCC projection" << endl; - geometry.Project (surfi, p); - } - }; - - - } diff --git a/libsrc/occ/occmeshsurf.hpp b/libsrc/occ/occmeshsurf.hpp index 38172072..b4c4b06e 100644 --- a/libsrc/occ/occmeshsurf.hpp +++ b/libsrc/occ/occmeshsurf.hpp @@ -6,9 +6,15 @@ #include "occgeom.hpp" #include "mydefs.hpp" +#include +#include +#include + #define PARAMETERSPACE -1 #define PLANESPACE 1 +namespace netgen +{ class OCCGeometry; class SingularMatrixException @@ -55,6 +61,7 @@ protected: public: OCCSurface (const TopoDS_Face & aface, int aprojecttype) { + static Timer t("occurface ctor"); RegionTimer r(t); topods_face = aface; occface = BRep_Tool::Surface(topods_face); orient = topods_face.Orientation(); @@ -112,7 +119,9 @@ class Meshing2OCCSurfaces : public Meshing2 public: /// - Meshing2OCCSurfaces (const TopoDS_Shape & asurf, const Box<3> & aboundingbox, int aprojecttype); + Meshing2OCCSurfaces (const NetgenGeometry& geo, + const TopoDS_Shape & asurf, const Box<3> & aboundingbox, + int aprojecttype, const MeshingParameters & mparam); /// int GetProjectionType () @@ -120,85 +129,29 @@ public: protected: /// - virtual void DefineTransformation (const Point3d & p1, const Point3d & p2, - const PointGeomInfo * geominfo1, - const PointGeomInfo * geominfo2); + void DefineTransformation (const Point<3> & p1, const Point<3> & p2, + const PointGeomInfo * geominfo1, + const PointGeomInfo * geominfo2) override; /// - virtual void TransformToPlain (const Point3d & locpoint, - const MultiPointGeomInfo & geominfo, - Point2d & plainpoint, - double h, int & zone); + void TransformToPlain (const Point<3> & locpoint, + const MultiPointGeomInfo & geominfo, + Point<2> & plainpoint, + double h, int & zone) override; /// - virtual int TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, - PointGeomInfo & gi, - double h); + int TransformFromPlain (const Point<2> & plainpoint, + Point<3> & locpoint, + PointGeomInfo & gi, + double h) override; /// - virtual double CalcLocalH (const Point3d & p, double gh) const; + double CalcLocalH (const Point<3> & p, double gh) const override; }; - - -/// -class MeshOptimize2dOCCSurfaces : public MeshOptimize2d - { - /// - const OCCGeometry & geometry; - -public: - /// - MeshOptimize2dOCCSurfaces (const OCCGeometry & ageometry); - - /// - virtual void ProjectPoint (INDEX surfind, Point<3> & p) const; - /// - virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const; - /// - virtual int ProjectPointGI (INDEX surfind, Point<3> & p, PointGeomInfo & gi) const; - /// - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const; - /// - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, PointGeomInfo & gi, Vec<3> & n) const; - - virtual int CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const; -}; - - - class OCCGeometry; - -class DLL_HEADER OCCRefinementSurfaces : public Refinement -{ - const OCCGeometry & geometry; - -public: - OCCRefinementSurfaces (const OCCGeometry & ageometry); - virtual ~OCCRefinementSurfaces (); - - 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; - - virtual void PointBetween (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; - - virtual void ProjectToSurface (Point<3> & p, int surfi) const; - - virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const; -}; - - +} // namespace netgen #endif - - #endif diff --git a/libsrc/occ/occpkg.cpp b/libsrc/occ/occpkg.cpp index 3a562641..ecc11af8 100644 --- a/libsrc/occ/occpkg.cpp +++ b/libsrc/occ/occpkg.cpp @@ -15,8 +15,11 @@ #include "vsocc.hpp" +#include +#include + // __declspec(dllimport) void AutoColourBcProps(Mesh & mesh, const char *bccolourfile); -// __declspec(dllimport) void GetFaceColours(Mesh & mesh, Array & face_colours); +// __declspec(dllimport) void GetFaceColours(Mesh & mesh, NgArray & face_colours); // __declspec(dllimport) bool ColourMatch(Vec3d col1, Vec3d col2, double eps = 2.5e-05); extern "C" int Ng_occ_Init (Tcl_Interp * interp); @@ -27,6 +30,8 @@ namespace netgen { extern DLL_HEADER shared_ptr ng_geometry; extern DLL_HEADER shared_ptr mesh; + extern DLL_HEADER MeshingParameters mparam; + extern DLL_HEADER OCCParameters occparam; char * err_needsoccgeometry = (char*) "This operation needs an OCC geometry"; extern char * err_needsmesh; @@ -38,19 +43,17 @@ namespace netgen class OCCGeometryRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const; + virtual NetgenGeometry * Load (const filesystem::path & filename) const; virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; virtual void SetParameters (Tcl_Interp * interp) { - occparam.resthcloseedgefac = - atof (Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0)); - occparam.resthcloseedgeenable = - atoi (Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0)); - occparam.resthminedgelen = - atof (Tcl_GetVar (interp, "::stloptions.resthminedgelen", 0)); - occparam.resthminedgelenenable = - atoi (Tcl_GetVar (interp, "::stloptions.resthminedgelenenable", 0)); + // occparam.resthminedgelen = + // atof (Tcl_GetVar (interp, "::stloptions.resthminedgelen", 0)); + // occparam.resthminedgelenenable = + // atoi (Tcl_GetVar (interp, "::stloptions.resthminedgelenenable", 0)); + if(auto geo = dynamic_pointer_cast(ng_geometry); geo) + geo->SetOCCParameters(occparam); } }; @@ -73,7 +76,7 @@ namespace netgen if (showvolume < 0 || showvolume > occgeometry->NrSolids()) { char buf[20]; - sprintf (buf, "%5i", vispar.occshowvolumenr); + snprintf (buf, size(buf), "%5i", vispar.occshowvolumenr); Tcl_SetVar (interp, "::occoptions.showvolumenr", buf, 0); } else @@ -519,7 +522,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; @@ -588,7 +592,7 @@ namespace netgen { if(!occgeometry->GetFaceMaxhModified(i)) { - occgeometry->SetFaceMaxH(i, mparam.maxh); + occgeometry->SetFaceMaxH(i, mparam.maxh, mparam); } } @@ -597,7 +601,7 @@ namespace netgen int facenr = atoi (argv[2]); double surfms = atof (argv[3]); if (occgeometry && facenr >= 1 && facenr <= occgeometry->NrFaces()) - occgeometry->SetFaceMaxH(facenr, surfms); + occgeometry->SetFaceMaxH(facenr, surfms, mparam); } @@ -608,7 +612,7 @@ namespace netgen { int nrFaces = occgeometry->NrFaces(); for (int i = 1; i <= nrFaces; i++) - occgeometry->SetFaceMaxH(i, surfms); + occgeometry->SetFaceMaxH(i, surfms, mparam); } } @@ -617,18 +621,18 @@ namespace netgen int facenr = atoi (argv[2]); if (occgeometry && facenr >= 1 && facenr <= occgeometry->NrFaces()) { - sprintf (buf, "%5.2f", occgeometry->GetFaceMaxH(facenr)); + snprintf (buf, size(buf), "%5.2f", occgeometry->GetFaceMaxH(facenr)); } else { - sprintf (buf, "%5.2f", mparam.maxh); + snprintf (buf, size(buf), "%5.2f", mparam.maxh); } Tcl_SetResult (interp, buf, TCL_STATIC); } if (strcmp (argv[1], "getactive") == 0) { - sprintf (buf, "%d", occgeometry->SelectedFace()); + snprintf (buf, size(buf), "%d", occgeometry->SelectedFace()); Tcl_SetResult (interp, buf, TCL_STATIC); } @@ -648,9 +652,9 @@ namespace netgen if (strcmp (argv[1], "getnfd") == 0) { if (occgeometry) - sprintf (buf, "%d", occgeometry->NrFaces()); + snprintf (buf, size(buf), "%d", occgeometry->NrFaces()); else - sprintf (buf, "0"); + snprintf (buf, size(buf), "0"); Tcl_SetResult (interp, buf, TCL_STATIC); } return TCL_OK; @@ -686,14 +690,14 @@ namespace netgen if(strcmp(argv[1], "getcolours") == 0) { stringstream outVar; - Array face_colours; + NgArray> face_colours; GetFaceColours(*mesh, face_colours); for(int i = 0; i < face_colours.Size();i++) { - outVar << "{ " << face_colours[i].X(1) - << " " << face_colours[i].X(2) - << " " << face_colours[i].X(3) + outVar << "{ " << face_colours[i][0] + << " " << face_colours[i][1] + << " " << face_colours[i][2] << " } "; } @@ -703,14 +707,14 @@ namespace netgen if(strcmp(argv[1], "showalso") == 0) { - Array face_colours; + NgArray> face_colours; GetFaceColours(*mesh,face_colours); int colourind = atoi (argv[2]); for(int i = 1; i <= mesh->GetNFD(); i++) { - Array surfElems; + Array surfElems; mesh->GetSurfaceElementsOfFace(i,surfElems); if(ColourMatch(face_colours[colourind],mesh->GetFaceDescriptor(i).SurfColour())) @@ -727,7 +731,7 @@ namespace netgen if(strcmp(argv[1], "hidealso") == 0) { - Array face_colours; + NgArray> face_colours; GetFaceColours(*mesh,face_colours); int colourind = atoi (argv[2]); @@ -751,7 +755,7 @@ namespace netgen if(strcmp(argv[1], "showonly") == 0) { - Array face_colours; + NgArray> face_colours; GetFaceColours(*mesh,face_colours); int colourind = atoi (argv[2]); @@ -782,7 +786,7 @@ namespace netgen if(strcmp(argv[1], "hideonly") == 0) { - Array face_colours; + NgArray> face_colours; GetFaceColours(*mesh,face_colours); int colourind = atoi (argv[2]); @@ -889,55 +893,27 @@ namespace netgen - NetgenGeometry * OCCGeometryRegister :: Load (string filename) const + NetgenGeometry * OCCGeometryRegister :: Load (const filesystem::path & filename) const { - const char * lgfilename = filename.c_str(); + string ext = ToLower(filename.extension()); - - /* - if (strcmp (&cfilename[strlen(cfilename)-3], "geo") == 0) + if (ext == ".iges" || ext == ".igs") { - PrintMessage (1, "Load OCCG geometry file ", cfilename); - - extern OCCGeometry * ParseOCCG (istream & istr); - - ifstream infile(cfilename); - - OCCGeometry * hgeom = ParseOCCG (infile); - if (!hgeom) - throw NgException ("geo-file should start with 'algebraic3d'"); - - hgeom -> FindIdenticSurfaces(1e-8 * hgeom->MaxSize()); - return hgeom; - } - */ - - - if ((strcmp (&lgfilename[strlen(lgfilename)-4], "iges") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-3], "igs") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-3], "IGS") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-4], "IGES") == 0)) - { - PrintMessage (1, "Load IGES geometry file ", lgfilename); - OCCGeometry * occgeometry = LoadOCC_IGES (lgfilename); + PrintMessage (1, "Load IGES geometry file ", filename); + OCCGeometry * occgeometry = LoadOCC_IGES (filename); return occgeometry; } - else if ((strcmp (&lgfilename[strlen(lgfilename)-4], "step") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-3], "stp") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-3], "STP") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-4], "STEP") == 0)) + else if (ext == ".stp" || ext == ".step") { - PrintMessage (1, "Load STEP geometry file ", lgfilename); - OCCGeometry * occgeometry = LoadOCC_STEP (lgfilename); + PrintMessage (1, "Load STEP geometry file ", filename); + OCCGeometry * occgeometry = LoadOCC_STEP (filename); return occgeometry; } - else if ((strcmp (&lgfilename[strlen(lgfilename)-4], "brep") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-4], "Brep") == 0) || - (strcmp (&lgfilename[strlen(lgfilename)-4], "BREP") == 0)) + else if (ext == ".brep") { - PrintMessage (1, "Load BREP geometry file ", lgfilename); - OCCGeometry * occgeometry = LoadOCC_BREP (lgfilename); + PrintMessage (1, "Load BREP geometry file ", filename); + OCCGeometry * occgeometry = LoadOCC_BREP (filename); return occgeometry; } diff --git a/libsrc/occ/python_occ.cpp b/libsrc/occ/python_occ.cpp index eec271a1..48089571 100644 --- a/libsrc/occ/python_occ.cpp +++ b/libsrc/occ/python_occ.cpp @@ -1,24 +1,145 @@ #ifdef NG_PYTHON #ifdef OCCGEOMETRY -#include <../general/ngpython.hpp> +#include +#include +#include +#include #include -#include + +#include "occgeom.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace netgen; namespace netgen { extern std::shared_ptr ng_geometry; + extern std::shared_ptr mesh; } +static string occparameter_description = R"delimiter( +OCC Specific Meshing Parameters +------------------------------- + +closeedgefac: Optional[float] = 2. + Factor for meshing close edges, if None it is disabled. + +minedgelen: Optional[float] = 0.001 + Minimum edge length to be used for dividing edges to mesh points. If + None this is disabled. + +)delimiter"; + +void CreateOCCParametersFromKwargs(OCCParameters& occparam, py::dict kwargs) +{ + if(kwargs.contains("minedgelen")) + { + auto val = kwargs.attr("pop")("minedgelen"); + if(val.is_none()) + occparam.resthminedgelenenable = false; + else + { + occparam.resthminedgelen = py::cast(val); + occparam.resthminedgelenenable = true; + } + } +} + +extern py::object CastShape(const TopoDS_Shape & s); + +DLL_HEADER void ExportNgOCCBasic(py::module &m); +DLL_HEADER void ExportNgOCCShapes(py::module &m); + + DLL_HEADER void ExportNgOCC(py::module &m) { + m.attr("occ_version") = OCC_VERSION_COMPLETE; + + // suppress info messages from occ (like statistics on Transfer) + Message_Gravity aGravity = Message_Alarm; + for (Message_SequenceOfPrinters::Iterator aPrinterIter (Message::DefaultMessenger()->Printers()); + aPrinterIter.More(); aPrinterIter.Next()) + { + aPrinterIter.Value()->SetTraceLevel (aGravity); + } + + ExportNgOCCBasic(m); + ExportNgOCCShapes(m); + + static py::exception exc(m, "OCCException"); + py::register_exception_translator([](std::exception_ptr p) + { + try { + if(p) std::rethrow_exception(p); + } catch (const Standard_Failure& e) { + exc((string(e.DynamicType()->Name()) + ": " + e.GetMessageString()).c_str()); + } + }); + 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 TopoDS_Shape& shape, int occdim, bool copy) + { + auto geo = make_shared (shape, occdim); + // ng_geometry = geo; + + // geo->BuildFMap(); + // geo->CalcBoundingBox(); + return geo; + }), py::arg("shape"), py::arg("dim")=3, py::arg("copy")=false, + "Create Netgen OCCGeometry from existing TopoDS_Shape") + + .def(py::init([] (const std::vector shapes) + { + BOPAlgo_Builder builder; + for (auto & s : shapes) + builder.AddArgument(s); + builder.Perform(); + for(auto& s : shapes) + PropagateProperties(builder, s); + auto geo = make_shared (builder.Shape()); + ng_geometry = geo; + // geo->BuildFMap(); + // geo->CalcBoundingBox(); + return geo; + }), py::arg("shape"), + "Create Netgen OCCGeometry from existing TopoDS_Shape") + + .def(py::init([] (const string& filename, int dim) + { + shared_ptr geo; + if(EndsWith(filename, ".step") || EndsWith(filename, ".stp")) + geo.reset(LoadOCC_STEP(filename)); + else if(EndsWith(filename, ".brep")) + geo.reset(LoadOCC_BREP(filename)); + else if(EndsWith(filename, ".iges")) + geo.reset(LoadOCC_IGES(filename)); + else + throw Exception("Cannot load file " + filename + "\nValid formats are: step, stp, brep, iges"); + if(dim<3) + geo->SetDimension(dim); + ng_geometry = geo; + return geo; + }), py::arg("filename"), py::arg("dim")=3, + "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; @@ -31,6 +152,14 @@ DLL_HEADER void ExportNgOCC(py::module &m) self.HealGeometry(); self.BuildFMap(); },py::arg("tolerance")=1e-3, py::arg("fixsmalledges")=true, py::arg("fixspotstripfaces")=true, py::arg("sewfaces")=true, py::arg("makesolids")=true, py::arg("splitpartitions")=false,R"raw_string(Heal the OCCGeometry.)raw_string",py::call_guard()) + .def("SetFaceMeshsize", [](OCCGeometry& self, size_t fnr, double meshsize) + { + self.SetFaceMaxH(fnr, meshsize); + }, "Set maximum meshsize for face fnr. Face numbers are 0 based.") + .def("Draw", [](shared_ptr geo) + { + ng_geometry = geo; + }) .def("_visualizationData", [] (shared_ptr occ_geo) { std::vector vertices; @@ -71,15 +200,15 @@ DLL_HEADER void ExportNgOCC(py::module &m) normals.reserve(normals.size() + triangulation->NbTriangles()*3*3); for (int j = 1; j < triangulation->NbTriangles()+1; j++) { - auto triangle = (triangulation->Triangles())(j); + auto triangle = triangulation->Triangle(j); for (int k = 1; k < 4; k++) - p[k-1] = (triangulation->Nodes())(triangle(k)).Transformed(loc); + p[k-1] = triangulation->Node(triangle(k)).Transformed(loc); for (int k = 1; k < 4; k++) { vertices.insert(vertices.end(),{float(p[k-1].X()), float(p[k-1].Y()), float(p[k-1].Z())}); trigs.insert(trigs.end(),{count, count+1, count+2,i}); count += 3; - uv = (triangulation->UVNodes())(triangle(k)); + uv = triangulation->UVNode(triangle(k)); prop.SetParameters(uv.X(), uv.Y()); if (prop.IsNormalDefined()) n = prop.Normal(); @@ -107,34 +236,121 @@ DLL_HEADER void ExportNgOCC(py::module &m) res["max"] = MoveToNumpy(max); return res; }, py::call_guard()) - ; - m.def("LoadOCCGeometry",FunctionPointer([] (const string & filename) - { - cout << "load OCC geometry"; - ifstream ist(filename); - OCCGeometry * instance = new OCCGeometry(); - instance = LoadOCC_STEP(filename.c_str()); - ng_geometry = shared_ptr(instance, NOOP_Deleter); - return ng_geometry; - }),py::call_guard()); - m.def("GenerateMesh", FunctionPointer([] (shared_ptr geo, MeshingParameters ¶m) - { - auto mesh = make_shared(); - SetGlobalMesh(mesh); - mesh->SetGeometry(geo); - ng_geometry = geo; + .def("GenerateMesh", [](shared_ptr geo, + MeshingParameters* pars, NgMPI_Comm comm, + shared_ptr mesh, py::kwargs kwargs) + { + MeshingParameters mp; + OCCParameters occparam; + { + py::gil_scoped_acquire aq; + if(pars) + { + auto mp_kwargs = CreateDictFromFlags(pars->geometrySpecificParameters); + CreateOCCParametersFromKwargs(occparam, mp_kwargs); + mp = *pars; + } + CreateOCCParametersFromKwargs(occparam, kwargs); + CreateMPfromKwargs(mp, kwargs); + } + geo->SetOCCParameters(occparam); + if(!mesh) + mesh = make_shared(); + mesh->SetCommunicator(comm); + mesh->SetGeometry(geo); - try - { - geo->GenerateMesh(mesh,param); - } - catch (NgException ex) - { - cout << "Caught NgException: " << ex.What() << endl; - } - return mesh; - }),py::call_guard()) + if (comm.Rank()==0) + { + SetGlobalMesh(mesh); + auto result = geo->GenerateMesh(mesh, mp); + if(result != 0) + { + netgen::mesh = mesh; // keep mesh for debugging + throw Exception("Meshing failed!"); + } + ng_geometry = geo; + if (comm.Size() > 1) + mesh->Distribute(); + } + else + { + mesh->SendRecvMesh(); + } + return mesh; + }, py::arg("mp") = nullptr, py::arg("comm")=NgMPI_Comm{}, + py::arg("mesh")=nullptr, py::call_guard(), + (meshingparameter_description + occparameter_description).c_str()) + .def_property_readonly("shape", [](const OCCGeometry & self) { return self.GetShape(); }) ; + + + + m.def("LoadOCCGeometry",[] (filesystem::path filename) + { + cout << "WARNING: LoadOCCGeometry is deprecated! Just use the OCCGeometry(filename) constructor. It is able to read brep and iges files as well!" << endl; + ifstream ist(filename); + OCCGeometry * instance = new OCCGeometry(); + instance = LoadOCC_STEP(filename.c_str()); + ng_geometry = shared_ptr(instance, NOOP_Deleter); + return ng_geometry; + },py::call_guard()); + + + m.def("TestXCAF", [] (TopoDS_Shape shape) { + + /*static*/ Handle(XCAFApp_Application) app = XCAFApp_Application::GetApplication(); + cout << endl << endl << endl; + cout << "app = " << *reinterpret_cast(&app) << endl; + Handle(TDocStd_Document) doc; + cout << "nbdocs = " << app->NbDocuments() << endl; + if(app->NbDocuments() > 0) + { + app->GetDocument(1,doc); + // app->Close(doc); + } + else + app->NewDocument ("STEP-XCAF",doc); + Handle(XCAFDoc_ShapeTool) shape_tool = XCAFDoc_DocumentTool::ShapeTool(doc->Main()); + Handle(XCAFDoc_MaterialTool) material_tool = XCAFDoc_DocumentTool::MaterialTool(doc->Main()); + // Handle(XCAFDoc_VisMaterialTool) vismaterial_tool = XCAFDoc_DocumentTool::VisMaterialTool(doc->Main()); + + // TDF_LabelSequence doc_shapes; + // shape_tool->GetShapes(doc_shapes); + // cout << "shape tool nbentities: " << doc_shapes.Size() << endl; + TDF_Label label = shape_tool -> FindShape(shape); + cout << "shape label = " << endl << label << endl; + if (label.IsNull()) return; + cout << "nbattr = " << label.NbAttributes() << endl; + + + if (!label.IsNull()) + { + Handle(TDF_Attribute) attribute; + cout << "create guid" << endl; + // Standard_GUID guid("c4ef4200-568f-11d1-8940-080009dc3333"); + Standard_GUID guid("2a96b608-ec8b-11d0-bee7-080009dc3333"); + cout << "have guid" << endl; + cout << "find attrib " << label.FindAttribute(guid, attribute) << endl; + cout << "attrib = " << attribute << endl; + cout << "tag = " << label.Tag() << endl; + cout << "father.tag = " << label.Father().Tag() << endl; + cout << "Data = " << label.Data() << endl; + + cout << "nbchild = " << label.NbChildren() << endl; + for (auto i : Range(label.NbChildren())) + { + TDF_Label child = label.FindChild(i+1); + cout << "child[" << i << "] = " << child << endl; + cout << "find attrib " << child.FindAttribute(guid, attribute) << endl; + cout << "attrib = " << attribute << endl; + } + + // cout << "findshape = " << shape_tool -> FindShape(shape) << endl; + cout << "IsMaterial = " << material_tool->IsMaterial(label) << endl; + // cout << "IsVisMaterial = " << vismaterial_tool->IsMaterial(label) << endl; + } + }, py::arg("shape")=TopoDS_Shape()); + } PYBIND11_MODULE(libNgOCC, m) { diff --git a/libsrc/occ/python_occ_basic.cpp b/libsrc/occ/python_occ_basic.cpp new file mode 100644 index 00000000..9768a22d --- /dev/null +++ b/libsrc/occ/python_occ_basic.cpp @@ -0,0 +1,378 @@ +#ifdef NG_PYTHON +#ifdef OCCGEOMETRY + +#include +#include +#include +#include + +#include "occgeom.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace netgen; + +DLL_HEADER void ExportNgOCCBasic(py::module &m) +{ + py::class_(m, "gp_Pnt", "3d OCC point") + .def(py::init([] (py::tuple pnt) + { + if (py::len(pnt) != 3) + throw Exception("need 3-tuple to create gp_Pnt"); + + return gp_Pnt(py::cast(pnt[0]), + py::cast(pnt[1]), + py::cast(pnt[2])); + })) + .def(py::init([] (double x, double y, double z) { + return gp_Pnt(x, y, z); + }), py::arg("x"), py::arg("y"), py::arg("z")) + .def_property("x", [](gp_Pnt&p) { return p.X(); }, [](gp_Pnt&p,double x) { p.SetX(x); }) + .def_property("y", [](gp_Pnt&p) { return p.Y(); }, [](gp_Pnt&p,double y) { p.SetY(y); }) + .def_property("z", [](gp_Pnt&p) { return p.Z(); }, [](gp_Pnt&p,double z) { p.SetZ(z); }) + .def("__str__", [] (const gp_Pnt & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ", " << p.Z() << ")"; + return str.str(); + }) + .def("__repr__", [] (const gp_Pnt & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ", " << p.Z() << ")"; + return str.str(); + }) + + .def("__sub__", [](gp_Pnt p1, gp_Pnt p2) { return gp_Vec(p2, p1); }) + .def("__add__", [](gp_Pnt p, gp_Vec v) { return p.Translated(v); }) // gp_Pnt(p.X()+v.X(), p.Y()+v.Y(), p.Z()+v.Z()); }) + .def("__sub__", [](gp_Pnt p, gp_Vec v) { return p.Translated(-v); }) // gp_Pnt(p.X()-v.X(), p.Y()-v.Y(), p.Z()-v.Z()); }) + .def("__getitem__", [](const gp_Pnt& p, int index) + { + if(index == 0) + return p.X(); + if(index == 1) + return p.Y(); + if(index == 2) + return p.Z(); + throw std::out_of_range("Point index must be in range [0,3)!"); + }) + ; + + py::class_(m, "gp_Vec", "3d OCC vector") + .def(py::init([] (py::tuple vec) + { + return gp_Vec(py::cast(vec[0]), + py::cast(vec[1]), + py::cast(vec[2])); + })) + .def(py::init([] (double x, double y, double z) { + return gp_Vec(x, y, z); + }), py::arg("x"), py::arg("y"), py::arg("z")) + .def_property("x", [](gp_Vec&p) { return p.X(); }, [](gp_Vec&p,double x) { p.SetX(x); }) + .def_property("y", [](gp_Vec&p) { return p.Y(); }, [](gp_Vec&p,double y) { p.SetY(y); }) + .def_property("z", [](gp_Vec&p) { return p.Z(); }, [](gp_Vec&p,double z) { p.SetZ(z); }) + .def("Norm", [](const gp_Vec& v) + { return v.Magnitude(); }) + .def("__str__", [] (const gp_Vec & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ", " << p.Z() << ")"; + return str.str(); + }) + .def("__repr__", [] (const gp_Vec & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ", " << p.Z() << ")"; + return str.str(); + }) + .def("__add__", [](gp_Vec v1, gp_Vec v2) { return v1+v2; }) + .def("__sub__", [](gp_Vec v1, gp_Vec v2) { return v1-v2; }) + .def("__rmul__", [](gp_Vec v, double s) { return s*v; }) + .def("__mul__", [](gp_Vec v1, gp_Vec v2) { return v1*v2; }) + .def("__neg__", [](gp_Vec v) { return -v; }) + .def("__xor__", [](gp_Vec v1, gp_Vec v2) { return v1^v2; }) + + .def("__lt__", [](gp_Vec v, double val) + { + cout << IM(6) << "vec, lt v - " << netgen::occ2ng(v) << ", val = " << val << endl; + return DirectionalInterval(v) < val; + }) + .def("__gt__", [](gp_Vec v, double val) + { + cout << IM(6) << "vec, gt v - " << netgen::occ2ng(v) << ", val = " << val << endl; + return DirectionalInterval(v) > val; + }) + .def("__le__", [](gp_Vec v, double val) + { + return DirectionalInterval(v) <= val; + }) + .def("__ge__", [](gp_Vec v, double val) + { + return DirectionalInterval(v) >= val; + }) + ; + + py::class_(m, "gp_Dir", "3d OCC direction") + .def(py::init([] (py::tuple dir) + { + return gp_Dir(py::cast(dir[0]), + py::cast(dir[1]), + py::cast(dir[2])); + })) + .def(py::init([] (double x, double y, double z) { + return gp_Dir(x, y, z); + }), py::arg("x"), py::arg("y"), py::arg("z")) + .def(py::init()) + .def("__str__", [] (const gp_Dir & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ", " << p.Z() << ")"; + return str.str(); + }) + ; + + py::class_(m, "Axis", "an OCC axis in 3d") + .def(py::init([](gp_Pnt p, gp_Dir d) { + return gp_Ax1(p,d); + }), py::arg("p"), py::arg("d")) + ; + py::class_(m, "gp_Ax2") + .def(py::init([](gp_Pnt p, gp_Dir d) { + return gp_Ax2(p,d); + })) + .def(py::init([](const gp_Ax3 & ax3) { + return gp_Ax2(ax3.Ax2()); + })) + ; + + py::class_(m, "Axes", "an OCC coordinate system in 3d") + .def(py::init([](gp_Pnt p, gp_Dir N, gp_Dir Vx) { + return gp_Ax3(p,N, Vx); + }), py::arg("p")=gp_Pnt(0,0,0), py::arg("n")=gp_Vec(0,0,1), py::arg("h")=gp_Vec(1,0,0)) + .def(py::init()) + .def_property("p", [](gp_Ax3 & ax) { return ax.Location(); }, [](gp_Ax3&ax, gp_Pnt p) { ax.SetLocation(p); }) + ; + + + py::class_(m, "gp_Pnt2d", "2d OCC point") + .def(py::init([] (py::tuple pnt) + { + if (py::len(pnt) != 2) + throw Exception("need 2-tuple to create gp_Pnt2d"); + return gp_Pnt2d(py::cast(pnt[0]), + py::cast(pnt[1])); + })) + .def(py::init([] (double x, double y) { + return gp_Pnt2d(x, y); + }), py::arg("x"), py::arg("y")) + .def_property("x", [](gp_Pnt2d&p) { return p.X(); }, [](gp_Pnt2d&p,double x) { p.SetX(x); }) + .def_property("y", [](gp_Pnt2d&p) { return p.Y(); }, [](gp_Pnt2d&p,double y) { p.SetY(y); }) + .def("__str__", [] (const gp_Pnt2d & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ")"; + return str.str(); + }) + .def("__repr__", [] (const gp_Pnt2d & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ")"; + return str.str(); + }) + + .def("__sub__", [](gp_Pnt2d p1, gp_Pnt2d p2) { return gp_Vec2d(p1.X()-p2.X(), p1.Y()-p2.Y()); }) + .def("__add__", [](gp_Pnt2d p, gp_Vec2d v) { return p.Translated(v); }) + .def("__sub__", [](gp_Pnt2d p, gp_Vec2d v) { return p.Translated(-v); }) + ; + + py::class_(m, "gp_Vec2d", "2d OCC vector") + .def(py::init([] (py::tuple vec) + { + if (py::len(vec) != 2) + throw Exception("need 2-tuple to create gp_Vec2d"); + return gp_Vec2d(py::cast(vec[0]), + py::cast(vec[1])); + })) + .def(py::init([] (double x, double y) { + return gp_Vec2d(x, y); + }), py::arg("x"), py::arg("y")) + .def_property("x", [](gp_Vec2d&p) { return p.X(); }, [](gp_Vec2d&p,double x) { p.SetX(x); }) + .def_property("y", [](gp_Vec2d&p) { return p.Y(); }, [](gp_Vec2d&p,double y) { p.SetY(y); }) + .def("__str__", [] (const gp_Vec & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ")"; + return str.str(); + }) + .def("__repr__", [] (const gp_Vec & p) { + stringstream str; + str << "(" << p.X() << ", " << p.Y() << ")"; + return str.str(); + }) + .def("__add__", [](gp_Vec2d v1, gp_Vec2d v2) { return v1+v2; }) + .def("__sub__", [](gp_Vec2d v1, gp_Vec2d v2) { return v1-v2; }) + .def("__rmul__", [](gp_Vec2d v, double s) { return s*v; }) + .def("__neg__", [](gp_Vec2d v) { return -v; }) + .def("__xor__", [](gp_Vec2d v1, gp_Vec2d v2) { return v1^v2; }) + ; + + py::class_(m, "gp_Dir2d", "2d OCC direction") + .def(py::init([] (py::tuple dir) + { + if (py::len(dir) != 2) + throw Exception("need 2-tuple to create gp_Dir2d"); + return gp_Dir2d(py::cast(dir[0]), + py::cast(dir[1])); + })) + .def(py::init([] (double x, double y) { + return gp_Dir2d(x, y); + }), py::arg("x"), py::arg("y")) + ; + + m.def("Pnt", [](double x, double y) { return gp_Pnt2d(x,y); }, + py::arg("x"), py::arg("y"), "create 2d OCC point"); + m.def("Pnt", [](double x, double y, double z) { return gp_Pnt(x,y,z); }, + py::arg("x"), py::arg("y"), py::arg("z"), "create 3d OCC point"); + m.def("Pnt", [](std::vector p) + { + if (p.size() == 2) + return py::cast(gp_Pnt2d(p[0], p[1])); + if (p.size() == 3) + return py::cast(gp_Pnt(p[0], p[1], p[2])); + throw Exception("OCC-Points only in 2D or 3D"); + }, py::arg("p"), "create 2d or 3d OCC point"); + + m.def("Vec", [](double x, double y) { return gp_Vec2d(x,y); }, + py::arg("x"), py::arg("y"), "create 2d OCC point"); + m.def("Vec", [](double x, double y, double z) { return gp_Vec(x,y,z); }, + py::arg("x"), py::arg("y"), py::arg("z"), "create 3d OCC point"); + m.def("Vec", [](std::vector p) + { + if (p.size() == 2) + return py::cast(gp_Vec2d(p[0], p[1])); + if (p.size() == 3) + return py::cast(gp_Vec(p[0], p[1], p[2])); + throw Exception("OCC-Vecs only in 2D or 3D"); + }, py::arg("v"), "create 2d or 3d OCC vector"); + + m.def("Dir", [](double x, double y) { return gp_Dir2d(x,y); }, + py::arg("x"), py::arg("y"), "create 2d OCC direction"); + m.def("Dir", [](double x, double y, double z) { return gp_Dir(x,y,z); }, + py::arg("x"), py::arg("y"), py::arg("z"), "create 3d OCC direction"); + m.def("Dir", [](std::vector p) + { + if (p.size() == 2) + return py::cast(gp_Dir2d(p[0], p[1])); + if (p.size() == 3) + return py::cast(gp_Dir(p[0], p[1], p[2])); + throw Exception("OCC-Dirs only in 2D or 3D"); + }, py::arg("d"), "create 2d or 3d OCC direction"); + + + + + py::class_(m, "gp_Ax2d", "2d OCC coordinate system") + .def(py::init([](gp_Pnt2d p, gp_Dir2d d) { + return gp_Ax2d(p,d); + }), py::arg("p")=gp_Pnt2d(0,0), py::arg("d")=gp_Dir2d(1,0)) + ; + + py::class_(m, "gp_GTrsf") + .def(py::init([](const std::vector& mat, + const std::vector& vec) + { + if(mat.size() != 9) + throw Exception("Need 9 matrix values for construction of gp_GTrsf"); + if(vec.size() != 3) + throw Exception("Need 3 vector values for construction of gp_GTrsf"); + gp_GTrsf trafo; + trafo.SetVectorialPart({ mat[0], mat[1], mat[2], + mat[3], mat[4], mat[5], + mat[6], mat[7], mat[8] }); + trafo.SetTranslationPart( { vec[0], vec[1], vec[2] }); + return trafo; + }), py::arg("mat"), py::arg("vec") = std::vector{ 0., 0., 0. }) + .def("__call__", [] (gp_GTrsf & trafo, const TopoDS_Shape & shape) { + BRepBuilderAPI_GTransform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }) + ; + + py::class_(m, "gp_Trsf") + .def(py::init<>()) + .def("SetMirror", [] (gp_Trsf & trafo, const gp_Ax1 & ax) { trafo.SetMirror(ax); return trafo; }) + .def("Inverted", &gp_Trsf::Inverted) + .def_static("Translation", [] (const gp_Vec & v) { gp_Trsf trafo; trafo.SetTranslation(v); return trafo; }) + .def_static("Scale", [] (const gp_Pnt & p, double s) { gp_Trsf trafo; trafo.SetScale(p,s); return trafo; }) + .def_static("Mirror", [] (const gp_Ax1 & ax) { gp_Trsf trafo; trafo.SetMirror(ax); return trafo; }) + .def_static("Rotation", [] (const gp_Ax1 & ax, double ang) { gp_Trsf trafo; trafo.SetRotation(ax, ang*M_PI/180); return trafo; }) + .def_static("Rotation", [] (const gp_Pnt & p, const gp_Dir & d, double ang) + { gp_Trsf trafo; trafo.SetRotation(gp_Ax1(p,d), ang*M_PI/180); return trafo; }) + .def_static("Transformation", [] (const gp_Ax3 & ax) { gp_Trsf trafo; trafo.SetTransformation(ax); return trafo; }) + .def_static("Transformation", [] (const gp_Ax3 & from, const gp_Ax3 to) + { gp_Trsf trafo; trafo.SetTransformation(from, to); return trafo; }) + .def(py::self * py::self) + .def("__call__", [] (gp_Trsf & trafo, const TopoDS_Shape & shape) { + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }) + .def("__str__", [](gp_Trsf & trafo) + { + stringstream str; + gp_XYZ xyz = trafo.TranslationPart(); + str << xyz.X() << ", " << xyz.Y() << ", " << xyz.Z(); + return str.str(); + }) + ; + + py::class_(m, "TopLoc_Location") + .def(py::init()) + .def("Transformation", [](const TopLoc_Location & loc) { return loc.Transformation(); }) + ; + + + py::class_ (m, "DirectionalInterval") + .def("__str__", [](DirectionalInterval self) + { + stringstream str; + str << "(" << self.minval << ", " << self.maxval << ")"; + return str.str(); + }) + + .def("__lt__", [](DirectionalInterval i, double val) + { + cout << "directionalinterval, lt, imin/max = " << i.minval << " / " << i.maxval << endl; + return i < val; + }) + .def("__gt__", [](DirectionalInterval i, double val) + { + cout << "directionalinterval, gt, imin/max = " << i.minval << " / " << i.maxval << endl; + return i > val; + }) + .def("__and__", [](DirectionalInterval self, DirectionalInterval other) { + cout << "and of intervals" << endl; + return self.Intersect(other); + }) + ; + + py::implicitly_convertible(); + py::implicitly_convertible(); + py::implicitly_convertible(); + py::implicitly_convertible(); + py::implicitly_convertible(); + py::implicitly_convertible(); + py::implicitly_convertible(); + + + py::implicitly_convertible(); + + m.attr("X") = py::cast(gp_Vec(1,0,0)); + m.attr("Y") = py::cast(gp_Vec(0,1,0)); + m.attr("Z") = py::cast(gp_Vec(0,0,1)); +} + + +#endif // OCCGEOMETRY +#endif // NG_PYTHON + diff --git a/libsrc/occ/python_occ_shapes.cpp b/libsrc/occ/python_occ_shapes.cpp new file mode 100644 index 00000000..29c3320c --- /dev/null +++ b/libsrc/occ/python_occ_shapes.cpp @@ -0,0 +1,2498 @@ +#ifdef NG_PYTHON +#ifdef OCCGEOMETRY + +#include + +#include +#include +#include +#include + +#include "occgeom.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace netgen; + +void ExtractEdgeData( const TopoDS_Edge & edge, int index, std::vector * p, Box<3> & box ) +{ + if (BRep_Tool::Degenerated(edge)) return; + + Handle(Poly_PolygonOnTriangulation) poly; + Handle(Poly_Triangulation) T; + TopLoc_Location loc; + BRep_Tool::PolygonOnTriangulation(edge, poly, T, loc); + + if (poly.IsNull()) + { + cout << IM(2) << "no edge mesh, do my own sampling" << endl; + + double s0, s1; + Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); + + constexpr int num = 100; + for (int i = 0; i < num; i++) + { + auto p0 = occ2ng(c->Value (s0 + i*(s1-s0)/num)); + auto p1 = occ2ng(c->Value (s0 + (i+1)*(s1-s0)/num)); + for(auto k : Range(3)) + { + p[0].push_back(p0[k]); + p[1].push_back(p1[k]); + } + p[0].push_back(index); + p[1].push_back(index); + box.Add(p0); + box.Add(p1); + } + return; + } + + int nbnodes = poly -> NbNodes(); + for (int j = 1; j < nbnodes; j++) + { + auto p0 = occ2ng((T -> Node(poly->Nodes()(j))).Transformed(loc)); + auto p1 = occ2ng((T -> Node(poly->Nodes()(j+1))).Transformed(loc)); + for(auto k : Range(3)) + { + p[0].push_back(p0[k]); + p[1].push_back(p1[k]); + } + p[0].push_back(index); + p[1].push_back(index); + box.Add(p0); + box.Add(p1); + } +} + +void ExtractFaceData( const TopoDS_Face & face, int index, std::vector * p, std::vector * n, Box<3> & box ) +{ + TopLoc_Location loc; + Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc); + Handle(Geom_Surface) surf = BRep_Tool::Surface (face); + BRepAdaptor_Surface sf(face, Standard_False); + BRepLProp_SLProps prop(sf, 1, 1e-5); + + bool flip = TopAbs_REVERSED == face.Orientation(); + + if (triangulation.IsNull()) + { + cout << "pls build face triangulation before" << endl; + return; + } + + int ntriangles = triangulation -> NbTriangles(); + for (int j = 1; j <= ntriangles; j++) + { + Poly_Triangle triangle = triangulation -> Triangle(j); + std::array,3> pts; + std::array,3> normals; + for (int k = 0; k < 3; k++) + pts[k] = occ2ng( (triangulation -> Node(triangle(k+1))).Transformed(loc) ); + + for (int k = 0; k < 3; k++) + { + auto uv = triangulation -> UVNode(triangle(k+1)); + prop.SetParameters (uv.X(), uv.Y()); + if (prop.IsNormalDefined()) + normals[k] = occ2ng (prop.Normal()); + else + normals[k] = Cross(pts[1]-pts[0], pts[2]-pts[0]); + } + + if(flip) + { + Swap(pts[1], pts[2]); + Swap(normals[1], normals[2]); + for (int k = 0; k < 3; k++) + normals[k] = -normals[k]; + } + + for (int k = 0; k < 3; k++) + { + box.Add(pts[k]); + for (int d = 0; d < 3; d++) + { + p[k].push_back( pts[k][d] ); + n[k].push_back( normals[k][d] ); + } + p[k].push_back( index ); + } + } +} + +py::object CastShape(const TopoDS_Shape & s) +{ + switch (s.ShapeType()) + { + case TopAbs_VERTEX: + return py::cast(TopoDS::Vertex(s)); + case TopAbs_FACE: + return py::cast(TopoDS::Face(s)); + case TopAbs_EDGE: + return py::cast(TopoDS::Edge(s)); + case TopAbs_WIRE: + return py::cast(TopoDS::Wire(s)); + + case TopAbs_COMPOUND: + case TopAbs_COMPSOLID: + case TopAbs_SOLID: + case TopAbs_SHELL: + case TopAbs_SHAPE: + return py::cast(s); + } + throw Exception("Invalid Shape type"); +}; + + +class WorkPlane : public enable_shared_from_this +{ + gp_Ax3 axes; + gp_Ax2d localpos; + gp_Pnt2d startpnt; + TopoDS_Vertex lastvertex, startvertex; + Handle(Geom_Surface) surf; + // Geom_Plane surf; + + BRepBuilderAPI_MakeWire wire_builder; + std::vector wires; + +public: + + WorkPlane (const gp_Ax3 & _axes, const gp_Ax2d _localpos = gp_Ax2d()) + : axes(_axes), localpos(_localpos) // , surf(_axis) + { + // surf = GC_MakePlane (gp_Ax1(axis.Location(), axis.Direction())); + surf = new Geom_Plane(axes); + } + + + auto Finish() + { + if (!startvertex.IsNull()) + { + wires.push_back (wire_builder.Wire()); + wire_builder = BRepBuilderAPI_MakeWire(); + startvertex.Nullify(); + } + return shared_from_this(); + } + + auto StartPnt() const { + return startpnt; + } + + auto CurrentLocation() const + { + return localpos.Location(); + } + + auto CurrentDirection() const + { + return gp_Vec2d(localpos.Direction()); + } + + auto MoveTo (double h, double v) + { + startpnt = gp_Pnt2d(h,v); + localpos.SetLocation(startpnt); + startvertex.Nullify(); + return shared_from_this(); + } + + auto Move(double len) + { + gp_Dir2d dir = localpos.Direction(); + gp_Pnt2d oldp = localpos.Location(); + auto newp = oldp.Translated(len*dir); + return MoveTo(newp.X(), newp.Y()); + } + + auto Direction (double h, double v) + { + localpos.SetDirection(gp_Dir2d(h,v)); + return shared_from_this(); + } + + auto LineTo (double h, double v, optional name = nullopt) + { + gp_Pnt2d old2d = localpos.Location(); + gp_Pnt oldp = axes.Location() . Translated(old2d.X() * axes.XDirection() + old2d.Y() * axes.YDirection()); + + // localpos.Translate (gp_Vec2d(h,v)); + localpos.SetLocation (gp_Pnt2d(h,v)); + gp_Pnt2d new2d = localpos.Location(); + gp_Pnt newp = axes.Location() . Translated(new2d.X() * axes.XDirection() + new2d.Y() * axes.YDirection()); + + if (new2d.Distance(old2d) < 1e-10) return shared_from_this(); + bool closing = new2d.Distance(startpnt) < 1e-10; + + + cout << IM(6) << "lineto, oldp = " << occ2ng(oldp) << endl; + cout << IM(6) << "lineto, newp = " << occ2ng(newp) << endl; + gp_Pnt pfromsurf = surf->Value(new2d.X(), new2d.Y()); + cout << IM(6) << "p from plane = " << occ2ng(pfromsurf) << endl; + + Handle(Geom_TrimmedCurve) curve = GC_MakeSegment(oldp, newp); + + if (startvertex.IsNull()) + startvertex = lastvertex = BRepBuilderAPI_MakeVertex(oldp); + auto endv = closing ? startvertex : BRepBuilderAPI_MakeVertex(newp); + // liefert noch Fehler bei close + auto edge = BRepBuilderAPI_MakeEdge(curve, lastvertex, endv).Edge(); + lastvertex = endv; + + // auto edge = BRepBuilderAPI_MakeEdge(curve).Edge(); + if (name) + OCCGeometry::GetProperties(edge).name = name; + wire_builder.Add(edge); + + if (closing) Finish(); + return shared_from_this(); + } + + auto Line(double h, double v, optional name = nullopt) + { + gp_Pnt2d oldp = localpos.Location(); + oldp.Translate(gp_Vec2d(h,v)); + return LineTo (oldp.X(), oldp.Y(), name); + } + + auto Line(double len, optional name = nullopt) + { + gp_Dir2d dir = localpos.Direction(); + cout << IM(6) << "dir = " << dir.X() << ", " << dir.Y() << endl; + gp_Pnt2d oldp = localpos.Location(); + oldp.Translate(len*dir); + return LineTo (oldp.X(), oldp.Y(), name); + } + + auto Rotate (double angle) + { + localpos.Rotate(localpos.Location(), angle*M_PI/180); + return shared_from_this(); + } + + auto Spline(const std::vector &points, bool periodic, double tol, const std::map &tangents, + bool start_from_localpos) + { + gp_Pnt2d P1 = start_from_localpos ? localpos.Location() : points.front(); + gp_Pnt P13d = surf->Value(P1.X(), P1.Y()); + + gp_Pnt2d PLast = points.back(); + gp_Pnt PLast3d = surf->Value(PLast.X(), PLast.Y()); + + Handle(TColgp_HArray1OfPnt2d) allpoints; + if (start_from_localpos) + { + if (points.front().Distance(P1) <= tol) + throw Exception("First item of given list of points is too close to current position (distance <= tol)."); + allpoints = new TColgp_HArray1OfPnt2d(1, points.size() + 1); + allpoints->SetValue(1, P1); + for (int i = 0; i < points.size(); i++) + allpoints->SetValue(i + 2, points[i]); + } + else + { + allpoints = new TColgp_HArray1OfPnt2d(1, points.size()); + for (int i = 0; i < points.size(); i++) + allpoints->SetValue(i + 1, points[i]); + } + + Geom2dAPI_Interpolate builder(allpoints, periodic, tol); + + if (tangents.size() > 0) + { + const gp_Vec2d dummy_vec = tangents.begin()->second; + TColgp_Array1OfVec2d tangent_vecs(1, allpoints->Length()); + Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, allpoints->Length()); + for (int i : Range(allpoints->Length())) + { + if (tangents.count(i) > 0) + { + tangent_vecs.SetValue(i+1, tangents.at(i)); + tangent_flags->SetValue(i+1, true); + } + else + { + tangent_vecs.SetValue(i+1, dummy_vec); + tangent_flags->SetValue(i+1, false); + } + } + builder.Load(tangent_vecs, tangent_flags); + } + + + builder.Perform(); + auto curve2d = builder.Curve(); + + const bool closing = periodic || PLast.Distance(startpnt) < 1e-10; + if (startvertex.IsNull()) + startvertex = lastvertex = BRepBuilderAPI_MakeVertex(P13d).Vertex(); + auto endv = closing ? startvertex : BRepBuilderAPI_MakeVertex(PLast3d).Vertex(); + + //create 3d edge from 2d curve using surf + auto edge = BRepBuilderAPI_MakeEdge(curve2d, surf, lastvertex, endv).Edge(); + lastvertex = endv; + BRepLib::BuildCurves3d(edge); + wire_builder.Add(edge); + + // update localpos + localpos.SetLocation(PLast); + + //compute angle of rotation + //compute tangent t2 in PLast + const auto dir = localpos.Direction(); + gp_Vec2d t = gp_Vec2d(dir.X(), dir.Y()); + gp_Vec2d t2 = curve2d->DN(curve2d->LastParameter(), 1); + + double angle = t.Angle(t2); //angle \in [-pi,pi] + + //update localpos.Direction() + Rotate(angle*180/M_PI); + + if (closing) + Finish(); + + return shared_from_this(); + } + + auto ArcTo (double h, double v, const gp_Vec2d t) + { + gp_Pnt2d P1 = localpos.Location(); + + //check input + if(P1.X() == h && P1.Y() == v) + throw Exception("points P1 and P2 must not be congruent"); + + localpos.SetLocation (gp_Pnt2d(h,v)); + gp_Pnt2d P2 = localpos.Location(); + + cout << IM(6) << "ArcTo:" << endl; + cout << IM(6) << "P1 = (" << P1.X() <<", " << P1.Y() << ")"< -M_PI/2 && angletp12n < M_PI/2) + P3 = gp_Pnt2d(M.X() + r * p12n.X() , M.Y() + r * p12n.Y()); + else + P3 = gp_Pnt2d(M.X() - r * p12n.X() , M.Y() - r * p12n.Y()); + + cout << IM(6) << "r = " << r <=0) + dirn = gp_Dir2d(-dir.Y(),dir.X()); + else + dirn = gp_Dir2d(dir.Y(),-dir.X()); + + gp_Pnt2d oldp = localpos.Location(); + + oldp.Translate(radius*dirn); + + cout << IM(6) << "M = (" << oldp.X() << ", " << oldp.Y() << ")" << endl; + + dirn.Rotate(newAngle-M_PI); + oldp.Translate(radius*dirn); + + //compute tangent vector in P1 + gp_Vec2d t = gp_Vec2d(dir.X(),dir.Y()); + + cout << IM(6) << "t = (" << t.X() << ", " << t.Y() << ")" << endl; + + //add arc + return ArcTo (oldp.X(), oldp.Y(), t); + } + + auto Rectangle (double l, double w) + { + Line (l); + Rotate (90); + Line(w); + Rotate (90); + Line (l); + Rotate (90); + Line(w); + Rotate (90); + return shared_from_this(); + } + + auto RectangleCentered (double l, double w) + { + Move(-l/2); + Rotate(-90); + Move(w/2); + Rotate(90); + Rectangle(l,w); + Rotate(-90); + Move(-w/2); + Rotate(90); + Move(l/2); + return shared_from_this(); + } + + + auto Circle(double x, double y, double r) + { + /* + MoveTo(x+r, y); + Direction (0, 1); + Arc(r, 180); + Arc(r, 180); + // wires.push_back (wire_builder.Wire()); + // wire_builder = BRepBuilderAPI_MakeWire(); + return shared_from_this(); + */ + + gp_Pnt2d p(x,y); + Handle(Geom2d_Circle) circ_curve = GCE2d_MakeCircle(p, r).Value(); + + auto edge = BRepBuilderAPI_MakeEdge(circ_curve, surf).Edge(); + BRepLib::BuildCurves3d(edge); + + wire_builder.Add(edge); + wires.push_back (wire_builder.Wire()); + wire_builder = BRepBuilderAPI_MakeWire(); + return shared_from_this(); + } + + auto NameVertex (string name) + { + if (!lastvertex.IsNull()) + OCCGeometry::GetProperties(lastvertex).name = name; + return shared_from_this(); + } + + auto Circle (double r) + { + gp_Pnt2d pos = localpos.Location(); + return Circle (pos.X(), pos.Y(), r); + } + + shared_ptr Close () + { + if (startpnt.Distance(localpos.Location()) > 1e-10) + { + LineTo (startpnt.X(), startpnt.Y()); + return shared_from_this(); + } + + if (!startvertex.IsNull()) + Finish(); + return shared_from_this(); + } + + auto Reverse() + { + wires.back().Reverse(); + return shared_from_this(); + } + + auto Offset(double d) + { + TopoDS_Wire wire = wires.back(); + wires.pop_back(); + BRepOffsetAPI_MakeOffset builder; + builder.AddWire(wire); + builder.Perform(d); + auto shape = builder.Shape(); + wires.push_back (TopoDS::Wire(shape.Reversed())); + return shared_from_this(); + } + + optional Last() + { + return wires.empty() ? + optional{} : + optional{wires.back()}; + } + + TopoDS_Face Face() + { + BRepBuilderAPI_MakeFace builder(surf, 1e-8); + for (auto w : wires) + builder.Add(w); + wires.clear(); + return builder.Face(); + } + + auto Wires() + { + ListOfShapes ws; + for (auto w : wires) + ws.push_back(w); + return ws; + } +}; + + + +DLL_HEADER void ExportNgOCCShapes(py::module &m) +{ + py::enum_(m, "TopAbs_ShapeEnum", "Enumeration of all supported TopoDS_Shapes") + .value("COMPOUND", TopAbs_COMPOUND) .value("COMPSOLID", TopAbs_COMPSOLID) + .value("SOLID", TopAbs_SOLID) .value("SHELL", TopAbs_SHELL) + .value("FACE", TopAbs_FACE) .value("WIRE", TopAbs_WIRE) + .value("EDGE", TopAbs_EDGE) .value("VERTEX", TopAbs_VERTEX) + .value("SHAPE", TopAbs_SHAPE) + .export_values() + ; + + + py::class_ (m, "TopoDS_Shape") + .def("__str__", [] (const TopoDS_Shape & shape) + { + stringstream str; +#ifdef OCC_HAVE_DUMP_JSON + shape.DumpJson(str); +#endif // OCC_HAVE_DUMP_JSON + return str.str(); + }) + + .def("ShapeType", [] (const TopoDS_Shape & shape) + { + throw Exception ("use 'shape.type' instead of 'shape.ShapeType()'"); + }, "deprecated, use 'shape.type' instead") + + .def_property_readonly("type", [](const TopoDS_Shape & shape) + { return shape.ShapeType(); }, "returns type of shape, i.e. 'EDGE', 'FACE', ...") + + .def("SubShapes", [] (const TopoDS_Shape & shape, TopAbs_ShapeEnum & type) + { + ListOfShapes sub; + for (TopExp_Explorer e(shape, type); e.More(); e.Next()) + sub.push_back(e.Current()); + return sub; + }, py::arg("type"), "returns list of sub-shapes of type 'type'") + + .def_property_readonly("solids", GetSolids, + "returns all sub-shapes of type 'SOLID'") + .def_property_readonly("faces", GetFaces, + "returns all sub-shapes of type 'FACE'") + .def_property_readonly("edges", GetEdges, + "returns all sub-shapes of type 'EDGE'") + .def_property_readonly("wires", GetWires, + "returns all sub-shapes of type 'WIRE'") + .def_property_readonly("vertices", GetVertices, + "returns all sub-shapes of type 'VERTEX'") + .def_property_readonly("bounding_box", [] ( const TopoDS_Shape &shape ) + { + auto box = GetBoundingBox(shape); + return py::make_tuple( ng2occ(box.PMin()), ng2occ(box.PMax()) ); + }, "returns bounding box (pmin, pmax)") + + .def("Properties", [] (const TopoDS_Shape & shape) + { + auto props = Properties(shape); + return tuple( py::cast(props.Mass()), py::cast(props.CentreOfMass()) ); + }, "returns tuple of shape properties, currently ('mass', 'center'") + + .def_property_readonly("center", [](const TopoDS_Shape & shape) { + return Center(shape); + }, "returns center of gravity of shape") + + .def_property_readonly("mass", [](const TopoDS_Shape & shape) { + return Mass(shape); + }, "returns mass of shape, what is length, face, or volume") + + .def("Move", [](const TopoDS_Shape & shape, const gp_Vec v) + { + // which one to choose ? + // version 1: Transoformation + gp_Trsf trafo; + trafo.SetTranslation(v); + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return CastShape(builder.Shape()); + // version 2: change location + // ... + }, py::arg("v"), "copy shape, and translate copy by vector 'v'") + + + .def("Rotate", [](const TopoDS_Shape & shape, const gp_Ax1 ax, double ang) + { + gp_Trsf trafo; + trafo.SetRotation(ax, ang*M_PI/180); + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }, py::arg("axis"), py::arg("ang"), + "copy shape, and rotet copy by 'ang' degrees around 'axis'") + + .def("Mirror", [] (const TopoDS_Shape & shape, const gp_Ax3 & ax) + { + gp_Trsf trafo; + trafo.SetMirror(ax.Ax2()); + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }, py::arg("axes"), + "copy shape, and mirror over plane defined by 'axes'") + + .def("Mirror", [] (const TopoDS_Shape & shape, const gp_Ax1 & ax) + { + gp_Trsf trafo; + trafo.SetMirror(ax); + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }, py::arg("axes"), + "copy shape, and mirror around axis 'axis'") + + .def("Scale", [](const TopoDS_Shape & shape, const gp_Pnt p, double s) + { + gp_Trsf trafo; + trafo.SetScale(p, s); + BRepBuilderAPI_Transform builder(shape, trafo, true); + PropagateProperties(builder, shape, occ2ng(trafo)); + return builder.Shape(); + }, py::arg("p"), py::arg("s"), + "copy shape, and scale copy by factor 's'") + + .def("WriteStep", [](const TopoDS_Shape & shape, string & filename) + { step_utils::WriteSTEP(shape, filename); } + , py::arg("filename"), "export shape in STEP - format") + + .def("bc", [](const TopoDS_Shape & shape, const string & name) + { + for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next()) + OCCGeometry::GetProperties(e.Current()).name = name; + return shape; + }, py::arg("name"), "sets 'name' property for all faces of shape") + + .def("mat", [](const TopoDS_Shape & shape, const string & name) + { + for (TopExp_Explorer e(shape, TopAbs_SOLID); e.More(); e.Next()) + OCCGeometry::GetProperties(e.Current()).name = name; + return shape; + }, py::arg("name"), "sets 'name' property to all solids of shape") + + .def_property("name", [](const TopoDS_Shape & self) -> optional { + if (auto name = OCCGeometry::GetProperties(self).name) + return *name; + else + return nullopt; + }, [](const TopoDS_Shape & self, optional name) { + OCCGeometry::GetProperties(self).name = name; + }, "'name' of shape") + + .def_property("maxh", + [](const TopoDS_Shape& self) + { + return OCCGeometry::GetProperties(self).maxh; + }, + [](TopoDS_Shape& self, double val) + { + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for (TopExp_Explorer e(self, typ); e.More(); e.Next()) + { + auto & maxh = OCCGeometry::GetProperties(e.Current()).maxh; + maxh = min2(val, maxh); + } + }, "maximal mesh-size for shape") + + .def_property("hpref", + [](const TopoDS_Shape& self) + { + return OCCGeometry::GetProperties(self).hpref; + }, + [](TopoDS_Shape& self, double val) + { + auto & hpref = OCCGeometry::GetProperties(self).hpref; + hpref = max2(val, hpref); + }, "number of refinement levels for geometric refinement") + + .def_property("col", [](const TopoDS_Shape & self) { + if(!OCCGeometry::HaveProperties(self) || !OCCGeometry::GetProperties(self).col) + return std::vector({ 0.2, 0.2, 0.2, 1. }); + auto col = *OCCGeometry::GetProperties(self).col; + return std::vector({ col(0), col(1), col(2), col(3) }); + }, [](const TopoDS_Shape & self, std::vector c) { + Vec<4> col(c[0], c[1], c[2], 1.0); + if(c.size() == 4) + col[3] = c[3]; + OCCGeometry::GetProperties(self).col = col; + }, "color of shape as RGB - tuple") + .def("UnifySameDomain", [](const TopoDS_Shape& shape, + bool edges, bool faces, + bool concatBSplines) + { + ShapeUpgrade_UnifySameDomain unify(shape, edges, faces, + concatBSplines); + unify.Build(); + Handle(BRepTools_History) history = unify.History (); + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } + return unify.Shape(); + }, py::arg("unifyEdges")=true, py::arg("unifyFaces")=true, + py::arg("concatBSplines")=true) + + .def_property("location", + [](const TopoDS_Shape & shape) { return shape.Location(); }, + [](TopoDS_Shape & shape, const TopLoc_Location & loc) + { shape.Location(loc); }, "Location of shape") + .def("Located", [](const TopoDS_Shape & shape, const TopLoc_Location & loc) + { return shape.Located(loc); }, py::arg("loc"), "copy shape and sets location of copy") + + .def("__add__", [] (const TopoDS_Shape & shape1, const TopoDS_Shape & shape2) { + + BRepAlgoAPI_Fuse builder(shape1, shape2); + PropagateProperties (builder, shape1); + PropagateProperties (builder, shape2); + /* +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = builder.History (); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (auto & s : { shape1, shape2 }) + for (TopExp_Explorer e(s, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } +#endif + */ + auto fused = builder.Shape(); + + // make one face when fusing in 2D + // from https://gitlab.onelab.info/gmsh/gmsh/-/issues/627 + // int cntsolid = 0; + // for (TopExp_Explorer e(shape1, TopAbs_SOLID); e.More(); e.Next()) + // cntsolid++; + // for (TopExp_Explorer e(shape2, TopAbs_SOLID); e.More(); e.Next()) + // cntsolid++; + // if (cntsolid == 0) + // { + ShapeUpgrade_UnifySameDomain unify(fused, true, true, true); + unify.Build(); + + // #ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = unify.History (); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (TopExp_Explorer e(fused, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } + // #endif + // PropagateProperties (unify, fused); + + return unify.Shape(); + // } + // else + // return fused; + }, "fuses shapes") + .def("__radd__", [] (const TopoDS_Shape & shape, int i) // for sum([shapes]) + { return shape; }, "needed for Sum([shapes])") + .def("__mul__", [] (const TopoDS_Shape & shape1, const TopoDS_Shape & shape2) { + + BRepAlgoAPI_Common builder(shape1, shape2); + /* +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = builder.History (); + + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (auto & s : { shape1, shape2 }) + for (TopExp_Explorer e(s, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } +#endif // OCC_HAVE_HISTORY + */ + PropagateProperties (builder, shape1); + PropagateProperties (builder, shape2); + + return builder.Shape(); + }, "common of shapes") + + .def("__sub__", [] (const TopoDS_Shape & shape1, const TopoDS_Shape & shape2) { + + BRepAlgoAPI_Cut builder(shape1, shape2); + /* +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = builder.History (); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (auto & s : { shape1, shape2 }) + for (TopExp_Explorer e(s, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } +#endif // OCC_HAVE_HISTORY + */ + PropagateProperties (builder, shape1); + PropagateProperties (builder, shape2); + + return builder.Shape(); + }, "cut of shapes") + .def("__eq__", [] (const TopoDS_Shape& shape1, const TopoDS_Shape& shape2) { + return shape1.IsSame(shape2); + }) + .def("__hash__", [] (const TopoDS_Shape& shape) { + OCCGeometry::GetProperties(shape); // make sure it is in global properties + return OCCGeometry::global_shape_property_indices.FindIndex(shape); + }) + + .def("Reversed", [](const TopoDS_Shape & shape) { + return CastShape(shape.Reversed()); }) + + .def("Extrude", [](const TopoDS_Shape & shape, double h, + optional dir, bool identify, + Identifications::ID_TYPE idtype, + string idname) + { + for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next()) + { + Handle(Geom_Surface) surf = BRep_Tool::Surface (TopoDS::Face(e.Current())); + gp_Vec edir; + if(dir.has_value()) + edir = *dir; + else + { + gp_Vec du, dv; + gp_Pnt p; + surf->D1 (0,0,p,du,dv); + edir = du^dv; + } + BRepPrimAPI_MakePrism builder(shape, h*edir, false); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX }) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : builder.Generated(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } + if(identify) + { + Transformation<3> trsf(h * occ2ng(edir)); + Identify(GetFaces(shape), GetFaces(builder.LastShape()), + idname, idtype, trsf); + } + return builder.Shape(); + } + throw Exception("no face found for extrusion"); + }, py::arg("h"), py::arg("dir")=nullopt, py::arg("identify")=false, + py::arg("idtype")=Identifications::CLOSESURFACES, + py::arg("idname") = "extrusion", + "extrude shape to thickness 'h', shape must contain a plane surface, optionally give an extrusion direction") + + .def("Extrude", [] (const TopoDS_Shape & face, gp_Vec vec) { + return BRepPrimAPI_MakePrism (face, vec).Shape(); + }, py::arg("v"), "extrude shape by vector 'v'") + + .def("Revolve", [](const TopoDS_Shape & shape, const gp_Ax1 &A, const double D) { + // for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next()) + { + // return BRepPrimAPI_MakeRevol (shape, A, D*M_PI/180).Shape(); + BRepPrimAPI_MakeRevol builder(shape, A, D*M_PI/180, true); + + for (auto typ : { TopAbs_FACE, TopAbs_EDGE, TopAbs_VERTEX}) + for (TopExp_Explorer e(shape, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : builder.Generated(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } + + return builder.Shape(); + } + // throw Exception("no face found for revolve"); + }, py::arg("axis"), py::arg("ang"), "revolve shape around 'axis' by 'ang' degrees") + + .def("MakeFillet", [](const TopoDS_Shape & shape, std::vector edges, double r) { + BRepFilletAPI_MakeFillet mkFillet(shape); + for (auto e : edges) + mkFillet.Add (r, TopoDS::Edge(e)); + mkFillet.Build(); + PropagateProperties (mkFillet, shape); + for (auto e : edges) + for (auto gen : mkFillet.Generated(e)) + OCCGeometry::GetProperties(gen).name = "fillet"; + return mkFillet.Shape(); + }, py::arg("edges"), py::arg("r"), "make fillets for edges 'edges' of radius 'r'") + + .def("MakeChamfer", [](const TopoDS_Shape & shape, std::vector edges, double d) { +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4 + BRepFilletAPI_MakeChamfer mkChamfer(shape); + for (auto e : edges) + mkChamfer.Add (d, TopoDS::Edge(e)); + mkChamfer.Build(); + PropagateProperties (mkChamfer, shape); + for (auto e : edges) + for (auto gen : mkChamfer.Generated(e)) + OCCGeometry::GetProperties(gen).name = "chamfer"; + return mkChamfer.Shape(); +#else + throw Exception("MakeChamfer not available for occ-version < 7.4"); +#endif + }, py::arg("edges"), py::arg("d"), "make symmetric chamfer for edges 'edges' of distrance 'd'") + + .def("MakeThickSolid", [](const TopoDS_Shape & body, std::vector facestoremove, + double offset, double tol, bool intersection, + string joinT, bool removeIntEdges) { + TopTools_ListOfShape faces; + for (auto f : facestoremove) + faces.Append(f); + + BRepOffsetAPI_MakeThickSolid maker; + GeomAbs_JoinType joinType; + if(joinT == "arc") + joinType = GeomAbs_Arc; + else if(joinT == "intersection") + joinType = GeomAbs_Intersection; + else + throw Exception("Only joinTypes 'arc' and 'intersection' exist!"); + maker.MakeThickSolidByJoin(body, faces, offset, tol, + BRepOffset_Skin, intersection, + false, joinType, removeIntEdges); + return maker.Shape(); + }, py::arg("facestoremove"), py::arg("offset"), py::arg("tol"), + py::arg("intersection") = false,py::arg("joinType")="arc", + py::arg("removeIntersectingEdges") = false, + "makes shell-like solid from faces") + + .def("MakeTriangulation", [](const TopoDS_Shape & shape) + { + BRepTools::Clean (shape); + double deflection = 0.01; + BRepMesh_IncrementalMesh (shape, deflection, true); + }) + + + .def("Identify", py::overload_cast>>(&Identify), + py::arg("other"), py::arg("name"), + py::arg("type")=Identifications::PERIODIC, py::arg("trafo")=nullopt, + "Identify shapes for periodic meshing") + + .def("Distance", [](const TopoDS_Shape& self, + const TopoDS_Shape& other) + { + return BRepExtrema_DistShapeShape(self, other).Value(); + }) + + .def("Triangulation", [](const TopoDS_Shape & shape) + { + // extracted from vsocc.cpp + TopoDS_Face face; + try + { + face = TopoDS::Face(shape); + } + catch (Standard_Failure & e) + { + e.Print (cout); + throw NgException ("Triangulation: shape is not a face"); + } + + /* + BRepTools::Clean (shape); + double deflection = 0.01; + BRepMesh_IncrementalMesh (shape, deflection, true); + */ + + Handle(Geom_Surface) surf = BRep_Tool::Surface (face); + + TopLoc_Location loc; + Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc); + + if (triangulation.IsNull()) + { + BRepTools::Clean (shape); + double deflection = 0.01; + BRepMesh_IncrementalMesh (shape, deflection, true); + triangulation = BRep_Tool::Triangulation (face, loc); + } + // throw Exception("Don't have a triangulation, call 'MakeTriangulation' first"); + + int ntriangles = triangulation -> NbTriangles(); + Array< std::array,3> > triangles; + for (int j = 1; j <= ntriangles; j++) + { + Poly_Triangle triangle = triangulation -> Triangle(j); + std::array,3> pts; + for (int k = 0; k < 3; k++) + pts[k] = occ2ng( (triangulation -> Node(triangle(k+1))).Transformed(loc) ); + triangles.Append ( pts ); + } + + // return MoveToNumpyArray(triangles); + return triangles; + }) + .def("_webgui_data", [](const TopoDS_Shape & shape) + { + BRepTools::Clean (shape); + double deflection = 0.01; + BRepMesh_IncrementalMesh (shape, deflection, true); + // triangulation = BRep_Tool::Triangulation (face, loc); + + std::vector p[3]; + std::vector n[3]; + py::list names, colors, solid_names; + std::vector> solid_face_map; + + int index = 0; + + Box<3> box(Box<3>::EMPTY_BOX); + TopTools_IndexedMapOfShape fmap; + for (TopExp_Explorer e(shape, TopAbs_FACE); e.More(); e.Next()) + { + TopoDS_Face face = TopoDS::Face(e.Current()); + if(fmap.Contains(face)) continue; + // Handle(TopoDS_Face) face = e.Current(); + fmap.Add(face); + ExtractFaceData(face, index, p, n, box); + auto & props = OCCGeometry::GetProperties(face); + if(props.col) + { + auto & c = *props.col; + colors.append(py::make_tuple(c[0], c[1], c[2])); + } + else + colors.append(py::make_tuple(0.0, 1.0, 0.0)); + if(props.name) + { + names.append(*props.name); + } + else + names.append(""); + index++; + } + + for(auto& solid : GetSolids(shape)) + { + std::vector faces; + for(auto& face : GetFaces(solid)) + faces.push_back(fmap.FindIndex(face)-1); + solid_face_map.push_back(std::move(faces)); + auto& props = OCCGeometry::GetProperties(solid); + if(props.name) + solid_names.append(*props.name); + else + solid_names.append(""); + } + + std::vector edge_p[2]; + py::list edge_names, edge_colors; + index = 0; + for (TopExp_Explorer e(shape, TopAbs_EDGE); e.More(); e.Next()) + { + TopoDS_Edge edge = TopoDS::Edge(e.Current()); + ExtractEdgeData(edge, index, edge_p, box); + auto & props = OCCGeometry::GetProperties(edge); + if(props.col) + { + auto & c = *props.col; + edge_colors.append(py::make_tuple(c[0], c[1], c[2])); + } + else + edge_colors.append(py::make_tuple(0.0, 0.0, 0.0)); + if(props.name) + { + edge_names.append(*props.name); + } + else + edge_names.append(""); + index++; + } + + + auto center = box.Center(); + + py::list mesh_center; + mesh_center.append(center[0]); + mesh_center.append(center[1]); + mesh_center.append(center[2]); + py::dict data; + data["ngsolve_version"] = "Netgen x.x"; // TODO + data["mesh_dim"] = 3; // TODO + data["mesh_center"] = mesh_center; + data["mesh_radius"] = box.Diam()/2; + data["order2d"] = 1; + data["order3d"] = 0; + data["draw_vol"] = false; + data["draw_surf"] = true; + data["funcdim"] = 0; + data["have_normals"] = true; + data["show_wireframe"] = true; + data["show_mesh"] = true; + data["Bezier_points"] = py::list{}; + py::list points; + points.append(p[0]); + points.append(p[1]); + points.append(p[2]); + points.append(n[0]); + points.append(n[1]); + points.append(n[2]); + data["Bezier_trig_points"] = points; + data["funcmin"] = 0; + data["funcmax"] = 1; + data["mesh_regions_2d"] = index; + data["autoscale"] = false; + data["colors"] = colors; + data["names"] = names; + data["solid_names"] = solid_names; + + py::list edges; + edges.append(edge_p[0]); + edges.append(edge_p[1]); + data["edges"] = edges; + data["edge_names"] = edge_names; + data["edge_colors"] = edge_colors; + data["solid_face_map"] = solid_face_map; + return data; + }) + ; + + py::class_ (m, "Vertex") + .def(py::init([] (const TopoDS_Shape & shape) { + return TopoDS::Vertex(shape); + })) + .def(py::init([] (const gp_Pnt & p) { + return BRepBuilderAPI_MakeVertex (p).Vertex(); + })) + .def_property_readonly("p", [] (const TopoDS_Vertex & v) -> gp_Pnt { + return BRep_Tool::Pnt (v); }, "coordinates of vertex") + ; + + py::class_ (m, "Edge") + .def(py::init([] (const TopoDS_Shape & shape) { + return TopoDS::Edge(shape); + })) + .def(py::init([] (Handle(Geom2d_Curve) curve2d, TopoDS_Face face) { + auto edge = BRepBuilderAPI_MakeEdge(curve2d, BRep_Tool::Surface (face)).Edge(); + BRepLib::BuildCurves3d(edge); + return edge; + })) + .def("Value", [](const TopoDS_Edge & e, double s) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + return curve->Value(s); + }, py::arg("s"), "evaluate curve for parameters 's'") + + .def("Tangent", [](const TopoDS_Edge & e, double s) { + gp_Pnt p; gp_Vec v; + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + curve->D1(s, p, v); + return v; + }, py::arg("s"), "tangent vector to curve at parameter 's'") + + .def_property_readonly("start", + [](const TopoDS_Edge & e) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + return curve->Value(s0); + }, + "start-point of curve") + .def_property_readonly("end", + [](const TopoDS_Edge & e) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + return curve->Value(s1); + }, + "end-point of curve") + .def_property_readonly("start_tangent", + [](const TopoDS_Edge & e) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + gp_Pnt p; gp_Vec v; + curve->D1(s0, p, v); + return v; + }, + "tangent at start-point") + .def_property_readonly("end_tangent", + [](const TopoDS_Edge & e) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + gp_Pnt p; gp_Vec v; + curve->D1(s1, p, v); + return v; + }, + "tangent at end-point") + .def_property_readonly("parameter_interval", + [](const TopoDS_Edge & e) { + double s0, s1; + auto curve = BRep_Tool::Curve(e, s0, s1); + return tuple(s0, s1); + }, + "parameter interval of curve") + + + .def("Split", [](const TopoDS_Edge& self, py::args args) + { + ListOfShapes new_edges; + double s0, s1; + auto curve = BRep_Tool::Curve(self, s0, s1); + double tstart, t, dist; + TopoDS_Vertex vstart, vend; + vstart = TopExp::FirstVertex(self); + IntTools_Context context; + tstart = s0; + for(auto arg : args) + { + if(py::isinstance(arg)) + t = s0 + py::cast(arg) * (s1-s0); + else + { + auto p = py::cast(arg); + auto result = context.ComputePE(p, 0., self, t, dist); + if(result != 0) + throw Exception("Error in finding splitting points on edge!"); + } + auto p = curve->Value(t); + vend = BRepBuilderAPI_MakeVertex(p); + auto newE = TopoDS::Edge(self.EmptyCopied()); + BOPTools_AlgoTools::MakeSplitEdge(self, vstart, tstart, vend, t, newE); + new_edges.push_back(newE); + vstart = vend; + tstart = t; + } + auto newE = TopoDS::Edge(self.EmptyCopied()); + t = s1; + vend = TopExp::LastVertex(self); + BOPTools_AlgoTools::MakeSplitEdge(self, vstart, tstart, vend, t, newE); + new_edges.push_back(newE); + return new_edges; + }, "Splits edge at given parameters. Parameters can either be floating values in (0,1), then edge parametrization is used. Or it can be points, then the projection of these points are used for splitting the edge.") + ; + + py::class_ (m, "Wire") + .def(py::init([](const TopoDS_Edge & edge) { + BRepBuilderAPI_MakeWire builder; + builder.Add(edge); + return builder.Wire(); + })) + .def(py::init([](std::vector edges) { + BRepBuilderAPI_MakeWire builder; + try + { + for (auto s : edges) + switch (s.ShapeType()) + { + case TopAbs_EDGE: + builder.Add(TopoDS::Edge(s)); break; + case TopAbs_WIRE: + builder.Add(TopoDS::Wire(s)); break; + default: + throw Exception("can make wire only from edges and wires"); + } + return builder.Wire(); + } + catch (Standard_Failure & e) + { + stringstream errstr; + e.Print(errstr); + throw NgException("error in wire builder: "+errstr.str()); + } + })) + ; + + py::class_ (m, "Face") + .def(py::init([](TopoDS_Wire wire) { + return BRepBuilderAPI_MakeFace(wire).Face(); + }), py::arg("w")) + .def(py::init([](const TopoDS_Face & face, const TopoDS_Wire & wire) { + return BRepBuilderAPI_MakeFace(BRep_Tool::Surface (face), wire).Face(); + }), py::arg("f"), py::arg("w")) + .def(py::init([](const TopoDS_Face & face, std::vector wires) { + auto surf = BRep_Tool::Surface (face); + BRepBuilderAPI_MakeFace builder(surf, 1e-8); + for (auto w : wires) + builder.Add(w); + return builder.Face(); + }), py::arg("f"), py::arg("w")) + .def(py::init([] (const TopoDS_Shape & shape) { + return TopoDS::Face(shape); + })) + .def_property("quad_dominated", [](const TopoDS_Face& self) -> optional + { + return OCCGeometry::GetProperties(self).quad_dominated; + }, + [](TopoDS_Face& self, optional quad_dominated) + { + OCCGeometry::GetProperties(self).quad_dominated = quad_dominated; + }) + .def_property_readonly("surf", [] (TopoDS_Face face) -> Handle(Geom_Surface) + { + Handle(Geom_Surface) surf = BRep_Tool::Surface (face); + return surf; + }) + .def("WorkPlane",[] (const TopoDS_Face & face) { + Handle(Geom_Surface) surf = BRep_Tool::Surface (face); + gp_Vec du, dv; + gp_Pnt p; + surf->D1 (0,0,p,du,dv); + auto ax = gp_Ax3(p, du^dv, du); + return make_shared (ax); + }) + .def("ProjectWire", [](const TopoDS_Face& face, + const TopoDS_Wire& wire) + { + BRepAlgo_NormalProjection builder(face); + builder.Add(wire); + builder.Build(); + return builder.Projection(); + }) + ; + py::class_ (m, "Solid") + .def(py::init([](const TopoDS_Shape& faces) + { + BRep_Builder builder; + TopoDS_Shell shell; + builder.MakeShell(shell); + for(auto& face : GetFaces(faces)) + builder.Add(shell, face); + TopoDS_Solid solid; + builder.MakeSolid(solid); + builder.Add(solid, shell); + return solid; + }), "Create solid from shell. Shell must consist of topologically closed faces (share vertices and edges).") + ; + + py::class_ (m, "Compound") + .def(py::init([](std::vector shapes, bool separate_layers) { + BRep_Builder builder; + TopoDS_Compound comp; + builder.MakeCompound(comp); + for(auto i : Range(shapes.size())) + { + builder.Add(comp, shapes[i]); + if(separate_layers) + { + for(auto & s : GetSolids(shapes[i])) + OCCGeometry::GetProperties(s).layer = i+1; + for(auto & s : GetFaces(shapes[i])) + OCCGeometry::GetProperties(s).layer = i+1; + for(auto & s : GetEdges(shapes[i])) + OCCGeometry::GetProperties(s).layer = i+1; + for(auto & s : GetVertices(shapes[i])) + OCCGeometry::GetProperties(s).layer = i+1; + } + } + + return comp; + }), py::arg("shapes"), py::arg("separate_layers")=false) + ; + + + + py::class_ (m, "Geom_Surface") + .def("Value", [] (const Handle(Geom_Surface) & surf, double u, double v) { + return surf->Value(u, v); }) + .def("D1", [] (const Handle(Geom_Surface) & surf, double u, double v) { + gp_Vec du, dv; + gp_Pnt p; + surf->D1 (u,v,p,du,dv); + return tuple(p,du,dv); + }) + + .def("Normal", [] (const Handle(Geom_Surface) & surf, double u, double v) { + GeomLProp_SLProps lprop(surf,u,v,1,1e-8); + if (lprop.IsNormalDefined()) + return lprop.Normal(); + throw Exception("normal not defined"); + }) + ; + + + py::implicitly_convertible(); + py::implicitly_convertible(); + + + + class ListOfShapesIterator + { + TopoDS_Shape * ptr; + public: + ListOfShapesIterator (TopoDS_Shape * aptr) : ptr(aptr) { } + ListOfShapesIterator operator++ () { return ListOfShapesIterator(++ptr); } + auto operator*() const { return CastShape(*ptr); } + bool operator!=(ListOfShapesIterator it2) const { return ptr != it2.ptr; } + bool operator==(ListOfShapesIterator it2) const { return ptr == it2.ptr; } + }; + + py::class_ (m, "ListOfShapes") + .def(py::init>()) + .def("__iter__", [](ListOfShapes &s) { + return py::make_iterator(ListOfShapesIterator(&*s.begin()), + ListOfShapesIterator(&*s.end())); + }, + py::keep_alive<0, 1>() /* Essential: keep object alive while iterator exists */) + .def("__getitem__", [](const ListOfShapes & list, size_t i) { + return CastShape(list.at(i)); }) + + .def("__getitem__", [](const ListOfShapes & self, py::slice inds) { + size_t start, step, n, stop; + if (!inds.compute(self.size(), &start, &stop, &step, &n)) + throw py::error_already_set(); + ListOfShapes sub; + sub.reserve(n); + for (size_t i = 0; i < n; i++) + sub.push_back (self[start+i*step]); + return sub; + }) + + .def("__add__", [](const ListOfShapes & l1, const ListOfShapes & l2) { + ListOfShapes l = l1; + for (auto s : l2) l.push_back(s); + return l; + } ) + .def("__add__", [](const ListOfShapes & l1, py::list l2) { + ListOfShapes l = l1; + for (auto s : l2) l.push_back(py::cast(s)); + return l; + } ) + .def("__len__", [](const ListOfShapes & self) { return self.size(); }) + .def("__getitem__",[](const ListOfShapes & self, string name) + { + ListOfShapes selected; + std::regex pattern(name); + for (auto s : self) + if (auto sname = OCCGeometry::GetProperties(s).name) + if (std::regex_match(*sname, pattern)) + selected.push_back(s); + return selected; + }, "returns list of all shapes named 'name'") + + .def("__getitem__",[](const ListOfShapes & self, DirectionalInterval interval) + { + ListOfShapes selected; + for (auto s : self) + if (interval.Contains(Center(s), GetBoundingBox(s).Diam() * 1e-7)) + selected.push_back(s); + return selected; + }) + .def_property_readonly("solids", &ListOfShapes::Solids) + .def_property_readonly("faces", &ListOfShapes::Faces) + .def_property_readonly("wires", &ListOfShapes::Wires) + .def_property_readonly("edges", &ListOfShapes::Edges) + .def_property_readonly("vertices", &ListOfShapes::Vertices) + .def(py::self * py::self) + + .def("Sorted",[](ListOfShapes self, gp_Vec dir) + { + TopTools_IndexedMapOfShape indices; + std::vector sortval; + + for (auto shape : self) + { + if(indices.FindIndex(shape) > 0) + continue; + GProp_GProps props; + gp_Pnt center; + + switch (shape.ShapeType()) + { + case TopAbs_VERTEX: + center = BRep_Tool::Pnt (TopoDS::Vertex(shape)); break; + case TopAbs_FACE: + BRepGProp::SurfaceProperties (shape, props); + center = props.CentreOfMass(); + break; + default: + BRepGProp::LinearProperties(shape, props); + center = props.CentreOfMass(); + } + + double val = center.X()*dir.X() + center.Y()*dir.Y() + center.Z() * dir.Z(); + indices.Add(shape); + sortval.push_back(val); + } + + std::sort (std::begin(self), std::end(self), + [&](const TopoDS_Shape& a, const TopoDS_Shape& b) + { return sortval[indices.FindIndex(a)-1] < + sortval[indices.FindIndex(b)-1]; }); + return self; + }, py::arg("dir"), "returns list of shapes, where center of gravity is sorted in direction of 'dir'") + + .def("Max", [] (ListOfShapes & shapes, gp_Vec dir) + { return CastShape(shapes.Max(dir)); }, + py::arg("dir"), "returns shape where center of gravity is maximal in the direction 'dir'") + + .def("Min", [] (ListOfShapes & shapes, gp_Vec dir) + { return CastShape(shapes.Max(-dir)); }, + py::arg("dir"), "returns shape where center of gravity is minimal in the direction 'dir'") + + .def("Nearest", [] (ListOfShapes & shapes, gp_Pnt pnt) + { return CastShape(shapes.Nearest(pnt)); }, + py::arg("p"), "returns shape nearest to point 'p'") + .def("Nearest", [] (ListOfShapes & shapes, gp_Pnt2d pnt) + { return CastShape(shapes.Nearest( { pnt.X(), pnt.Y(), 0 })); }, + py::arg("p"), "returns shape nearest to point 'p'") + + .def_property("name", [](ListOfShapes& shapes) + { + throw Exception("Cannot get property of ListOfShapes, get the property from individual shapes!"); + }, + [](ListOfShapes& shapes, optional name) + { + for(auto& shape : shapes) + { + OCCGeometry::GetProperties(shape).name = name; + } + }, "set name for all elements of list") + .def_property("col", [](ListOfShapes& shapes) { + throw Exception("Cannot get property of ListOfShapes, get the property from individual shapes!"); + }, [](ListOfShapes& shapes, std::vector c) { + Vec<4> col(c[0], c[1], c[2], 1.0); + if(c.size() == 4) + col[3] = c[3]; + for(auto& shape : shapes) + OCCGeometry::GetProperties(shape).col = col; + }, "set col for all elements of list") + + .def_property("maxh", [](ListOfShapes& shapes) + { + throw Exception("Cannot get property of ListOfShapes, get the property from individual shapes!"); + }, + [](ListOfShapes& shapes, double maxh) + { + for(auto& shape : shapes) + { + for(auto& s : GetSolids(shape)) + OCCGeometry::GetProperties(s).maxh = maxh; + for(auto& s : GetFaces(shape)) + OCCGeometry::GetProperties(s).maxh = maxh; + for(auto& s : GetEdges(shape)) + OCCGeometry::GetProperties(s).maxh = maxh; + for(auto& s : GetVertices(shape)) + OCCGeometry::GetProperties(s).maxh = maxh; + } + }, "set maxh for all elements of list") + .def_property("hpref", [](ListOfShapes& shapes) + { + throw Exception("Cannot get property of ListOfShapes, get the property from individual shapes!"); + }, + [](ListOfShapes& shapes, double hpref) + { + for(auto& shape : shapes) + { + auto& val = OCCGeometry::GetProperties(shape).hpref; + val = max2(hpref, val); + } + }, "set hpref for all elements of list") + .def_property("quad_dominated", [](ListOfShapes& shapes) + { + throw Exception("Cannot get property of ListOfShapes, get the property from individual shapes!"); + }, + [](ListOfShapes& shapes, optional quad_dominated) + { + for(auto& shape : shapes) + OCCGeometry::GetProperties(shape).quad_dominated = quad_dominated; + }) + + .def("Identify", [](const ListOfShapes& me, + const ListOfShapes& other, + string name, + Identifications::ID_TYPE type, + std::variant trafo) + { + Identify(me, other, name, type, occ2ng(trafo)); + }, py::arg("other"), py::arg("name"), + py::arg("type")=Identifications::PERIODIC, py::arg("trafo"), + "Identify shapes for periodic meshing") + + ; + + + + + + + + + + + + + py::class_ (m, "Geom2d_Curve") + .def("Trim", [](Handle(Geom2d_Curve) curve, double u1, double u2) -> Handle(Geom2d_Curve) + { + return new Geom2d_TrimmedCurve (curve, u1, u2); + }) + .def("Value", [](Handle(Geom2d_Curve) curve, double s) { + return curve->Value(s); + }) + .def_property_readonly("start", [](Handle(Geom2d_Curve) curve) { + return curve->Value(curve->FirstParameter()); + }) + .def_property_readonly("end", [](Handle(Geom2d_Curve) curve) { + return curve->Value(curve->LastParameter()); + }) + .def("Edge", [](Handle(Geom2d_Curve) curve) { + // static Geom_Plane surf{gp_Ax3()}; // crashes in nbconvert ??? + static auto surf = new Geom_Plane{gp_Ax3()}; + auto edge = BRepBuilderAPI_MakeEdge(curve, surf).Edge(); + BRepLib::BuildCurves3d(edge); + return edge; + }) + .def("Wire", [](Handle(Geom2d_Curve) curve) { + // static Geom_Plane surf{gp_Ax3()}; // crashes in nbconvert ??? + static auto surf = new Geom_Plane{gp_Ax3()}; + auto edge = BRepBuilderAPI_MakeEdge(curve, surf).Edge(); + BRepLib::BuildCurves3d(edge); + return BRepBuilderAPI_MakeWire(edge).Wire(); + }) + .def("Face", [](Handle(Geom2d_Curve) curve) { + // static Geom_Plane surf{gp_Ax3()}; // crashes in nbconvert ??? + static auto surf = new Geom_Plane{gp_Ax3()}; + auto edge = BRepBuilderAPI_MakeEdge(curve, surf).Edge(); + BRepLib::BuildCurves3d(edge); + auto wire = BRepBuilderAPI_MakeWire(edge).Wire(); + return BRepBuilderAPI_MakeFace(wire).Face(); + }) + ; + + + py::enum_(m, "ShapeContinuity", "Wrapper for OCC enum GeomAbs_Shape") + .value("C0", GeomAbs_Shape::GeomAbs_C0) + .value("C1", GeomAbs_Shape::GeomAbs_C1) + .value("C2", GeomAbs_Shape::GeomAbs_C2) + .value("C3", GeomAbs_Shape::GeomAbs_C3) + .value("CN", GeomAbs_Shape::GeomAbs_CN) + .value("G1", GeomAbs_Shape::GeomAbs_G1) + .value("G2", GeomAbs_Shape::GeomAbs_G2); + + py::enum_(m, "ApproxParamType", "Wrapper for Approx_ParametrizationType") + .value("Centripetal", Approx_ParametrizationType::Approx_Centripetal) + .value("ChordLength", Approx_ParametrizationType::Approx_ChordLength) + .value("IsoParametric", Approx_ParametrizationType::Approx_IsoParametric); + + + m.def("HalfSpace", [] (gp_Pnt p, gp_Vec n) + { + gp_Pln plane(p, n); + BRepBuilderAPI_MakeFace bface(plane); + auto face = bface.Face(); + auto refpnt = p.Translated(-n); + BRepPrimAPI_MakeHalfSpace builder(face, refpnt); + return builder.Shape(); + }, py::arg("p"), py::arg("n"), "Create a half space threw point p normal to n"); + m.def("Sphere", [] (gp_Pnt cc, double r) { + return BRepPrimAPI_MakeSphere (cc, r).Solid(); + }, py::arg("c"), py::arg("r"), "create sphere with center 'c' and radius 'r'"); + + m.def("Cylinder", [] (gp_Pnt cpnt, gp_Dir cdir, double r, double h, + optional bot, optional top, optional mantle) { + auto builder = BRepPrimAPI_MakeCylinder (gp_Ax2(cpnt, cdir), r, h); + if(mantle) + OCCGeometry::GetProperties(builder.Face()).name = *mantle; + auto pyshape = py::cast(builder.Solid()); + gp_Vec v = cdir; + if(bot) + pyshape.attr("faces").attr("Min")(v).attr("name") = *bot; + if(top) + pyshape.attr("faces").attr("Max")(v).attr("name") = *top; + return pyshape; + }, py::arg("p"), py::arg("d"), py::arg("r"), py::arg("h"), + py::arg("bottom") = nullopt, py::arg("top") = nullopt, + py::arg("mantle") = nullopt, + "create cylinder with base point 'p', axis direction 'd', radius 'r', and height 'h'"); + + m.def("Cylinder", [] (gp_Ax2 ax, double r, double h) { + return BRepPrimAPI_MakeCylinder (ax, r, h).Solid(); + }, py::arg("axis"), py::arg("r"), py::arg("h"), + "create cylinder given by axis, radius and height"); + + m.def("Cone", [] (gp_Ax2 ax, double r1, double r2, double h, double angle) { + return BRepPrimAPI_MakeCone (ax, r1, r2, h, angle).Solid(); + }, py::arg("axis"), py::arg("r1"), py::arg("r2"), py::arg("h"), py::arg("angle"), + "create cone given by axis, radius at bottom (z=0) r1, radius at top (z=h) r2, height and angle"); + + m.def("Box", [] (gp_Pnt cp1, gp_Pnt cp2) { + return BRepPrimAPI_MakeBox (cp1, cp2).Solid(); + }, py::arg("p1"), py::arg("p2"), + "create box with opposite points 'p1' and 'p2'"); + + m.def("Prism", [] (const TopoDS_Shape & face, gp_Vec vec) { + return BRepPrimAPI_MakePrism (face, vec, true).Shape(); + }, py::arg("face"), py::arg("v"), + "extrude face along the vector 'v'"); + + m.def("Revolve", [] (const TopoDS_Shape & face,const gp_Ax1 &A, const double D) { + //convert angle from deg to rad + return BRepPrimAPI_MakeRevol (face, A, D*M_PI/180, true).Shape(); + }); + + m.def("Pipe", [] (const TopoDS_Wire & spine, const TopoDS_Shape & profile, + optional> twist, + optional auxspine) { + if (twist) + { + auto [pnt, angle] = *twist; + + /* + cyl = Cylinder((0,0,0), Z, r=1, h=1).faces[0] + heli = Edge(Segment((0,0), (2*math.pi, 1)), cyl) + auxspine = Wire( [heli] ) + + Handle(Geom_Surface) cyl = new Geom_CylindricalSurface (gp_Ax3(pnt, gp_Vec(0,0,1)), 1); + auto edge = BRepBuilderAPI_MakeEdge(curve2d, cyl).Edge(); + BRepLib::BuildCurves3d(edge); + */ + throw Exception("twist not implemented"); + } + if (auxspine) + { + BRepOffsetAPI_MakePipeShell builder(spine); + builder.SetMode (*auxspine, Standard_True); + for (TopExp_Explorer e(profile, TopAbs_WIRE); e.More(); e.Next()) + builder.Add (TopoDS::Wire(e.Current())); + builder.Build(); + builder.MakeSolid(); + return builder.Shape(); + } + + return BRepOffsetAPI_MakePipe (spine, profile).Shape(); + }, py::arg("spine"), py::arg("profile"), py::arg("twist")=nullopt, py::arg("auxspine")=nullopt); + + m.def("PipeShell", [] (const TopoDS_Wire & spine, const TopoDS_Shape & profile, const TopoDS_Wire & auxspine) { + try + { + BRepOffsetAPI_MakePipeShell builder(spine); + builder.SetMode (auxspine, Standard_True); + builder.Add (profile); + // builder.Build(); + // builder.MakeSolid(); + return builder.Shape(); + } + catch (Standard_Failure & e) + { + stringstream errstr; + e.Print(errstr); + throw NgException("cannot create PipeShell: "+errstr.str()); + } + }, py::arg("spine"), py::arg("profile"), py::arg("auxspine")); + + + // Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor); + m.def("Ellipse", [] (const gp_Ax2d & ax, double major, double minor) -> Handle(Geom2d_Curve) + { + return new Geom2d_Ellipse(ax, major, minor); + }, py::arg("axes"), py::arg("major"), py::arg("minor"), "create 2d ellipse curve"); + + m.def("Segment", [](gp_Pnt2d p1, gp_Pnt2d p2) -> Handle(Geom2d_Curve) { + return Handle(Geom2d_TrimmedCurve)(GCE2d_MakeSegment(p1, p2)); + /* + Handle(Geom2d_TrimmedCurve) curve = GCE2d_MakeSegment(p1, p2); + return curve; + */ + }, py::arg("p1"), py::arg("p2"), "create 2d line curve"); + + m.def("Circle", [](gp_Pnt2d p1, double r) -> Handle(Geom2d_Curve) { + return Handle(Geom2d_Circle)(GCE2d_MakeCircle(p1, r)); + /* + Handle(Geom2d_Circle) curve = GCE2d_MakeCircle(p1, r); + return curve; + */ + }, py::arg("c"), py::arg("r"), "create 2d circle curve"); + + m.def("SplineApproximation", [](const std::vector &points, Approx_ParametrizationType approx_type, int deg_min, + int deg_max, GeomAbs_Shape continuity, double tol) -> Handle(Geom2d_Curve) { + TColgp_Array1OfPnt2d hpoints(0, 0); + hpoints.Resize(0, points.size() - 1, true); + for (int i = 0; i < points.size(); i++) + hpoints.SetValue(i, points[i]); + + Geom2dAPI_PointsToBSpline builder(hpoints, approx_type, deg_min, deg_max, continuity, tol); + return Handle(Geom2d_BSplineCurve)(builder.Curve()); + }, + py::arg("points"), + py::arg("approx_type") = Approx_ParametrizationType::Approx_ChordLength, + py::arg("deg_min") = 3, + py::arg("deg_max") = 8, + py::arg("continuity") = GeomAbs_Shape::GeomAbs_C2, + py::arg("tol")=1e-8, + R"delimiter( +Generate a piecewise continuous spline-curve approximating a list of points in 2d. + +Parameters +---------- + +points : List|Tuple[gp_Pnt2d] + List (or tuple) of gp_Pnt. + +approx_type : ApproxParamType + Assumption on location of parameters wrt points. + +deg_min : int + Minimum polynomial degree of splines + +deg_max : int + Maximum polynomial degree of splines + +continuity : ShapeContinuity + Continuity requirement on the approximating surface + +tol : float + Tolerance for the distance from individual points to the approximating curve. + +)delimiter"); + + m.def("SplineInterpolation", [](const std::vector &points, bool periodic, double tol, const std::map &tangents) -> Handle(Geom2d_Curve) { + Handle(TColgp_HArray1OfPnt2d) hpoints = new TColgp_HArray1OfPnt2d(1, points.size()); + for (int i = 0; i < points.size(); i++) + hpoints->SetValue(i+1, points[i]); + Geom2dAPI_Interpolate builder(hpoints, periodic, tol); + + if (tangents.size() > 0) + { + const gp_Vec2d dummy_vec = tangents.begin()->second; + TColgp_Array1OfVec2d tangent_vecs(1, points.size()); + Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, points.size()); + for (int i : Range(points.size())) + { + if (tangents.count(i) > 0) + { + tangent_vecs.SetValue(i+1, tangents.at(i)); + tangent_flags->SetValue(i+1, true); + } else{ + tangent_vecs.SetValue(i+1, dummy_vec); + tangent_flags->SetValue(i+1, false); + } + } + builder.Load(tangent_vecs, tangent_flags); + } + + builder.Perform(); + return Handle(Geom2d_BSplineCurve)(builder.Curve()); + }, + py::arg("points"), + py::arg("periodic")=false, + py::arg("tol")=1e-8, + py::arg("tangents")=std::map{}, + R"delimiter( +Generate a piecewise continuous spline-curve interpolating a list of points in 2d. + +Parameters +---------- + +points : List|Tuple[gp_Pnt2d] + List (or tuple) of gp_Pnt2d. + +periodic : bool + Whether the result should be periodic + +tol : float + Tolerance for the distance between points. + +tangents : Dict[int, gp_Vec2d] + Tangent vectors for the points indicated by the key value (0-based). + +)delimiter"); + + m.def("Glue", [] (const std::vector shapes) -> TopoDS_Shape + { + if(shapes.size() == 1) + return shapes[0]; + BOPAlgo_Builder builder; + for (auto & s : shapes) + { + bool has_solid = false; + for (TopExp_Explorer e(s, TopAbs_SOLID); e.More(); e.Next()) + { + builder.AddArgument(e.Current()); + has_solid = true; + } + if (has_solid) continue; + + bool has_face = false; + for (TopExp_Explorer e(s, TopAbs_FACE); e.More(); e.Next()) + { + builder.AddArgument(e.Current()); + has_face = true; + } + if (has_face) continue; + + bool has_edge = false; + for (TopExp_Explorer e(s, TopAbs_EDGE); e.More(); e.Next()) + { + builder.AddArgument(e.Current()); + has_edge = true; + } + if (has_edge) continue; + + + for (TopExp_Explorer e(s, TopAbs_VERTEX); e.More(); e.Next()) + { + builder.AddArgument(e.Current()); + } + } + + builder.Perform(); + + /* +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = builder.History (); + + for (auto typ : { TopAbs_SOLID, TopAbs_FACE, TopAbs_EDGE }) + for (auto & s : shapes) + for (TopExp_Explorer e(s, typ); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } +#endif // OCC_HAVE_HISTORY + */ + for (auto & s : shapes) + PropagateProperties (builder, s); + return builder.Shape(); + }, py::arg("shapes"), "glue together shapes of list"); + + m.def("Glue", [] (TopoDS_Shape shape) -> TopoDS_Shape + { + BOPAlgo_Builder builder; + + for (TopExp_Explorer e(shape, TopAbs_SOLID); e.More(); e.Next()) + builder.AddArgument(e.Current()); + + builder.Perform(); + + if (builder.HasErrors()) + builder.DumpErrors(cout); + if (builder.HasWarnings()) + builder.DumpWarnings(cout); + + /* +#ifdef OCC_HAVE_HISTORY + Handle(BRepTools_History) history = builder.History (); + + for (TopExp_Explorer e(shape, TopAbs_SOLID); e.More(); e.Next()) + { + auto prop = OCCGeometry::GetProperties(e.Current()); + for (auto mods : history->Modified(e.Current())) + OCCGeometry::GetProperties(mods).Merge(prop); + } +#endif // OCC_HAVE_HISTORY + */ + PropagateProperties (builder, shape); + + return builder.Shape(); + }, py::arg("shape"), "glue together shapes from shape, typically a compound"); + m.def("Fuse", [](const vector& shapes) -> TopoDS_Shape + { + auto s = shapes[0]; + for(auto i : Range(size_t(1), shapes.size())) + { + BRepAlgoAPI_Fuse builder(s, shapes[i]); + PropagateProperties(builder, s); + PropagateProperties(builder, shapes[i]); + s = builder.Shape(); + } + return s; + }); + + + // py::class_ (m, "Geom_TrimmedCurve") + // ; + + m.def("Segment", [](gp_Pnt p1, gp_Pnt p2) { + Handle(Geom_TrimmedCurve) curve = GC_MakeSegment(p1, p2); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }); + m.def("Circle", [](gp_Pnt c, gp_Dir n, double r) { + Handle(Geom_Circle) curve = GC_MakeCircle (c, n, r); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }); + + m.def("ArcOfCircle", [](gp_Pnt p1, gp_Pnt p2, gp_Pnt p3) { + Handle(Geom_TrimmedCurve) curve = GC_MakeArcOfCircle(p1, p2, p3); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }, py::arg("p1"), py::arg("p2"), py::arg("p3"), + "create arc from p1 through p2 to p3"); + + m.def("ArcOfCircle", [](gp_Pnt p1, gp_Vec v, gp_Pnt p2) { + Handle(Geom_TrimmedCurve) curve = GC_MakeArcOfCircle(p1, v, p2); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }, py::arg("p1"), py::arg("v"), py::arg("p2"), + "create arc from p1, with tangent vector v, to point p2"); + + + m.def("BSplineCurve", [](std::vector vpoles, int degree) { + // not yet working ???? + TColgp_Array1OfPnt poles(0, vpoles.size()-1); + TColStd_Array1OfReal knots(0, vpoles.size()+degree); + TColStd_Array1OfInteger mult(0, vpoles.size()+degree); + int cnt = 0; + + for (int i = 0; i < vpoles.size(); i++) + { + poles.SetValue(i, vpoles[i]); + knots.SetValue(i, i); + mult.SetValue(i,1); + } + for (int i = vpoles.size(); i < vpoles.size()+degree+1; i++) + { + knots.SetValue(i, i); + mult.SetValue(i, 1); + } + + Handle(Geom_Curve) curve = new Geom_BSplineCurve(poles, knots, mult, degree); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }); + + m.def("BezierCurve", [](std::vector vpoles) { + TColgp_Array1OfPnt poles(0, vpoles.size()-1); + + for (int i = 0; i < vpoles.size(); i++) + poles.SetValue(i, vpoles[i]); + + Handle(Geom_Curve) curve = new Geom_BezierCurve(poles); + return BRepBuilderAPI_MakeEdge(curve).Edge(); + }, py::arg("points"), "create Bezier curve"); + + + m.def("SplineApproximation", [](const std::vector &points, Approx_ParametrizationType approx_type, int deg_min, + int deg_max, GeomAbs_Shape continuity, double tol) { + TColgp_Array1OfPnt hpoints(0, 0); + hpoints.Resize(0, points.size() - 1, true); + for (int i = 0; i < points.size(); i++) + hpoints.SetValue(i, points[i]); + + GeomAPI_PointsToBSpline builder(hpoints, approx_type, deg_min, deg_max, continuity, tol); + return BRepBuilderAPI_MakeEdge(builder.Curve()).Edge(); + }, + py::arg("points"), + py::arg("approx_type") = Approx_ParametrizationType::Approx_ChordLength, + py::arg("deg_min") = 3, + py::arg("deg_max") = 8, + py::arg("continuity") = GeomAbs_Shape::GeomAbs_C2, + py::arg("tol")=1e-8, + R"delimiter( +Generate a piecewise continuous spline-curve approximating a list of points in 3d. + +Parameters +---------- + +points : List[gp_Pnt] or Tuple[gp_Pnt] + List (or tuple) of gp_Pnt. + +approx_type : ApproxParamType + Assumption on location of parameters wrt points. + +deg_min : int + Minimum polynomial degree of splines + +deg_max : int + Maximum polynomial degree of splines + +continuity : ShapeContinuity + Continuity requirement on the approximating surface + +tol : float + Tolerance for the distance from individual points to the approximating curve. + +)delimiter"); + + m.def("SplineInterpolation", [](const std::vector &points, bool periodic, double tol, const std::map &tangents) { + Handle(TColgp_HArray1OfPnt) hpoints = new TColgp_HArray1OfPnt(1, points.size()); + for (int i = 0; i < points.size(); i++) + hpoints->SetValue(i+1, points[i]); + + GeomAPI_Interpolate builder(hpoints, periodic, tol); + + if (tangents.size() > 0) + { + const gp_Vec dummy_vec = tangents.begin()->second; + TColgp_Array1OfVec tangent_vecs(1, points.size()); + Handle(TColStd_HArray1OfBoolean) tangent_flags = new TColStd_HArray1OfBoolean(1, points.size()); + for (int i : Range(points.size())) + { + if (tangents.count(i) > 0) + { + tangent_vecs.SetValue(i+1, tangents.at(i)); + tangent_flags->SetValue(i+1, true); + } else{ + tangent_vecs.SetValue(i+1, dummy_vec); + tangent_flags->SetValue(i+1, false); + } + } + builder.Load(tangent_vecs, tangent_flags); + } + + builder.Perform(); + return BRepBuilderAPI_MakeEdge(builder.Curve()).Edge(); + }, + py::arg("points"), + py::arg("periodic")=false, + py::arg("tol")=1e-8, + py::arg("tangents")=std::map{}, + R"delimiter( +Generate a piecewise continuous spline-curve interpolating a list of points in 3d. + +Parameters +---------- + +points : List|Tuple[gp_Pnt] + List (or tuple) of gp_Pnt + +periodic : bool + Whether the result should be periodic + +tol : float + Tolerance for the distance between points. + +tangents : Dict[int, gp_Vec] + Tangent vectors for the points indicated by the key value (0-based). + +)delimiter"); + + + m.def("SplineSurfaceApproximation", [](py::array_t pnt_array, + Approx_ParametrizationType approx_type, int deg_min, int deg_max, GeomAbs_Shape continuity, double tol, + bool periodic, double degen_tol) { + if (pnt_array.ndim() != 3) + throw Exception("`points` array must have dimension 3."); + if (pnt_array.shape(2) != 3) + throw Exception("The third dimension must have size 3."); + + auto array = py::extract>(pnt_array)(); + TColgp_Array2OfPnt points(1, pnt_array.shape(0), 1, pnt_array.shape(1)); + auto pnts_unchecked = pnt_array.unchecked<3>(); + for (int i = 0; i < pnt_array.shape(0); ++i) + for (int j = 0; j < pnt_array.shape(1); ++j) + points.SetValue(i+1, j+1, gp_Pnt(pnts_unchecked(i, j, 0), pnts_unchecked(i, j, 1), pnts_unchecked(i, j, 2))); + + GeomAPI_PointsToBSplineSurface builder; +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4 + builder.Init(points, approx_type, deg_min, deg_max, continuity, tol, periodic); +#else + if(periodic) + throw Exception("periodic not supported"); + builder.Init(points, approx_type, deg_min, deg_max, continuity, tol); +#endif + return BRepBuilderAPI_MakeFace(builder.Surface(), tol).Face(); + }, + py::arg("points"), + py::arg("approx_type") = Approx_ParametrizationType::Approx_ChordLength, + py::arg("deg_min") = 3, + py::arg("deg_max") = 8, + py::arg("continuity") = GeomAbs_Shape::GeomAbs_C2, + py::arg("tol") = 1e-3, + py::arg("periodic") = false, + py::arg("degen_tol") = 1e-8, + R"delimiter( +Generate a piecewise continuous spline-surface approximating an array of points. + +Parameters +---------- + +points : np.ndarray + Array of points coordinates. The first dimension corresponds to the first surface coordinate point + index, the second dimension to the second surface coordinate point index. The third dimension refers to physical + coordinates. Such an array can be generated with code like:: + + px, py = np.meshgrid(*[np.linspace(0, 1, N)]*2) + points = np.array([[(px[i,j], py[i,j], px[i,j]*py[i,j]**2) for j in range(N)] for i in range(N)]) + +approx_type : ApproxParamType + Assumption on location of parameters wrt points. + +deg_min : int + Minimum polynomial degree of splines + +deg_max : int + Maximum polynomial degree of splines + +continuity : ShapeContinuity + Continuity requirement on the approximating surface + +tol : float + Tolerance for the distance from individual points to the approximating surface. + +periodic : bool + Whether the result should be periodic in the first surface parameter + +degen_tol : double + Tolerance for resolution of degenerate edges + +)delimiter"); + + m.def("SplineSurfaceInterpolation", []( + py::array_t pnt_array, Approx_ParametrizationType approx_type, bool periodic, double degen_tol) { + + if (pnt_array.ndim() != 3) + throw Exception("`points` array must have dimension 3."); + if (pnt_array.shape(2) != 3) + throw Exception("The third dimension must have size 3."); + + auto array = py::extract>(pnt_array)(); + TColgp_Array2OfPnt points(1, pnt_array.shape(0), 1, pnt_array.shape(1)); + auto pnts_unchecked = pnt_array.unchecked<3>(); + for (int i = 0; i < pnt_array.shape(0); ++i) + for (int j = 0; j < pnt_array.shape(1); ++j) + points.SetValue(i+1, j+1, gp_Pnt(pnts_unchecked(i, j, 0), pnts_unchecked(i, j, 1), pnts_unchecked(i, j, 2))); + + GeomAPI_PointsToBSplineSurface builder; +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=4 + builder.Interpolate(points, approx_type, periodic); +#else + if(periodic) + throw Exception("periodic not supported"); + builder.Interpolate(points, approx_type); +#endif + return BRepBuilderAPI_MakeFace(builder.Surface(), degen_tol).Face(); + }, + py::arg("points"), + py::arg("approx_type") = Approx_ParametrizationType::Approx_ChordLength, + py::arg("periodic") = false, + py::arg("degen_tol") = 1e-8, + R"delimiter( +Generate a piecewise continuous spline-surface interpolating an array of points. + +Parameters +---------- + +points : np.ndarray + Array of points coordinates. The first dimension corresponds to the first surface coordinate point + index, the second dimension to the second surface coordinate point index. The third dimension refers to physical + coordinates. Such an array can be generated with code like:: + + px, py = np.meshgrid(*[np.linspace(0, 1, N)]*2) + points = np.array([[(px[i,j], py[i,j], px[i,j]*py[i,j]**2) for j in range(N)] for i in range(N)]) + +approx_type : ApproxParamType + Assumption on location of parameters wrt points. + +periodic : bool + Whether the result should be periodic in the first surface parameter + +degen_tol : double + Tolerance for resolution of degenerate edges + +)delimiter"); + + + m.def("MakeFillet", [](TopoDS_Shape shape, std::vector edges, double r) { + throw Exception("call 'shape.MakeFilled'"); + BRepFilletAPI_MakeFillet mkFillet(shape); + for (auto e : edges) + mkFillet.Add (r, TopoDS::Edge(e)); + return mkFillet.Shape(); + }, "deprecated, use 'shape.MakeFillet'"); + + m.def("MakeThickSolid", [](TopoDS_Shape body, std::vector facestoremove, + double offset, double tol) { + throw Exception("call 'shape.MakeThickSolid'"); + TopTools_ListOfShape faces; + for (auto f : facestoremove) + faces.Append(f); + + BRepOffsetAPI_MakeThickSolid maker; + maker.MakeThickSolidByJoin(body, faces, offset, tol); + return maker.Shape(); + }, "deprecated, use 'shape.MakeThickSolid'"); + + m.def("ThruSections", [](std::vector wires, bool solid) + { + BRepOffsetAPI_ThruSections aTool(solid); // Standard_True); + for (auto shape : wires) + aTool.AddWire(TopoDS::Wire(shape)); + aTool.CheckCompatibility(Standard_False); + return aTool.Shape(); + }, py::arg("wires"), py::arg("solid")=true, + "Building a loft. This is a shell or solid passing through a set of sections (wires). " + "First and last sections may be vertices. See https://dev.opencascade.org/doc/refman/html/class_b_rep_offset_a_p_i___thru_sections.html#details"); + + m.def("ConnectEdgesToWires", [](const vector& edges, + double tol, bool shared) + { + Handle(TopTools_HSequenceOfShape) sedges = new TopTools_HSequenceOfShape; + Handle(TopTools_HSequenceOfShape) swires = new TopTools_HSequenceOfShape; + for(auto& e : edges) + sedges->Append(e); + ShapeAnalysis_FreeBounds::ConnectEdgesToWires(sedges, tol, shared, swires); + vector wires; + for(auto& w : *swires) + wires.push_back(TopoDS::Wire(w)); + return wires; + }, py::arg("edges"), py::arg("tol")=1e-8, py::arg("shared")=true); + + py::class_> (m, "WorkPlane") + .def(py::init(), py::arg("axes")=gp_Ax3(), py::arg("pos")=gp_Ax2d()) + .def_property_readonly("cur_loc", &WorkPlane::CurrentLocation) + .def_property_readonly("cur_dir", &WorkPlane::CurrentDirection) + .def_property_readonly("start_pnt", &WorkPlane::StartPnt) + .def("MoveTo", &WorkPlane::MoveTo, py::arg("h"), py::arg("v"), "moveto (h,v), and start new wire") + .def("Move", &WorkPlane::Move, py::arg("l"), "move 'l' from current position and direction, start new wire") + .def("Direction", &WorkPlane::Direction, py::arg("dirh"), py::arg("dirv"), "reset direction to (dirh, dirv)") + // .def("LineTo", &WorkPlane::LineTo) + .def("LineTo", [](WorkPlane&wp, double x, double y, optional name) { return wp.LineTo(x, y, name); }, + py::arg("h"), py::arg("v"), py::arg("name")=nullopt, "draw line to position (h,v)") + .def("ArcTo", &WorkPlane::ArcTo) + .def("Arc", &WorkPlane::Arc, py::arg("r"), py::arg("ang"), "draw arc tangential to current pos/dir, of radius 'r' and angle 'ang', draw to the left/right if ang is positive/negative") + .def("Rotate", &WorkPlane::Rotate, py::arg("ang"), "rotate current direction by 'ang' degrees") + .def("Line", [](WorkPlane&wp,double l, optional name) { return wp.Line(l, name); }, + py::arg("l"), py::arg("name")=nullopt) + .def("Line", [](WorkPlane&wp,double h,double v, optional name) { return wp.Line(h,v,name); }, + py::arg("dx"), py::arg("dy"), py::arg("name")=nullopt) + .def("Spline", &WorkPlane::Spline, py::arg("points"), py::arg("periodic")=false, py::arg("tol")=1e-8, + py::arg("tangents")=std::map{}, py::arg("start_from_localpos")=true, + "draw spline (default: starting from current position, which is implicitly added to given list of points), tangents can be specified for each point (0 refers to starting point)") + .def("Rectangle", &WorkPlane::Rectangle, py::arg("l"), py::arg("w"), "draw rectangle, with current position as corner, use current direction") + .def("RectangleC", &WorkPlane::RectangleCentered, py::arg("l"), py::arg("w"), "draw rectangle, with current position as center, use current direction") + .def("Circle", [](WorkPlane&wp, double x, double y, double r) { + return wp.Circle(x,y,r); }, py::arg("h"), py::arg("v"), py::arg("r"), "draw circle with center (h,v) and radius 'r'") + .def("Circle", [](WorkPlane&wp, double r) { return wp.Circle(r); }, py::arg("r"), "draw circle with center in current position") + .def("NameVertex", &WorkPlane::NameVertex, py::arg("name"), "name vertex at current position") + .def("Offset", &WorkPlane::Offset, py::arg("d"), "replace current wire by offset curve of distance 'd'") + .def("Reverse", &WorkPlane::Reverse, "revert orientation of current wire") + .def("Close", &WorkPlane::Close, "draw line to start point of wire, and finish wire") + .def("Finish", &WorkPlane::Finish, "finish current wire without closing") + .def("Last", &WorkPlane::Last, "(deprecated) returns current wire") + .def("Wire", &WorkPlane::Last, "returns current wire") + .def("Face", &WorkPlane::Face, "generate and return face of all wires, resets list of wires") + .def("Wires", &WorkPlane::Wires, "returns all wires") + ; +} + +#endif // OCCGEOMETRY +#endif // NG_PYTHON diff --git a/libsrc/occ/vsocc.cpp b/libsrc/occ/vsocc.cpp index 4d806006..d4631da7 100644 --- a/libsrc/occ/vsocc.cpp +++ b/libsrc/occ/vsocc.cpp @@ -8,19 +8,18 @@ #include -#include "TopoDS_Shape.hxx" -#include "TopoDS_Vertex.hxx" -#include "TopExp_Explorer.hxx" -#include "BRep_Tool.hxx" -#include "TopoDS.hxx" -#include "gp_Pnt.hxx" -#include "Geom_Curve.hxx" -#include "Poly_Triangulation.hxx" -#include "Poly_Array1OfTriangle.hxx" -#include "TColgp_Array1OfPnt2d.hxx" -#include "Poly_Triangle.hxx" -#include "Poly_Polygon3D.hxx" -#include "Poly_PolygonOnTriangulation.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include @@ -87,7 +86,7 @@ namespace netgen // Added clipping planes to Geometry view SetClippingPlane(); - GLfloat matcoledge[] = { 0, 0, 1, 1}; + GLfloat matcoledge[] = { 0, 0, 0, 1}; GLfloat matcolhiedge[] = { 1, 0, 0, 1}; glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcoledge); @@ -463,8 +462,15 @@ namespace netgen glBegin (GL_LINE_STRIP); for (int j = 1; j <= nbnodes; j++) { - gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); - glVertex3f (p.X(), p.Y(), p.Z()); + /* +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=5 + gp_Pnt p = T -> Node(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); +#else + gp_Pnt p = T -> Nodes()(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); +#endif + */ + gp_Pnt p = T -> Node(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); + glVertex3f (p.X(), p.Y(), p.Z()); } glEnd (); } @@ -509,10 +515,17 @@ namespace netgen int nbnodes = aEdgePoly -> NbNodes(); glBegin (GL_LINE_STRIP); for (int j = 1; j <= nbnodes; j++) - { - gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); - glVertex3f (p.X(), p.Y(), p.Z()); - } + { + /* +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=5 + gp_Pnt p = T -> Node(aEdgePoly->Node(j)).Transformed(aEdgeLoc); +#else + gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); +#endif + */ + gp_Pnt p = T -> Node(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); + glVertex3f (p.X(), p.Y(), p.Z()); + } glEnd (); } @@ -535,25 +548,9 @@ namespace netgen if (!occgeometry->fvispar[i-1].IsHighlighted()) { - // Philippose - 30/01/2009 - // OpenCascade XDE Support - Quantity_Color face_colour; - // Philippose - 23/02/2009 - // Check to see if colours have been extracted first!! - // Forum bug-fox (Jean-Yves - 23/02/2009) - if(!(occgeometry->face_colours.IsNull()) - && (occgeometry->face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour))) - { - mat_col[0] = face_colour.Red(); - mat_col[1] = face_colour.Green(); - mat_col[2] = face_colour.Blue(); - } - else - { - mat_col[0] = 0.0; - mat_col[1] = 1.0; - mat_col[2] = 0.0; - } + auto c = OCCGeometry::GetProperties(face).col.value_or(Vec<4>(0,1,0,1) ); + for(auto j : Range(4)) + mat_col[j] = c[j]; } else { @@ -582,18 +579,29 @@ namespace netgen gp_Vec n; glBegin (GL_TRIANGLES); - int ntriangles = triangulation -> NbTriangles(); for (int j = 1; j <= ntriangles; j++) { - Poly_Triangle triangle = (triangulation -> Triangles())(j); + /* +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=5 + Poly_Triangle triangle = triangulation -> Triangle(j); +#else + Poly_Triangle triangle = triangulation -> Triangles()(j); +#endif + */ + Poly_Triangle triangle = triangulation -> Triangle(j); + gp_Pnt p[3]; for (int k = 1; k <= 3; k++) - p[k-1] = (triangulation -> Nodes())(triangle(k)).Transformed(loc); + p[k-1] = (triangulation -> Node(triangle(k))).Transformed(loc); for (int k = 1; k <= 3; k++) { - uv = (triangulation -> UVNodes())(triangle(k)); +#if OCC_VERSION_MAJOR>=7 && OCC_VERSION_MINOR>=5 + uv = triangulation -> UVNode(triangle(k)); +#else + uv = triangulation -> UVNodes()(triangle(k)); +#endif prop.SetParameters (uv.X(), uv.Y()); // surf->D0 (uv.X(), uv.Y(), pnt); diff --git a/libsrc/occ/vsocc.hpp b/libsrc/occ/vsocc.hpp index 3021fe7c..777028f4 100644 --- a/libsrc/occ/vsocc.hpp +++ b/libsrc/occ/vsocc.hpp @@ -12,8 +12,8 @@ namespace netgen class VisualSceneOCCGeometry : public VisualScene { - Array trilists; - Array linelists; + NgArray trilists; + NgArray linelists; int selsurf; class OCCGeometry * occgeometry; public: diff --git a/libsrc/stlgeom/CMakeLists.txt b/libsrc/stlgeom/CMakeLists.txt index 8925c828..11b23912 100644 --- a/libsrc/stlgeom/CMakeLists.txt +++ b/libsrc/stlgeom/CMakeLists.txt @@ -1,24 +1,10 @@ -add_library(stl ${NG_LIB_TYPE} +target_sources(nglib PRIVATE meshstlsurface.cpp stlgeom.cpp stlgeomchart.cpp stlgeommesh.cpp stlline.cpp stltool.cpp stltopology.cpp python_stl.cpp - ) - -if(NOT WIN32) - target_link_libraries( stl mesh ${PYTHON_LIBRARIES}) - install( TARGETS stl ${NG_INSTALL_DIR}) -endif(NOT WIN32) - -target_link_libraries( stl ngcore ) - +) if(USE_GUI) - add_library(stlvis ${NG_LIB_TYPE} - vsstl.cpp - ) - if(NOT WIN32) - target_link_libraries( stlvis stl ) - install( TARGETS stlvis ${NG_INSTALL_DIR}) - endif(NOT WIN32) + target_sources(nggui PRIVATE vsstl.cpp stlpkg.cpp) endif(USE_GUI) install(FILES diff --git a/libsrc/stlgeom/meshstlsurface.cpp b/libsrc/stlgeom/meshstlsurface.cpp index 02753fb8..231d8f10 100644 --- a/libsrc/stlgeom/meshstlsurface.cpp +++ b/libsrc/stlgeom/meshstlsurface.cpp @@ -13,20 +13,21 @@ namespace netgen { -static void STLFindEdges (STLGeometry & geom, - class Mesh & mesh) +static void STLFindEdges (STLGeometry & geom, Mesh & mesh, + const MeshingParameters& mparam, + const STLParameters& stlparam) { double h = mparam.maxh; // mark edge points: //int ngp = geom.GetNP(); - geom.RestrictLocalH(mesh, h); + geom.RestrictLocalH(mesh, h, stlparam, mparam); PushStatusF("Mesh Lines"); - Array meshlines; - Array meshpoints; + NgArray meshlines; + NgArray meshpoints; PrintMessage(3,"Mesh Lines"); @@ -105,7 +106,7 @@ static void STLFindEdges (STLGeometry & geom, << ", trig2 = " << trig2 << ", trig2b = " << trig2b << endl; - if (trig1 <= 0 || trig2 <= 0 || trig1b <= 0 || trig2b <= 0) + if (trig1 <= 0 || trig2 < 0 || trig1b <= 0 || trig2b < 0) { cout << "negative trigs, " << ", trig1 = " << trig1 @@ -176,10 +177,13 @@ static void STLFindEdges (STLGeometry & geom, mesh.AddSegment (seg); + if(trig2 != 0) + { Segment seg2; seg2[0] = p2 + PointIndex::BASE-1;; seg2[1] = p1 + PointIndex::BASE-1;; seg2.si = geom.GetTriangle(trig2).GetFaceNum(); + seg2.edgenr = i; seg2.epgeominfo[0].edgenr = i; @@ -218,8 +222,8 @@ static void STLFindEdges (STLGeometry & geom, (*testout) << "Get GeomInfo PROBLEM" << endl; } */ - mesh.AddSegment (seg2); + } } } @@ -229,18 +233,19 @@ static void STLFindEdges (STLGeometry & geom, -void STLSurfaceMeshing1 (STLGeometry & geom, class Mesh & mesh, - int retrynr); +void STLSurfaceMeshing1 (STLGeometry & geom, class Mesh & mesh, const MeshingParameters& mparam, + int retrynr, const STLParameters& stlparam); -int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) +int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh, const MeshingParameters& mparam, + const STLParameters& stlparam) { PrintFnStart("Do Surface Meshing"); geom.PrepareSurfaceMeshing(); if (mesh.GetNSeg() == 0) - STLFindEdges (geom, mesh); + STLFindEdges (geom, mesh, mparam, stlparam); int nopen; int outercnt = 20; @@ -272,11 +277,14 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) if (multithread.terminate) { return MESHING3_TERMINATE; } trialcnt++; - STLSurfaceMeshing1 (geom, mesh, trialcnt); + STLSurfaceMeshing1 (geom, mesh, mparam, trialcnt, stlparam); mesh.FindOpenSegments(); nopen = mesh.GetNOpenSegments(); + auto n_illegal_trigs = mesh.FindIllegalTrigs(); + PrintMessage (3, n_illegal_trigs, " illegal triangles"); + if (nopen) { geom.ClearMarkedSegs(); @@ -294,15 +302,14 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) geom.SetMarkedTrig(seg.geominfo[1].trignum,1); } - MeshOptimizeSTLSurface optmesh(geom); + MeshOptimize2d optmesh(mesh); optmesh.SetFaceIndex (0); optmesh.SetImproveEdges (0); optmesh.SetMetricWeight (0); mesh.CalcSurfacesOfNode(); - optmesh.EdgeSwapping (mesh, 0); - mesh.CalcSurfacesOfNode(); - optmesh.ImproveMesh (mesh, mparam); + optmesh.EdgeSwapping (0); + optmesh.ImproveMesh (mparam); } mesh.Compress(); @@ -436,8 +443,9 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) PrintMessage(5,"check overlapping"); // mesh.FindOpenElements(); // would leed to locked points - if(mesh.CheckOverlappingBoundary()) - return MESHING3_BADSURFACEMESH; + mesh.CheckOverlappingBoundary(); + // if(mesh.CheckOverlappingBoundary()) ; + // return MESHING3_BADSURFACEMESH; geom.InitMarkedTrigs(); @@ -451,28 +459,28 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) - Array refpts; - Array refh; + NgArray refpts; + NgArray refh; // was commented: - for (int i = 1; i <= mesh.GetNSE(); i++) - if (mesh.SurfaceElement(i).BadElement()) + for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) + if (mesh[sei].BadElement()) { for (int j = 1; j <= 3; j++) { - refpts.Append (mesh.Point (mesh.SurfaceElement(i).PNum(j))); + refpts.Append (mesh.Point (mesh[sei].PNum(j))); refh.Append (mesh.GetH (refpts.Last()) / 2); } - mesh.DeleteSurfaceElement(i); + mesh.Delete(sei); } // delete wrong oriented element - for (int i = 1; i <= mesh.GetNSE(); i++) + for (SurfaceElementIndex sei = 0; sei < mesh.GetNSE(); sei++) { - const Element2d & el = mesh.SurfaceElement(i); - if (!el.PNum(1)) - continue; + const Element2d & el = mesh[sei]; + if (el.IsDeleted()) continue; + if (!el.PNum(1).IsValid()) continue; Vec3d n = Cross (Vec3d (mesh.Point(el.PNum(1)), mesh.Point(el.PNum(2))), @@ -481,9 +489,9 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) Vec3d ng = geom.GetTriangle(el.GeomInfoPi(1).trignum).Normal(); if (n * ng < 0) { - refpts.Append (mesh.Point (mesh.SurfaceElement(i).PNum(1))); + refpts.Append (mesh.Point (mesh[sei].PNum(1))); refh.Append (mesh.GetH (refpts.Last()) / 2); - mesh.DeleteSurfaceElement(i); + mesh.Delete(sei); } } // end comments @@ -492,7 +500,78 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) mesh.RestrictLocalH (refpts.Get(i), refh.Get(i)); mesh.RemoveOneLayerSurfaceElements(); + // Open edge-segments will be refined ! + INDEX_2_HASHTABLE openseght (nopen+1); + for (int i = 1; i <= mesh.GetNOpenSegments(); i++) + { + const Segment & seg = mesh.GetOpenSegment (i); + INDEX_2 i2(seg[0], seg[1]); + i2.Sort(); + openseght.Set (i2, 1); + } + mesh.FindOpenSegments (); + mesh.RemoveOneLayerSurfaceElements(); + mesh.FindOpenSegments (); + int nsegold = mesh.GetNSeg(); + INDEX_2_HASHTABLE newpht(100); + for (int i = 1; i <= nsegold; i++) + { + Segment seg = mesh.LineSegment(i); + INDEX_2 i2(seg[0], seg[1]); + i2.Sort(); + if (openseght.Used (i2)) + { + // segment will be split + PrintMessage(7,"Split segment ", int(seg[0]), "-", int(seg[1])); + + Segment nseg1, nseg2; + EdgePointGeomInfo newgi; + + const EdgePointGeomInfo & gi1 = seg.epgeominfo[0]; + const EdgePointGeomInfo & gi2 = seg.epgeominfo[1]; + + newgi.dist = 0.5 * (gi1.dist + gi2.dist); + newgi.edgenr = gi1.edgenr; + int hi; + + Point3d newp; + int newpi; + + if (!newpht.Used (i2)) + { + newp = geom.GetLine (gi1.edgenr)-> + GetPointInDist (geom.GetPoints(), newgi.dist, hi); + newpi = mesh.AddPoint (newp); + newpht.Set (i2, newpi); + } + else + { + newpi = newpht.Get (i2); + newp = mesh.Point (newpi); + } + + nseg1 = seg; + nseg2 = seg; + nseg1[1] = newpi; + nseg1.epgeominfo[1] = newgi; + + nseg2[0] = newpi; + nseg2.epgeominfo[0] = newgi; + + mesh.LineSegment(i) = nseg1; + mesh.AddSegment (nseg2); + + mesh.RestrictLocalH (Center (mesh.Point(nseg1[0]), + mesh.Point(nseg1[1])), + Dist (mesh.Point(nseg1[0]), + mesh.Point(nseg1[1]))); + mesh.RestrictLocalH (Center (mesh.Point(nseg2[0]), + mesh.Point(nseg2[1])), + Dist (mesh.Point(nseg2[0]), + mesh.Point(nseg2[1]))); + } + } mesh.Compress(); mesh.FindOpenSegments (); @@ -514,6 +593,9 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) } while (nopen); + if(mesh.CheckOverlappingBoundary()) + return MESHING3_BADSURFACEMESH; + mesh.Compress(); mesh.CalcSurfacesOfNode(); @@ -526,8 +608,10 @@ int STLSurfaceMeshing (STLGeometry & geom, class Mesh & mesh) void STLSurfaceMeshing1 (STLGeometry & geom, - class Mesh & mesh, - int retrynr) + Mesh & mesh, + const MeshingParameters& mparam, + int retrynr, + const STLParameters& stlparam) { static int timer1 = NgProfiler::CreateTimer ("STL surface meshing1"); static int timer1a = NgProfiler::CreateTimer ("STL surface meshing1a"); @@ -539,7 +623,7 @@ void STLSurfaceMeshing1 (STLGeometry & geom, mesh.FindOpenSegments(); - Array spiralps(0); + NgArray spiralps(0); spiralps.SetSize(0); for (int i = 1; i <= geom.GetNP(); i++) if (geom.GetSpiralPoint(i)) @@ -549,18 +633,18 @@ void STLSurfaceMeshing1 (STLGeometry & geom, //int spfound; /* - Array meshsp(mesh.GetNP()); + NgArray meshsp(mesh.GetNP()); meshsp = 0; for (int i = 1; i <= mesh.GetNP(); i++) for (int j = 1; j <= spiralps.Size(); j++) if (Dist2(geom.GetPoint(spiralps.Get(j)), mesh.Point(i)) < 1e-20) meshsp.Elem(i) = spiralps.Get(j); - Array imeshsp; + NgArray imeshsp; for (int i = 1; i <= meshsp.Size(); i++) if (meshsp.Elem(i)) imeshsp.Append(i); */ - Array imeshsp; - Array ispiral_point; + NgArray imeshsp; + NgArray ispiral_point; for (int i = 1; i <= mesh.GetNP(); i++) { for (int j = 1; j <= spiralps.Size(); j++) @@ -577,11 +661,11 @@ void STLSurfaceMeshing1 (STLGeometry & geom, // int oldnp = mesh.GetNP(); - Array compress(mesh.GetNP()); + NgArray compress(mesh.GetNP()); compress = 0; - Array icompress; + NgArray icompress; - Array opensegsperface(mesh.GetNFD()); + NgArray opensegsperface(mesh.GetNFD()); opensegsperface = 0; for (int i = 1; i <= mesh.GetNOpenSegments(); i++) opensegsperface[mesh.GetOpenSegment(i).si]++; @@ -637,7 +721,7 @@ void STLSurfaceMeshing1 (STLGeometry & geom, } } */ - FlatArray segs = opensegments[fnr]; + NgFlatArray segs = opensegments[fnr]; for (int hi = 0; hi < segs.Size(); hi++) { int i = segs[hi]; @@ -706,7 +790,7 @@ void STLSurfaceMeshing1 (STLGeometry & geom, */ - // FlatArray segs = opensegments[fnr]; + // NgFlatArray segs = opensegments[fnr]; for (int hi = 0; hi < segs.Size(); hi++) { int i = segs[hi]; @@ -740,21 +824,21 @@ void STLSurfaceMeshing1 (STLGeometry & geom, void STLSurfaceOptimization (STLGeometry & geom, - class Mesh & mesh, - MeshingParameters & meshparam) + Mesh & mesh, + const MeshingParameters & mparam) { PrintFnStart("optimize STL Surface"); - MeshOptimizeSTLSurface optmesh(geom); + MeshOptimize2d optmesh(mesh); optmesh.SetFaceIndex (0); optmesh.SetImproveEdges (0); - optmesh.SetMetricWeight (meshparam.elsizeweight); + optmesh.SetMetricWeight (mparam.elsizeweight); - PrintMessage(5,"optimize string = ", meshparam.optimize2d, " elsizew = ", meshparam.elsizeweight); + PrintMessage(5,"optimize string = ", mparam.optimize2d, " elsizew = ", mparam.elsizeweight); - for (int i = 1; i <= meshparam.optsteps2d; i++) - for (size_t j = 1; j <= meshparam.optimize2d.length(); j++) + for (int i = 1; i <= mparam.optsteps2d; i++) + for (size_t j = 1; j <= mparam.optimize2d.length(); j++) { if (multithread.terminate) break; @@ -762,29 +846,45 @@ void STLSurfaceOptimization (STLGeometry & geom, //(*testout) << "optimize, before, step = " << meshparam.optimize2d[j-1] << mesh.Point (3679) << endl; mesh.CalcSurfacesOfNode(); - switch (meshparam.optimize2d[j-1]) + switch (mparam.optimize2d[j-1]) { case 's': { - optmesh.EdgeSwapping (mesh, 0); + optmesh.EdgeSwapping(0); break; } case 'S': { - optmesh.EdgeSwapping (mesh, 1); + optmesh.EdgeSwapping(1); break; } case 'm': { - optmesh.ImproveMesh(mesh, mparam); + optmesh.ImproveMesh(mparam); break; } case 'c': { - optmesh.CombineImprove (mesh); + optmesh.CombineImprove(); break; } } + // while(mesh.CheckOverlappingBoundary()) + // { + // for(const auto & el : mesh.SurfaceElements()) + // { + // if(el.BadElement()) + // { + // cout << "Restrict localh at el nr " << el << endl; + // for(const auto& p : el.PNums()) + // { + // const auto& pnt = mesh[p]; + // mesh.RestrictLocalH(pnt, 0.5*mesh.GetH(pnt)); + // } + // } + // } + // optmesh.SplitImprove(); + // } //(*testout) << "optimize, after, step = " << meshparam.optimize2d[j-1] << mesh.Point (3679) << endl; } @@ -800,12 +900,12 @@ void STLSurfaceOptimization (STLGeometry & geom, MeshingSTLSurface :: MeshingSTLSurface (STLGeometry & ageom, const MeshingParameters & mp) - : Meshing2(mp, ageom.GetBoundingBox()), geom(ageom) + : Meshing2(ageom, mp, ageom.GetBoundingBox()), geom(ageom) { ; } -void MeshingSTLSurface :: DefineTransformation (const Point3d & p1, const Point3d & p2, +void MeshingSTLSurface :: DefineTransformation (const Point<3> & p1, const Point<3> & p2, const PointGeomInfo * geominfo, const PointGeomInfo * geominfo2) { @@ -814,8 +914,8 @@ void MeshingSTLSurface :: DefineTransformation (const Point3d & p1, const Point3 geom.DefineTangentialPlane(p1, p2, transformationtrig); } -void MeshingSTLSurface :: TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo & gi, - Point2d & plainpoint, double h, int & zone) +void MeshingSTLSurface :: TransformToPlain (const Point<3> & locpoint, const MultiPointGeomInfo & gi, + Point<2> & plainpoint, double h, int & zone) { int trigs[10000]; @@ -831,9 +931,7 @@ void MeshingSTLSurface :: TransformToPlain (const Point3d & locpoint, const Mult // int trig = gi.trignum; // (*testout) << "locpoint = " << locpoint; - Point<2> hp2d; - geom.ToPlane (locpoint, trigs, hp2d, h, zone, 1); - plainpoint = hp2d; + geom.ToPlane (locpoint, trigs, plainpoint, h, zone, 1); // geom.ToPlane (locpoint, NULL, plainpoint, h, zone, 1); /* @@ -926,9 +1024,9 @@ IsLineVertexOnChart (const Point3d & p1, const Point3d & p2, } void MeshingSTLSurface :: -GetChartBoundary (Array & points, - Array & points3d, - Array & lines, double h) const +GetChartBoundary (NgArray> & points, + NgArray> & points3d, + NgArray & lines, double h) const { points.SetSize (0); points3d.SetSize (0); @@ -939,8 +1037,8 @@ GetChartBoundary (Array & points, -int MeshingSTLSurface :: TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, +int MeshingSTLSurface :: TransformFromPlain (const Point<2> & plainpoint, + Point<3> & locpoint, PointGeomInfo & gi, double h) { @@ -962,7 +1060,7 @@ BelongsToActiveChart (const Point3d & p, -double MeshingSTLSurface :: CalcLocalH (const Point3d & p, double gh) const +double MeshingSTLSurface :: CalcLocalH (const Point<3> & p, double gh) const { return gh; } @@ -972,195 +1070,4 @@ double MeshingSTLSurface :: Area () const return geom.Area(); } - - - - - -MeshOptimizeSTLSurface :: MeshOptimizeSTLSurface (STLGeometry & ageom) - : MeshOptimize2d(), geom(ageom) -{ - ; -} - - -void MeshOptimizeSTLSurface :: SelectSurfaceOfPoint (const Point<3> & p, - const PointGeomInfo & gi) -{ - // (*testout) << "sel char: " << gi.trignum << endl; - - geom.SelectChartOfTriangle (gi.trignum); - // geom.SelectChartOfPoint (p); -} - - -void MeshOptimizeSTLSurface :: ProjectPoint (INDEX surfind, Point<3> & p) const -{ - if (!geom.Project (p)) - { - PrintMessage(7,"project failed"); - - if (!geom.ProjectOnWholeSurface(p)) - { - PrintMessage(7, "project on whole surface failed"); - } - } - - // geometry.GetSurface(surfind)->Project (p); -} - -void MeshOptimizeSTLSurface :: ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const -{ - /* - ProjectToEdge ( geometry.GetSurface(surfind), - geometry.GetSurface(surfind2), p); - */ -} - -int MeshOptimizeSTLSurface :: CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & p3) const -{ - Point<3> hp = p3; - gi.trignum = geom.Project (hp); - - if (gi.trignum) return 1; - - return 0; - -} - -void MeshOptimizeSTLSurface :: GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const -{ - n = geom.GetChartNormalVector(); -} - - - - - - - - - - -RefinementSTLGeometry :: RefinementSTLGeometry (const STLGeometry & ageom) - : Refinement(), geom(ageom) -{ - ; -} - -RefinementSTLGeometry :: ~RefinementSTLGeometry () -{ - ; -} - -void RefinementSTLGeometry :: -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 -{ - newp = p1+secpoint*(p2-p1); - - /* - (*testout) << "surf-between: p1 = " << p1 << ", p2 = " << p2 - << ", gi = " << gi1 << " - " << gi2 << endl; - */ - - if (gi1.trignum > 0) - { - // ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum); - - Point<3> np1 = newp; - Point<3> np2 = newp; - ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum); - int tn1 = geom.Project (np1); - - ((STLGeometry&)geom).SelectChartOfTriangle (gi2.trignum); - int tn2 = geom.Project (np2); - - newgi.trignum = tn1; //urspruengliche version - newp = np1; //urspruengliche version - - if (!newgi.trignum) - { newgi.trignum = tn2; newp = np2; } - if (!newgi.trignum) newgi.trignum = gi1.trignum; - - /* - if (tn1 != 0 && tn2 != 0 && ((STLGeometry&)geom).GetAngle(tn1,tn2) < M_PI*0.05) { - newgi.trignum = tn1; - newp = np1; - } - else - { - newp = ((STLGeometry&)geom).PointBetween(p1, gi1.trignum, p2, gi2.trignum); - tn1 = ((STLGeometry&)geom).Project(newp); - newgi.trignum = tn1; - - if (!tn1) - { - newp = Center (p1, p2); - newgi.trignum = 0; - - } - } - */ - } - else - { - // (*testout) << "WARNING: PointBetween got geominfo = 0" << endl; - newp = p1+secpoint*(p2-p1); - newgi.trignum = 0; - } - - // (*testout) << "newp = " << newp << ", ngi = " << newgi << endl; -} - -void RefinementSTLGeometry :: -PointBetween (const Point<3> & p1, const Point<3> & p2, double secpoint, - int surfi1, int surfi2, - const EdgePointGeomInfo & gi1, - const EdgePointGeomInfo & gi2, - Point<3> & newp, EdgePointGeomInfo & newgi) const -{ - /* - (*testout) << "edge-between: p1 = " << p1 << ", p2 = " << p2 - << ", gi1,2 = " << gi1 << ", " << gi2 << endl; - */ - /* - newp = Center (p1, p2); - ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum); - newgi.trignum = geom.Project (newp); - */ - int hi; - newgi.dist = (1.0-secpoint) * gi1.dist + secpoint*gi2.dist; - newgi.edgenr = gi1.edgenr; - - /* - (*testout) << "p1 = " << p1 << ", p2 = " << p2 << endl; - (*testout) << "refedge: " << gi1.edgenr - << " d1 = " << gi1.dist << ", d2 = " << gi2.dist << endl; - */ - newp = geom.GetLine (gi1.edgenr)->GetPointInDist (geom.GetPoints(), newgi.dist, hi); - - // (*testout) << "newp = " << newp << endl; -} - - -void RefinementSTLGeometry :: ProjectToSurface (Point<3> & p, int surfi) const -{ - cout << "RefinementSTLGeometry :: ProjectToSurface not implemented!" << endl; -}; - - -void RefinementSTLGeometry :: ProjectToSurface (Point<3> & p, int surfi, - PointGeomInfo & gi) const -{ - ((STLGeometry&)geom).SelectChartOfTriangle (gi.trignum); - gi.trignum = geom.Project (p); - // if (!gi.trignum) - // cout << "projectSTL failed" << endl; -}; - - } diff --git a/libsrc/stlgeom/meshstlsurface.hpp b/libsrc/stlgeom/meshstlsurface.hpp index 4e678439..aad08708 100644 --- a/libsrc/stlgeom/meshstlsurface.hpp +++ b/libsrc/stlgeom/meshstlsurface.hpp @@ -27,95 +27,41 @@ public: protected: /// - virtual void DefineTransformation (const Point3d & p1, const Point3d & p2, - const PointGeomInfo * geominfo1, - const PointGeomInfo * geominfo2); + void DefineTransformation (const Point<3> & p1, const Point<3> & p2, + const PointGeomInfo * geominfo1, + const PointGeomInfo * geominfo2) override; /// - virtual void TransformToPlain (const Point3d & locpoint, const MultiPointGeomInfo & geominfo, - Point2d & plainpoint, double h, int & zone); + void TransformToPlain (const Point<3> & locpoint, const MultiPointGeomInfo & geominfo, + Point<2> & plainpoint, double h, int & zone) override; /// - virtual int TransformFromPlain (Point2d & plainpoint, - Point3d & locpoint, - PointGeomInfo & gi, - double h); + int TransformFromPlain (const Point<2>& plainpoint, + Point<3> & locpoint, + PointGeomInfo & gi, + double h) override; /// - virtual int BelongsToActiveChart (const Point3d & p, - const PointGeomInfo & gi); + int BelongsToActiveChart (const Point3d & p, + const PointGeomInfo & gi) override; /// - virtual int ComputePointGeomInfo (const Point3d & p, PointGeomInfo & gi); + int ComputePointGeomInfo (const Point3d & p, PointGeomInfo & gi) override; /// - virtual int ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, - PointGeomInfo & pgi); + int ChooseChartPointGeomInfo (const MultiPointGeomInfo & mpgi, + PointGeomInfo & pgi) override; /// - virtual int IsLineVertexOnChart (const Point3d & p1, const Point3d & p2, - int endpoint, const PointGeomInfo & gi); + int IsLineVertexOnChart (const Point3d & p1, const Point3d & p2, + int endpoint, const PointGeomInfo & gi) override; - virtual void GetChartBoundary (Array & points, - Array & poitns3d, - Array & lines, double h) const; + void GetChartBoundary (NgArray> & points, + NgArray> & poitns3d, + NgArray & lines, double h) const override; /// - virtual double CalcLocalH (const Point3d & p, double gh) const; + double CalcLocalH (const Point<3> & p, double gh) const override; /// - virtual double Area () const; + double Area () const override; }; - - -/// -class MeshOptimizeSTLSurface : public MeshOptimize2d - { - /// - STLGeometry & geom; - -public: - /// - MeshOptimizeSTLSurface (STLGeometry & ageom); - - /// - virtual void SelectSurfaceOfPoint (const Point<3> & p, - const PointGeomInfo & gi); - /// - virtual void ProjectPoint (INDEX surfind, Point<3> & p) const; - /// - virtual void ProjectPoint2 (INDEX surfind, INDEX surfind2, Point<3> & p) const; - /// - virtual int CalcPointGeomInfo(PointGeomInfo& gi, const Point<3> & p3) const; - /// - virtual void GetNormalVector(INDEX surfind, const Point<3> & p, Vec<3> & n) const; -}; - - - - -class RefinementSTLGeometry : public Refinement -{ - const STLGeometry & geom; - -public: - RefinementSTLGeometry (const STLGeometry & ageom); - virtual ~RefinementSTLGeometry (); - - 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; - - virtual void PointBetween (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; - - virtual void ProjectToSurface (Point<3> & p, int surfi) const; - virtual void ProjectToSurface (Point<3> & p, int surfi, PointGeomInfo & gi) const; -}; - - - #endif diff --git a/libsrc/stlgeom/python_stl.cpp b/libsrc/stlgeom/python_stl.cpp index 4968078e..6bda1953 100644 --- a/libsrc/stlgeom/python_stl.cpp +++ b/libsrc/stlgeom/python_stl.cpp @@ -2,24 +2,136 @@ #ifdef NG_PYTHON #include <../general/ngpython.hpp> +#include #include - -#ifdef WIN32 - #define DLL_HEADER __declspec(dllexport) -#endif +#include "../meshing/python_mesh.hpp" using namespace netgen; namespace netgen { - //extern shared_ptr mesh; + extern shared_ptr mesh; extern shared_ptr ng_geometry; } +static string stlparameter_description = R"delimiter( +STL Specific Meshing Parameters +------------------------------- -DLL_HEADER void ExportSTL(py::module & m) +yangle: float = 30. + Angle for edge detection + +contyangle: float = 20. + Edges continue if angle > contyangle + +edgecornerangle: float = 60. + Angle of geometry edge at which the mesher should set a point. + +closeedgefac: Optional[float] = 1. + Factor for meshing close edges, if None it is disabled. + +minedgelen: Optional[float] = 0.001 + Minimum edge length to be used for dividing edges to mesh points. If + None this is disabled. +)delimiter"; + +void CreateSTLParametersFromKwargs(STLParameters& stlparam, py::dict kwargs) +{ + if(kwargs.contains("yangle")) + stlparam.yangle = py::cast(kwargs.attr("pop")("yangle")); + if(kwargs.contains("contyangle")) + stlparam.contyangle = py::cast(kwargs.attr("pop")("contyangle")); + if(kwargs.contains("edgecornerangle")) + stlparam.edgecornerangle = py::cast(kwargs.attr("pop")("edgecornerangle")); + if(kwargs.contains("chartangle")) + stlparam.chartangle = py::cast(kwargs.attr("pop")("chartangle")); + if(kwargs.contains("outerchartangle")) + stlparam.outerchartangle = py::cast(kwargs.attr("pop")("outerchartangle")); + if(kwargs.contains("usesearchtree")) + stlparam.usesearchtree = py::cast(kwargs.attr("pop")("usesearchtree")); + if(kwargs.contains("atlasfac")) + { + auto val = kwargs.attr("pop")("resthatlasfac"); + if(val.is_none()) + stlparam.resthatlasenable = false; + else + { + stlparam.resthatlasenable = true; + stlparam.resthatlasfac = py::cast(val); + } + } + if(kwargs.contains("atlasminh")) + stlparam.atlasminh = py::cast(kwargs.attr("pop")("atlasminh")); + if(kwargs.contains("surfcurvfac")) + { + auto val = kwargs.attr("pop")("surfcurvfac"); + if(val.is_none()) + stlparam.resthsurfcurvenable = false; + else + { + stlparam.resthsurfcurvenable = true; + stlparam.resthsurfcurvfac = py::cast(val); + } + } + if(kwargs.contains("chartdistfac")) + { + auto val = kwargs.attr("pop")("chartdistfac"); + if(val.is_none()) + stlparam.resthchartdistenable = false; + else + { + stlparam.resthchartdistenable = true; + stlparam.resthchartdistfac = py::cast(val); + } + } + if(kwargs.contains("edgeanglefac")) + { + auto val = kwargs.attr("pop")("edgeanglefac"); + if(val.is_none()) + stlparam.resthedgeangleenable = false; + else + { + stlparam.resthedgeangleenable = true; + stlparam.resthedgeanglefac = py::cast(val); + } + } + if(kwargs.contains("surfmeshcurvfac")) + { + auto val = kwargs.attr("pop")("surfmeshcurvfac"); + if(val.is_none()) + stlparam.resthsurfmeshcurvenable = false; + else + { + stlparam.resthsurfmeshcurvenable = true; + stlparam.resthsurfmeshcurvfac = py::cast(val); + } + } + if(kwargs.contains("linelengthfac")) + { + auto val = kwargs.attr("pop")("linelengthfac"); + if(val.is_none()) + stlparam.resthlinelengthenable = false; + else + { + stlparam.resthlinelengthenable = true; + stlparam.resthlinelengthfac = py::cast(val); + } + } + if(kwargs.contains("recalc_h_opt")) + stlparam.recalc_h_opt = py::cast(kwargs.attr("pop")("recalc_h_opt")); +} + + +NGCORE_API_EXPORT void ExportSTL(py::module & m) { py::class_, NetgenGeometry> (m,"STLGeometry") .def(py::init<>()) + .def(py::init<>([](const string& filename, bool surface) + { + ifstream ist(filename); + return shared_ptr(STLGeometry::Load(ist, + surface)); + }), py::arg("filename"), py::arg("surface")=false, + py::call_guard()) .def(NGSPickle()) .def("_visualizationData", [](shared_ptr stl_geo) { @@ -70,29 +182,54 @@ DLL_HEADER void ExportSTL(py::module & m) res["max"] = MoveToNumpy(max); return res; }, py::call_guard()) + .def("GenerateMesh", [] (shared_ptr geo, + MeshingParameters* pars, + shared_ptr mesh, py::kwargs kwargs) + { + MeshingParameters mp; + STLParameters stlparam; + { py::gil_scoped_acquire aq; + if(pars) + { + auto mp_flags = pars->geometrySpecificParameters; + auto mp_kwargs = CreateDictFromFlags(mp_flags); + CreateSTLParametersFromKwargs(stlparam, mp_kwargs); + mp = *pars; + } + CreateSTLParametersFromKwargs(stlparam, kwargs); + CreateMPfromKwargs(mp, kwargs); // this will throw if any kwargs are not passed + } + if(!mesh) + { + mesh = make_shared(); + } + mesh->SetGeometry(geo); + ng_geometry = geo; + SetGlobalMesh(mesh); + auto result = STLMeshingDummy(geo.get(), mesh, mp, stlparam); + if(result != 0) + { + netgen::mesh = mesh; + throw Exception("Meshing failed!"); + } + + return mesh; + }, py::arg("mp") = nullptr, py::arg("mesh") = nullptr, + py::call_guard(), + (meshingparameter_description + stlparameter_description).c_str()) + .def("Draw", FunctionPointer + ([] (shared_ptr self) + { + ng_geometry = self; + }) + ) ; - m.def("LoadSTLGeometry", FunctionPointer([] (const string & filename) - { - ifstream ist(filename); - return shared_ptr(STLGeometry::Load(ist)); - }),py::call_guard()); - m.def("GenerateMesh", FunctionPointer([] (shared_ptr geo, MeshingParameters ¶m) - { - auto mesh = make_shared(); - SetGlobalMesh(mesh); - mesh->SetGeometry(geo); - ng_geometry = geo; - try - { - geo->GenerateMesh(mesh,param); - } - catch (NgException ex) - { - cout << "Caught NgException: " << ex.What() << endl; - } - return mesh; - }),py::call_guard()) - ; + m.def("LoadSTLGeometry", [] (const string & filename) + { + cout << "WARNING: LoadSTLGeometry is deprecated, use the STLGeometry(filename) constructor instead!" << endl; + ifstream ist(filename); + return shared_ptr(STLGeometry::Load(ist)); + },py::call_guard()); } PYBIND11_MODULE(libstl, m) { diff --git a/libsrc/stlgeom/stlgeom.cpp b/libsrc/stlgeom/stlgeom.cpp index ebb5136f..0a8496e8 100644 --- a/libsrc/stlgeom/stlgeom.cpp +++ b/libsrc/stlgeom/stlgeom.cpp @@ -1,4 +1,5 @@ #include +#include #include "stlgeom.hpp" @@ -13,15 +14,17 @@ int usechartnormal = 1; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void STLMeshing (STLGeometry & geom, - Mesh & mesh) + Mesh & mesh, + const MeshingParameters& mparam, + const STLParameters& stlpar) { geom.Clear(); - geom.BuildEdges(); - geom.MakeAtlas(mesh); + geom.BuildEdges(stlpar); + geom.MakeAtlas(mesh, mparam, stlpar); if (multithread.terminate) { return; } geom.CalcFaceNums(); geom.AddFaceEdges(); - geom.LinkEdges(); + geom.LinkEdges(stlpar); mesh.ClearFaceDescriptors(); for (int i = 1; i <= geom.GetNOFaces(); i++) @@ -42,8 +45,7 @@ void STLMeshing (STLGeometry & geom, lineendpoints(), spiralpoints(), selectedmultiedge() */ { - ref = NULL; - edgedata = new STLEdgeDataList(*this); + edgedata = make_unique(*this); externaledges.SetSize(0); Clear(); meshchart = 0; // initialize all ?? JS @@ -62,53 +64,160 @@ void STLMeshing (STLGeometry & geom, STLGeometry :: ~STLGeometry() { - for (auto p : atlas) delete p; - delete edgedata; - delete ref; + // for (auto p : atlas) delete p; + // delete edgedata; } -void STLGeometry :: Save (string filename) const +void STLGeometry :: Save (const filesystem::path & filename) const { - const char * cfilename = filename.c_str(); - if (strlen(cfilename) < 4) - throw NgException ("illegal filename"); + string ext = ToLower(filename.extension()); - if (strlen(cfilename) > 3 && - strcmp (&cfilename[strlen(cfilename)-3], "stl") == 0) + if (ext == ".stl") { - STLTopology::Save (cfilename); + STLTopology::Save (filename); + return; } - else if (strlen(cfilename) > 4 && - strcmp (&cfilename[strlen(cfilename)-4], "stlb") == 0) + else if (ext == ".stlb") { - SaveBinary (cfilename,"Binary STL Geometry"); - + SaveBinary (filename,"Binary STL Geometry"); + return; } - else if (strlen(cfilename) > 4 && - strcmp (&cfilename[strlen(cfilename)-4], "stle") == 0) + else if (ext == ".stle") { - SaveSTLE (cfilename); + SaveSTLE (filename); + return; } + + throw Exception ("Unknown target format: " + filename.string()); } +DLL_HEADER extern STLParameters stlparam; int STLGeometry :: GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) { - return STLMeshingDummy (this, mesh, mparam); + STLParameters stlpar = stlparam; + return STLMeshingDummy (this, mesh, mparam, stlpar); } - -const Refinement & STLGeometry :: GetRefinement () const +Vec<3> STLGeometry :: GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi) const { - delete ref; - ref = new RefinementSTLGeometry(*this); - // ref -> Set2dOptimizer(new MeshOptimizeSTLSurface(*this)); ??? copied from CSG - return *ref; - + if(!gi) + throw Exception("STLGeometry::GetNormal without PointGeomInfo called"); + return GetChart(GetChartNr(gi->trignum)).GetNormal(); } +bool STLGeometry :: CalcPointGeomInfo(int /*surfind*/, PointGeomInfo& gi, const Point<3> & p3) const +{ + Point<3> hp = p3; + SelectChartOfTriangle(gi.trignum); + gi.trignum = Project (hp); + + if (gi.trignum) return true; + + return false; +} + +bool STLGeometry :: ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const +{ + static std::mutex mutex_project_whole_surface; + int meshchart = GetChartNr(gi.trignum); + const STLChart& chart = GetChart(meshchart); + int trignum = chart.ProjectNormal(p); + if(trignum==0) + { + // non-thread-safe implementation + std::lock_guard guard(mutex_project_whole_surface); + PrintMessage(7,"project failed"); + SelectChartOfTriangle (gi.trignum); // needed because ProjectOnWholeSurface uses meshchartnv (the normal vector of selected chart) + trignum = ProjectOnWholeSurface(p); + if(trignum==0) + { + PrintMessage(7, "project on whole surface failed"); + return false; + } + } + return true; +} + +PointGeomInfo STLGeometry :: ProjectPoint (INDEX surfind, Point<3> & p) const +{ + throw Exception("ProjectPoint without PointGeomInfo not implemented"); +} + +void STLGeometry :: +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 +{ + newp = p1+secpoint*(p2-p1); + + /* + (*testout) << "surf-between: p1 = " << p1 << ", p2 = " << p2 + << ", gi = " << gi1 << " - " << gi2 << endl; + */ + + if (gi1.trignum > 0) + { + // ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum); + + Point<3> np1 = newp; + Point<3> np2 = newp; + auto ngi1 = gi1; + auto ngi2 = gi2; + // SelectChartOfTriangle (gi1.trignum); + int tn1 = ProjectPointGI (surfi, np1, ngi1); + + // SelectChartOfTriangle (gi2.trignum); + int tn2 = ProjectPointGI (surfi, np2, ngi2); + + newgi.trignum = tn1; //urspruengliche version + newp = np1; //urspruengliche version + + if (!newgi.trignum) + { newgi.trignum = tn2; newp = np2; } + if (!newgi.trignum) newgi.trignum = gi1.trignum; + } + else + { + // (*testout) << "WARNING: PointBetween got geominfo = 0" << endl; + newp = p1+secpoint*(p2-p1); + newgi.trignum = 0; + } +} + +void STLGeometry :: +PointBetweenEdge (const Point<3> & p1, const Point<3> & p2, double secpoint, + int surfi1, int surfi2, + const EdgePointGeomInfo & gi1, + const EdgePointGeomInfo & gi2, + Point<3> & newp, EdgePointGeomInfo & newgi) const +{ + /* + (*testout) << "edge-between: p1 = " << p1 << ", p2 = " << p2 + << ", gi1,2 = " << gi1 << ", " << gi2 << endl; + */ + /* + newp = Center (p1, p2); + ((STLGeometry&)geom).SelectChartOfTriangle (gi1.trignum); + newgi.trignum = geom.Project (newp); + */ + int hi; + newgi.dist = (1.0-secpoint) * gi1.dist + secpoint*gi2.dist; + newgi.edgenr = gi1.edgenr; + + /* + (*testout) << "p1 = " << p1 << ", p2 = " << p2 << endl; + (*testout) << "refedge: " << gi1.edgenr + << " d1 = " << gi1.dist << ", d2 = " << gi2.dist << endl; + */ + newp = GetLine (gi1.edgenr)->GetPointInDist (GetPoints(), newgi.dist, hi); + + // (*testout) << "newp = " << newp << endl; +} void STLGeometry :: STLInfo(double* data) { @@ -132,7 +241,7 @@ void STLGeometry :: STLInfo(double* data) data[7] = cons; } -void STLGeometry :: MarkNonSmoothNormals() +void STLGeometry :: MarkNonSmoothNormals(const STLParameters& stlparam) { PrintFnStart("Mark Non-Smooth Normals"); @@ -149,7 +258,7 @@ void STLGeometry :: MarkNonSmoothNormals() double dirtyangle = stlparam.yangle/180.*M_PI; int cnt = 0; - int lp1,lp2; + STLPointId lp1,lp2; for (i = 1; i <= GetNT(); i++) { for (j = 1; j <= NONeighbourTrigs(i); j++) @@ -169,13 +278,13 @@ void STLGeometry :: MarkNonSmoothNormals() } -void STLGeometry :: SmoothNormals() +void STLGeometry :: SmoothNormals(const STLParameters& stlparam) { multithread.terminate = 0; // UseExternalEdges(); - BuildEdges(); + BuildEdges(stlparam); DenseMatrix m(3), hm(3); @@ -239,7 +348,7 @@ void STLGeometry :: SmoothNormals() int nbt = 0; - int fp1,fp2; + STLPointId fp1,fp2; for (k = 1; k <= NONeighbourTrigs(i); k++) { trig.GetNeighbourPoints(GetTriangle(NeighbourTrig(i, k)),fp1,fp2); @@ -307,7 +416,7 @@ void STLGeometry :: SmoothNormals() // find critical: - Array critpairs; + NgArray critpairs; for (i = 1; i <= nt; i++) { const STLTriangle & trig = GetTriangle (i); @@ -360,7 +469,7 @@ void STLGeometry :: SmoothNormals() if (critpairs.Size()) { - Array friends; + NgArray friends; double area1 = 0, area2 = 0; for (i = 1; i <= critpairs.Size(); i++) @@ -597,7 +706,7 @@ twoint STLGeometry :: GetNearestSelectedDefinedEdge() //Point3d pestimate = GetTriangle(GetSelectTrig()).center; int i, j, en; - Array vic; + NgArray vic; GetVicinity(GetSelectTrig(),4,vic); @@ -716,7 +825,7 @@ void STLGeometry :: ImportEdges() int ne; fin >> ne; - Array > eps; + NgArray > eps; int i; Point<3> p; @@ -730,18 +839,18 @@ void STLGeometry :: ImportEdges() AddEdges(eps); } -void STLGeometry :: AddEdges(const Array >& eps) +void STLGeometry :: AddEdges(const NgArray >& eps) { int i; int ne = eps.Size()/2; - Array epsi; + NgArray epsi; Box<3> bb = GetBoundingBox(); bb.Increase(1); Point3dTree ptree (bb.PMin(), bb.PMax()); - Array pintersect; + NgArray pintersect; double gtol = GetBoundingBox().Diam()/1.E10; Point<3> p; @@ -809,9 +918,9 @@ void STLGeometry :: ImportExternalEdges(const char * filename) filter[flen] = 0; char buf[20]; - Array importpoints; - Array importlines; - Array importpnums; + NgArray importpoints; + NgArray importlines; + NgArray importpnums; while (inf.good()) { @@ -897,7 +1006,7 @@ void STLGeometry :: ImportExternalEdges(const char * filename) ebb.AddPoint (importpoints.Get(i)); PrintMessage(7,"edgep - bb: ", ebb.PMin(), " - ", ebb.PMax()); - Array pintersect; + NgArray pintersect; double gtol = GetBoundingBox().Diam()/1.E6; @@ -990,22 +1099,22 @@ void STLGeometry :: ExportEdges() } -void STLGeometry :: LoadEdgeData(const char* file) +void STLGeometry :: LoadEdgeData(const filesystem::path & filename) { StoreEdgeData(); - PrintFnStart("Load edges from file '", file, "'"); - ifstream fin(file); + PrintFnStart("Load edges from file '", filename, "'"); + ifstream fin(filename); edgedata->Read(fin); // calcedgedataanglesnew = 1; } -void STLGeometry :: SaveEdgeData(const char* file) +void STLGeometry :: SaveEdgeData(const filesystem::path & filename) { - PrintFnStart("save edges to file '", file, "'"); - ofstream fout(file); + PrintFnStart("save edges to file '", filename, "'"); + ofstream fout(filename); edgedata->Write(fout); } @@ -1240,13 +1349,13 @@ void STLGeometry :: ClearEdges() } -void STLGeometry :: STLDoctorBuildEdges() +void STLGeometry :: STLDoctorBuildEdges(const STLParameters& stlparam) { // if (!trigsconverted) {return;} ClearEdges(); meshlines.SetSize(0); - FindEdgesFromAngles(); + FindEdgesFromAngles(stlparam); } void STLGeometry :: DeleteExternalEdgeAtSelected() @@ -1385,7 +1494,7 @@ void STLGeometry :: DestroyDirtyTrigs() { for (k = i+1; k <= GetNT(); k++) { - trias.Elem(k-1) = trias.Get(k); + trias[k-1] = trias[k]; // readtrias: not longer permanent, JS // readtrias.Elem(k-1) = readtrias.Get(k); } @@ -1491,11 +1600,11 @@ void STLGeometry :: PrintSelectInfo() //int p = GetTriangle(trig).PNum(GetNodeOfSelTrig()); PrintMessage(1,"touch triangle ", GetSelectTrig() - , ", local node ", GetNodeOfSelTrig() - , " (=", GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig()), ")"); + , ", local node ", GetNodeOfSelTrig() + , " (=", int(GetTriangle(GetSelectTrig()).PNum(GetNodeOfSelTrig())), ")"); if (AtlasMade() && GetSelectTrig() >= 1 && GetSelectTrig() <= GetNT()) { - PrintMessage(1," chartnum=",GetChartNr(GetSelectTrig())); + PrintMessage(1," chartnum=", int(GetChartNr(GetSelectTrig()))); /* PointBetween(Center(Center(GetPoint(GetTriangle(270).PNum(1)), GetPoint(GetTriangle(270).PNum(2))), @@ -1513,7 +1622,7 @@ void STLGeometry :: ShowSelectedTrigChartnum() int st = GetSelectTrig(); if (st >= 1 && st <= GetNT() && AtlasMade()) - PrintMessage(1,"selected trig ", st, " has chartnumber ", GetChartNr(st)); + PrintMessage(1,"selected trig ", st, " has chartnumber ", int(GetChartNr(st))); } void STLGeometry :: ShowSelectedTrigCoords() @@ -1522,18 +1631,18 @@ void STLGeometry :: ShowSelectedTrigCoords() /* //testing!!!! - Array trigs; + NgArray trigs; GetSortedTrianglesAroundPoint(GetTriangle(st).PNum(GetNodeOfSelTrig()),st,trigs); */ if (st >= 1 && st <= GetNT()) { PrintMessage(1, "coordinates of selected trig ", st, ":"); - PrintMessage(1, " p1 = ", GetTriangle(st).PNum(1), " = ", + PrintMessage(1, " p1 = ", int(GetTriangle(st).PNum(1)), " = ", Point3d (GetPoint(GetTriangle(st).PNum(1)))); - PrintMessage(1, " p2 = ", GetTriangle(st).PNum(2), " = ", + PrintMessage(1, " p2 = ", int(GetTriangle(st).PNum(2)), " = ", Point3d (GetPoint(GetTriangle(st).PNum(2)))); - PrintMessage(1, " p3 = ", GetTriangle(st).PNum(3), " = ", + PrintMessage(1, " p3 = ", int(GetTriangle(st).PNum(3)), " = ", Point3d (GetPoint(GetTriangle(st).PNum(3)))); } } @@ -1605,7 +1714,7 @@ void STLGeometry :: NeighbourAnglesOfSelectedTrig() PrintMessage(1,"Angle to triangle ", st, ":"); for (i = 1; i <= NONeighbourTrigs(st); i++) { - PrintMessage(1," triangle ", NeighbourTrig(st,i), ": angle = ", + PrintMessage(1," triangle ", int(NeighbourTrig(st,i)), ": angle = ", 180./M_PI*GetAngle(st, NeighbourTrig(st,i)), "°", ", calculated = ", 180./M_PI*Angle(GetTriangle(st).GeomNormal(points), GetTriangle(NeighbourTrig(st,i)).GeomNormal(points)), "°"); @@ -1613,11 +1722,11 @@ void STLGeometry :: NeighbourAnglesOfSelectedTrig() } } -void STLGeometry :: GetVicinity(int starttrig, int size, Array& vic) +void STLGeometry :: GetVicinity(int starttrig, int size, NgArray& vic) { if (starttrig == 0 || starttrig > GetNT()) {return;} - Array vicarray; + NgArray vicarray; vicarray.SetSize(GetNT()); int i; @@ -1630,9 +1739,9 @@ void STLGeometry :: GetVicinity(int starttrig, int size, Array& vic) int j = 0,k; - Array list1; + NgArray list1; list1.SetSize(0); - Array list2; + NgArray list2; list2.SetSize(0); list1.Append(starttrig); @@ -1684,9 +1793,9 @@ void STLGeometry :: CalcVicinity(int starttrig) int j = 0,k; - Array list1; + NgArray list1; list1.SetSize(0); - Array list2; + NgArray list2; list2.SetSize(0); list1.Append(starttrig); @@ -1737,7 +1846,7 @@ void STLGeometry :: InitMarkedTrigs() } } -void STLGeometry :: MarkDirtyTrigs() +void STLGeometry :: MarkDirtyTrigs(const STLParameters& stlparam) { PrintFnStart("mark dirty trigs"); int i,j; @@ -1799,7 +1908,7 @@ double STLGeometry :: CalcTrigBadness(int i) { int j; double maxbadness = 0; - int ap1, ap2; + STLPointId ap1, ap2; for (j = 1; j <= NONeighbourTrigs(i); j++) { GetTriangle(i).GetNeighbourPoints(GetTriangle(NeighbourTrig(i,j)), ap1, ap2); @@ -1813,12 +1922,12 @@ double STLGeometry :: CalcTrigBadness(int i) } -void STLGeometry :: GeomSmoothRevertedTrigs() +void STLGeometry :: GeomSmoothRevertedTrigs(const STLParameters& stlparam) { //double revertedangle = stldoctor.smoothangle/180.*M_PI; double fact = stldoctor.dirtytrigfact; - MarkRevertedTrigs(); + MarkRevertedTrigs(stlparam); int i, j, k, l, p; @@ -1860,13 +1969,13 @@ void STLGeometry :: GeomSmoothRevertedTrigs() } } } - MarkRevertedTrigs(); + MarkRevertedTrigs(stlparam); } -void STLGeometry :: MarkRevertedTrigs() +void STLGeometry :: MarkRevertedTrigs(const STLParameters& stlparam) { int i,j; - if (edgesperpoint.Size() != GetNP()) {BuildEdges();} + if (edgesperpoint.Size() != GetNP()) {BuildEdges(stlparam);} PrintFnStart("mark reverted trigs"); @@ -1876,7 +1985,7 @@ void STLGeometry :: MarkRevertedTrigs() double revertedangle = stldoctor.smoothangle/180.*M_PI; int cnt = 0; - int ap1, ap2; + STLPointId ap1, ap2; for (i = 1; i <= GetNT(); i++) { found = 0; @@ -1906,15 +2015,15 @@ void STLGeometry :: MarkRevertedTrigs() } -void STLGeometry :: SmoothDirtyTrigs() +void STLGeometry :: SmoothDirtyTrigs(const STLParameters& stlparam) { PrintFnStart("smooth dirty trigs"); - MarkDirtyTrigs(); + MarkDirtyTrigs(stlparam); int i,j; int changed = 1; - int ap1, ap2; + STLPointId ap1, ap2; while (changed) { @@ -1953,7 +2062,7 @@ void STLGeometry :: SmoothDirtyTrigs() calcedgedataanglesnew = 1; - MarkDirtyTrigs(); + MarkDirtyTrigs(stlparam); int cnt = 0; for (i = 1; i <= GetNT(); i++) @@ -2033,7 +2142,7 @@ double STLGeometry :: GetGeomAngle(int t1, int t2) } -void STLGeometry :: InitSTLGeometry(const Array & readtrias) +void STLGeometry :: InitSTLGeometry(const NgArray & readtrias) { PrintFnStart("Init STL Geometry"); STLTopology::InitSTLGeometry(readtrias); @@ -2045,7 +2154,7 @@ void STLGeometry :: InitSTLGeometry(const Array & readtrias) int np = GetNP(); PrintMessage(5,"NO points= ", GetNP()); normals.SetSize(GetNP()); - Array normal_cnt(GetNP()); // counts number of added normals in a point + NgArray normal_cnt(GetNP()); // counts number of added normals in a point for (i = 1; i <= np; i++) { @@ -2144,59 +2253,59 @@ int STLGeometry :: CheckGeometryOverlapping() mutex inters_mutex; ParallelFor( 1, GetNT()+1, [&] (int first, int next) - { - Array inters; - for (int i=first; i inters; + for (int i=first; i tpmin = tri.box.PMin(); - Point<3> tpmax = tri.box.PMax(); + Point<3> tpmin = tri.box.PMin(); + Point<3> tpmax = tri.box.PMax(); - setree.GetIntersecting (tpmin, tpmax, inters); + setree.GetIntersecting (tpmin, tpmax, inters); - for (int j = 1; j <= inters.Size(); j++) - { - const STLTriangle & tri2 = GetTriangle(inters.Get(j)); + for (int j = 1; j <= inters.Size(); j++) + { + const STLTriangle & tri2 = GetTriangle(inters.Get(j)); - const Point<3> *trip1[3], *trip2[3]; - Point<3> hptri1[3], hptri2[3]; - /* - for (k = 1; k <= 3; k++) - { - trip1[k-1] = &GetPoint (tri.PNum(k)); - trip2[k-1] = &GetPoint (tri2.PNum(k)); - } - */ + const Point<3> *trip1[3], *trip2[3]; + Point<3> hptri1[3], hptri2[3]; + /* + for (k = 1; k <= 3; k++) + { + trip1[k-1] = &GetPoint (tri.PNum(k)); + trip2[k-1] = &GetPoint (tri2.PNum(k)); + } + */ - for (int k = 0; k < 3; k++) - { - hptri1[k] = GetPoint (tri[k]); - hptri2[k] = GetPoint (tri2[k]); - trip1[k] = &hptri1[k]; - trip2[k] = &hptri2[k]; - } + for (int k = 0; k < 3; k++) + { + hptri1[k] = GetPoint (tri[k]); + hptri2[k] = GetPoint (tri2[k]); + trip1[k] = &hptri1[k]; + trip2[k] = &hptri2[k]; + } - if (IntersectTriangleTriangle (&trip1[0], &trip2[0])) - { - lock_guard guard(inters_mutex); - { - oltrigs++; - PrintMessage(5,"Intersecting Triangles: trig ",i," with ",inters.Get(j),"!"); - SetMarkedTrig(i, 1); - SetMarkedTrig(inters.Get(j), 1); - } - } - } - } - }); + if (IntersectTriangleTriangle (&trip1[0], &trip2[0])) + { + lock_guard guard(inters_mutex); + { + oltrigs++; + PrintMessage(5,"Intersecting Triangles: trig ",i," with ",inters.Get(j),"!"); + SetMarkedTrig(i, 1); + SetMarkedTrig(inters.Get(j), 1); + } + } + } + } + }); } PrintMessage(3,"Check overlapping geometry ... ", oltrigs, " triangles overlap"); return oltrigs; } /* -void STLGeometry :: InitSTLGeometry() -{ + void STLGeometry :: InitSTLGeometry() + { STLTopology::InitSTLGeometry(); int i, j, k; @@ -2208,91 +2317,91 @@ void STLGeometry :: InitSTLGeometry() points.SetSize(0); normals.SetSize(0); - Array normal_cnt; // counts number of added normals in a point + NgArray normal_cnt; // counts number of added normals in a point Box3d bb (GetBoundingBox().PMin() + Vec3d (-1,-1,-1), - GetBoundingBox().PMax() + Vec3d (1, 1, 1)); + GetBoundingBox().PMax() + Vec3d (1, 1, 1)); Point3dTree pointtree (bb.PMin(), - bb.PMax()); - Array pintersect; + bb.PMax()); + NgArray pintersect; double gtol = GetBoundingBox().CalcDiam()/geometry_tol_fact; for(i = 1; i <= GetReadNT(); i++) - { - //if (i%500==499) {(*mycout) << (double)i/(double)GetReadNT()*100. << "%" << endl;} + { + //if (i%500==499) {(*mycout) << (double)i/(double)GetReadNT()*100. << "%" << endl;} - STLReadTriangle t = GetReadTriangle(i); - STLTriangle st; - Vec3d n = t.normal; + STLReadTriangle t = GetReadTriangle(i); + STLTriangle st; + Vec3d n = t.normal; - for (k = 0; k < 3; k++) - { - Point3d p = t.pts[k]; + for (k = 0; k < 3; k++) + { + Point3d p = t.pts[k]; - Point3d pmin = p - Vec3d (gtol, gtol, gtol); - Point3d pmax = p + Vec3d (gtol, gtol, gtol); + Point3d pmin = p - Vec3d (gtol, gtol, gtol); + Point3d pmax = p + Vec3d (gtol, gtol, gtol); - pointtree.GetIntersecting (pmin, pmax, pintersect); + pointtree.GetIntersecting (pmin, pmax, pintersect); - if (pintersect.Size() > 1) - (*mycout) << "found too much " << char(7) << endl; - int foundpos = 0; - if (pintersect.Size()) - foundpos = pintersect.Get(1); + if (pintersect.Size() > 1) + (*mycout) << "found too much " << char(7) << endl; + int foundpos = 0; + if (pintersect.Size()) + foundpos = pintersect.Get(1); - if (foundpos) - { - normal_cnt[foundpos]++; - SetNormal(foundpos,GetNormal(foundpos)+n); - // (*testout) << "found p " << p << endl; - } - else - { - foundpos = AddPoint(p); - AddNormal(n); - normal_cnt.Append(1); + if (foundpos) + { + normal_cnt[foundpos]++; + SetNormal(foundpos,GetNormal(foundpos)+n); + // (*testout) << "found p " << p << endl; + } + else + { + foundpos = AddPoint(p); + AddNormal(n); + normal_cnt.Append(1); - pointtree.Insert (p, foundpos); - } - //(*mycout) << "foundpos=" << foundpos << endl; - st.pts[k] = foundpos; - } + pointtree.Insert (p, foundpos); + } + //(*mycout) << "foundpos=" << foundpos << endl; + st.pts[k] = foundpos; + } - if ( (st.pts[0] == st.pts[1]) || - (st.pts[0] == st.pts[2]) || - (st.pts[1] == st.pts[2]) ) - { - (*mycout) << "ERROR: STL Triangle degenerated" << endl; - } - else - { - // do not add ? js - AddTriangle(st); - } - //(*mycout) << "TRIG" << i << " = " << st << endl; + if ( (st.pts[0] == st.pts[1]) || + (st.pts[0] == st.pts[2]) || + (st.pts[1] == st.pts[2]) ) + { + (*mycout) << "ERROR: STL Triangle degenerated" << endl; + } + else + { + // do not add ? js + AddTriangle(st); + } + //(*mycout) << "TRIG" << i << " = " << st << endl; - } + } //normal the normals for (i = 1; i <= GetNP(); i++) - { - SetNormal(i,1./(double)normal_cnt[i]*GetNormal(i)); - } + { + SetNormal(i,1./(double)normal_cnt[i]*GetNormal(i)); + } trigsconverted = 1; vicinity.SetSize(GetNT()); markedtrigs.SetSize(GetNT()); for (i = 1; i <= GetNT(); i++) - { - markedtrigs.Elem(i) = 0; - vicinity.Elem(i) = 1; - } + { + markedtrigs.Elem(i) = 0; + vicinity.Elem(i) = 1; + } ha_points.SetSize(GetNP()); for (i = 1; i <= GetNP(); i++) - ha_points.Elem(i) = 0; + ha_points.Elem(i) = 0; calcedgedataanglesnew = 0; edgedatastored = 0; @@ -2304,7 +2413,7 @@ void STLGeometry :: InitSTLGeometry() ClearLineEndPoints(); (*mycout) << "done" << endl; -} + } */ @@ -2360,12 +2469,12 @@ int STLGeometry :: IsEdgeNum(int ap1, int ap2) } -void STLGeometry :: BuildEdges() +void STLGeometry :: BuildEdges(const STLParameters& stlparam) { //PrintFnStart("build edges"); edges.SetSize(0); meshlines.SetSize(0); - FindEdgesFromAngles(); + FindEdgesFromAngles(stlparam); } void STLGeometry :: UseExternalEdges() @@ -2397,13 +2506,13 @@ void STLGeometry :: StoreEdgeData() // put stlgeom-edgedata to stltopology edgedata /* - int i; - for (i = 1; i <= GetNTE(); i++) + int i; + for (i = 1; i <= GetNTE(); i++) { - const STLTopEdge & topedge = GetTopEdge (i); - int ednum = edgedata->GetEdgeNum (topedge.PNum(1), - topedge.PNum(2)); - topedges.Elem(i).SetStatus (edgedata->Get (ednum).status); + const STLTopEdge & topedge = GetTopEdge (i); + int ednum = edgedata->GetEdgeNum (topedge.PNum(1), + topedge.PNum(2)); + topedges.Elem(i).SetStatus (edgedata->Get (ednum).status); } */ } @@ -2420,7 +2529,7 @@ void STLGeometry :: CalcEdgeData() { PushStatus("Calc Edge Data"); - int np1, np2; + STLPointId np1, np2; int ecnt = 0; edgedata->SetSize(GetNT()/2*3); @@ -2469,7 +2578,7 @@ void STLGeometry :: CalcEdgeDataAngles() for (int i = 1; i <= GetNTE(); i++) { STLTopEdge & edge = GetTopEdge (i); - double cosang = + double cosang = edge.TrigNum(2) == 0 ? 1. : GetTriangle(edge.TrigNum(1)).Normal() * GetTriangle(edge.TrigNum(2)).Normal(); edge.SetCosAngle (cosang); @@ -2487,7 +2596,7 @@ void STLGeometry :: CalcEdgeDataAngles() PrintMessage (5,"calc edge data angles ... done"); } -void STLGeometry :: FindEdgesFromAngles() +void STLGeometry :: FindEdgesFromAngles(const STLParameters& stlparam) { // PrintFnStart("find edges from angles"); @@ -2502,6 +2611,8 @@ void STLGeometry :: FindEdgesFromAngles() for (int i = 1; i <= edgedata->Size(); i++) { STLTopEdge & sed = edgedata->Elem(i); + if(sed.TrigNum(2) == 0) + sed.SetStatus(ED_CONFIRMED); if (sed.GetStatus() == ED_CANDIDATE || sed.GetStatus() == ED_UNDEFINED) { @@ -2647,8 +2758,8 @@ void STLGeometry :: AddFaceEdges() //für Kugel eine STLLine hinzufügen (Vorteil: verfeinerbar, unabhängig von Auflösung der Geometrie!!!): //Grenze von 1. gefundener chart - Array edgecnt; - Array chartindex; + NgArray edgecnt; + NgArray chartindex; edgecnt.SetSize(GetNOFaces()); chartindex.SetSize(GetNOFaces()); @@ -2674,7 +2785,7 @@ void STLGeometry :: AddFaceEdges() } int changed = 0; - int ap1, ap2; + STLPointId ap1, ap2; for (int i = 1; i <= GetNOFaces(); i++) { if (!edgecnt.Get(i)) @@ -2685,10 +2796,10 @@ void STLGeometry :: AddFaceEdges() double maxlen = -1; for (int j = 1; j <= c.GetNChartT(); j++) { - const STLTriangle& t1 = GetTriangle(c.GetChartTrig(j)); + const STLTriangle& t1 = GetTriangle(c.GetChartTrig1(j)); for (int k = 1; k <= 3; k++) { - int nt = NeighbourTrig(c.GetChartTrig(j),k); + int nt = NeighbourTrig(c.GetChartTrig1(j),k); if (GetChartNr(nt) != chartindex.Get(i)) { t1.GetNeighbourPoints(GetTriangle(nt),ap1,ap2); @@ -2714,7 +2825,7 @@ void STLGeometry :: AddFaceEdges() } -void STLGeometry :: LinkEdges() +void STLGeometry :: LinkEdges(const STLParameters& stlparam) { PushStatusF("Link Edges"); PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree"); @@ -2728,7 +2839,7 @@ void STLGeometry :: LinkEdges() int rev(0); //indicates, that edge is inserted reverse //worked edges - Array we(GetNE()); + NgArray we(GetNE()); //setlineendpoints; wenn 180°, dann keine endpunkte //nur punkte mit 2 edges kommen in frage, da bei mehr oder weniger punkten ohnehin ein meshpoint hinkommt @@ -2919,7 +3030,7 @@ int STLGeometry :: GetNOBodys() int i, k, nnt; int bodycnt = 0; - Array bodynum(GetNT()); + NgArray bodynum(GetNT()); for (i = 1; i <= GetNT(); i++) bodynum.Elem(i)=0; @@ -2936,8 +3047,8 @@ int STLGeometry :: GetNOBodys() } } //add all triangles around starttriangle, which is reachable without going over an edge - Array todolist; - Array nextlist; + NgArray todolist; + NgArray nextlist; bodycnt++; markedtrigs1++; bodynum.Elem(starttrig) = bodycnt; @@ -2998,13 +3109,13 @@ void STLGeometry :: CalcFaceNums() } } //add all triangles around starttriangle, which is reachable without going over an edge - Array todolist; - Array nextlist; + NgArray todolist; + NgArray nextlist; facecnt++; markedtrigs1++; GetTriangle(starttrig).SetFaceNum(facecnt); todolist.Append(starttrig); - int ap1, ap2; + STLPointId ap1, ap2; while(todolist.Size()) { @@ -3078,14 +3189,14 @@ void STLGeometry :: BuildSmoothEdges () ng1 = trig.GeomNormal(points); ng1 /= (ng1.Length() + 1e-24); - for (int j = 1; j <= 3; j++) + for (int j = 1; j <= NONeighbourTrigs(i); j++) { int nbt = NeighbourTrig (i, j); ng2 = GetTriangle(nbt).GeomNormal(points); ng2 /= (ng2.Length() + 1e-24); - int pi1, pi2; + STLPointId pi1, pi2; trig.GetNeighbourPoints(GetTriangle(nbt), pi1, pi2); if (!IsEdge(pi1,pi2)) @@ -3107,10 +3218,10 @@ void STLGeometry :: BuildSmoothEdges () -int STLGeometry :: IsSmoothEdge (int pi1, int pi2) const +bool STLGeometry :: IsSmoothEdge (int pi1, int pi2) const { if (!smoothedges) - return 0; + return false; INDEX_2 i2(pi1, pi2); i2.Sort(); return smoothedges->Used (i2); @@ -3120,7 +3231,7 @@ int STLGeometry :: IsSmoothEdge (int pi1, int pi2) const /* //function is not used now -int IsInArray(int n, const Array& ia) +int IsInArray(int n, const NgArray& ia) { int i; for (i = 1; i <= ia.Size(); i++) @@ -3131,30 +3242,30 @@ int IsInArray(int n, const Array& ia) } */ -void STLGeometry :: AddConeAndSpiralEdges() +void STLGeometry :: AddConeAndSpiralEdges(const STLParameters& stlparam) { PrintMessage(5,"have now ", GetNE(), " edges with yellow angle = ", stlparam.yangle, " degree"); - + PrintFnStart("AddConeAndSpiralEdges"); - int i,j,k,n; + // int i,j,k,n; // int changed = 0; //check edges, where inner chart and no outer chart come together without an edge - int np1, np2, nt; + STLPointId np1, np2; int cnt = 0; - for (i = 1; i <= GetNOCharts(); i++) + for (ChartId i = 1; i <= GetNOCharts(); i++) { STLChart& chart = GetChart(i); - for (j = 1; j <= chart.GetNChartT(); j++) + for (int j = 1; j <= chart.GetNChartT(); j++) { - int t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(t); k++) { - nt = NeighbourTrig(t,k); + STLTrigId nt = NeighbourTrig(t,k); if (GetChartNr(nt) != i && !TrigIsInOC(nt,i)) { tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); @@ -3167,8 +3278,8 @@ void STLGeometry :: AddConeAndSpiralEdges() AddEdgePP(np1,edgenum); AddEdgePP(np2,edgenum); //changed = 1; - PrintWarning("Found a spiral like structure: chart=", i, - ", trig=", t, ", p1=", np1, ", p2=", np2); + PrintWarning("Found a spiral like structure: chart=", int(i), + ", trig=", int(t), ", p1=", int(np1), ", p2=", int(np2)); cnt++; } } @@ -3183,17 +3294,14 @@ void STLGeometry :: AddConeAndSpiralEdges() cnt = 0; int edgecnt = 0; - Array trigsaroundp; - Array chartpointchecked; //gets number of chart, if in this chart already checked - chartpointchecked.SetSize(GetNP()); + Array trigsaroundp; + NgArray chartpointchecked(GetNP()); //gets number of chart, if in this chart already checked + chartpointchecked = 0; - for (i = 1; i <= GetNP(); i++) - { - chartpointchecked.Elem(i) = 0; - } int onoc, notonoc, tpp, pn; - int ap1, ap2, tn1, tn2, l, problem; + STLPointId ap1, ap2; + int tn1, tn2, l, problem; if (!stldoctor.conecheck) {PrintWarning("++++++++++++ \ncone checking deactivated by user!!!!!\n+++++++++++++++"); return ;} @@ -3201,26 +3309,26 @@ void STLGeometry :: AddConeAndSpiralEdges() int addedges = 0; - for (i = 1; i <= GetNOCharts(); i++) + for (ChartId i = 1; i <= GetNOCharts(); i++) { SetThreadPercent((double)i/(double)GetNOCharts()*100.); if (multithread.terminate) {PopStatus();return;} STLChart& chart = GetChart(i); - for (j = 1; j <= chart.GetNChartT(); j++) + for (int j = 1; j <= chart.GetNChartT(); j++) { - int t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= 3; k++) { pn = tt.PNum(k); if (chartpointchecked.Get(pn) == i) {continue;} int checkpoint = 0; - for (n = 1; n <= trigsperpoint.EntrySize(pn); n++) + for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++) { if (trigsperpoint.Get(pn,n) != t && GetChartNr(trigsperpoint.Get(pn,n)) != i && @@ -3236,10 +3344,10 @@ void STLGeometry :: AddConeAndSpiralEdges() trigsaroundp.Append(t); problem = 0; - for (l = 2; l <= trigsaroundp.Size()-1; l++) + for (int l = 2; l <= trigsaroundp.Size()-1; l++) { - tn1 = trigsaroundp.Get(l-1); - tn2 = trigsaroundp.Get(l); + tn1 = trigsaroundp[l-2]; + tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); @@ -3250,10 +3358,10 @@ void STLGeometry :: AddConeAndSpiralEdges() if (problem) { - for (l = 2; l <= trigsaroundp.Size()-1; l++) + for (int l = 2; l <= trigsaroundp.Size()-1; l++) { - tn1 = trigsaroundp.Get(l-1); - tn2 = trigsaroundp.Get(l); + tn1 = trigsaroundp[l-2]; + tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); @@ -3283,10 +3391,10 @@ void STLGeometry :: AddConeAndSpiralEdges() } //backwards: problem = 0; - for (l = trigsaroundp.Size()-1; l >= 2; l--) + for (int l = trigsaroundp.Size()-1; l >= 2; l--) { - tn1 = trigsaroundp.Get(l+1); - tn2 = trigsaroundp.Get(l); + tn1 = trigsaroundp[l]; + tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); @@ -3295,10 +3403,10 @@ void STLGeometry :: AddConeAndSpiralEdges() if (GetChartNr(tn2) != i && !TrigIsInOC(tn2,i)) {problem = 1;} } if (problem) - for (l = trigsaroundp.Size()-1; l >= 2; l--) + for (int l = trigsaroundp.Size()-1; l >= 2; l--) { - tn1 = trigsaroundp.Get(l+1); - tn2 = trigsaroundp.Get(l); + tn1 = trigsaroundp[l]; + tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); @@ -3354,22 +3462,22 @@ void STLGeometry :: AddConeAndSpiralEdges() //search points where inner chart and outer chart and "no chart" trig come together at edge-point PrintMessage(7,"search for special chart points"); - for (i = 1; i <= GetNOCharts(); i++) + for (ChartId i = 1; i <= GetNOCharts(); i++) { STLChart& chart = GetChart(i); - for (j = 1; j <= chart.GetNChartT(); j++) + for (int j = 1; j <= chart.GetNChartT(); j++) { - int t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= 3; k++) { pn = tt.PNum(k); if (GetNEPP(pn) == 2) { onoc = 0; notonoc = 0; - for (n = 1; n <= trigsperpoint.EntrySize(pn); n++) + for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++) { tpp = trigsperpoint.Get(pn,n); if (tpp != t && GetChartNr(tpp) != i) @@ -3386,11 +3494,11 @@ void STLGeometry :: AddConeAndSpiralEdges() int thereNotOC = 0; for (l = 2; l <= trigsaroundp.Size(); l++) { - GetTriangle(trigsaroundp.Get(l-1)). - GetNeighbourPoints(GetTriangle(trigsaroundp.Get(l)), ap1, ap2); + GetTriangle(trigsaroundp[l-2]). + GetNeighbourPoints(GetTriangle(trigsaroundp[l-1]), ap1, ap2); if (IsEdge(ap1,ap2)) {here = (here+1)%2;} - if (!here && TrigIsInOC(trigsaroundp.Get(l),i)) {thereOC = 1;} - if (!here && !TrigIsInOC(trigsaroundp.Get(l),i)) {thereNotOC = 1;} + if (!here && TrigIsInOC(trigsaroundp[l-1],i)) {thereOC = 1;} + if (!here && !TrigIsInOC(trigsaroundp[l-1],i)) {thereNotOC = 1;} } if (thereOC && thereNotOC) { @@ -3407,31 +3515,31 @@ void STLGeometry :: AddConeAndSpiralEdges() } //get trigs at a point, started with starttrig, then every left -void STLGeometry :: GetSortedTrianglesAroundPoint(int p, int starttrig, Array& trigs) +void STLGeometry :: GetSortedTrianglesAroundPoint(STLPointId p, STLTrigId starttrig, Array& trigs) { - int acttrig = starttrig; + STLTrigId acttrig = starttrig; trigs.SetAllocSize(trigsperpoint.EntrySize(p)); trigs.SetSize(0); trigs.Append(acttrig); - int i, j, t, ap1, ap2, locindex1(0), locindex2(0); - + int locindex1(0), locindex2(0); //(*mycout) << "trigs around point " << p << endl; int end = 0; while (!end) { const STLTriangle& at = GetTriangle(acttrig); - for (i = 1; i <= trigsperpoint.EntrySize(p); i++) + for (int i = 1; i <= trigsperpoint.EntrySize(p); i++) { - t = trigsperpoint.Get(p,i); + STLTrigId t = trigsperpoint.Get(p,i); const STLTriangle& nt = GetTriangle(t); if (at.IsNeighbourFrom(nt)) { + STLPointId ap1, ap2; at.GetNeighbourPoints(nt, ap1, ap2); if (ap2 == p) {Swap(ap1,ap2);} if (ap1 != p) {PrintSysError("In GetSortedTrianglesAroundPoint!!!");} - for (j = 1; j <= 3; j++) + for (int j = 1; j <= 3; j++) { if (at.PNum(j) == ap1) {locindex1 = j;}; if (at.PNum(j) == ap2) {locindex2 = j;}; @@ -3520,43 +3628,112 @@ void STLGeometry :: SmoothGeometry () } } +void STLGeometry :: WriteChartToFile( ChartId chartnumber, filesystem::path filename ) +{ + PrintMessage(1,"write chart ", int(chartnumber), " to ", filename); + Array trignums; + + if (chartnumber >= 1 && chartnumber <= GetNOCharts()) + { + const STLChart& chart = GetChart(chartnumber); + + for (int j = 1; j <= chart.GetNChartT(); j++) + trignums.Append(chart.GetChartTrig1(j)); + + for (int j = 1; j <= chart.GetNOuterT(); j++) + trignums.Append(chart.GetOuterTrig1(j)); + + QuickSort(trignums); + STLGeometry geo; + NgArray readtrigs; + const auto & first_trig = GetTriangle(chart.GetChartTrig1(1)); + auto normal = first_trig.Normal(); + Box<3> box{Box<3>::EMPTY_BOX}; + + for(auto j : trignums) + { + const auto& trig = GetTriangle(j); + Point<3> pts[3]; + for(auto k : Range(3)) + { + pts[k] = GetPoint(trig[k]); + box.Add(pts[k]); + } + Vec3d normal = Cross( pts[1]-pts[0], pts[2]-pts[0] ); + readtrigs.Append(STLReadTriangle(pts, trig.Normal())); + } + auto dist = box.PMax() - box.PMin(); + auto extra_point = GetPoint(first_trig[0]) - dist.Length()*normal; + + NgArray acttrigs(GetNT()); + acttrigs = -1; + for (int j = 1; j <= chart.GetNT(); j++) + acttrigs.Elem(chart.GetTrig1(j)) = chartnumber; + + for (int j = 1; j <= chart.GetNT(); j++) + { + auto t = chart.GetTrig1(j); + const auto & tt = GetTriangle(t); + for (int k = 1; k <= 3; k++) + { + int nt = NeighbourTrig(t,k); + if (acttrigs.Get(nt) != chartnumber) + { + STLPointId np1, np2; + tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); + + Point<3> pts[3]; + pts[0] = GetPoint(np2); + pts[1] = GetPoint(np1); + pts[2] = extra_point; + Vec3d normal = -Cross( pts[2]-pts[0], pts[1]-pts[0] ); + readtrigs.Append(STLReadTriangle(pts, normal)); + } + } + } + + geo.InitSTLGeometry(readtrigs); + geo.Save(filename); + } +} + class STLGeometryRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const; + virtual NetgenGeometry * Load (const filesystem::path & filename) const; }; - NetgenGeometry * STLGeometryRegister :: Load (string filename) const + NetgenGeometry * STLGeometryRegister :: Load (const filesystem::path & filename) const { - const char * cfilename = filename.c_str(); + string ext = ToLower(filename.extension()); - if (strcmp (&cfilename[strlen(cfilename)-3], "stl") == 0) + if (ext == ".stl") { - PrintMessage (1, "Load STL geometry file ", cfilename); + PrintMessage (1, "Load STL geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); STLGeometry * hgeom = STLGeometry :: Load (infile); hgeom -> edgesfound = 0; return hgeom; } - else if (strcmp (&cfilename[strlen(cfilename)-4], "stlb") == 0) + else if (ext == ".stlb") { - PrintMessage (1, "Load STL binary geometry file ", cfilename); + PrintMessage (1, "Load STL binary geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); STLGeometry * hgeom = STLGeometry :: LoadBinary (infile); hgeom -> edgesfound = 0; return hgeom; } - else if (strcmp (&cfilename[strlen(cfilename)-3], "nao") == 0) + else if (ext == ".nao") { - PrintMessage (1, "Load naomi (F. Kickinger) geometry file ", cfilename); + PrintMessage (1, "Load naomi (F. Kickinger) geometry file ", filename); - ifstream infile(cfilename); + ifstream infile(filename); STLGeometry * hgeom = STLGeometry :: LoadNaomi (infile); hgeom -> edgesfound = 0; diff --git a/libsrc/stlgeom/stlgeom.hpp b/libsrc/stlgeom/stlgeom.hpp index 8d8d640f..8cc5739e 100644 --- a/libsrc/stlgeom/stlgeom.hpp +++ b/libsrc/stlgeom/stlgeom.hpp @@ -2,7 +2,7 @@ #define FILE_STLGEOM /**************************************************************************/ -/* File: stlgeom.hpp */ +/* File: stlgeom.hpp */ /* Author: Joachim Schoeberl */ /* Author2: Johannes Gerstmayr */ /* Date: 26. Jul. 99 */ @@ -23,16 +23,21 @@ #include +#include "stltopology.hpp" +#include "stltool.hpp" +#include "stlline.hpp" + + namespace netgen { /* - inline int IsInArray(int n, const Array& ia) + inline int IsInArray(int n, const NgArray& ia) { return ia.Contains(n); } - inline bool AddIfNotExists(Array& list, int x) + inline bool AddIfNotExists(NgArray& list, int x) { if (list.Contains(x)) return false; list.Append(x); @@ -40,14 +45,10 @@ namespace netgen } */ - extern DLL_HEADER MeshingParameters mparam; +// extern DLL_HEADER MeshingParameters mparam; -#include "stltopology.hpp" -#include "stltool.hpp" -#include "stlline.hpp" - @@ -56,7 +57,7 @@ namespace netgen class STLEdgeDataList { - Array storedstatus; + NgArray storedstatus; STLTopology & geom; public: @@ -88,8 +89,8 @@ namespace netgen void Write(ofstream& of) const; void Read(ifstream& ifs); - void BuildLineWithEdge(int ep1, int ep2, Array& line); - void BuildClusterWithEdge(int ep1, int ep2, Array& line); + void BuildLineWithEdge(int ep1, int ep2, NgArray& line); + void BuildClusterWithEdge(int ep1, int ep2, NgArray& line); int GetNEPPStat(int p, int status) const; int GetNConfCandEPP(int p) const; @@ -100,25 +101,25 @@ namespace netgen - class STLGeometry : public NetgenGeometry, public STLTopology + class DLL_HEADER STLGeometry : public NetgenGeometry, public STLTopology { // edges to be meshed: - Array edges; + NgArray edges; //edges per point TABLE edgesperpoint; // line: a connection of edges - Array lines; - Array lineendpoints; //per geometrypoint, 1 = is endpoint; 0 = no endpoint, + NgArray lines; + NgArray lineendpoints; //per geometrypoint, 1 = is endpoint; 0 = no endpoint, - Array normals; //normals belong to points! + NgArray normals; //normals belong to points! - Array externaledges; + NgArray externaledges; int undoexternaledges; - Array storedexternaledges; + NgArray storedexternaledges; - STLEdgeDataList * edgedata; + unique_ptr edgedata; // STLEdgeDataList edgedata_store; int calcedgedataanglesnew; @@ -129,27 +130,27 @@ namespace netgen int facecnt; //meshpoint is only set, if an edge is at this point!!! - Array vicinity; //is one, if a triangle belongs to vicinity (eg. of selecttrig) - Array markedtrigs; //is one, if a triangle belongs to marked triangles (calcdirtystrigs) - Array markedsegs; //every pointpair is a segment!!! - Array selectedmultiedge; + NgArray vicinity; //is one, if a triangle belongs to vicinity (eg. of selecttrig) + NgArray markedtrigs; //is one, if a triangle belongs to marked triangles (calcdirtystrigs) + NgArray markedsegs; //every pointpair is a segment!!! + NgArray selectedmultiedge; //spiralpoints: - Array spiralpoints; + NgArray spiralpoints; // - Array atlas; + Array, ChartId> atlas; //marks all already charted trigs with chartnumber - Array chartmark; + Array chartmark; //outerchartspertrig, ascending sorted TABLE outerchartspertrig; //for meshing and project: - Array meshcharttrigs; //per trig: 1=belong to chart, 0 not - int meshchart; + NgArray meshcharttrigs; //per trig: 1=belong to chart, 0 not + mutable int meshchart; - Array ha_points; // help array, np long, filled with 0 + NgArray ha_points; // help array, np long, filled with 0 // sharp geometric edges not declared as edges @@ -158,12 +159,10 @@ namespace netgen //transformation: - Vec<3> meshtrignv; + mutable Vec<3> meshtrignv; Vec<3> ex, ey, ez; Point<3> p1; - mutable class RefinementSTLGeometry * ref; - public: int edgesfound; int surfacemeshed; @@ -176,104 +175,121 @@ namespace netgen //int selecttrig, nodeofseltrig; //only for testing; - Array meshlines; - Array meshpoints; + NgArray meshlines; + NgArray meshpoints; double area; public: STLGeometry(); virtual ~STLGeometry(); - void DoArchive(Archive& ar) + void DoArchive(Archive& ar) override { STLTopology::DoArchive(ar); } void Clear(); - virtual void Save (string filename) const; + virtual void Save (const filesystem::path & filename) const override; + + bool CalcPointGeomInfo(int surfind, PointGeomInfo& gi, const Point<3> & p3) const override; + PointGeomInfo ProjectPoint(INDEX surfind, Point<3> & p) const override; + bool ProjectPointGI (int surfind, Point<3> & p, PointGeomInfo & gi) const override; + Vec<3> GetNormal(int surfind, const Point<3> & p, const PointGeomInfo* gi = nullptr) const override; + 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; + + 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; - DLL_HEADER void STLInfo(double* data); + + void STLInfo(double* data); //stldoctor: - DLL_HEADER void SmoothNormals(); - DLL_HEADER void MarkNonSmoothNormals(); + void SmoothNormals(const STLParameters& stlparam); + void MarkNonSmoothNormals(const STLParameters& stlparam); - DLL_HEADER void CalcEdgeData(); - DLL_HEADER void CalcEdgeDataAngles(); + void CalcEdgeData(); + void CalcEdgeDataAngles(); const STLEdgeDataList& EdgeDataList() const {return *edgedata;} - DLL_HEADER void UndoEdgeChange(); - DLL_HEADER void StoreEdgeData(); - DLL_HEADER void RestoreEdgeData(); + void UndoEdgeChange(); + void StoreEdgeData(); + void RestoreEdgeData(); //void ClearSelectedMultiEdge() {selectedmultiedge.SetSize(0);} //void AddSelectedMultiEdge(twoint ep) {selectedmultiedge.Append(ep);} //int SelectedMultiEdgeSize() {return selectedmultiedge.Size();} - const Array& SelectedMultiEdge() {return selectedmultiedge;} + const NgArray& SelectedMultiEdge() {return selectedmultiedge;} twoint GetNearestSelectedDefinedEdge(); void BuildSelectedMultiEdge(twoint ep); void BuildSelectedEdge(twoint ep); void BuildSelectedCluster(twoint ep); - DLL_HEADER void ImportEdges(); - DLL_HEADER void AddEdges(const Array >& eps); - DLL_HEADER void ExportEdges(); - DLL_HEADER void LoadEdgeData(const char* file); - DLL_HEADER void SaveEdgeData(const char* file); + void ImportEdges(); + void AddEdges(const NgArray >& eps); + void ExportEdges(); + void LoadEdgeData(const filesystem::path & file); + void SaveEdgeData(const filesystem::path & file); // void SetEdgeAtSelected(int mode); - DLL_HEADER void STLDoctorConfirmEdge(); - DLL_HEADER void STLDoctorCandidateEdge(); - DLL_HEADER void STLDoctorExcludeEdge(); - DLL_HEADER void STLDoctorUndefinedEdge(); + void STLDoctorConfirmEdge(); + void STLDoctorCandidateEdge(); + void STLDoctorExcludeEdge(); + void STLDoctorUndefinedEdge(); - DLL_HEADER void STLDoctorSetAllUndefinedEdges(); - DLL_HEADER void STLDoctorEraseCandidateEdges(); - DLL_HEADER void STLDoctorConfirmCandidateEdges(); - DLL_HEADER void STLDoctorConfirmedToCandidateEdges(); + void STLDoctorSetAllUndefinedEdges(); + void STLDoctorEraseCandidateEdges(); + void STLDoctorConfirmCandidateEdges(); + void STLDoctorConfirmedToCandidateEdges(); - DLL_HEADER void STLDoctorDirtyEdgesToCandidates(); - DLL_HEADER void STLDoctorLongLinesToCandidates(); + void STLDoctorDirtyEdgesToCandidates(); + void STLDoctorLongLinesToCandidates(); - DLL_HEADER void UndoExternalEdges(); - DLL_HEADER void StoreExternalEdges(); - DLL_HEADER void RestoreExternalEdges(); + void UndoExternalEdges(); + void StoreExternalEdges(); + void RestoreExternalEdges(); - DLL_HEADER void ImportExternalEdges(const char * filename); // Flame edges, JS + void ImportExternalEdges(const char * filename); // Flame edges, JS // void LoadExternalEdges(); - DLL_HEADER void BuildExternalEdgesFromEdges(); - DLL_HEADER void SaveExternalEdges(); - DLL_HEADER void AddExternalEdgeAtSelected(); - DLL_HEADER void AddClosedLinesToExternalEdges(); - DLL_HEADER void AddLongLinesToExternalEdges(); - DLL_HEADER void AddAllNotSingleLinesToExternalEdges(); - DLL_HEADER void STLDoctorBuildEdges(); - DLL_HEADER void AddExternalEdgesFromGeomLine(); - DLL_HEADER void DeleteDirtyExternalEdges(); - DLL_HEADER void DeleteExternalEdgeAtSelected(); - DLL_HEADER void DeleteExternalEdgeInVicinity(); + void BuildExternalEdgesFromEdges(); + void SaveExternalEdges(); + void AddExternalEdgeAtSelected(); + void AddClosedLinesToExternalEdges(); + void AddLongLinesToExternalEdges(); + void AddAllNotSingleLinesToExternalEdges(); + void STLDoctorBuildEdges(const STLParameters& stlparam); + void AddExternalEdgesFromGeomLine(); + void DeleteDirtyExternalEdges(); + void DeleteExternalEdgeAtSelected(); + void DeleteExternalEdgeInVicinity(); void AddExternalEdge(int p1, int p2); void DeleteExternalEdge(int p1, int p2); int IsExternalEdge(int p1, int p2); int NOExternalEdges() const {return externaledges.Size();} twoint GetExternalEdge(int i) const {return externaledges.Get(i);} - DLL_HEADER void DestroyDirtyTrigs(); - DLL_HEADER void CalcNormalsFromGeometry(); - DLL_HEADER void MoveSelectedPointToMiddle(); - DLL_HEADER void NeighbourAnglesOfSelectedTrig(); - DLL_HEADER void PrintSelectInfo(); - DLL_HEADER void ShowSelectedTrigChartnum(); - DLL_HEADER void ShowSelectedTrigCoords(); - DLL_HEADER void SmoothGeometry (); + void DestroyDirtyTrigs(); + void CalcNormalsFromGeometry(); + void MoveSelectedPointToMiddle(); + void NeighbourAnglesOfSelectedTrig(); + void PrintSelectInfo(); + void ShowSelectedTrigChartnum(); + void ShowSelectedTrigCoords(); + void SmoothGeometry (); - DLL_HEADER void LoadMarkedTrigs(); - DLL_HEADER void SaveMarkedTrigs(); + void LoadMarkedTrigs(); + void SaveMarkedTrigs(); void ClearMarkedSegs() {markedsegs.SetSize(0);} void AddMarkedSeg(const Point<3> & ap1, const Point<3> & ap2) { @@ -286,26 +302,26 @@ namespace netgen ap2=markedsegs.Get(i*2); } int GetNMarkedSegs() {return markedsegs.Size()/2;} - DLL_HEADER void CalcVicinity(int starttrig); - DLL_HEADER void GetVicinity(int starttrig, int size, Array& vic); + void CalcVicinity(int starttrig); + void GetVicinity(int starttrig, int size, NgArray& vic); - DLL_HEADER int Vicinity(int trig) const; + int Vicinity(int trig) const; - DLL_HEADER void InitMarkedTrigs(); - DLL_HEADER void MarkDirtyTrigs(); - DLL_HEADER void SmoothDirtyTrigs(); - DLL_HEADER void GeomSmoothRevertedTrigs(); - DLL_HEADER void MarkRevertedTrigs(); - DLL_HEADER double CalcTrigBadness(int i); - DLL_HEADER int IsMarkedTrig(int trig) const; - DLL_HEADER void SetMarkedTrig(int trig, int num); - DLL_HEADER void MarkTopErrorTrigs (); + void InitMarkedTrigs(); + void MarkDirtyTrigs(const STLParameters& stlparam); + void SmoothDirtyTrigs(const STLParameters& stlparam); + void GeomSmoothRevertedTrigs(const STLParameters& stlparam); + void MarkRevertedTrigs(const STLParameters& stlparam); + double CalcTrigBadness(int i); + int IsMarkedTrig(int trig) const; + void SetMarkedTrig(int trig, int num); + void MarkTopErrorTrigs (); //Selected triangle - DLL_HEADER void SetSelectTrig(int trig); - DLL_HEADER int GetSelectTrig() const; - DLL_HEADER void SetNodeOfSelTrig(int n); - DLL_HEADER int GetNodeOfSelTrig() const; + void SetSelectTrig(int trig); + int GetSelectTrig() const; + void SetNodeOfSelTrig(int n); + int GetNodeOfSelTrig() const; int AddNormal(const Vec3d& n) { normals.Append(n); return normals.Size(); } @@ -330,8 +346,8 @@ namespace netgen /// ///ReadTriangle->STLTriangle, initialise some important variables, always after load!!! - virtual void InitSTLGeometry (const Array & readtrigs); - virtual void TopologyChanged(); //do some things, if topology changed! + virtual void InitSTLGeometry (const NgArray & readtrigs) override; + virtual void TopologyChanged() override; //do some things, if topology changed! int CheckGeometryOverlapping(); //get NO edges per point @@ -353,36 +369,36 @@ namespace netgen ///Build EdgeSegments void ClearEdges(); - void BuildEdges(); + void BuildEdges(const STLParameters& stlparam); void BuildEdgesPerPoint(); void UseExternalEdges(); - void FindEdgesFromAngles(); + void FindEdgesFromAngles(const STLParameters& stlparam); void CalcFaceNums(); int GetNOBodys(); int GetNOFaces() {return facecnt;} - void LinkEdges(); + void LinkEdges(const STLParameters& stlparam); - void AddConeAndSpiralEdges(); + void AddConeAndSpiralEdges(const STLParameters& stlparam); void AddFaceEdges(); //each face should have at least one starting edge (outherwise it won't be meshed) - void GetDirtyChartTrigs(int chartnum, STLChart& chart, const Array& outercharttrigs, - Array& chartpointchecked, Array& dirtytrigs); + void GetDirtyChartTrigs(int chartnum, STLChart& chart, const Array& outercharttrigs, + NgArray& chartpointchecked, NgArray& dirtytrigs); void ClearSpiralPoints(); void SetSpiralPoint(int pn) {spiralpoints.Elem(pn) = 1;}; int GetSpiralPoint(int pn) const {return spiralpoints.Get(pn);}; - void GetSortedTrianglesAroundPoint(int p, int starttrig, Array& trigs); + void GetSortedTrianglesAroundPoint(STLPointId p, STLTrigId starttrig, Array& trigs); // smooth edges: sharp geometric edges not declared as edges void BuildSmoothEdges (); - int IsSmoothEdge (int pi1, int pi2) const; + bool IsSmoothEdge (int pi1, int pi2) const; //make charts with regions of a max. angle - void MakeAtlas(class Mesh & mesh); + void MakeAtlas(class Mesh & mesh, const MeshingParameters& mparam, const STLParameters& stlparam); //outerchartspertrig, sorted! int GetOCPTSize() const {return outerchartspertrig.Size();}; @@ -393,23 +409,22 @@ namespace netgen int TrigIsInOC(int tn, int ocn) const; //get chart number of a trig or 0 if unmarked - int GetChartNr(int i) const; - int GetMarker(int i) const - { return chartmark.Get(i); } - void SetMarker(int nr, int m); - int GetNOCharts() const; + ChartId GetChartNr(STLTrigId i) const; + ChartId GetMarker(STLTrigId i) const { return chartmark[i]; } + void SetMarker(STLTrigId nr, ChartId m); + size_t GetNOCharts() const { return atlas.Size(); } //get a chart from atlas - const STLChart& GetChart(int nr) const; - STLChart& GetChart(int nr) {return *(atlas.Get(nr));}; + const STLChart& GetChart(ChartId nr) const { return *atlas[nr];}; + STLChart & GetChart(ChartId nr) { return *atlas[nr];}; int AtlasMade() const; - void GetInnerChartLimes(Array& limes, int chartnum); + void GetInnerChartLimes(NgArray& limes, ChartId chartnum); //FOR MESHING int GetMeshChartNr () { return meshchart; } - void GetMeshChartBoundary (Array & points, - Array & points3d, - Array & lines, double h); + void GetMeshChartBoundary (NgArray> & points, + NgArray> & points3d, + NgArray & lines, double h); Point<3> PointBetween(const Point<3> & p1, int t1, const Point<3> & p2, int t2); @@ -419,7 +434,7 @@ namespace netgen // void DefineTangentialPlane(const Point<3> & ap1, const Point<3> & ap2, int trig); // - void SelectChartOfTriangle (int trignum); + void SelectChartOfTriangle (int trignum) const; // void SelectChartOfPoint (const Point<3> & p); // @@ -450,17 +465,17 @@ namespace netgen int LineEndPointsSet() const {return lineendpoints.Size() == GetNP();} void ClearLineEndPoints(); - DLL_HEADER void RestrictLocalH(class Mesh & mesh, double gh); - void RestrictLocalHCurv(class Mesh & mesh, double gh); - void RestrictHChartDistOneChart(int chartnum, Array& acttrigs, class Mesh & mesh, - double gh, double fact, double minh); + void RestrictLocalH(class Mesh & mesh, double gh, const STLParameters& stlparam, const MeshingParameters& mparam); + void RestrictLocalHCurv(class Mesh & mesh, double gh, const STLParameters& stlparam); + void RestrictHChartDistOneChart(ChartId chartnum, NgArray& acttrigs, class Mesh & mesh, + double gh, double fact, double minh, const STLParameters& stlparam); friend class MeshingSTLSurface; - - virtual int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam); + int GenerateMesh (shared_ptr & mesh, MeshingParameters & mparam) override; - virtual const Refinement & GetRefinement () const; + // Add additional Point to chart to close the surface and write the resulting stl to a file + void WriteChartToFile( ChartId chartnumber, filesystem::path filename="chart.slb" ); }; @@ -468,7 +483,8 @@ namespace netgen - extern int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, MeshingParameters & mparam); +extern int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, const MeshingParameters & mparam, + const STLParameters& stlpar); } diff --git a/libsrc/stlgeom/stlgeomchart.cpp b/libsrc/stlgeom/stlgeomchart.cpp index 5962ac82..a3a22f89 100644 --- a/libsrc/stlgeom/stlgeomchart.cpp +++ b/libsrc/stlgeom/stlgeomchart.cpp @@ -17,8 +17,11 @@ int chartdebug = 0; -void STLGeometry :: MakeAtlas(Mesh & mesh) +void STLGeometry :: MakeAtlas(Mesh & mesh, const MeshingParameters& mparam, const STLParameters& stlparam) { + static Timer t("makeatlas"); RegionTimer reg(t); + static Timer tinner("find innner chart"); + static Timer touter("find outer chart"); // int timer1 = NgProfiler::CreateTimer ("makeatlas"); /* int timerb = NgProfiler::CreateTimer ("makeatlas - begin"); @@ -69,16 +72,16 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) double sinchartangle = sin(chartangle); double sinouterchartangle = sin(outerchartangle); - Array outermark(GetNT()); //marks all trigs form actual outer region - Array outertested(GetNT()); //marks tested trigs for outer region - Array pointstochart(GetNP()); //point in chart becomes chartnum - Array innerpointstochart(GetNP()); //point in chart becomes chartnum - Array chartpoints; //point in chart becomes chartnum - Array innerchartpoints; + Array outermark(GetNT()); //marks all trigs form actual outer region + Array outertested(GetNT()); //marks tested trigs for outer region + Array pointstochart(GetNP()); //point in chart becomes chartnum + Array innerpointstochart(GetNP()); //point in chart becomes chartnum + Array chartpoints; //point in chart becomes chartnum + Array innerchartpoints; Array> innerchartpts; - Array dirtycharttrigs; + NgArray dirtycharttrigs; - Array chartdistacttrigs (GetNT()); //outercharttrigs + NgArray chartdistacttrigs (GetNT()); //outercharttrigs chartdistacttrigs = 0; @@ -86,38 +89,32 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) //int chartboundarydivisions = 10; markedsegs.SetSize(0); //for testing!!! - Array chartpointchecked(GetNP()); //for dirty-chart-trigs + NgArray chartpointchecked(GetNP()); //for dirty-chart-trigs - pointstochart.SetSize(GetNP()); - innerpointstochart.SetSize(GetNP()); chartmark.SetSize(GetNT()); - for (int i = 1; i <= GetNP(); i++) - { - innerpointstochart.Elem(i) = 0; - pointstochart.Elem(i) = 0; - chartpointchecked.Elem(i) = 0; - } + innerpointstochart = ChartId::INVALID; + pointstochart = ChartId::INVALID; + chartpointchecked = ChartId::INVALID; double eps = 1e-12 * Dist (boundingbox.PMin(), boundingbox.PMax()); int spiralcheckon = stldoctor.spiralcheck; if (!spiralcheckon) {PrintWarning("++++++++++++\nspiral deactivated by user!!!!\n+++++++++++++++"); } - chartmark = 0; - outermark = 0; - outertested = 0; + chartmark = ChartId::INVALID; + outermark = ChartId::INVALID; + outertested = ChartId::INVALID; double atlasarea = Area(); double workedarea = 0; double showinc = 100.*5000./(double)GetNT(); double nextshow = 0; - // Point<3> startp; - int lastunmarked = 1; + STLTrigId lastunmarked = 1; PrintMessage(5,"one dot per 5000 triangles: "); - int markedtrigcnt = 0; + size_t markedtrigcnt = 0; while (markedtrigcnt < GetNT()) { if (multithread.terminate) { PopStatus(); return; } @@ -128,16 +125,14 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) SetThreadPercent(100.0 * workedarea / atlasarea); - STLChart * chart = new STLChart(this); - atlas.Append(chart); - - // *testout << "Chart " << atlas.Size() << endl; - + atlas.Append (make_unique (this, stlparam)); + STLChart & chart = *atlas.Last(); + //find unmarked trig - int prelastunmarked = lastunmarked; + STLTrigId prelastunmarked = lastunmarked; bool found = false; - for (int j = lastunmarked; j <= GetNT(); j++) + for (STLTrigId j = lastunmarked; j <= GetNT(); j++) if (!GetMarker(j)) { found = true; @@ -149,39 +144,38 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) innerchartpoints.SetSize(0); innerchartpts.SetSize(0); chartbound.Clear(); - chartbound.SetChart(chart); + chartbound.SetChart(&chart); - chartbound.BuildSearchTree(); // different !!! - - if (!found) { PrintSysError("Make Atlas, no starttrig found"); return; } + if (!found) throw Exception("Make Atlas, no starttrig found"); //find surrounding trigs // int starttrig = j; - int starttrig = lastunmarked; + STLTrigId starttrig = lastunmarked; - Point<3> startp = GetPoint(GetTriangle(starttrig).PNum(1)); + Point<3> startp = GetPoint(GetTriangle(starttrig)[0]); - int accepted; - int chartnum = GetNOCharts(); + bool accepted; + ChartId chartnum = GetNOCharts(); Vec<3> sn = GetTriangle(starttrig).Normal(); - chart->SetNormal (startp, sn); + chart.SetNormal (startp, sn); // *testout << "first trig " << starttrig << ", n = " << sn << endl; SetMarker(starttrig, chartnum); markedtrigcnt++; - chart->AddChartTrig(starttrig); + chart.AddChartTrig(starttrig); chartbound.AddTriangle(GetTriangle(starttrig)); workedarea += GetTriangle(starttrig).Area(points); - for (int i = 1; i <= 3; i++) - { - innerpointstochart.Elem(GetTriangle(starttrig).PNum(i)) = chartnum; - pointstochart.Elem(GetTriangle(starttrig).PNum(i)) = chartnum; - chartpoints.Append(GetTriangle(starttrig).PNum(i)); - innerchartpoints.Append(GetTriangle(starttrig).PNum(i)); + for (int i = 0; i < 3; i++) + { + STLPointId pi = GetTriangle(starttrig)[i]; + innerpointstochart[pi] = chartnum; + pointstochart[pi] = chartnum; + chartpoints.Append(pi); + innerchartpoints.Append(pi); } bool changed = true; @@ -190,23 +184,23 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) // NgProfiler::StopTimer (timerb); // NgProfiler::StartTimer (timer2); - + tinner.Start(); while (changed) { changed = false; oldstartic2 = oldstartic; - oldstartic = chart->GetNT(); + oldstartic = chart.GetNT(); // for (ic = oldstartic2; ic <= chart->GetNT(); ic++) for (int ic = oldstartic2; ic <= oldstartic; ic++) { - int i = chart->GetTrig(ic); + STLTrigId i = chart.GetTrig1(ic); if (GetMarker(i) == chartnum) { for (int j = 1; j <= NONeighbourTrigs(i); j++) { - int nt = NeighbourTrig(i,j); + STLTrigId nt = NeighbourTrig(i,j); // *testout << "check trig " << nt << endl; - int np1, np2; + STLPointId np1, np2; GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2); if (GetMarker(nt) == 0 && !IsEdge(np1,np2)) { @@ -215,7 +209,7 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) if ( (n2 * sn) >= coschartangle ) { // *testout << "good angle " << endl; - accepted = 1; + accepted = true; /* //alter spiralentest, schnell, aber ungenau for (k = 1; k <= 3; k++) @@ -244,12 +238,12 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) */ //find overlapping charts exacter (fast, too): - for (int k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(nt); k++) { int nnt = NeighbourTrig(nt,k); if (GetMarker(nnt) != chartnum) { - int nnp1, nnp2; + STLPointId nnp1, nnp2; GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2); accepted = chartbound.TestSeg(GetPoint(nnp1), @@ -261,12 +255,38 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) Vec<3> n3 = GetTriangle(nnt).Normal(); if ( (n3 * sn) >= coschartangle && IsSmoothEdge (nnp1, nnp2) ) - accepted = 1; + accepted = true; } if (!accepted) break; } - + + /* + // new check 2019-09-22 + if (accepted) + { + auto & trig = GetTriangle(nt); + Point<3> p0 = GetPoint(trig[0]); + Point<3> p1 = GetPoint(trig[1]); + Point<3> p2 = GetPoint(trig[2]); + Point<3> p01 = Center(p0,p1); + Point<3> p02 = Center(p0,p2); + Point<3> p12 = Center(p1,p2); + Point<3> p012 = Center(p0,p1,p2); + p01 += 1e-5 * (p012-p01); + p02 += 1e-5 * (p012-p02); + p12 += 1e-5 * (p012-p12); + bool test1 = chartbound.TestSegChartNV(p01,p012,sn); + bool test2 = chartbound.TestSegChartNV(p02,p012,sn); + bool test3 = chartbound.TestSegChartNV(p12,p012,sn); + if (!test1 || !test2 || !test3) + { + cout << "more stringent" << endl; + accepted = false; + } + } + */ + if (accepted) { @@ -275,19 +295,19 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) changed = true; markedtrigcnt++; workedarea += GetTriangle(nt).Area(points); - chart->AddChartTrig(nt); + chart.AddChartTrig(nt); chartbound.AddTriangle(GetTriangle(nt)); for (int k = 1; k <= 3; k++) { - if (innerpointstochart.Get(GetTriangle(nt).PNum(k)) - != chartnum) + STLPointId pi = GetTriangle(nt).PNum(k); + if (innerpointstochart[pi] != chartnum) { - innerpointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum; - pointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum; - chartpoints.Append(GetTriangle(nt).PNum(k)); - innerchartpoints.Append(GetTriangle(nt).PNum(k)); + innerpointstochart[pi] = chartnum; + pointstochart[pi] = chartnum; + chartpoints.Append(pi); + innerchartpoints.Append(pi); } } } @@ -297,11 +317,11 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) } } } - + tinner.Stop(); + innerchartpts.SetSize(innerchartpoints.Size()); for (size_t i = 0; i < innerchartpoints.Size(); i++) innerchartpts[i] = GetPoint(innerchartpoints[i]); - // chartbound.BuildSearchTree(); // different !!! // NgProfiler::StopTimer (timer2); // NgProfiler::StartTimer (timer3); @@ -310,9 +330,9 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) // chartbound.Clear(); // warum, ic-bound auf edge macht Probleme js ??? - - - outermark.Elem(starttrig) = chartnum; + + touter.Start(); + outermark[starttrig] = chartnum; //chart->AddOuterTrig(starttrig); changed = true; oldstartic = 1; @@ -320,20 +340,20 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) { changed = false; oldstartic2 = oldstartic; - oldstartic = chart->GetNT(); + oldstartic = chart.GetNT(); for (int ic = oldstartic2; ic <= oldstartic; ic++) { - int i = chart->GetTrig(ic); - if (outermark.Get(i) != chartnum) continue; + STLTrigId i = chart.GetTrig1(ic); + if (outermark[i] != chartnum) continue; for (int j = 1; j <= NONeighbourTrigs(i); j++) { - int nt = NeighbourTrig(i,j); - if (outermark.Get(nt) == chartnum) continue; + STLTrigId nt = NeighbourTrig(i,j); + if (outermark[nt] == chartnum) continue; const STLTriangle & ntrig = GetTriangle(nt); - int np1, np2; + STLPointId np1, np2; GetTriangle(i).GetNeighbourPoints(GetTriangle(nt),np1,np2); if (IsEdge (np1, np2)) continue; @@ -343,14 +363,14 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) if (outertested.Get(nt) == chartnum) continue; */ - outertested.Elem(nt) = chartnum; + outertested[nt] = chartnum; Vec<3> n2 = GetTriangle(nt).Normal(); //abfragen, ob noch im tolerierten Winkel if ( (n2 * sn) >= cosouterchartangle ) { - accepted = 1; + accepted = true; // NgProfiler::StartTimer (timer4); bool isdirtytrig = false; @@ -367,15 +387,15 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) // NgProfiler::StartTimer (timer4a); if (spiralcheckon && !isdirtytrig) - for (int k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(nt); k++) { // NgProfiler::StartTimer (timer4b); - int nnt = NeighbourTrig(nt,k); + STLTrigId nnt = NeighbourTrig(nt,k); - if (outermark.Elem(nnt) != chartnum) + if (outermark[nnt] != chartnum) { // NgProfiler::StartTimer (timer4c); - int nnp1, nnp2; + STLPointId nnp1, nnp2; GetTriangle(nt).GetNeighbourPoints(GetTriangle(nnt),nnp1,nnp2); // NgProfiler::StopTimer (timer4c); @@ -392,7 +412,7 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) Vec<3> n3 = GetTriangle(nnt).Normal(); if ( (n3 * sn) >= cosouterchartangle && IsSmoothEdge (nnp1, nnp2) ) - accepted = 1; + accepted = true; // NgProfiler::StopTimer (timer4e); } // NgProfiler::StopTimer (timer4b); @@ -411,12 +431,12 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) if (accepted) { // NgProfiler::StartTimer (timer5a); - accepted = 0; + accepted = false; for (int k = 1; k <= 3; k++) - if (innerpointstochart.Get(ntrig.PNum(k)) == chartnum) + if (innerpointstochart[ntrig.PNum(k)] == chartnum) { - accepted = 1; + accepted = true; break; } @@ -445,7 +465,7 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) double tdist = Dist2(pt, innerchartpts[l]); if (tdist < 4 * h2) { - accepted = 1; + accepted = true; break; } } @@ -459,18 +479,18 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) if (accepted) { changed = true; - outermark.Elem(nt) = chartnum; + outermark[nt] = chartnum; if (GetMarker(nt) != chartnum) { chartbound.AddTriangle(GetTriangle(nt)); - chart->AddOuterTrig(nt); + chart.AddOuterTrig(nt); for (int k = 1; k <= 3; k++) { - if (pointstochart.Get(GetTriangle(nt).PNum(k)) + if (pointstochart[GetTriangle(nt).PNum(k)] != chartnum) { - pointstochart.Elem(GetTriangle(nt).PNum(k)) = chartnum; + pointstochart[GetTriangle(nt).PNum(k)] = chartnum; chartpoints.Append(GetTriangle(nt).PNum(k)); } } @@ -481,31 +501,31 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) } } } - + touter.Stop(); // NgProfiler::StopTimer (timer3); // NgProfiler::StartTimer (timere); // NgProfiler::StartTimer (timere1); //end of while loop for outer chart - GetDirtyChartTrigs(chartnum, *chart, outermark, chartpointchecked, dirtycharttrigs); + GetDirtyChartTrigs(chartnum, chart, outermark, chartpointchecked, dirtycharttrigs); //dirtycharttrigs are local (chart) point numbers!!!!!!!!!!!!!!!! if (dirtycharttrigs.Size() != 0 && - (dirtycharttrigs.Size() != chart->GetNChartT() || dirtycharttrigs.Size() != 1)) + (dirtycharttrigs.Size() != chart.GetNChartT() || dirtycharttrigs.Size() != 1)) { - if (dirtycharttrigs.Size() == chart->GetNChartT() && dirtycharttrigs.Size() != 1) + if (dirtycharttrigs.Size() == chart.GetNChartT() && dirtycharttrigs.Size() != 1) { //if all trigs would be eliminated -> leave 1 trig! dirtycharttrigs.SetSize(dirtycharttrigs.Size() - 1); } for (int k = 1; k <= dirtycharttrigs.Size(); k++) { - int tn = chart->GetChartTrig(dirtycharttrigs.Get(k)); - outermark.Elem(tn) = 0; //not necessary, for later use + STLTrigId tn = chart.GetChartTrig1(dirtycharttrigs.Get(k)); + outermark[tn] = 0; //not necessary, for later use SetMarker(tn, 0); markedtrigcnt--; workedarea -= GetTriangle(tn).Area(points); } - chart->MoveToOuterChart(dirtycharttrigs); + chart.MoveToOuterChart(dirtycharttrigs); lastunmarked = 1; lastunmarked = prelastunmarked; } @@ -520,7 +540,7 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) // char key; // cin >> key; //calculate an estimate meshsize, not to produce too large outercharts, with factor 2 larger! - RestrictHChartDistOneChart(chartnum, chartdistacttrigs, mesh, h, 0.5, atlasminh); + RestrictHChartDistOneChart(chartnum, chartdistacttrigs, mesh, h, 0.5, atlasminh, stlparam); // NgProfiler::Print(stdout); // NgProfiler::StopTimer (timere2); @@ -537,11 +557,12 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) int cnttrias = 0; outerchartspertrig.SetSize(GetNT()); - for (int i = 1; i <= atlas.Size(); i++) + // for (int i = 1; i <= atlas.Size(); i++) + for (ChartId i : atlas.Range()) { for (int j = 1; j <= GetChart(i).GetNT(); j++) { - int tn = GetChart(i).GetTrig(j); + STLTrigId tn = GetChart(i).GetTrig1(j); AddOCPT(tn,i); } @@ -572,10 +593,14 @@ void STLGeometry :: MakeAtlas(Mesh & mesh) mesh.SetMinimalH(mparam.minh); - AddConeAndSpiralEdges(); + AddConeAndSpiralEdges(stlparam); PrintMessage(5,"Make Atlas finished"); + + for (auto & chart : atlas) + chart->BuildInnerSearchTree(); + PopStatus(); } @@ -618,14 +643,14 @@ int STLGeometry::TrigIsInOC(int tn, int ocn) const return GetOCPT(tn, start) == ocn; } -int STLGeometry :: GetChartNr(int i) const +ChartId STLGeometry :: GetChartNr(STLTrigId i) const { if (i > chartmark.Size()) { - PrintSysError("GetChartNr(", i, ") not possible!!!"); + PrintSysError("GetChartNr(", int(i), ") not possible!!!"); i = 1; } - return chartmark.Get(i); + return chartmark[i]; } /* int STLGeometry :: GetMarker(int i) const @@ -633,22 +658,9 @@ int STLGeometry :: GetMarker(int i) const return chartmark.Get(i); } */ -void STLGeometry :: SetMarker(int nr, int m) +void STLGeometry :: SetMarker(STLTrigId nr, ChartId m) { - chartmark.Elem(nr) = m; -} -int STLGeometry :: GetNOCharts() const -{ - return atlas.Size(); -} -const STLChart& STLGeometry :: GetChart(int nr) const -{ - if (nr > atlas.Size()) - { - PrintSysError("GetChart(", nr, ") not possible!!!"); - nr = 1; - } - return *(atlas.Get(nr)); + chartmark[nr] = m; } int STLGeometry :: AtlasMade() const @@ -659,7 +671,7 @@ int STLGeometry :: AtlasMade() const /* //return 1 if not exists -int AddIfNotExists(Array& list, int x) +int AddIfNotExists(NgArray& list, int x) { int i; for (i = 1; i <= list.Size(); i++) @@ -671,23 +683,21 @@ int AddIfNotExists(Array& list, int x) } */ -void STLGeometry :: GetInnerChartLimes(Array& limes, int chartnum) +void STLGeometry :: GetInnerChartLimes(NgArray& limes, ChartId chartnum) { - int j, k; - - int t, nt, np1, np2; + STLPointId np1, np2; limes.SetSize(0); STLChart& chart = GetChart(chartnum); - for (j = 1; j <= chart.GetNChartT(); j++) + for (int j = 1; j <= chart.GetNChartT(); j++) { - t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(t); k++) { - nt = NeighbourTrig(t,k); + STLTrigId nt = NeighbourTrig(t,k); if (GetChartNr(nt) != chartnum) { tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); @@ -732,26 +742,26 @@ void STLGeometry :: GetInnerChartLimes(Array& limes, int chartnum) void STLGeometry :: GetDirtyChartTrigs(int chartnum, STLChart& chart, - const Array& outercharttrigs, - Array& chartpointchecked, - Array& dirtytrigs) + const Array& outercharttrigs, + NgArray& chartpointchecked, + NgArray& dirtytrigs) { dirtytrigs.SetSize(0); - int j,k,n; - int np1, np2, nt; + int cnt = 0; - for (j = 1; j <= chart.GetNChartT(); j++) + for (int j = 1; j <= chart.GetNChartT(); j++) { - int t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(t); k++) { - nt = NeighbourTrig(t,k); - if (GetChartNr(nt) != chartnum && outercharttrigs.Get(nt) != chartnum) - { + STLTrigId nt = NeighbourTrig(t,k); + if (GetChartNr(nt) != chartnum && outercharttrigs[nt] != chartnum) + { + STLPointId np1, np2; tt.GetNeighbourPoints(GetTriangle(nt),np1,np2); if (!IsEdge(np1,np2)) { @@ -764,26 +774,26 @@ void STLGeometry :: GetDirtyChartTrigs(int chartnum, STLChart& chart, } cnt = 0; - int ap1, ap2, tn1, tn2, l, problem, pn; - Array trigsaroundp; + STLPointId ap1, ap2, pn; + Array trigsaroundp; - for (j = chart.GetNChartT(); j >= 1; j--) + for (int j = chart.GetNChartT(); j >= 1; j--) { - int t = chart.GetChartTrig(j); + STLTrigId t = chart.GetChartTrig1(j); const STLTriangle& tt = GetTriangle(t); - for (k = 1; k <= 3; k++) + for (int k = 1; k <= 3; k++) { pn = tt.PNum(k); //if (chartpointchecked.Get(pn) == chartnum) //{continue;} int checkpoint = 0; - for (n = 1; n <= trigsperpoint.EntrySize(pn); n++) + for (int n = 1; n <= trigsperpoint.EntrySize(pn); n++) { if (trigsperpoint.Get(pn,n) != t && //ueberfluessig??? GetChartNr(trigsperpoint.Get(pn,n)) != chartnum && - outercharttrigs.Get(trigsperpoint.Get(pn,n)) != chartnum) {checkpoint = 1;}; + outercharttrigs[trigsperpoint.Get(pn,n)] != chartnum) {checkpoint = 1;}; } if (checkpoint) { @@ -792,31 +802,31 @@ void STLGeometry :: GetDirtyChartTrigs(int chartnum, STLChart& chart, GetSortedTrianglesAroundPoint(pn,t,trigsaroundp); trigsaroundp.Append(t); //ring - problem = 0; + bool problem = false; //forward: - for (l = 2; l <= trigsaroundp.Size()-1; l++) + for (int l = 2; l <= trigsaroundp.Size()-1; l++) { - tn1 = trigsaroundp.Get(l-1); - tn2 = trigsaroundp.Get(l); + STLTrigId tn1 = trigsaroundp[l-2]; + STLTrigId tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); if (IsEdge(ap1,ap2)) break; - if (GetChartNr(tn2) != chartnum && outercharttrigs.Get(tn2) != chartnum) {problem = 1;} + if (GetChartNr(tn2) != chartnum && outercharttrigs[tn2] != chartnum) {problem = true;} } //backwards: - for (l = trigsaroundp.Size()-1; l >= 2; l--) + for (int l = trigsaroundp.Size()-1; l >= 2; l--) { - tn1 = trigsaroundp.Get(l+1); - tn2 = trigsaroundp.Get(l); + STLTrigId tn1 = trigsaroundp[l]; + STLTrigId tn2 = trigsaroundp[l-1]; const STLTriangle& t1 = GetTriangle(tn1); const STLTriangle& t2 = GetTriangle(tn2); t1.GetNeighbourPoints(t2, ap1, ap2); if (IsEdge(ap1,ap2)) break; - if (GetChartNr(tn2) != chartnum && outercharttrigs.Get(tn2) != chartnum) {problem = 1;} + if (GetChartNr(tn2) != chartnum && outercharttrigs[tn2] != chartnum) {problem = true;} } // if (problem && !IsInArray(j,dirtytrigs)) if (problem && !dirtytrigs.Contains(j)) diff --git a/libsrc/stlgeom/stlgeommesh.cpp b/libsrc/stlgeom/stlgeommesh.cpp index bc4613d3..00e38724 100644 --- a/libsrc/stlgeom/stlgeommesh.cpp +++ b/libsrc/stlgeom/stlgeommesh.cpp @@ -12,7 +12,7 @@ namespace netgen { -int EdgeUsed(int p1, int p2, Array& edges, INDEX_2_HASHTABLE& hashtab) +int EdgeUsed(int p1, int p2, NgArray& edges, INDEX_2_HASHTABLE& hashtab) { if (p1 > p2) {swap (p1,p2);} @@ -41,9 +41,9 @@ Point<3> STLGeometry :: PointBetween(const Point<3> & ap1, int t1, TABLE edgepointorigines; TABLE edgepointoriginps; - Array edgetrigs; - Array edgepointnums; - Array edgetriglocinds; + NgArray edgetrigs; + NgArray edgepointnums; + NgArray edgetriglocinds; int size = 3*GetNT(); INDEX_2_HASHTABLE hashtab(size); @@ -59,8 +59,8 @@ Point<3> STLGeometry :: PointBetween(const Point<3> & ap1, int t1, edgepointnums.SetSize(size); edgetriglocinds.SetSize(size); - Array edgelist1; - Array edgelist2; + NgArray edgelist1; + NgArray edgelist2; edgelist1.SetSize(0); edgelist2.SetSize(0); @@ -238,7 +238,7 @@ Point<3> STLGeometry :: PointBetween(const Point<3> & ap1, int t1, if (!endpointorigine) {PrintSysError("No connection found!");} - Array plist; + NgArray plist; plist.Append(ap2); int laste = endpointorigine; @@ -300,9 +300,9 @@ void STLGeometry :: PrepareSurfaceMeshing() meshcharttrigs = 0; } -void STLGeometry::GetMeshChartBoundary (Array & apoints, - Array & points3d, - Array & alines, double h) +void STLGeometry::GetMeshChartBoundary (NgArray> & apoints, + NgArray> & points3d, + NgArray & alines, double h) { twoint seg, newseg; int zone; @@ -392,7 +392,7 @@ void STLGeometry :: DefineTangentialPlane (const Point<3> & ap1, const Point<3> } -void STLGeometry :: SelectChartOfTriangle (int trignum) +void STLGeometry :: SelectChartOfTriangle (int trignum) const { meshchart = GetChartNr(trignum); meshtrignv = GetTriangle(trignum).Normal(); @@ -403,7 +403,7 @@ void STLGeometry :: SelectChartOfPoint (const Point<3> & p) { int i, ii; - Array trigsinbox; + NgArray trigsinbox; Box<3> box(p,p); box.Increase (1e-6); @@ -462,7 +462,7 @@ void STLGeometry :: ToPlane (const Point<3> & locpoint, int * trigs, else { - Array trigsinbox; + NgArray trigsinbox; if (!geomsearchtreeon) { @@ -473,7 +473,7 @@ void STLGeometry :: ToPlane (const Point<3> & locpoint, int * trigs, } else { - Array trigsinbox2; + NgArray trigsinbox2; Box<3> box(locpoint, locpoint); box.Increase (range); GetTrianglesInBox (box, trigsinbox2); @@ -520,14 +520,9 @@ void STLGeometry :: ToPlane (const Point<3> & locpoint, int * trigs, int STLGeometry :: FromPlane (const Point<2> & plainpoint, Point<3> & locpoint, double h) { - Point2d plainpoint2 (plainpoint); - - plainpoint2.X() *= h; - plainpoint2.Y() *= h; - Vec3d p1p = plainpoint2.X() * ex + plainpoint2.Y() * ey; + Vec<3> p1p = h * plainpoint[0] * ex + h * plainpoint[1] * ey; locpoint = p1 + p1p; - int rv = Project(locpoint); if (!rv) {return 1;} //project nicht gegangen return 0; @@ -541,7 +536,7 @@ int STLGeometry :: Project(Point<3> & p3d) const { Point<3> p, pf; - int i, j; + int j; int fi = 0; int cnt = 0; int different = 0; @@ -549,6 +544,13 @@ int STLGeometry :: Project(Point<3> & p3d) const const STLChart& chart = GetChart(meshchart); + STLTrigId trig = chart.ProjectNormal(p3d); + return trig; + // cout << "new, trig = " << trig << endl; + + // #define MOVEDTOCHART +#ifdef MOVEDTOCHART + int nt = chart.GetNT(); QuadraticFunction3d quadfun(p3d, meshtrignv); @@ -563,7 +565,7 @@ int STLGeometry :: Project(Point<3> & p3d) const for (j = 1; j <= nt; j++) { - i = chart.GetTrig(j); + STLTrigId i = chart.GetTrig1(j); const Point<3> & c = GetTriangle(i).center; /* @@ -604,10 +606,12 @@ int STLGeometry :: Project(Point<3> & p3d) const } if (inside) - break; - + break; } +#endif + // cout << "oldtrig = " << fi << endl; + // if (cnt == 2) {(*testout) << "WARNING: found 2 triangles to project" << endl;} //if (cnt == 3) {(*testout) << "WARNING: found 3 triangles to project" << endl;} //if (cnt > 3) {(*testout) << "WARNING: found more than 3 triangles to project" << endl;} @@ -687,12 +691,12 @@ int STLGeometry :: ProjectNearest(Point<3> & p3d) const for (i = 1; i <= chart.GetNT(); i++) { p = p3d; - dist = GetTriangle(chart.GetTrig(i)).GetNearestPoint(points, p); + dist = GetTriangle(chart.GetTrig1(i)).GetNearestPoint(points, p); if (dist < nearest) { pf = p; nearest = dist; - ft = chart.GetTrig(i); + ft = chart.GetTrig1(i); } } p3d = pf; @@ -705,7 +709,7 @@ int STLGeometry :: ProjectNearest(Point<3> & p3d) const //Restrict local h due to curvature for make atlas -void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh) +void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh, const STLParameters& stlparam) { PushStatusF("Restrict H due to surface curvature"); @@ -713,7 +717,7 @@ void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh) //die Meshsize auf ein bestimmtes Mass limitieren int i,j; - int ap1,ap2,p3,p4; + STLPointId ap1,ap2,p3,p4; Point<3> p1p, p2p, p3p, p4p; Vec<3> n, ntn; double rzyl, localh; @@ -737,7 +741,7 @@ void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh) if (stlparam.resthatlasenable) { - Array minh; //minimales h pro punkt + NgArray minh; //minimales h pro punkt minh.SetSize(GetNP()); for (i = 1; i <= GetNP(); i++) { @@ -810,14 +814,14 @@ void STLGeometry :: RestrictLocalHCurv(class Mesh & mesh, double gh) } //restrict local h due to near edges and due to outer chart distance -void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) +void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh, const STLParameters& stlparam, const MeshingParameters& mparam) { //bei jedem Dreieck alle Nachbardreiecke vergleichen, und, fallskein Kante dazwischen, //die Meshsize auf ein bestimmtes Mass limitieren int i,j; - int ap1,ap2,p3,p4; + STLPointId ap1,ap2,p3,p4; Point3d p1p, p2p, p3p, p4p; Vec3d n, ntn; double rzyl, localh; @@ -841,7 +845,7 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) { PushStatusF("Restrict H due to surface curvature"); - Array minh; //minimales h pro punkt + NgArray minh; //minimales h pro punkt minh.SetSize(GetNP()); for (i = 1; i <= GetNP(); i++) { @@ -917,12 +921,12 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) PopStatus(); } - if (stlparam.resthcloseedgeenable) + if (mparam.closeedgefac.has_value()) { PushStatusF("Restrict H due to close edges"); //geht nicht für spiralen!!!!!!!!!!!!!!!!!! - double disttohfact = sqr(10.0 / stlparam.resthcloseedgefac); + double disttohfact = sqr(10.0 / *mparam.closeedgefac); int k,l; double h1, h2, dist; int rc = 0; @@ -933,8 +937,8 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) BoxTree<3> * lsearchtree = new BoxTree<3> (GetBoundingBox().PMin() - Vec3d(1,1,1), GetBoundingBox().PMax() + Vec3d(1,1,1)); - Array pmins(GetNLines()); - Array pmaxs(GetNLines()); + NgArray pmins(GetNLines()); + NgArray pmaxs(GetNLines()); double maxhline; for (i = 1; i <= GetNLines(); i++) @@ -958,7 +962,7 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) pmaxs.Elem(i) = box.PMax(); } - Array linenums; + NgArray linenums; int k2; for (i = 1; i <= GetNLines(); i++) @@ -1068,7 +1072,7 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) //berechne minimale distanz von chart zu einem nicht-outerchart-punkt in jedem randpunkt einer chart - Array acttrigs(GetNT()); //outercharttrigs + NgArray acttrigs(GetNT()); //outercharttrigs acttrigs = 0; for (i = 1; i <= GetNOCharts(); i++) @@ -1077,7 +1081,7 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) if (multithread.terminate) {PopStatus(); return;} - RestrictHChartDistOneChart(i, acttrigs, mesh, gh, 1., 0.); + RestrictHChartDistOneChart(i, acttrigs, mesh, gh, 1., 0., stlparam); } PopStatus(); @@ -1116,8 +1120,9 @@ void STLGeometry :: RestrictLocalH(class Mesh & mesh, double gh) } } -void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrigs, - class Mesh & mesh, double gh, double fact, double minh) +void STLGeometry :: RestrictHChartDistOneChart(ChartId chartnum, NgArray& acttrigs, + class Mesh & mesh, double gh, double fact, double minh, + const STLParameters& stlparam) { static int timer1 = NgProfiler::CreateTimer ("restrictH OneChart 1"); static int timer2 = NgProfiler::CreateTimer ("restrictH OneChart 2"); @@ -1131,20 +1136,20 @@ void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrig // mincalch = 1E10; //maxcalch = -1E10; - Array limes1; - Array limes2; + NgArray limes1; + NgArray limes2; - Array plimes1; - Array plimes2; + NgArray plimes1; + NgArray plimes2; - Array plimes1trigs; //check from which trig the points come - Array plimes2trigs; + NgArray plimes1trigs; //check from which trig the points come + NgArray plimes2trigs; - Array plimes1origin; //either the original pointnumber or zero, if new point + NgArray plimes1origin; //either the original pointnumber or zero, if new point int divisions = 10; - int np1, np2; + STLPointId np1, np2; // Point3d p3p1, p3p2; STLTriangle tt; @@ -1162,9 +1167,9 @@ void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrig for (int j = 1; j <= chart.GetNChartT(); j++) { - int t = chart.GetChartTrig(j); + int t = chart.GetChartTrig1(j); tt = GetTriangle(t); - for (int k = 1; k <= 3; k++) + for (int k = 1; k <= NONeighbourTrigs(t); k++) { int nt = NeighbourTrig(t,k); if (GetChartNr(nt) != chartnum) @@ -1212,11 +1217,11 @@ void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrig NgProfiler::StartTimer (timer2); for (int j = 1; j <= chart.GetNT(); j++) - acttrigs.Elem(chart.GetTrig(j)) = chartnum; + acttrigs.Elem(chart.GetTrig1(j)) = chartnum; for (int j = 1; j <= chart.GetNOuterT(); j++) { - int t = chart.GetOuterTrig(j); + int t = chart.GetOuterTrig1(j); tt = GetTriangle(t); for (int k = 1; k <= 3; k++) { @@ -1276,7 +1281,7 @@ void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrig Point3dTree stree(bbox.PMin(), bbox.PMax()); for (int j = 1; j <= plimes2.Size(); j++) stree.Insert (plimes2.Get(j), j); - Array foundpts; + NgArray foundpts; NgProfiler::StopTimer (timer3a); NgProfiler::StartTimer (timer3b); @@ -1345,7 +1350,8 @@ void STLGeometry :: RestrictHChartDistOneChart(int chartnum, Array& acttrig } -int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, MeshingParameters & mparam) +int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, const MeshingParameters & mparam, + const STLParameters& stlparam) { if (mparam.perfstepsstart > mparam.perfstepsend) return 0; @@ -1367,12 +1373,16 @@ int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, MeshingP stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10), mparam.grading); mesh -> LoadLocalMeshSize (mparam.meshsizefilename); + + if (mparam.uselocalh) + for (auto mspnt : mparam.meshsize_points) + mesh->RestrictLocalH(mspnt.pnt, mspnt.h); success = 0; //mesh->DeleteMesh(); - STLMeshing (*stlgeometry, *mesh); + STLMeshing (*stlgeometry, *mesh, mparam, stlparam); stlgeometry->edgesfound = 1; stlgeometry->surfacemeshed = 0; @@ -1399,7 +1409,7 @@ int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, MeshingP } success = 0; - int retval = STLSurfaceMeshing (*stlgeometry, *mesh); + int retval = STLSurfaceMeshing (*stlgeometry, *mesh, mparam, stlparam); if (retval == MESHING3_OK) { PrintMessage(3,"Success !!!!"); @@ -1471,19 +1481,23 @@ int STLMeshingDummy (STLGeometry* stlgeometry, shared_ptr & mesh, MeshingP mesh -> LoadLocalMeshSize (mparam.meshsizefilename); mesh -> CalcLocalHFromSurfaceCurvature (mparam.grading, stlparam.resthsurfmeshcurvfac); - mparam.optimize2d = "cmsmSm"; - STLSurfaceOptimization (*stlgeometry, *mesh, mparam); + MeshingParameters mpar = mparam; + mpar.optimize2d = "cmsmSm"; + STLSurfaceOptimization (*stlgeometry, *mesh, mpar); #ifdef STAT_STREAM (*statout) << GetTime() << " & "; #endif - mparam.Render(); + mpar.Render(); } stlgeometry->surfaceoptimized = 1; } if (multithread.terminate) return 0; + if(stlgeometry->IsSurfaceSTL()) + return 0; + if (mparam.perfstepsstart <= MESHCONST_MESHVOLUME && mparam.perfstepsend >= MESHCONST_MESHVOLUME) { diff --git a/libsrc/stlgeom/stlline.cpp b/libsrc/stlgeom/stlline.cpp index 9a49b748..0f4a5369 100644 --- a/libsrc/stlgeom/stlline.cpp +++ b/libsrc/stlgeom/stlline.cpp @@ -197,7 +197,7 @@ int STLEdgeDataList :: GetNConfCandEPP(int p) const } -void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, Array& line) +void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, NgArray& line) { int status = Get(GetEdgeNum(ep1,ep2)).GetStatus(); @@ -424,7 +424,7 @@ int STLEdgeDataList :: GetNConfCandEPP(int p) const } -void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, Array& line) +void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, NgArray& line) { int status = Get(GetEdgeNum(ep1,ep2)).GetStatus(); @@ -473,7 +473,7 @@ void STLEdgeDataList :: BuildLineWithEdge(int ep1, int ep2, Array& line) } -int Exists(int p1, int p2, const Array& line) +int Exists(int p1, int p2, const NgArray& line) { int i; for (i = 1; i <= line.Size(); i++) @@ -485,7 +485,7 @@ int Exists(int p1, int p2, const Array& line) return 0; } -void STLEdgeDataList :: BuildClusterWithEdge(int ep1, int ep2, Array& line) +void STLEdgeDataList :: BuildClusterWithEdge(int ep1, int ep2, NgArray& line) { int status = Get(GetEdgeNum(ep1,ep2)).GetStatus(); @@ -493,7 +493,8 @@ void STLEdgeDataList :: BuildClusterWithEdge(int ep1, int ep2, Array& li int j, i, k; int oldend; int newend = 1; - int pnew, ennew(0); + STLPointId pnew; + int ennew(0); int changed = 1; while (changed) @@ -581,59 +582,57 @@ int STLLine :: GetRightTrig(int nr) const return righttrigs.Get(nr); }; -double STLLine :: GetSegLen(const Array >& ap, int nr) const +double STLLine :: GetSegLen(const Array,STLPointId>& ap, int nr) const { - return Dist(ap.Get(PNum(nr)),ap.Get(PNum(nr+1))); + return Dist(ap[PNum(nr)],ap[PNum(nr+1)]); } -double STLLine :: GetLength(const Array >& ap) const +double STLLine :: GetLength(const Array,STLPointId>& ap) const { double len = 0; for (int i = 2; i <= pts.Size(); i++) - { - len += (ap.Get(pts.Get(i)) - ap.Get(pts.Get(i-1))).Length(); - } + len += (ap[pts.Get(i)] - ap[pts.Get(i-1)]).Length(); return len; } -void STLLine :: GetBoundingBox (const Array > & ap, Box<3> & box) const +void STLLine :: GetBoundingBox (const Array,STLPointId> & ap, Box<3> & box) const { - box.Set (ap.Get (pts[0])); + box.Set (ap[pts[0]]); for (int i = 1; i < pts.Size(); i++) - box.Add (ap.Get(pts[i])); + box.Add (ap[pts[i]]); } Point<3> STLLine :: -GetPointInDist(const Array >& ap, double dist, int& index) const +GetPointInDist(const Array,STLPointId>& ap, double dist, int& index) const { if (dist <= 0) { index = 1; - return ap.Get(StartP()); + return ap[StartP()]; } double len = 0; int i; for (i = 1; i < pts.Size(); i++) { - double seglen = Dist (ap.Get(pts.Get(i)), - ap.Get(pts.Get(i+1))); + double seglen = Dist (ap[pts.Get(i)], + ap[pts.Get(i+1)]); if (len + seglen > dist) { index = i; double relval = (dist - len) / (seglen + 1e-16); - Vec3d v (ap.Get(pts.Get(i)), ap.Get(pts.Get(i+1))); - return ap.Get(pts.Get(i)) + relval * v; + Vec3d v (ap[pts.Get(i)], ap[pts.Get(i+1)]); + return ap[pts.Get(i)] + relval * v; } len += seglen; } index = pts.Size() - 1; - return ap.Get(EndP()); + return ap[EndP()]; } @@ -644,8 +643,8 @@ double GetH(const Point3d& p, double x) return stlgh;//+0.5)*(x+0.5); } */ -STLLine* STLLine :: Mesh(const Array >& ap, - Array& mp, double ghi, +STLLine* STLLine :: Mesh(const Array,STLPointId>& ap, + NgArray& mp, double ghi, class Mesh& mesh) const { static int timer1a = NgProfiler::CreateTimer ("mesh stl-line 1a"); @@ -678,8 +677,8 @@ STLLine* STLLine :: Mesh(const Array >& ap, int nph = 10+int(maxseglen / minh); //anzahl der integralauswertungen pro segment - Array inthi(GetNS()*nph); - Array curvelen(GetNS()*nph); + NgArray inthi(GetNS()*nph); + NgArray curvelen(GetNS()*nph); NgProfiler::StopTimer (timer1a); NgProfiler::StartTimer (timer1b); @@ -720,7 +719,7 @@ STLLine* STLLine :: Mesh(const Array >& ap, int j = 1; - p = ap.Get(StartP()); + p = ap[StartP()]; int pn = AddPointIfNotExists(mp, p, 1e-10*diam); int segn = 1; @@ -773,7 +772,7 @@ STLLine* STLLine :: Mesh(const Array >& ap, NgProfiler::StartTimer (timer3); - p = ap.Get(EndP()); + p = ap[EndP()]; pn = AddPointIfNotExists(mp, p, 1e-10*diam); segn = GetNS(); line->AddPoint(pn); diff --git a/libsrc/stlgeom/stlline.hpp b/libsrc/stlgeom/stlline.hpp index 06ce5857..c6a06c7b 100644 --- a/libsrc/stlgeom/stlline.hpp +++ b/libsrc/stlgeom/stlline.hpp @@ -9,6 +9,8 @@ /* Date: 20. Nov. 99 */ /**************************************************************************/ +namespace netgen { + class STLGeometry; class STLTopology; @@ -79,7 +81,7 @@ class STLEdgeDataList { private: INDEX_2_HASHTABLE hashtab; - Array edgedata; + NgArray edgedata; TABLE edgesperpoint; public: @@ -118,7 +120,7 @@ public: void Write(ofstream& of) const; void Read(ifstream& ifs); - void BuildLineWithEdge(int ep1, int ep2, Array& line); + void BuildLineWithEdge(int ep1, int ep2, NgArray& line); int GetNEPPStat(int p, int status) const; int GetNConfCandEPP(int p) const; @@ -145,10 +147,10 @@ class STLLine { private: const STLGeometry * geometry; - Array pts; - Array lefttrigs; - Array righttrigs; - Array dists; + NgArray pts; + NgArray lefttrigs; + NgArray righttrigs; + NgArray dists; int split; public: @@ -158,11 +160,11 @@ public: int NP() const {return pts.Size();} int GetNS() const; void GetSeg(int nr, int& p1, int& p2) const; - double GetSegLen(const Array >& ap, int nr) const; + double GetSegLen(const Array,STLPointId>& ap, int nr) const; int GetLeftTrig(int nr) const; int GetRightTrig(int nr) const; double GetDist(int nr) const { return dists.Get(nr);}; - void GetBoundingBox (const Array > & ap, Box<3> & box) const; + void GetBoundingBox (const Array,STLPointId> & ap, Box<3> & box) const; void AddLeftTrig(int nr) {lefttrigs.Append(nr);} void AddRightTrig(int nr) {righttrigs.Append(nr);} @@ -170,19 +172,22 @@ public: int StartP() const {return pts.Get(1);} int EndP() const {return pts.Get(pts.Size());} - double GetLength(const Array >& ap) const; + double GetLength(const Array,STLPointId>& ap) const; //suche punkt in entfernung (in linienkoordinaten) dist //in index ist letzter punkt VOR dist (d.h. max pts.Size()-1) - Point<3> GetPointInDist(const Array >& ap, double dist, int& index) const; + Point<3> GetPointInDist(const Array,STLPointId>& ap, double dist, int& index) const; //return a meshed polyline - STLLine* Mesh(const Array >& ap, - Array& mp, double ghi, + STLLine* Mesh(const Array,STLPointId>& ap, + NgArray& mp, double ghi, class Mesh& mesh) const; void DoSplit() {split = 1;} int ShouldSplit() const {return split;} }; + +} // namespace netgen + #endif diff --git a/libsrc/stlgeom/stlpkg.cpp b/libsrc/stlgeom/stlpkg.cpp index a016a71b..6ca94118 100644 --- a/libsrc/stlgeom/stlpkg.cpp +++ b/libsrc/stlgeom/stlpkg.cpp @@ -21,6 +21,8 @@ namespace netgen { DLL_HEADER extern shared_ptr ng_geometry; DLL_HEADER extern shared_ptr mesh; + DLL_HEADER extern MeshingParameters mparam; + DLL_HEADER extern STLParameters stlparam; static VisualSceneSTLGeometry vsstlgeom; static VisualSceneSTLMeshing vsstlmeshing; @@ -34,7 +36,7 @@ namespace netgen class STLGeometryVisRegister : public GeometryRegister { public: - virtual NetgenGeometry * Load (string filename) const { return NULL; } + virtual NetgenGeometry * Load (const filesystem::path & filename) const { return NULL; } virtual VisualScene * GetVisualScene (const NetgenGeometry * geom) const; virtual void SetParameters (Tcl_Interp * interp) { @@ -76,11 +78,6 @@ namespace netgen stlparam.resthlinelengthenable = atoi (Tcl_GetVar (interp, "::stloptions.resthlinelengthenable", 0)); - stlparam.resthcloseedgefac = - atof (Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0)); - stlparam.resthcloseedgeenable = - atoi (Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0)); - stlparam.resthedgeanglefac = atof (Tcl_GetVar (interp, "::stloptions.resthedgeanglefac", 0)); stlparam.resthedgeangleenable = @@ -243,15 +240,15 @@ namespace netgen } else if (strcmp (argv[1], "markdirtytrigs") == 0) { - stlgeometry->MarkDirtyTrigs(); + stlgeometry->MarkDirtyTrigs(stlparam); } else if (strcmp (argv[1], "smoothdirtytrigs") == 0) { - stlgeometry->SmoothDirtyTrigs(); + stlgeometry->SmoothDirtyTrigs(stlparam); } else if (strcmp (argv[1], "smoothrevertedtrigs") == 0) { - stlgeometry->GeomSmoothRevertedTrigs(); + stlgeometry->GeomSmoothRevertedTrigs(stlparam); } else if (strcmp (argv[1], "invertselectedtrig") == 0) { @@ -306,11 +303,11 @@ namespace netgen } else if (strcmp (argv[1], "smoothnormals") == 0) { - stlgeometry->SmoothNormals(); + stlgeometry->SmoothNormals(stlparam); } else if (strcmp (argv[1], "marknonsmoothnormals") == 0) { - stlgeometry->MarkNonSmoothNormals(); + stlgeometry->MarkNonSmoothNormals(stlparam); } else if (strcmp (argv[1], "addexternaledge") == 0) { @@ -359,7 +356,7 @@ namespace netgen } else if (strcmp (argv[1], "buildedges") == 0) { - stlgeometry->STLDoctorBuildEdges(); + stlgeometry->STLDoctorBuildEdges(stlparam); } else if (strcmp (argv[1], "confirmedge") == 0) { @@ -393,6 +390,16 @@ namespace netgen { stlgeometry->STLDoctorConfirmedToCandidateEdges(); } + else if (strcmp (argv[1], "writechart") == 0) + { + int st = stlgeometry->GetSelectTrig(); + + if (st >= 1 && st <= stlgeometry->GetNT() && stlgeometry->AtlasMade()) + { + auto chartnumber = stlgeometry->GetChartNr(st); + stlgeometry->WriteChartToFile(chartnumber, "chart.stlb"); + } + } } return TCL_OK; @@ -456,12 +463,12 @@ namespace netgen } if (strcmp (argv[1], "topology_ok") == 0) { - sprintf (buf, "%d", stlgeometry->Topology_Ok()); + snprintf (buf, size(buf), "%d", stlgeometry->Topology_Ok()); Tcl_SetResult (interp, buf, TCL_STATIC); } if (strcmp (argv[1], "orientation_ok") == 0) { - sprintf (buf, "%d", stlgeometry->Orientation_Ok()); + snprintf (buf, size(buf), "%d", stlgeometry->Orientation_Ok()); Tcl_SetResult (interp, buf, TCL_STATIC); } } @@ -481,24 +488,24 @@ namespace netgen - sprintf (buf, "%i", (int)data[0]); + snprintf (buf, size(buf), "%i", (int)data[0]); Tcl_SetVar (interp, argv[1], buf, 0); - sprintf (buf, "%5.3g", data[1]); + snprintf (buf, size(buf), "%5.3g", data[1]); Tcl_SetVar (interp, argv[2], buf, 0); - sprintf (buf, "%5.3g", data[2]); + snprintf (buf, size(buf), "%5.3g", data[2]); Tcl_SetVar (interp, argv[3], buf, 0); - sprintf (buf, "%5.3g", data[3]); + snprintf (buf, size(buf), "%5.3g", data[3]); Tcl_SetVar (interp, argv[4], buf, 0); - sprintf (buf, "%5.3g", data[4]); + snprintf (buf, size(buf), "%5.3g", data[4]); Tcl_SetVar (interp, argv[5], buf, 0); - sprintf (buf, "%5.3g", data[5]); + snprintf (buf, size(buf), "%5.3g", data[5]); Tcl_SetVar (interp, argv[6], buf, 0); - sprintf (buf, "%5.3g", data[6]); + snprintf (buf, size(buf), "%5.3g", data[6]); Tcl_SetVar (interp, argv[7], buf, 0); - sprintf (buf, "%i", (int)data[7]); + snprintf (buf, size(buf), "%i", (int)data[7]); Tcl_SetVar (interp, argv[8], buf, 0); return TCL_OK; @@ -526,7 +533,7 @@ namespace netgen mesh -> SetLocalH (stlgeometry->GetBoundingBox().PMin() - Vec3d(10, 10, 10), stlgeometry->GetBoundingBox().PMax() + Vec3d(10, 10, 10), mparam.grading); - stlgeometry -> RestrictLocalH(*mesh, mparam.maxh); + stlgeometry -> RestrictLocalH(*mesh, mparam.maxh, stlparam, mparam); if (stlparam.resthsurfmeshcurvenable) mesh -> CalcLocalHFromSurfaceCurvature (mparam.grading, diff --git a/libsrc/stlgeom/stltool.cpp b/libsrc/stlgeom/stltool.cpp index 41f0f1d7..d2377cb1 100644 --- a/libsrc/stlgeom/stltool.cpp +++ b/libsrc/stlgeom/stltool.cpp @@ -13,7 +13,7 @@ namespace netgen //add a point into a pointlist, return pointnumber -int AddPointIfNotExists(Array& ap, const Point3d& p, double eps) +int AddPointIfNotExists(NgArray& ap, const Point3d& p, double eps) { double eps2 = sqr(eps); for (int i = 1; i <= ap.Size(); i++) @@ -282,7 +282,7 @@ STLReadTriangle :: STLReadTriangle (const Point<3> * apts, -STLTriangle :: STLTriangle(const int * apts) +STLTriangle :: STLTriangle(const STLPointId * apts) { pts[0] = apts[0]; pts[1] = apts[1]; @@ -318,7 +318,7 @@ int STLTriangle :: IsWrongNeighbourFrom(const STLTriangle& t) const return 0; } -void STLTriangle :: GetNeighbourPoints(const STLTriangle& t, int& p1, int& p2) const +void STLTriangle :: GetNeighbourPoints(const STLTriangle& t, STLPointId & p1, STLPointId & p2) const { for(int i = 1; i <= 3; i++) for(int j = 1; j <= 3; j++) @@ -333,7 +333,8 @@ void STLTriangle :: GetNeighbourPoints(const STLTriangle& t, int& p1, int& p2) c PrintSysError("Get neighbourpoints failed!"); } -int STLTriangle :: GetNeighbourPointsAndOpposite(const STLTriangle& t, int& p1, int& p2, int& po) const +int STLTriangle :: GetNeighbourPointsAndOpposite(const STLTriangle& t, STLPointId & p1, + STLPointId & p2, STLPointId & po) const { for(int i = 1; i <= 3; i++) for(int j = 1; j <= 3; j++) @@ -349,11 +350,11 @@ int STLTriangle :: GetNeighbourPointsAndOpposite(const STLTriangle& t, int& p1, return 0; } -Vec<3> STLTriangle :: GeomNormal(const Array >& ap) const +Vec<3> STLTriangle :: GeomNormal(const Array,STLPointId>& ap) const { - const Point<3> & p1 = ap.Get(PNum(1)); - const Point<3> & p2 = ap.Get(PNum(2)); - const Point<3> & p3 = ap.Get(PNum(3)); + const Point<3> & p1 = ap[PNum(1)]; + const Point<3> & p2 = ap[PNum(2)]; + const Point<3> & p3 = ap[PNum(3)]; return Cross(p2-p1, p3-p1); } @@ -382,13 +383,13 @@ void STLTriangle :: ChangeOrientation() -double STLTriangle :: Area(const Array >& ap) const +double STLTriangle :: Area(const Array,STLPointId>& ap) const { - return 0.5 * Cross(ap.Get(PNum(2))-ap.Get(PNum(1)), - ap.Get(PNum(3))-ap.Get(PNum(1))).Length(); + return 0.5 * Cross(ap[PNum(2)]-ap[PNum(1)], + ap[PNum(3)]-ap[PNum(1)]).Length(); } -double STLTriangle :: MinHeight(const Array >& ap) const +double STLTriangle :: MinHeight(const Array,STLPointId>& ap) const { double ml = MaxLength(ap); if (ml != 0) {return 2.*Area(ap)/ml;} @@ -396,19 +397,19 @@ double STLTriangle :: MinHeight(const Array >& ap) const return 0; } -double STLTriangle :: MaxLength(const Array >& ap) const +double STLTriangle :: MaxLength(const Array,STLPointId>& ap) const { - return max3(Dist(ap.Get(PNum(1)),ap.Get(PNum(2))), - Dist(ap.Get(PNum(2)),ap.Get(PNum(3))), - Dist(ap.Get(PNum(3)),ap.Get(PNum(1)))); + return max3(Dist(ap[PNum(1)],ap[PNum(2)]), + Dist(ap[PNum(2)],ap[PNum(3)]), + Dist(ap[PNum(3)],ap[PNum(1)])); } -void STLTriangle :: ProjectInPlain(const Array >& ap, +void STLTriangle :: ProjectInPlain(const Array,STLPointId>& ap, const Vec<3> & n, Point<3> & pp) const { - const Point<3> & p1 = ap.Get(PNum(1)); - const Point<3> & p2 = ap.Get(PNum(2)); - const Point<3> & p3 = ap.Get(PNum(3)); + const Point<3> & p1 = ap[PNum(1)]; + const Point<3> & p2 = ap[PNum(2)]; + const Point<3> & p3 = ap[PNum(3)]; Vec<3> v1 = p2 - p1; Vec<3> v2 = p3 - p1; @@ -430,13 +431,13 @@ void STLTriangle :: ProjectInPlain(const Array >& ap, } -int STLTriangle :: ProjectInPlain (const Array >& ap, +int STLTriangle :: ProjectInPlain (const Array,STLPointId>& ap, const Vec<3> & nproj, Point<3> & pp, Vec<3> & lam) const { - const Point<3> & p1 = ap.Get(PNum(1)); - const Point<3> & p2 = ap.Get(PNum(2)); - const Point<3> & p3 = ap.Get(PNum(3)); + const Point<3> & p1 = ap[PNum(1)]; + const Point<3> & p2 = ap[PNum(2)]; + const Point<3> & p3 = ap[PNum(3)]; Vec<3> v1 = p2-p1; Vec<3> v2 = p3-p1; @@ -468,12 +469,12 @@ int STLTriangle :: ProjectInPlain (const Array >& ap, -void STLTriangle :: ProjectInPlain(const Array >& ap, +void STLTriangle :: ProjectInPlain(const Array,STLPointId>& ap, Point<3> & pp) const { - const Point<3> & p1 = ap.Get(PNum(1)); - const Point<3> & p2 = ap.Get(PNum(2)); - const Point<3> & p3 = ap.Get(PNum(3)); + const Point<3> & p1 = ap[PNum(1)]; + const Point<3> & p2 = ap[PNum(2)]; + const Point<3> & p3 = ap[PNum(3)]; Vec<3> v1 = p2 - p1; Vec<3> v2 = p3 - p1; @@ -488,12 +489,12 @@ void STLTriangle :: ProjectInPlain(const Array >& ap, pp = pp + (nfact) * nt; } -int STLTriangle :: PointInside(const Array > & ap, +bool STLTriangle :: PointInside(const Array,STLPointId> & ap, const Point<3> & pp) const { - const Point<3> & p1 = ap.Get(PNum(1)); - const Point<3> & p2 = ap.Get(PNum(2)); - const Point<3> & p3 = ap.Get(PNum(3)); + const Point<3> & p1 = ap[PNum(1)]; + const Point<3> & p2 = ap[PNum(2)]; + const Point<3> & p3 = ap[PNum(3)]; Vec<3> v1 = p2 - p1; Vec<3> v2 = p3 - p1; @@ -532,7 +533,7 @@ int STLTriangle :: PointInside(const Array > & ap, return 0; } -double STLTriangle :: GetNearestPoint(const Array >& ap, +double STLTriangle :: GetNearestPoint(const Array,STLPointId>& ap, Point<3> & p3d) const { Point<3> p = p3d; @@ -548,7 +549,7 @@ double STLTriangle :: GetNearestPoint(const Array >& ap, for (int j = 1; j <= 3; j++) { p = p3d; - dist = GetDistFromLine(ap.Get(PNum(j)), ap.Get(PNumMod(j+1)), p); + dist = GetDistFromLine(ap[PNum(j)], ap[PNumMod(j+1)], p); if (dist < nearest) { nearest = dist; @@ -560,14 +561,12 @@ double STLTriangle :: GetNearestPoint(const Array >& ap, } } -int STLTriangle :: HasEdge(int p1, int p2) const +bool STLTriangle :: HasEdge(STLPointId p1, STLPointId p2) const { - int i; - for (i = 1; i <= 3; i++) - { - if (p1 == PNum(i) && p2 == PNumMod(i+1)) {return 1;} - } - return 0; + for (int i = 1; i <= 3; i++) + if (p1 == PNum(i) && p2 == PNumMod(i+1)) + return true; + return false; } ostream& operator<<(ostream& os, const STLTriangle& t) @@ -590,7 +589,7 @@ STLTopEdge :: STLTopEdge () status = ED_UNDEFINED; } -STLTopEdge :: STLTopEdge (int p1, int p2, int trig1, int trig2) +STLTopEdge :: STLTopEdge (STLPointId p1, STLPointId p2, int trig1, int trig2) { pts[0] = p1; pts[1] = p2; @@ -607,12 +606,13 @@ STLTopEdge :: STLTopEdge (int p1, int p2, int trig1, int trig2) //+++++++++++++++++++ STL CHART +++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -STLChart :: STLChart(STLGeometry * ageometry) +STLChart :: STLChart(STLGeometry * ageometry, const STLParameters& astlparam) + : geometry(ageometry), stlparam(astlparam) { - // charttrigs = new Array (0,0); - // outertrigs = new Array (0,0); - // ilimit = new Array (0,0); - // olimit = new Array (0,0); + // charttrigs = new NgArray (0,0); + // outertrigs = new NgArray (0,0); + // ilimit = new NgArray (0,0); + // olimit = new NgArray (0,0); geometry = ageometry; @@ -620,7 +620,7 @@ STLChart :: STLChart(STLGeometry * ageometry) { Box<3> box = geometry->GetBoundingBox(); box.Increase (0.2*box.Diam()+1e-12); - searchtree = new BoxTree<3> (box); + searchtree = new BoxTree<3,STLTrigId> (box); /* searchtree = new BoxTree<3> (geometry->GetBoundingBox().PMin() - Vec3d(1,1,1), geometry->GetBoundingBox().PMax() + Vec3d(1,1,1)); @@ -635,7 +635,7 @@ STLChart :: ~STLChart() delete searchtree; } -void STLChart :: AddChartTrig(int i) +void STLChart :: AddChartTrig(STLTrigId i) { // static int timer = NgProfiler::CreateTimer ("STLChart::AddChartTrig"); // NgProfiler::RegionTimer reg(timer); @@ -667,7 +667,7 @@ void STLChart :: AddChartTrig(int i) } } -void STLChart :: AddOuterTrig(int i) +void STLChart :: AddOuterTrig(STLTrigId i) { // static int timer = NgProfiler::CreateTimer ("STLChart::AddOuterTrig"); // NgProfiler::RegionTimer reg(timer); @@ -689,20 +689,14 @@ void STLChart :: AddOuterTrig(int i) {searchtree->Insert (pmin, pmax, i);} } -int STLChart :: IsInWholeChart(int nr) const +bool STLChart :: IsInWholeChart(int nr) const { - for (int i = 1; i <= charttrigs.Size(); i++) - if (charttrigs.Get(i) == nr) return 1; - - for (int i = 1; i <= outertrigs.Size(); i++) - if (outertrigs.Get(i) == nr) return 1; - - return 0; + return charttrigs.Contains(nr) || outertrigs.Contains(nr); } void STLChart :: GetTrianglesInBox (const Point3d & pmin, const Point3d & pmax, - Array & trias) const + NgArray & trias) const { if (geomsearchtreeon) {PrintMessage(5,"geomsearchtreeon is set!!!");} @@ -718,7 +712,7 @@ void STLChart :: GetTrianglesInBox (const Point3d & pmin, int nt = GetNT(); for (int i = 1; i <= nt; i++) { - int trignum = GetTrig(i); + STLTrigId trignum = GetTrig1(i); const STLTriangle & trig = geometry->GetTriangle(trignum); Box<3> box2(geometry->GetPoint (trig.PNum(1)), geometry->GetPoint (trig.PNum(2)), @@ -731,33 +725,33 @@ void STLChart :: GetTrianglesInBox (const Point3d & pmin, } //trigs may contain the same triangle double -void STLChart :: MoveToOuterChart(const Array& trigs) +void STLChart :: MoveToOuterChart(const NgArray& trigs) { if (!trigs.Size()) return; for (int i = 1; i <= trigs.Size(); i++) { - if (charttrigs.Get(trigs.Get(i)) != -1) - {AddOuterTrig(charttrigs.Get(trigs.Get(i)));} - charttrigs.Elem(trigs.Get(i)) = -1; + if (charttrigs[trigs.Get(i)-1] != -1) + AddOuterTrig(charttrigs[trigs.Get(i)-1]); + charttrigs[trigs.Get(i)-1] = -1; } DelChartTrigs(trigs); } //trigs may contain the same triangle double -void STLChart :: DelChartTrigs(const Array& trigs) +void STLChart :: DelChartTrigs(const NgArray& trigs) { if (!trigs.Size()) return; for (int i = 1; i <= trigs.Size(); i++) - charttrigs.Elem(trigs.Get(i)) = -1; + charttrigs[trigs.Get(i)-1] = -1; int cnt = 0; for (int i = 1; i <= charttrigs.Size(); i++) { - if (charttrigs.Elem(i) == -1) + if (charttrigs[i-1] == -1) cnt++; if (cnt != 0 && i < charttrigs.Size()) - charttrigs.Elem(i-cnt+1) = charttrigs.Get(i+1); + charttrigs[i-cnt] = charttrigs[i]; } int i = charttrigs.Size() - trigs.Size(); @@ -767,8 +761,8 @@ void STLChart :: DelChartTrigs(const Array& trigs) { PrintMessage(7, "Warning: unsecure routine due to first use of searchtrees!!!"); //bould new searchtree!!! - searchtree = new BoxTree<3> (geometry->GetBoundingBox().PMin() - Vec3d(1,1,1), - geometry->GetBoundingBox().PMax() + Vec3d(1,1,1)); + searchtree = new BoxTree<3,STLTrigId> (geometry->GetBoundingBox().PMin() - Vec3d(1,1,1), + geometry->GetBoundingBox().PMax() + Vec3d(1,1,1)); for (int i = 1; i <= charttrigs.Size(); i++) { @@ -801,6 +795,104 @@ void STLChart :: SetNormal (const Point<3> & apref, const Vec<3> & anormal) t2 = Cross (normal, t1); } +void STLChart :: BuildInnerSearchTree() +{ + Box<2> chart_bbox(Box<2>::EMPTY_BOX); + for (STLTrigId trigid : charttrigs) + { + for (STLPointId pi : (*geometry)[trigid].PNums()) + { + Point<3> p = (*geometry)[pi]; + Point<2> p2d = Project2d(p); + chart_bbox.Add(p2d); + } + } + chart_bbox.Increase (1e-2*chart_bbox.Diam()); + inner_searchtree = make_unique> (chart_bbox); + for (STLTrigId trigid : charttrigs) + { + Box<2> bbox(Box<2>::EMPTY_BOX); + for (STLPointId pi : (*geometry)[trigid].PNums()) + { + Point<3> p = (*geometry)[pi]; + Point<2> p2d = Project2d(p); + bbox.Add(p2d); + } + inner_searchtree->Insert (bbox, trigid); + } +} + +STLTrigId STLChart :: ProjectNormal (Point<3> & p3d) const +{ + + int nt = GetNT(); + double lamtol = 1e-6; + QuadraticFunction3d quadfun(p3d, GetNormal()); + + int starttrig = 1; + if (inner_searchtree) + { + starttrig = GetNChartT()+1; + Point<2> p2d = Project2d (p3d); + + + bool inside = false; + STLTrigId trignum; + inner_searchtree->GetFirstIntersecting(p2d, p2d, [&](auto i) + { + auto & trig = geometry->GetTriangle(i); + const Point<3> & c = trig.center; + + if (quadfun.Eval(c) > sqr (trig.rad)) + return false; + + Point<3> p = p3d; + Vec<3> lam; + int err = trig.ProjectInPlain(geometry->GetPoints(), GetNormal(), p, lam); + inside = (err == 0 && lam(0) > -lamtol && + lam(1) > -lamtol && (1-lam(0)-lam(1)) > -lamtol); + + if (inside) + { + trignum=i; + p3d = p; + return true; + } + return false; + }); + + if(inside) + return trignum; + } + + + for (int j = starttrig; j <= nt; j++) + { + STLTrigId i = GetTrig1(j); + auto & trig = geometry->GetTriangle(i); + const Point<3> & c = trig.center; + + if (quadfun.Eval(c) > sqr (trig.rad)) + continue; + + Point<3> p = p3d; + Vec<3> lam; + int err = trig.ProjectInPlain(geometry->GetPoints(), GetNormal(), p, lam); + bool inside = (err == 0 && lam(0) > -lamtol && + lam(1) > -lamtol && (1-lam(0)-lam(1)) > -lamtol); + + if (inside) + { + p3d = p; + return i; + } + } + + return 0; +} + + + /* Point<2> STLChart :: Project2d (const Point<3> & p3d) const { @@ -820,7 +912,7 @@ public: /* STLBoundarySeg :: -STLBoundarySeg (int ai1, int ai2, const Array > & points, +STLBoundarySeg (int ai1, int ai2, const NgArray > & points, const STLChart * chart) { i1 = ai1; @@ -851,6 +943,7 @@ STLBoundary :: STLBoundary (STLGeometry * ageometry) { ; } +/* void STLBoundary :: AddOrDelSegment(const STLBoundarySeg & seg) { bool found = false; @@ -868,9 +961,11 @@ void STLBoundary :: AddOrDelSegment(const STLBoundarySeg & seg) boundary.SetSize(boundary.Size()-1); } } +*/ void STLBoundary ::AddTriangle(const STLTriangle & t) { + // static Timer timer("STLBoundary::AddTriangle"); RegionTimer reg(timer); // static int timer_old = NgProfiler::CreateTimer ("STLChart::AddTriangle_old"); // static int timer_new = NgProfiler::CreateTimer ("STLChart::AddTriangle_new"); @@ -1006,6 +1101,9 @@ void STLBoundary ::AddTriangle(const STLTriangle & t) segs[1] = INDEX_2(t[1], t[2]); segs[2] = INDEX_2(t[2], t[0]); + if(!searchtree) + BuildSearchTree(); + for (auto seg : segs) { STLBoundarySeg bseg(seg[0], seg[1], geometry->GetPoints(), chart); @@ -1014,33 +1112,21 @@ void STLBoundary ::AddTriangle(const STLTriangle & t) INDEX_2 op(seg[1], seg[0]); if (boundary_ht.Used(op)) { - // cout << "delete " << op << endl; boundary_ht.Delete(op); + if (searchtree) + searchtree->DeleteElement(op); } else { - // cout << "insert " << seg << endl; boundary_ht[seg] = bseg; + if (searchtree) + searchtree->Insert (bseg.BoundingBox(), seg); } } - /* - // cout << "bounds = " << boundary << endl; - cout << "bounds:"; - for (auto & val : boundary) - cout << val.I1() << "-" << val.I2() << endl; - cout << "ht = " << boundary_ht << endl; - if (boundary_ht.UsedElements() != boundary.Size()) - { - cout << "wrong count" << endl; - char key; - cin >> key; - } - */ - // NgProfiler::StopTimer (timer_new); } -int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> & sn, - double sinchartangle, int divisions, Array >& points, double eps) +bool STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> & sn, + double sinchartangle, int divisions, Array,STLPointId>& points, double eps) { if (usechartnormal) return TestSegChartNV (p1, p2, sn); @@ -1049,7 +1135,7 @@ int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> // for statistics { int i; - static Array cntclass; + static NgArray cntclass; static int cnt = 0; static int cnti = 0, cnto = 0; static long int cntsegs = 0; @@ -1076,7 +1162,7 @@ int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> /* (*testout) << "TestSeg-calls for classes:" << endl; (*testout) << cnti << " inner calls, " << cnto << " outercalls" << endl; - (*testout) << "total testes segments: " << cntsegs << endl; + (*testout) << "total tested segments: " << cntsegs << endl; for (i = 1; i <= cntclass.Size(); i++) { (*testout) << int (exp (i * log(2.0))) << " bnd segs: " << cntclass.Get(i) << endl; @@ -1106,12 +1192,15 @@ int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> double maxl = max2(scalp1, scalp2); Point<3> c = Center (p1, p2); double dist1 = Dist (c, p1); - + + /* int nseg = NOSegments(); for (j = 1; j <= nseg; j++) { const STLBoundarySeg & seg = GetSegment(j); - + */ + for(auto [i2, seg] : boundary_ht) + { if (seg.IsSmoothEdge()) continue; @@ -1232,57 +1321,30 @@ int STLBoundary :: TestSeg(const Point<3>& p1, const Point<3> & p2, const Vec<3> void STLBoundary :: BuildSearchTree() { - // static int timer = NgProfiler::CreateTimer ("BuildSearchTree"); - // NgProfiler::RegionTimer reg(timer); - - delete searchtree; - - /* - Box<2> box2d(Box<2>::EMPTY_BOX); - - int nseg = NOSegments(); - for (int j = 1; j <= nseg; j++) - { - const STLBoundarySeg & seg = GetSegment(j); - if (seg.IsSmoothEdge()) continue; - box2d.Add(seg.BoundingBox().PMin()); - box2d.Add(seg.BoundingBox().PMax()); - } - - searchtree = new BoxTree<2> (box2d); - - for (int j = 1; j <= nseg; j++) - { - const STLBoundarySeg & seg = GetSegment(j); - if (seg.IsSmoothEdge()) continue; - searchtree -> Insert (seg.BoundingBox(), j); - } - */ Box<2> box2d(Box<2>::EMPTY_BOX); Box<3> box3d = geometry->GetBoundingBox(); + for (size_t i = 0; i < 8; i++) box2d.Add ( chart->Project2d (box3d.GetPointNr(i))); - searchtree = new BoxTree<2,INDEX_2> (box2d); + + searchtree = make_unique> (box2d); +// searchtree = nullptr; } void STLBoundary :: DeleteSearchTree() { - // static int timer = NgProfiler::CreateTimer ("DeleteSearchTree"); - // NgProfiler::RegionTimer reg(timer); - - delete searchtree; searchtree = nullptr; } + // checks, whether 2d projection intersects -int STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2, +bool STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2, const Vec3d& sn) { - // static int timerquick = NgProfiler::CreateTimer ("TestSegChartNV-searchtree"); - // static int timer = NgProfiler::CreateTimer ("TestSegChartNV"); - - int nseg = NOSegments(); + // static int timerquick = NgProfiler::CreateTimer ("TestSegChartNV-searchtree"); + // static Timer timer("TestSegChartNV"); RegionTimer reg(timer); + Point<2> p2d1 = chart->Project2d (p1); Point<2> p2d2 = chart->Project2d (p2); @@ -1292,79 +1354,50 @@ int STLBoundary :: TestSegChartNV(const Point3d & p1, const Point3d& p2, Line2d l1 (p2d1, p2d2); - double eps = 1e-3; - bool ok = true; + double eps = 1e-6; - /* - static long int cnt = 0; - static long int totnseg = 0; - totnseg += nseg; - cnt++; - if ( (cnt % 100000) == 0) - cout << "avg nseg = " << double(totnseg)/cnt << endl; - */ + auto hasIntersection = [&] (auto i2) NETGEN_LAMBDA_INLINE + { + const STLBoundarySeg & seg = boundary_ht[i2]; + + if (seg.IsSmoothEdge()) return false; + if (!box2d.Intersect (seg.BoundingBox())) return false; + + const Point<2> & sp1 = seg.P2D1(); + const Point<2> & sp2 = seg.P2D2(); + + Line2d l2 (sp1, sp2); + double lam1, lam2; + + int err = CrossPointBarycentric (l1, l2, lam1, lam2); + bool in1 = (lam1 > eps) && (lam1 < 1-eps); + bool on1 = (lam1 > -eps) && (lam1 < 1 + eps); + bool in2 = (lam2 > eps) && (lam2 < 1-eps); + bool on2 = (lam2 > -eps) && (lam2 < 1 + eps); + + if(!err && ((on1 && in2) || (on2 && in1))) + return true; + return false; + }; if (searchtree) { - // NgProfiler::RegionTimer reg(timerquick); - - ArrayMem pis; - searchtree -> GetIntersecting (box2d.PMin(), box2d.PMax(), pis); - - for (auto i2 : pis) - { - // const STLBoundarySeg & seg = GetSegment(j); - const STLBoundarySeg & seg = boundary_ht[i2]; - - if (seg.IsSmoothEdge()) continue; - if (!box2d.Intersect (seg.BoundingBox())) continue; - - const Point<2> & sp1 = seg.P2D1(); - const Point<2> & sp2 = seg.P2D2(); - - Line2d l2 (sp1, sp2); - double lam1, lam2; - - int err = CrossPointBarycentric (l1, l2, lam1, lam2); - - if (!err && lam1 > eps && lam1 < 1-eps && - lam2 > eps && lam2 < 1-eps) - { - ok = false; - break; - } - } + bool has_intersection = false; + searchtree -> GetFirstIntersecting (box2d.PMin(), box2d.PMax(), + [&] (auto i2) NETGEN_LAMBDA_INLINE + { + has_intersection = hasIntersection(i2); + return has_intersection; + }); + return !has_intersection; } - else - { - // NgProfiler::RegionTimer reg(timer); - for (int j = 1; j <= nseg; j++) - { - const STLBoundarySeg & seg = GetSegment(j); - - if (seg.IsSmoothEdge()) continue; - if (!box2d.Intersect (seg.BoundingBox())) continue; - - const Point<2> & sp1 = seg.P2D1(); - const Point<2> & sp2 = seg.P2D2(); - - Line2d l2 (sp1, sp2); - double lam1, lam2; - - int err = CrossPointBarycentric (l1, l2, lam1, lam2); - - if (!err && lam1 > eps && lam1 < 1-eps && - lam2 > eps && lam2 < 1-eps) - { - ok = false; - break; - } - } - + { + for(auto [i2, seg] : boundary_ht) + if(hasIntersection(i2)) + return false; + return true; } - - return ok; } @@ -1429,8 +1462,8 @@ STLParameters :: STLParameters() resthchartdistenable = 1; resthlinelengthfac = 0.5; resthlinelengthenable = 1; - resthcloseedgefac = 1; - resthcloseedgeenable = 1; + // resthcloseedgefac = 1; + // resthcloseedgeenable = 1; resthedgeanglefac = 1; resthedgeangleenable = 0; resthsurfmeshcurvfac = 1; @@ -1455,14 +1488,13 @@ void STLParameters :: Print (ostream & ost) const << ", fac = " << resthchartdistfac << endl << "line length: " << resthlinelengthenable << ", fac = " << resthlinelengthfac << endl - << "close edges: " << resthcloseedgeenable - << ", fac = " << resthcloseedgefac << endl + // << "close edges: " << resthcloseedgeenable + // << ", fac = " << resthcloseedgefac << endl << "edge angle: " << resthedgeangleenable << ", fac = " << resthedgeanglefac << endl; } +DLL_HEADER extern STLParameters stlparam; STLParameters stlparam; - - } diff --git a/libsrc/stlgeom/stltool.hpp b/libsrc/stlgeom/stltool.hpp index 1f2478a4..e3e57385 100644 --- a/libsrc/stlgeom/stltool.hpp +++ b/libsrc/stlgeom/stltool.hpp @@ -1,7 +1,6 @@ #ifndef FILE_STLTOOL #define FILE_STLTOOL - //#include "gprim/gprim.hh" /**************************************************************************/ @@ -11,14 +10,14 @@ /* Date: 20. Nov. 99 */ /**************************************************************************/ - +namespace netgen { // use one normal vector for whole chart extern int usechartnormal; extern int chartdebug; extern int geomsearchtreeon; -extern int AddPointIfNotExists(Array& ap, const Point3d& p, double eps = 1e-8); +extern int AddPointIfNotExists(NgArray& ap, const Point3d& p, double eps = 1e-8); //get distance from line lp1-lp2 to point p extern double GetDistFromLine(const Point<3>& lp1, const Point<3>& lp2, Point<3>& p); extern double GetDistFromInfiniteLine(const Point<3>& lp1, const Point<3>& lp2, const Point<3>& p); @@ -35,62 +34,90 @@ extern void FIOReadStringE(istream& ios, char* str, int len); extern void FIOWriteString(ostream& ios, char* str, int len); -typedef Array * ArrayINTPTR; +typedef NgArray * ArrayINTPTR; class STLGeometry; +class STLParameters; +// typedef int ChartId +class ChartId +{ + int i; +public: + class t_invalid { public: constexpr t_invalid() = default; }; + static constexpr t_invalid INVALID{}; + + ChartId() { } + constexpr ChartId(t_invalid inv) : i(0) { ; } + constexpr ChartId(int ai) : i(ai) { } + operator int() const { return i; } + ChartId operator++ (int) { ChartId hi(*this); i++; return hi; } + ChartId & operator++ () { i++; return *this; } +}; +} + +namespace ngcore +{ + template<> + constexpr netgen::ChartId IndexBASE () { return netgen::ChartId(1); } +} + + +namespace netgen { + class STLChart { private: STLGeometry * geometry; - Array charttrigs; // trigs which only belong to this chart - Array outertrigs; // trigs which belong to other charts - BoxTree<3> * searchtree; // ADT containing outer trigs + Array charttrigs; // trigs which only belong to this chart + Array outertrigs; // trigs which belong to other charts + BoxTree<3,STLTrigId> * searchtree; // ADT containing outer trigs - Array olimit; //outer limit of outer chart - Array ilimit; //outer limit of inner chart + NgArray olimit; //outer limit of outer chart + NgArray ilimit; //outer limit of inner chart + const STLParameters& stlparam; public: - STLChart(STLGeometry * ageometry); + STLChart(STLGeometry * ageometry, const STLParameters& astlparam); ~STLChart(); - void AddChartTrig(int i); - void AddOuterTrig(int i); + void AddChartTrig(STLTrigId i); + void AddOuterTrig(STLTrigId i); - int IsInWholeChart(int nr) const; + bool IsInWholeChart(int nr) const; - int GetChartTrig(int i) const {return charttrigs.Get(i);} - int GetOuterTrig(int i) const {return outertrigs.Get(i);} + STLTrigId GetChartTrig1(int i) const {return charttrigs[i-1];} + STLTrigId GetOuterTrig1(int i) const {return outertrigs[i-1];} //get all trigs: - int GetTrig(int i) const + STLTrigId GetTrig1(int i) const { - if (i <= charttrigs.Size()) {return charttrigs.Get(i);} - else {return outertrigs.Get(i-charttrigs.Size());} + if (i <= charttrigs.Size()) {return charttrigs[i-1];} + else {return outertrigs[i-charttrigs.Size()-1];} } - int GetNChartT() const {return charttrigs.Size();} - int GetNOuterT() const {return outertrigs.Size();} - int GetNT() const {return charttrigs.Size()+outertrigs.Size(); } + size_t GetNChartT() const {return charttrigs.Size();} + size_t GetNOuterT() const {return outertrigs.Size();} + size_t GetNT() const {return charttrigs.Size()+outertrigs.Size(); } void GetTrianglesInBox (const Point3d & pmin, const Point3d & pmax, - Array & trias) const; + NgArray & trias) const; void AddOLimit(twoint l) {olimit.Append(l);} void AddILimit(twoint l) {ilimit.Append(l);} void ClearOLimit() {olimit.SetSize(0);} void ClearILimit() {ilimit.SetSize(0);} - int GetNOLimit() const {return olimit.Size();} - int GetNILimit() const {return ilimit.Size();} + size_t GetNOLimit() const {return olimit.Size();} + size_t GetNILimit() const {return ilimit.Size();} twoint GetOLimit(int i) const {return olimit.Get(i);} twoint GetILimit(int i) const {return ilimit.Get(i);} //move triangles trigs (local chart-trig numbers) to outer chart - void MoveToOuterChart(const Array& trigs); - void DelChartTrigs(const Array& trigs); + void MoveToOuterChart(const NgArray& trigs); + void DelChartTrigs(const NgArray& trigs); // define local coordinate system, JS: @@ -98,6 +125,7 @@ private: Vec<3> normal; Point<3> pref; Vec<3> t1, t2; + unique_ptr> inner_searchtree; public: void SetNormal (const Point<3> & apref, const Vec<3> & anormal); const Vec<3> & GetNormal () const { return normal; } @@ -106,6 +134,8 @@ public: Vec<3> v = p3d-pref; return Point<2> (t1 * v, t2 * v); } + void BuildInnerSearchTree(); + STLTrigId ProjectNormal (Point<3> & p) const; }; class STLBoundarySeg @@ -113,16 +143,15 @@ class STLBoundarySeg Point<3> p1, p2, center; Point<2> p2d1, p2d2; Box<2> boundingbox; - // Point<2> p2dmin, p2dmax; double rad; - int i1, i2; - int smoothedge; + STLPointId i1, i2; + bool smoothedge; public: STLBoundarySeg () { ; } - STLBoundarySeg (int ai1, int ai2, const Array > & points, + STLBoundarySeg (STLPointId ai1, STLPointId ai2, const Array,STLPointId> & points, const STLChart * chart) - : p1(points.Get(ai1)), p2(points.Get(ai2)), + : p1(points[ai1]), p2(points[ai2]), i1(ai1), i2(ai2) { center = ::netgen::Center (p1, p2); @@ -138,8 +167,8 @@ public: int operator== (const STLBoundarySeg & s2) const { return i1 == s2.i1 && i2 == s2.i2; } void Swap (); - int I1() const { return i1; } - int I2() const { return i2; } + STLPointId I1() const { return i1; } + STLPointId I2() const { return i2; } const Point<3> & P1() const { return p1; } const Point<3> & P2() const { return p2; } const Point<2> & P2D1() const { return p2d1; } @@ -150,8 +179,8 @@ public: const Box<2> & BoundingBox() const { return boundingbox; } double Radius () const { return rad; } - void SetSmoothEdge (int se) { smoothedge = se; } - int IsSmoothEdge () const { return smoothedge; } + void SetSmoothEdge (bool se) { smoothedge = se; } + bool IsSmoothEdge () const { return smoothedge; } friend class STLBoundary; }; @@ -160,31 +189,31 @@ class STLBoundary private: STLGeometry * geometry; const STLChart * chart; - Array boundary; + // NgArray boundary; ClosedHashTable boundary_ht; - BoxTree<2,INDEX_2> * searchtree = nullptr; + unique_ptr> searchtree; public: STLBoundary(STLGeometry * ageometry); - ~STLBoundary() { delete searchtree; } + ~STLBoundary() {} - void Clear() {boundary.SetSize(0); boundary_ht = ClosedHashTable(); } + void Clear() { /* boundary.SetSize(0); */ boundary_ht = ClosedHashTable(); } void SetChart (const STLChart * achart) { chart = achart; } //don't check, if already exists! - void AddNewSegment(const STLBoundarySeg & seg) {boundary.Append(seg);}; + // void AddNewSegment(const STLBoundarySeg & seg) {boundary.Append(seg);}; //check if segment exists - void AddOrDelSegment(const STLBoundarySeg & seg); + // void AddOrDelSegment(const STLBoundarySeg & seg); //addordelsegment for all 3 triangle segments! void AddTriangle(const STLTriangle & t); - int NOSegments() {return boundary.Size();}; - const STLBoundarySeg & GetSegment(int i) {return boundary.Get(i);} + int NOSegments() {return boundary_ht.UsedElements();}; + // const STLBoundarySeg & GetSegment(int i) {return boundary.Get(i);} void BuildSearchTree(); void DeleteSearchTree(); - int TestSeg(const Point<3> & p1, const Point<3> & p2, const Vec<3> & sn, - double sinchartangle, int divisions, Array >& points, - double eps); - - int TestSegChartNV(const Point3d& p1, const Point3d& p2, const Vec3d& sn); + bool TestSeg(const Point<3> & p1, const Point<3> & p2, const Vec<3> & sn, + double sinchartangle, int divisions, Array,STLPointId>& points, + double eps); + + bool TestSegChartNV(const Point3d& p1, const Point3d& p2, const Vec3d& sn); }; @@ -227,66 +256,76 @@ DLL_HEADER extern STLDoctorParams stldoctor; -class STLParameters +// TODO change enable flag to optional parameters +class DLL_HEADER STLParameters { public: /// angle for edge detection - double yangle; - double contyangle; //edges continued with contyangle + double yangle = 30.; + double contyangle = 20.; //edges continued with contyangle /// angle of geometry edge at which the mesher should set a point - double edgecornerangle; + double edgecornerangle = 60.; /// angle inside on chart - double chartangle; + double chartangle = 15.; /// angle for overlapping parts of char - double outerchartangle; + double outerchartangle = 70.; /// 0 .. no, 1 .. local, (2 .. global) - int usesearchtree; + int usesearchtree = 0; /// - double resthatlasfac; - int resthatlasenable; - double atlasminh; + double resthatlasfac = 2.; + bool resthatlasenable = true; + double atlasminh = 0.1; - double resthsurfcurvfac; - int resthsurfcurvenable; + double resthsurfcurvfac = 2.; + bool resthsurfcurvenable = false; - double resthchartdistfac; - int resthchartdistenable; + double resthchartdistfac = 1.2; + bool resthchartdistenable = true; - double resthcloseedgefac; - int resthcloseedgeenable; + // double resthcloseedgefac = 1.; + // bool resthcloseedgeenable = true; - double resthedgeanglefac; - int resthedgeangleenable; + double resthedgeanglefac = 1.; + bool resthedgeangleenable = false; - double resthsurfmeshcurvfac; - int resthsurfmeshcurvenable; + double resthsurfmeshcurvfac = 1.; + bool resthsurfmeshcurvenable = false; - double resthlinelengthfac; - int resthlinelengthenable; + double resthlinelengthfac = 0.5; + bool resthlinelengthenable = true; /// - int recalc_h_opt; + bool recalc_h_opt = true; /// STLParameters(); /// void Print (ostream & ost) const; }; -DLL_HEADER extern STLParameters stlparam; +inline ostream & operator<< (ostream & ost, const STLParameters & stlparam) + { + stlparam.Print (ost); + return ost; + } + void STLMeshing (STLGeometry & geom, - class Mesh & mesh); + Mesh & mesh, + const MeshingParameters& mparam, + const STLParameters& stlpar); int STLSurfaceMeshing (STLGeometry & geom, - class Mesh & mesh); + Mesh & mesh, + const MeshingParameters& mparam, + const STLParameters& stlpar); void STLSurfaceOptimization (STLGeometry & geom, - class Mesh & mesh, - class MeshingParameters & mparam); - + Mesh & mesh, + const MeshingParameters & mparam); +} // namespace netgen #endif diff --git a/libsrc/stlgeom/stltopology.cpp b/libsrc/stlgeom/stltopology.cpp index 7c32b0e4..2ef61885 100644 --- a/libsrc/stlgeom/stltopology.cpp +++ b/libsrc/stlgeom/stltopology.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -8,6 +9,7 @@ #include "stlgeom.hpp" #include +#include namespace netgen { @@ -31,7 +33,7 @@ STLTopology :: ~STLTopology() STLGeometry * STLTopology :: LoadBinary (istream & ist) { STLGeometry * geom = new STLGeometry(); - Array readtrigs; + NgArray readtrigs; PrintMessage(1,"Read STL binary file"); @@ -88,7 +90,7 @@ STLGeometry * STLTopology :: LoadBinary (istream & ist) } -void STLTopology :: SaveBinary (const char* filename, const char* aname) const +void STLTopology :: SaveBinary (const filesystem::path & filename, const char* aname) const { ofstream ost(filename); PrintFnStart("Write STL binary file '",filename,"'"); @@ -147,7 +149,7 @@ void STLTopology :: SaveBinary (const char* filename, const char* aname) const } -void STLTopology :: SaveSTLE (const char* filename) const +void STLTopology :: SaveSTLE (const filesystem::path & filename) const { ofstream outf (filename); int i, j; @@ -191,7 +193,7 @@ STLGeometry * STLTopology :: LoadNaomi (istream & ist) { int i; STLGeometry * geom = new STLGeometry(); - Array readtrigs; + NgArray readtrigs; PrintFnStart("read NAOMI file format"); @@ -204,13 +206,13 @@ STLGeometry * STLTopology :: LoadNaomi (istream & ist) int noface, novertex; - Array > readpoints; + NgArray > readpoints; ist >> buf; if (strcmp (buf, "NODES") == 0) { ist >> novertex; - PrintMessage(5,"nuber of vertices = ", novertex); + PrintMessage(5,"number of vertices = ", novertex); for (i = 0; i < novertex; i++) { ist >> px; @@ -264,7 +266,7 @@ STLGeometry * STLTopology :: LoadNaomi (istream & ist) return geom; } -void STLTopology :: Save (const char* filename) const +void STLTopology :: Save (const filesystem::path & filename) const { PrintFnStart("Write stl-file '",filename, "'"); @@ -283,9 +285,9 @@ void STLTopology :: Save (const char* filename) const fout << "facet normal "; const Vec3d& n = GetTriangle(i).Normal(); - sprintf(buf1,"%1.9g",n.X()); - sprintf(buf2,"%1.9g",n.Y()); - sprintf(buf3,"%1.9g",n.Z()); + snprintf(buf1, size(buf1), "%1.9g",n.X()); + snprintf(buf2, size(buf2), "%1.9g",n.Y()); + snprintf(buf3, size(buf3), "%1.9g",n.Z()); fout << buf1 << " " << buf2 << " " << buf3 << "\n"; fout << "outer loop\n"; @@ -294,9 +296,9 @@ void STLTopology :: Save (const char* filename) const { const Point3d p = GetPoint(t.PNum(j)); - sprintf(buf1,"%1.9g",p.X()); - sprintf(buf2,"%1.9g",p.Y()); - sprintf(buf3,"%1.9g",p.Z()); + snprintf(buf1, size(buf1), "%1.9g",p.X()); + snprintf(buf2, size(buf2), "%1.9g",p.Y()); + snprintf(buf3, size(buf3), "%1.9g",p.Z()); fout << "vertex " << buf1 << " " << buf2 << " " << buf3 << "\n"; } @@ -336,11 +338,37 @@ void STLTopology :: Save (const char* filename) const } -STLGeometry * STLTopology ::Load (istream & ist) +STLGeometry * STLTopology ::Load (istream & ist, bool surface) { + // Check if the file starts with "solid". If not, the file is binary + { + // binary header is 80 bytes, so don't load more than that + constexpr int buflen = 80; + char buf[buflen+1]; + FIOReadStringE(ist,buf,buflen); + + // ignore whitespaces at start of line + int istart; + for (istart=0; istart binary file + if (strncmp(buf+istart, "solid", 5) != 0) + return LoadBinary(ist); + + // Check if there is a non-printable character in first 80 bytes + for (auto i : Range(istart, buflen)) + if(std::isprint(buf[i])==0 && std::isspace(buf[i])==0) + return LoadBinary(ist); + } + STLGeometry * geom = new STLGeometry(); - Array readtrigs; + NgArray readtrigs; char buf[100]; Point<3> pts[3]; @@ -349,6 +377,7 @@ STLGeometry * STLTopology ::Load (istream & ist) int cntface = 0; int vertex = 0; bool badnormals = false; + ist >> buf; // skip first line while (ist.good()) { @@ -428,6 +457,7 @@ STLGeometry * STLTopology ::Load (istream & ist) PrintWarning("File has normal vectors which differ extremely from geometry->correct with stldoctor!!!"); } + geom->surface = surface; geom->InitSTLGeometry(readtrigs); return geom; } @@ -444,7 +474,7 @@ STLGeometry * STLTopology ::Load (istream & ist) -void STLTopology :: InitSTLGeometry(const Array & readtrigs) +void STLTopology :: InitSTLGeometry(const NgArray & readtrigs) { // const double geometry_tol_fact = 1E6; // distances lower than max_box_size/tol are ignored @@ -470,7 +500,7 @@ void STLTopology :: InitSTLGeometry(const Array & readtrigs) pointtree = new Point3dTree (bb.PMin(), bb.PMax()); - Array pintersect; + NgArray pintersect; pointtol = boundingbox.Diam() * stldoctor.geom_tol_fact; PrintMessage(5,"point tolerance = ", pointtol); @@ -503,9 +533,9 @@ void STLTopology :: InitSTLGeometry(const Array & readtrigs) foundpos = AddPoint(p); pointtree->Insert (p, foundpos); } - if (Dist(p, points.Get(foundpos)) > 1e-10) - cout << "identify close points: " << p << " " << points.Get(foundpos) - << ", dist = " << Dist(p, points.Get(foundpos)) + if (Dist(p, points[foundpos]) > 1e-10) + cout << "identify close points: " << p << " " << points[foundpos] + << ", dist = " << Dist(p, points[foundpos]) << endl; st[k] = foundpos; } @@ -534,7 +564,7 @@ int STLTopology :: GetPointNum (const Point<3> & p) Point<3> pmin = p - Vec<3> (pointtol, pointtol, pointtol); Point<3> pmax = p + Vec<3> (pointtol, pointtol, pointtol); - Array pintersect; + NgArray pintersect; pointtree->GetIntersecting (pmin, pmax, pintersect); if (pintersect.Size() == 1) @@ -621,6 +651,7 @@ void STLTopology :: FindNeighbourTrigs() } } + if(!surface) for (int i = 1; i <= ne; i++) { const STLTopEdge & edge = GetTopEdge (i); @@ -639,9 +670,12 @@ void STLTopology :: FindNeighbourTrigs() const STLTriangle & t = GetTriangle (i); for (int j = 1; j <= 3; j++) { - const STLTriangle & nbt = GetTriangle (t.NBTrigNum(j)); - if (!t.IsNeighbourFrom (nbt)) - orientation_ok = 0; + if(t.NBTrigNum(j) != 0) + { + const STLTriangle & nbt = GetTriangle (t.NBTrigNum(j)); + if (!t.IsNeighbourFrom (nbt)) + orientation_ok = 0; + } } } } @@ -699,14 +733,15 @@ void STLTopology :: FindNeighbourTrigs() - for (STLTrigIndex ti = 0; ti < GetNT(); ti++) + // for (STLTrigId ti = 0; ti < GetNT(); ti++) + for (STLTrigId ti : Range(trias)) { STLTriangle & trig = trias[ti]; for (int k = 0; k < 3; k++) { - STLPointIndex pi = trig[k] - STLBASE; - STLPointIndex pi2 = trig[(k+1)%3] - STLBASE; - STLPointIndex pi3 = trig[(k+2)%3] - STLBASE; + STLPointId pi = trig[k]; // - STLBASE; + STLPointId pi2 = trig[(k+1)%3]; // - STLBASE; + STLPointId pi3 = trig[(k+2)%3]; // - STLBASE; // vector along edge Vec<3> ve = points[pi2] - points[pi]; @@ -724,24 +759,24 @@ void STLTopology :: FindNeighbourTrigs() for (int j = 0; j < trigsperpoint[pi].Size(); j++) { - STLTrigIndex ti2 = trigsperpoint[pi][j] - STLBASE; + STLTrigId ti2 = trigsperpoint[pi][j]; // - STLBASE; const STLTriangle & trig2 = trias[ti2]; if (ti == ti2) continue; bool hasboth = 0; for (int l = 0; l < 3; l++) - if (trig2[l] - STLBASE == pi2) + if (trig2[l] /* - STLBASE */ == pi2) { hasboth = 1; break; } if (!hasboth) continue; - STLPointIndex pi4(0); + STLPointId pi4(0); for (int l = 0; l < 3; l++) - if (trig2[l] - STLBASE != pi && trig2[l] - STLBASE != pi2) - pi4 = trig2[l] - STLBASE; + if (trig2[l] /* - STLBASE */ != pi && trig2[l] /* - STLBASE */ != pi2) + pi4 = trig2[l] /* - STLBASE */; Vec<3> vt2 = points[pi4] - points[pi]; @@ -751,12 +786,12 @@ void STLTopology :: FindNeighbourTrigs() if (phi < phimin) { phimin = phi; - trig.NBTrig (0, (k+2)%3) = ti2 + STLBASE; + trig.NBTrig (0, (k+2)%3) = ti2; // + STLBASE; } if (phi > phimax) { phimax = phi; - trig.NBTrig (1, (k+2)%3) = ti2 + STLBASE; + trig.NBTrig (1, (k+2)%3) = ti2; // + STLBASE; } } } @@ -771,7 +806,8 @@ void STLTopology :: FindNeighbourTrigs() neighbourtrigs.SetSize(GetNT()); for (int i = 1; i <= GetNT(); i++) for (int k = 1; k <= 3; k++) - AddNeighbourTrig (i, GetTriangle(i).NBTrigNum(k)); + if(GetTriangle(i).NBTrigNum(k) != 0) + AddNeighbourTrig (i, GetTriangle(i).NBTrigNum(k)); } else { @@ -816,7 +852,7 @@ void STLTopology :: FindNeighbourTrigs() PrintError("TRIG ",i," has ",NONeighbourTrigs(i)," neighbours!!!!"); for (int kk=1; kk <= NONeighbourTrigs(i); kk++) { - PrintMessage(5,"neighbour-trig",kk," = ",NeighbourTrig(i,kk)); + PrintMessage(5,"neighbour-trig",kk," = ",int(NeighbourTrig(i,kk))); } }; } @@ -853,7 +889,7 @@ void STLTopology :: GetTrianglesInBox (/* const Point<3> & pmax, */ const Box<3> & box, - Array & btrias) const + NgArray & btrias) const { if (searchtree) @@ -934,10 +970,10 @@ int STLTopology :: GetRightTrig(int p1, int p2) const int STLTopology :: NeighbourTrigSorted(int trig, int edgenum) const { - int i, p1, p2; - int psearch = GetTriangle(trig).PNum(edgenum); + STLPointId p1, p2; + STLPointId psearch = GetTriangle(trig).PNum(edgenum); - for (i = 1; i <= 3; i++) + for (int i = 1; i <= 3; i++) { GetTriangle(trig).GetNeighbourPoints(GetTriangle(NeighbourTrig(trig,i)),p1,p2); if (p1 == psearch) {return NeighbourTrig(trig,i);} @@ -1004,7 +1040,7 @@ void STLTopology :: OrientAfterTrig (int trig) if (starttrig >= 1 && starttrig <= GetNT()) { - Array oriented; + NgArray oriented; oriented.SetSize(GetNT()); int i; for (i = 1; i <= oriented.Size(); i++) @@ -1016,9 +1052,9 @@ void STLTopology :: OrientAfterTrig (int trig) int k; - Array list1; + NgArray list1; list1.SetSize(0); - Array list2; + NgArray list2; list2.SetSize(0); list1.Append(starttrig); diff --git a/libsrc/stlgeom/stltopology.hpp b/libsrc/stlgeom/stltopology.hpp index a76d5ee5..a14dc5bd 100644 --- a/libsrc/stlgeom/stltopology.hpp +++ b/libsrc/stlgeom/stltopology.hpp @@ -14,40 +14,66 @@ */ +namespace netgen { + class STLGeometry; -#define STLBASE 1 + // #define STLBASE 1 -class STLPointIndex +class STLPointId { int i; public: - STLPointIndex () { ; } - STLPointIndex (int ai) : i(ai) { ; } - STLPointIndex & operator= (const STLPointIndex & ai) { i = ai.i; return *this; } - STLPointIndex & operator= (int ai) { i = ai; return *this; } - operator int () const { return i; } - STLPointIndex operator++ (int) { return i++; } - STLPointIndex operator-- (int) { return i--; } + STLPointId () { ; } + constexpr STLPointId (int ai) : i(ai) { ; } + STLPointId & operator= (const STLPointId & ai) { i = ai.i; return *this; } + STLPointId & operator= (int ai) { i = ai; return *this; } + constexpr operator int () const { return i; } + STLPointId operator++ (int) { return STLPointId(i++); } + STLPointId operator-- (int) { return STLPointId(i--); } + STLPointId & operator++ () { ++i; return *this; } + STLPointId & operator-- () { --i; return *this; } + + void DoArchive(Archive& ar) { ar & i; } }; -class STLTrigIndex +class STLTrigId { int i; public: - STLTrigIndex () { ; } - STLTrigIndex (int ai) : i(ai) { ; } - STLTrigIndex & operator= (const STLTrigIndex & ai) { i = ai.i; return *this; } - STLTrigIndex & operator= (int ai) { i = ai; return *this; } - operator int () const { return i; } - STLTrigIndex operator++ (int) { return i++; } - STLTrigIndex operator-- (int) { return i--; } + STLTrigId () { ; } + constexpr STLTrigId (int ai) : i(ai) { ; } + STLTrigId & operator= (const STLTrigId & ai) { i = ai.i; return *this; } + STLTrigId & operator= (int ai) { i = ai; return *this; } + constexpr operator int () const { return i; } + + STLTrigId operator++ (int) { return STLTrigId(i++); } + STLTrigId operator-- (int) { return STLTrigId(i--); } + STLTrigId & operator++ () { ++i; return *this; } + STLTrigId & operator-- () { --i; return *this; } + + int operator- (STLTrigId i2) const { return i-i2.i; } }; + inline void SetInvalid (STLTrigId & id) { id = 0; } + inline bool IsInvalid (STLTrigId & id) { return id == 0; } + +} +namespace ngcore +{ + template<> + constexpr netgen::STLPointId IndexBASE () { return netgen::STLPointId(1); } + template<> + constexpr netgen::STLTrigId IndexBASE () { return netgen::STLTrigId(1); } +} + + + +namespace netgen { // triangle structure for loading stl files @@ -73,7 +99,7 @@ class STLTriangle // normalized stored normal vector ?? Vec<3> normal; // point numbers of triangle - int pts[3]; + STLPointId pts[3]; // front-side and back-side domains int domains[2]; @@ -93,22 +119,28 @@ public: - STLTriangle (const int * apts); - STLTriangle () {pts[0]=0;pts[1]=0;pts[2]=0;} + STLTriangle (const STLPointId * apts); + STLTriangle () + { + pts[0]=0;pts[1]=0;pts[2]=0; + nbtrigs[0][0] = nbtrigs[0][1] = nbtrigs[0][2] = 0.; + nbtrigs[1][0] = nbtrigs[1][1] = nbtrigs[1][2] = 0.; + } void DoArchive(Archive& ar) { ar.Do(&topedges[0],3); ar.Do(&nbtrigs[0][0], 6); - ar.Do(&pts[0],3); + // ar.Do(&pts[0],3); + ar & pts[0] & pts[1] & pts[2]; ar.Do(&domains[0],2); size_t i = flags.toperror; ar & normal & box & center & rad & facenum & i; flags.toperror = i; } - int operator[] (int i) const { return pts[i]; } - int & operator[] (int i) { return pts[i]; } + STLPointId operator[] (int i) const { return pts[i]; } + STLPointId & operator[] (int i) { return pts[i]; } int EdgeNum(int i) const { return topedges[(i-1)]; } int & EdgeNum(int i) { return topedges[(i-1)]; } @@ -123,11 +155,13 @@ public: // obsolete: - int PNum(int i) const { return pts[(i-1)]; } - int & PNum(int i) { return pts[(i-1)]; } - int PNumMod(int i) const { return pts[(i-1)%3]; } - int & PNumMod(int i) { return pts[(i-1)%3]; } + STLPointId PNum(int i) const { return pts[(i-1)]; } + STLPointId & PNum(int i) { return pts[(i-1)]; } + STLPointId PNumMod(int i) const { return pts[(i-1)%3]; } + STLPointId & PNumMod(int i) { return pts[(i-1)%3]; } + FlatArray PNums() const { return { 3, pts }; } + int EdgeNumMod(int i) const { return topedges[(i-1)%3]; } int & EdgeNumMod(int i) { return topedges[(i-1)%3]; } @@ -143,13 +177,13 @@ public: int IsWrongNeighbourFrom(const STLTriangle& t) const; ///Get the two points of neighbour-Triangles in orientation of this-Triangle - void GetNeighbourPoints(const STLTriangle& t, int& p1, int& p2) const; - int GetNeighbourPointsAndOpposite(const STLTriangle& t, int& p1, int& p2, int& po) const; + void GetNeighbourPoints(const STLTriangle& t, STLPointId & p1, STLPointId & p2) const; + int GetNeighbourPointsAndOpposite(const STLTriangle& t, STLPointId & p1, STLPointId & p2, STLPointId & po) const; // NON-normalized geometry - normal vector - Vec<3> GeomNormal(const Array >& ap) const; + Vec<3> GeomNormal(const Array,STLPointId>& ap) const; // Stored normal vector, normalized void SetNormal (const Vec<3> & n); @@ -159,10 +193,10 @@ public: void ChangeOrientation(); //project with a certain normal vector in plane - void ProjectInPlain(const Array >& ap, + void ProjectInPlain(const Array, STLPointId>& ap, const Vec<3> & n, Point<3> & pp) const; //project with the triangle's normal vector in plane - void ProjectInPlain(const Array > & ap, Point<3> & pp) const; + void ProjectInPlain(const Array, STLPointId> & ap, Point<3> & pp) const; /* @@ -177,26 +211,26 @@ public: pp(output) = P1 + lam1 v1 + lam2 v2 */ - int ProjectInPlain (const Array >& ap, + int ProjectInPlain (const Array,STLPointId>& ap, const Vec<3> & nproj, Point<3> & pp, Vec<3> & lam) const; - int PointInside(const Array >& ap, const Point<3> & pp) const; + bool PointInside(const Array,STLPointId>& ap, const Point<3> & pp) const; //get nearest point on triangle and distance to it - double GetNearestPoint(const Array >& ap, + double GetNearestPoint(const Array,STLPointId>& ap, Point<3> & p3d) const; - double Area(const Array >& ap) const; + double Area(const Array,STLPointId>& ap) const; - double MinHeight(const Array >& ap) const; - double MaxLength(const Array >& ap) const; + double MinHeight(const Array,STLPointId>& ap) const; + double MaxLength(const Array,STLPointId>& ap) const; //max length of a side of triangle int GetFaceNum() {return facenum;} void SetFaceNum(int i) {facenum = i;} - int HasEdge(int p1, int p2) const; + bool HasEdge(STLPointId p1, STLPointId p2) const; }; @@ -207,22 +241,22 @@ public: */ class STLTopEdge { - int pts[2]; + STLPointId pts[2]; int trigs[2]; double cosangle; int status; // excluded, confirmed, candidate, undefined public: STLTopEdge (); - STLTopEdge (int p1, int p2, int trig1, int trig2); + STLTopEdge (STLPointId p1, STLPointId p2, int trig1, int trig2); - int operator[] (int i) const { return pts[i]; } - int & operator[] (int i) { return pts[i]; } + STLPointId operator[] (int i) const { return pts[i]; } + STLPointId & operator[] (int i) { return pts[i]; } - int PNum(int i) const { return pts[(i-1)]; } - int & PNum(int i) { return pts[(i-1)]; } - int PNumMod(int i) const { return pts[(i-1)%2]; } - int & PNumMod(int i) { return pts[(i-1)%2]; } + STLPointId PNum(int i) const { return pts[(i-1)]; } + STLPointId & PNum(int i) { return pts[(i-1)]; } + STLPointId PNumMod(int i) const { return pts[(i-1)%2]; } + STLPointId & PNumMod(int i) { return pts[(i-1)%2]; } int TrigNum(int i) const { return trigs[(i-1)]; } int & TrigNum(int i) { return trigs[(i-1)]; } @@ -250,14 +284,15 @@ ostream& operator<<(ostream& os, const STLTriangle& t); class STLTopology { protected: - Array trias; - Array topedges; - Array > points; + Array trias; + NgArray topedges; + Array, STLPointId> points; + bool surface = false; // mapping of sorted pair of points to topedge INDEX_2_HASHTABLE * ht_topedges; // mapping of node to trigs - TABLE trigsperpoint; + TABLE()> trigsperpoint; // mapping of node to edges TABLE topedgesperpoint; @@ -284,12 +319,14 @@ public: virtual ~STLTopology(); static STLGeometry * LoadNaomi (istream & ist); - static STLGeometry * Load (istream & ist); + DLL_HEADER static STLGeometry * Load (istream & ist, bool surface=false); static STLGeometry * LoadBinary (istream & ist); - void Save (const char* filename) const; - void SaveBinary (const char* filename, const char* aname) const; - void SaveSTLE (const char * filename) const; // stores trigs and edges + void Save (const filesystem::path & filename) const; + void SaveBinary (const filesystem::path & filename, const char* aname) const; + void SaveSTLE (const filesystem::path & filename) const; // stores trigs and edges + + bool IsSurfaceSTL() const { return surface; } virtual void DoArchive(Archive& ar) { @@ -298,7 +335,7 @@ public: FindNeighbourTrigs(); } - virtual void InitSTLGeometry (const Array & readtrigs); + virtual void InitSTLGeometry (const NgArray & readtrigs); virtual void TopologyChanged() {}; //do some things, if topology changed! @@ -307,35 +344,35 @@ public: void GetTrianglesInBox (const Box<3> & box, - Array & trias) const; + NgArray & trias) const; int GetNP() const { return points.Size(); } int AddPoint(const Point<3> & p) { points.Append(p); return points.Size(); } - const Point<3> & GetPoint(int nr) const { return points.Get(nr); } + const Point<3> & GetPoint(STLPointId nr) const { return points[nr]; } // .Get(nr); } int GetPointNum (const Point<3> & p); - void SetPoint(int nr, const Point<3> & p) { points.Elem(nr) = p; } - const Array >& GetPoints() const { return points; } + void SetPoint(STLPointId nr, const Point<3> & p) { points[nr] = p; } // { points.Elem(nr) = p; } + auto & GetPoints() const { return points; } - const Point<3> & operator[] (STLPointIndex i) const { return points[i]; } - Point<3> & operator[] (STLPointIndex i) { return points[i]; } + const Point<3> & operator[] (STLPointId i) const { return points[i]; } + Point<3> & operator[] (STLPointId i) { return points[i]; } int GetNT() const { return trias.Size(); } void AddTriangle(const STLTriangle& t); - const STLTriangle & GetTriangle (int nr) const { return trias.Get(nr); } - STLTriangle & GetTriangle (int nr) { return trias.Elem(nr); } + const STLTriangle & GetTriangle (STLTrigId nr) const { return trias[nr]; } // .Get(nr); } + STLTriangle & GetTriangle (STLTrigId nr) { return trias[nr]; } // .Elem(nr); } - const STLTriangle & operator[] (STLTrigIndex i) const { return trias[i]; } - STLTriangle & operator[] (STLTrigIndex i) { return trias[i]; } + const STLTriangle & operator[] (STLTrigId i) const { return trias[i]; } + STLTriangle & operator[] (STLTrigId i) { return trias[i]; } int GetNTE() const { return topedges.Size(); } const STLTopEdge & GetTopEdge (int nr) const { return topedges.Get(nr); } STLTopEdge & GetTopEdge (int nr) { return topedges.Elem(nr); } - int GetTopEdgeNum (int pi1, int pi2) const; + DLL_HEADER int GetTopEdgeNum (int pi1, int pi2) const; int NOTrigsPerPoint(int pn) { return trigsperpoint.EntrySize(pn); } @@ -359,13 +396,13 @@ public: // Table will be constructed, if topology is not ok /// neighbourtrigs for surfacetrigs - TABLE neighbourtrigs; + TABLE neighbourtrigs; /// get nr-th neighbour Triangle for triangle trig - int NONeighbourTrigs(int trig) const { return neighbourtrigs.EntrySize(trig); } - int NeighbourTrig(int trig, int nr) const { return neighbourtrigs.Get(trig,nr); } + int NONeighbourTrigs(STLTrigId trig) const { return neighbourtrigs.EntrySize(int(trig)); } + STLTrigId NeighbourTrig(STLTrigId trig, int nr) const { return neighbourtrigs.Get(int(trig),nr); } int NeighbourTrigSorted(int trig, int nr) const; - void AddNeighbourTrig(int i, int nt) { neighbourtrigs.Add1(i, nt); } + void AddNeighbourTrig(STLTrigId i, STLTrigId nt) { neighbourtrigs.Add1(int(i), nt); } @@ -376,5 +413,6 @@ public: const Box<3> & GetBoundingBox () const { return boundingbox; } }; +} // namespace netgen #endif diff --git a/libsrc/stlgeom/vsstl.cpp b/libsrc/stlgeom/vsstl.cpp index ca1f2cbf..0f450038 100644 --- a/libsrc/stlgeom/vsstl.cpp +++ b/libsrc/stlgeom/vsstl.cpp @@ -21,8 +21,7 @@ namespace netgen /* *********************** Draw STL Geometry **************** */ -extern STLGeometry * stlgeometry; -extern shared_ptr mesh; +DLL_HEADER extern shared_ptr mesh; // #include "../../ngtcltk/mvdraw.hpp" @@ -33,8 +32,6 @@ VisualSceneSTLMeshing :: VisualSceneSTLMeshing () { selecttrig = 0; nodeofseltrig = 1; - stlgeometry->SetSelectTrig(selecttrig); - stlgeometry->SetNodeOfSelTrig(nodeofseltrig); } VisualSceneSTLMeshing :: ~VisualSceneSTLMeshing () @@ -153,7 +150,7 @@ void VisualSceneSTLMeshing :: DrawScene () MoCombine cb1(&z1,&z2); model.Add(&cb1); - Array trigs; + NgArray trigs; model.GetTriangles(trigs); int i, k; glBegin (GL_TRIANGLES); @@ -382,7 +379,7 @@ void VisualSceneSTLMeshing :: DrawScene () { //multiedge - const Array& me = stlgeometry->SelectedMultiEdge(); + const NgArray& me = stlgeometry->SelectedMultiEdge(); if (stlgeometry->GetSelectTrig() > 0 && stlgeometry->GetSelectTrig() <= stlgeometry->GetNT() && me.Size()) @@ -544,10 +541,10 @@ void VisualSceneSTLMeshing :: DrawScene () else {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);} */ - const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetChartTrig(j)); + const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetChartTrig1(j)); - const Vec3d & n = stlgeometry->GetTriangle(chart.GetChartTrig(j)).Normal(); + const Vec3d & n = stlgeometry->GetTriangle(chart.GetChartTrig1(j)).Normal(); glNormal3f (n.X(), n.Y(), n.Z()); /* const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetChartTrig(j)); @@ -567,9 +564,9 @@ void VisualSceneSTLMeshing :: DrawScene () for (j = 1; j <= chart.GetNOuterT(); j++) { - const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetOuterTrig(j)); + const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetOuterTrig1(j)); - const Vec3d & n = stlgeometry->GetTriangle(chart.GetOuterTrig(j)).Normal(); + const Vec3d & n = stlgeometry->GetTriangle(chart.GetOuterTrig1(j)).Normal(); glNormal3f (n.X(), n.Y(), n.Z()); @@ -1216,13 +1213,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 8ebd52bc..24ebbbb9 100644 --- a/libsrc/stlgeom/vsstl.hpp +++ b/libsrc/stlgeom/vsstl.hpp @@ -10,32 +10,37 @@ namespace netgen { - class VisualSceneSTLGeometry : public VisualScene + class NGGUI_API VisualSceneSTLGeometry : public VisualScene { - Array trilists; + NgArray trilists; class STLGeometry * stlgeometry; public: - DLL_HEADER VisualSceneSTLGeometry (); - DLL_HEADER virtual ~VisualSceneSTLGeometry (); - DLL_HEADER 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 NGGUI_API VisualSceneSTLMeshing : public VisualScene { - Array trilists; + 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; } + void SetGeometry (class STLGeometry * astlgeometry) + { + stlgeometry = astlgeometry; + stlgeometry->SetSelectTrig(selecttrig); + stlgeometry->SetNodeOfSelTrig(nodeofseltrig); + } virtual void BuildScene (int zoomall = 0); virtual void DrawScene (); diff --git a/libsrc/visualization/CMakeLists.txt b/libsrc/visualization/CMakeLists.txt index 4288862c..d1b8b184 100644 --- a/libsrc/visualization/CMakeLists.txt +++ b/libsrc/visualization/CMakeLists.txt @@ -1,19 +1,16 @@ -add_definitions(-DNGINTERFACE_EXPORTS) -install(FILES soldata.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel ) - -if(USE_GUI) - set( LIB_VISUAL_SOURCES meshdoc.cpp mvdraw.cpp vsfieldlines.cpp vsmesh.cpp vssolution.cpp importsolution.cpp ) -else(USE_GUI) - set( LIB_VISUAL_SOURCES visual_dummy.cpp ) -endif(USE_GUI) - -add_library(visual ${NG_LIB_TYPE} ${LIB_VISUAL_SOURCES}) - -target_link_libraries( visual ngcore ${PYTHON_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) -install( TARGETS visual ${NG_INSTALL_DIR}) +target_sources(nggui PRIVATE + importsolution.cpp + meshdoc.cpp + mvdraw.cpp + vsfieldlines.cpp + vsmesh.cpp + vssolution.cpp + visualpkg.cpp +) +target_link_libraries( nggui PUBLIC "$" ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} nglib) install(FILES - meshdoc.hpp mvdraw.hpp + meshdoc.hpp mvdraw.hpp visual_api.hpp vispar.hpp visual.hpp vssolution.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/visualization COMPONENT netgen_devel ) diff --git a/libsrc/visualization/importsolution.cpp b/libsrc/visualization/importsolution.cpp index 0532f2c9..dade1529 100644 --- a/libsrc/visualization/importsolution.cpp +++ b/libsrc/visualization/importsolution.cpp @@ -2,6 +2,7 @@ // Read solution file // +#include "visual_api.hpp" #include @@ -16,15 +17,15 @@ namespace netgen { - extern shared_ptr mesh; + DLL_HEADER extern shared_ptr mesh; -DLL_HEADER void ImportSolution2 (const char * filename) +NGGUI_API void ImportSolution2 (const char * filename) { ifstream inf (filename); char buf[100], name[1000]; int i, size, comps, order; bool iscomplex; - const char * type; + std::string type; Flags flags; while (1) @@ -83,19 +84,19 @@ DLL_HEADER void ImportSolution2 (const char * filename) soldata.soltype = NG_SOLUTION_NODAL; soldata.draw_surface = 1; soldata.draw_volume = 1; - if (strcmp (type, "element") == 0) + if (type == "element") { soldata.soltype = NG_SOLUTION_ELEMENT; soldata.draw_surface = 0; } - if (strcmp (type, "surfaceelement") == 0) + if (type == "surfaceelement") { soldata.soltype = NG_SOLUTION_SURFACE_ELEMENT; soldata.draw_volume = 0; } - if (strcmp (type, "noncontinuous") == 0) + if (type == "noncontinuous") soldata.soltype = NG_SOLUTION_NONCONTINUOUS; - if (strcmp (type, "surfacenoncontinuous") == 0) + if (type == "surfacenoncontinuous") soldata.soltype = NG_SOLUTION_SURFACE_NONCONTINUOUS; Ng_SetSolutionData (&soldata); diff --git a/libsrc/visualization/meshdoc.cpp b/libsrc/visualization/meshdoc.cpp index 58bcef75..d0993272 100644 --- a/libsrc/visualization/meshdoc.cpp +++ b/libsrc/visualization/meshdoc.cpp @@ -119,9 +119,6 @@ void VisualSceneMeshDoctor :: DrawScene () void VisualSceneMeshDoctor :: BuildScene (int zoomall) { - int i, j; - - if (zoomall) { Point3d pmin, pmax; @@ -159,15 +156,16 @@ void VisualSceneMeshDoctor :: BuildScene (int zoomall) glDisable (GL_COLOR_MATERIAL); - for (i = 1; i <= mesh->GetNSE(); i++) + for (int i = 1; i <= mesh->GetNSE(); i++) { glLoadName (i); // copy to be thread-safe - Element2d el = mesh->SurfaceElement (i); + // Element2d el = mesh->SurfaceElement (i); + Element2d el = (*mesh)[SurfaceElementIndex(i-1)]; int drawel = 1; - for (j = 1; j <= el.GetNP(); j++) + for (int j = 1; j <= el.GetNP(); j++) { if (!el.PNum(j)) drawel = 0; @@ -245,7 +243,7 @@ void VisualSceneMeshDoctor :: BuildScene (int zoomall) { 3, 5, 4 }, { 4, 5, 6 } }; - for (j = 0; j < 4; j++) + for (int j = 0; j < 4; j++) { const Point3d & lp1 = mesh->Point (el.PNum(trigs[j][0])); const Point3d & lp2 = mesh->Point (el.PNum(trigs[j][1])); @@ -275,12 +273,12 @@ void VisualSceneMeshDoctor :: BuildScene (int zoomall) glColor3f (0.0f, 0.0f, 0.0f); glEnable (GL_COLOR_MATERIAL); - for (i = 1; i <= mesh->GetNSE(); i++) + for (int i = 1; i <= mesh->GetNSE(); i++) { - Element2d el = mesh->SurfaceElement(i); + Element2d el = (*mesh)[SurfaceElementIndex(i-1)]; int drawel = 1; - for (j = 1; j <= el.GetNP(); j++) + for (int j = 1; j <= el.GetNP(); j++) { if (!el.PNum(j)) drawel = 0; @@ -372,7 +370,7 @@ void VisualSceneMeshDoctor :: BuildScene (int zoomall) glLineWidth (2.0f); - for (i = 1; i <= mesh->GetNSeg(); i++) + for (int i = 1; i <= mesh->GetNSeg(); i++) { const Segment & seg = mesh->LineSegment(i); const Point3d & p1 = mesh->Point(seg[0]); @@ -498,14 +496,14 @@ void VisualSceneMeshDoctor :: SetMarkEdgeDist (int dist) void VisualSceneMeshDoctor :: ClickElement (int elnr) { selelement = elnr; - int oldlocpi = locpi; locpi = locpi % 3 + 1; if (selelement > 0 && selelement <= mesh->GetNSE()) { - selpoint = mesh->SurfaceElement(selelement).PNum(locpi); - selpoint2 = mesh->SurfaceElement(selelement).PNum(oldlocpi); + SurfaceElementIndex sei(elnr-1); + selpoint = (*mesh)[sei].PNum(locpi); + selpoint2 = (*mesh)[sei].PNum(oldlocpi); cout << "selpts = " << selpoint << ", " << selpoint2 << endl; } diff --git a/libsrc/visualization/meshdoc.hpp b/libsrc/visualization/meshdoc.hpp index a5091b23..7fce2772 100644 --- a/libsrc/visualization/meshdoc.hpp +++ b/libsrc/visualization/meshdoc.hpp @@ -12,22 +12,22 @@ class VisualSceneMeshDoctor : public VisualScene int selpoint, selpoint2; // for edgemarking: - Array edgedist; + NgArray edgedist; int markedgedist; public: - DLL_HEADER VisualSceneMeshDoctor (); - DLL_HEADER virtual ~VisualSceneMeshDoctor (); + NGGUI_API VisualSceneMeshDoctor (); + NGGUI_API virtual ~VisualSceneMeshDoctor (); - DLL_HEADER virtual void BuildScene (int zoomall = 0); - DLL_HEADER virtual void DrawScene (); - DLL_HEADER virtual void MouseDblClick (int px, int py); + NGGUI_API virtual void BuildScene (int zoomall = 0); + NGGUI_API virtual void DrawScene (); + NGGUI_API virtual void MouseDblClick (int px, int py); - DLL_HEADER void SetMarkEdgeDist (int dist); - DLL_HEADER void ClickElement (int elnr); - DLL_HEADER void UpdateTables (); - DLL_HEADER int IsSegmentMarked (int segnr) const; + NGGUI_API void SetMarkEdgeDist (int dist); + NGGUI_API void ClickElement (int elnr); + NGGUI_API void UpdateTables (); + NGGUI_API int IsSegmentMarked (int segnr) const; }; class MeshDoctorParameters @@ -37,6 +37,6 @@ public: }; -DLL_HEADER extern MeshDoctorParameters meshdoctor; +NGGUI_API extern MeshDoctorParameters meshdoctor; } diff --git a/libsrc/visualization/mvdraw.cpp b/libsrc/visualization/mvdraw.cpp index ca563714..b6f70627 100644 --- a/libsrc/visualization/mvdraw.cpp +++ b/libsrc/visualization/mvdraw.cpp @@ -22,9 +22,11 @@ namespace netgen { - DLL_HEADER Point3d VisualScene :: center; - DLL_HEADER double VisualScene :: rad; - DLL_HEADER GLdouble VisualScene :: backcolor; + NGGUI_API Point3d VisualScene :: center; + NGGUI_API double VisualScene :: rad; + NGGUI_API GLdouble VisualScene :: backcolor; + NGGUI_API VisualScene visual_scene_cross; + NGGUI_API VisualScene *visual_scene = &visual_scene_cross; /* #if TOGL_MAJOR_VERSION!=2 @@ -36,9 +38,11 @@ namespace netgen */ void (*opengl_text_function)(const char * text) = NULL; - void Set_OpenGLText_Callback ( void (*fun) (const char * text) ) + int opengl_text_width = 0; + void Set_OpenGLText_Callback ( void (*fun) (const char * text), int width ) { opengl_text_function = fun; + opengl_text_width = width; } void MyOpenGLText (const char * text) @@ -48,6 +52,11 @@ namespace netgen // cout << "MyOpenGLText: " << text << endl; } + int MyOpenGLTextWidth () + { + return opengl_text_width; + } + // texture for color decoding // GLubyte * VisualScene :: colortexture = NULL; @@ -68,7 +77,10 @@ namespace netgen int VisualScene :: locpi; int VisualScene :: seledge; - int VisualScene :: selecttimestamp; + optional> VisualScene :: marker = nullopt; + + int VisualScene :: subdivision_timestamp = -1; + int VisualScene :: subdivisions = 2; int VisualScene :: viewport[4]; @@ -81,7 +93,7 @@ namespace netgen transp = 0.3; locviewer = 0; showstltrias = 0; - centerpoint = 0; + centerpoint = PointIndex::INVALID; usedispllists = 1; strcpy (selectvisual, "cross"); @@ -112,18 +124,6 @@ namespace netgen } - extern DLL_HEADER void Render(bool blocking); - DLL_HEADER void Render (bool blocking) - { - if (blocking && multithread.running) - { - multithread.redraw = 2; - while (multithread.redraw == 2) ; - } - else - multithread.redraw = 1; - } - void VisualScene :: BuildScene (int zoomall) { @@ -202,7 +202,7 @@ namespace netgen } - void VisualScene :: ArbitraryRotation (const Array & alpha, const Array & vec) + void VisualScene :: ArbitraryRotation (const NgArray & alpha, const NgArray & vec) { glPushMatrix(); @@ -229,8 +229,8 @@ namespace netgen void VisualScene :: ArbitraryRotation (const double alpha, const Vec3d & vec) { - Array a(1); a[0] = alpha; - Array v(1); v[0] = vec; + NgArray a(1); a[0] = alpha; + NgArray v(1); v[0] = vec; ArbitraryRotation(a,v); } @@ -492,8 +492,6 @@ namespace netgen void VisualScene :: CreateTexture (int ncols, int linear, double alpha, int typ) { if (linear) ncols = 32; - else ncols = 8; - if (ntexcols != ncols) { @@ -570,11 +568,11 @@ namespace netgen - void VisualScene :: DrawColorBar (double minval, double maxval, int logscale, bool linear) + void VisualScene :: DrawColorBar (double minval, double maxval, int logscale, bool linear, string format, string unit) { if (!vispar.drawcolorbar) return; - CreateTexture (8, linear, 1, GL_DECAL); + CreateTexture (GetVSSolution().numtexturecols, linear, 1, GL_DECAL); if (logscale && maxval <= 0) maxval = 1; if (logscale && minval <= 0) minval = 1e-4 * maxval; @@ -593,8 +591,9 @@ namespace netgen glDisable (GL_DEPTH_TEST); glBegin (GL_QUAD_STRIP); - for (double x = minx; x <= maxx; x += (maxx - minx) / 50) + for (auto i : Range(50)) { + double x = minx + i*1.0/49*(maxx-minx); SetOpenGlColor (x, minx, maxx); glVertex3d (x, miny, -5); glVertex3d (x, maxy, -5); @@ -612,23 +611,56 @@ namespace netgen glPushAttrib (GL_LIST_BIT); // glListBase (fontbase); - char buf[20]; + constexpr size_t buf_size = 20; + char buf[buf_size]; + GLint viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + double char_width = 2.0*MyOpenGLTextWidth()/(viewport[3]); for (int i = 0; i <= 4; i++) { - double x = minx + i * (maxx-minx) / 4; - glRasterPos3d (x, 0.7,-5); - double val; if (logscale) val = minval * pow (maxval / minval, i / 4.0); else val = minval + i * (maxval-minval) / 4; - sprintf (buf, "%8.3e", val); - // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf); + snprintf (buf, buf_size, format.c_str(), val); + auto n = strlen(buf); + double x = minx + i * (maxx-minx) / 4; + x -= 0.5*char_width * n; // center text + glRasterPos3d (x, 0.7,-5); + MyOpenGLText (buf); } + if(unit != "") + MyOpenGLText (unit.c_str()); + + glPopAttrib (); + glEnable (GL_DEPTH_TEST); + } + + void VisualScene :: DrawTitle (string title) + { + if(title=="") + return; + glDisable (GL_LIGHTING); + glDisable (GL_DEPTH_TEST); + + glEnable (GL_COLOR_MATERIAL); + GLfloat textcol[3] = { GLfloat(1 - backcolor), + GLfloat(1 - backcolor), + GLfloat(1 - backcolor) }; + glColor3fv (textcol); + + glPushAttrib (GL_LIST_BIT); + + GLint viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + double char_width = 2.0*MyOpenGLTextWidth()/(viewport[3]); + double x = -0.5*char_width * title.size(); // center text + glRasterPos3d (x, 0.82,-5); + MyOpenGLText (title.c_str()); glPopAttrib (); glEnable (GL_DEPTH_TEST); } @@ -684,15 +716,15 @@ namespace netgen char buf[20]; glRasterPos3d (len, 0.0f, 0.0f); - sprintf (buf, "x"); + snprintf (buf, size(buf), "x"); // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); glRasterPos3d (0.0f, len, 0.0f); - sprintf (buf, "y"); + snprintf (buf, size(buf), "y"); // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); glRasterPos3d (0.0f, 0.0f, len); - sprintf (buf, "z"); + snprintf (buf, size(buf), "z"); // glCallLists (GLsizei(strlen (buf)), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); @@ -708,6 +740,27 @@ namespace netgen } + void VisualScene :: DrawMarker() + { + static constexpr GLubyte cross[] = { 0xc6, 0xee, 0x7c, 0x38, 0x7c, 0xee, 0xc6 }; + + if(!marker) + return; + + glColor3d (0, 0, 1); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glDisable (GL_COLOR_MATERIAL); + glDisable (GL_LIGHTING); + glDisable (GL_CLIP_PLANE0); + + auto & p = *marker; + glRasterPos3d (p[0], p[1], p[2]); + glBitmap (7, 7, 3, 3, 0, 0, &cross[0]); + } + + void VisualScene :: DrawNetgenLogo () { if (!vispar.drawnetgenlogo) return; @@ -758,11 +811,308 @@ namespace netgen } + void VisualSceneSurfaceMeshing::MouseMove(int oldx, int oldy, + int newx, int newy, + char mode) + { + double fac = 0.001; + if(mode == 'M') + { + shiftx += fac * (newx - oldx); + shifty += fac * (oldy - newy); + return; + } + else if(mode == 'Z') + { + scalex *= (1 - fac * (newy - oldy)); + scaley *= (1 - fac * (newy - oldy)); + return; + } + + VisualScene::MouseMove(oldx, oldy, newx, newy, mode); + } + + std::vector Snapshot( int w, int h ) + { + // save current settings + GLint viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + + glMatrixMode (GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + double pnear = 0.1; + double pfar = 10; + + gluPerspective(20.0f, double(w) / h, pnear, pfar); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glViewport(0,0,w,h); + + GLuint fb = 0; + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + + // create, reserve and attach color and depth renderbuffer + GLuint rbs[2]; + glGenRenderbuffers(2, rbs); + glBindRenderbuffer(GL_RENDERBUFFER, rbs[0]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbs[0]); + + glBindRenderbuffer(GL_RENDERBUFFER, rbs[1]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, w, h); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbs[1]); + + // check if framebuffer status is complete + if(int fbstatus; (fbstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) + cerr << "no frame buffer " << fbstatus << endl; + + visual_scene->DrawScene(); + glFinish(); + + std::vector buffer(w*h*3); + glPixelStorei(GL_UNPACK_ALIGNMENT,1); + glPixelStorei(GL_PACK_ALIGNMENT,1); + glReadPixels (0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &buffer[0]); + + glDeleteRenderbuffers(2, rbs); + glDeleteFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + // restore previous settings + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + glMatrixMode (GL_PROJECTION); + glPopMatrix(); + glMatrixMode (GL_MODELVIEW); + glPopMatrix(); + return buffer; + } + + VisualSceneSurfaceMeshing :: VisualSceneSurfaceMeshing () + : VisualScene() + { + ; + } + + VisualSceneSurfaceMeshing :: ~VisualSceneSurfaceMeshing () + { + ; + } + + void VisualSceneSurfaceMeshing :: DrawScene () + { + // int i, j, k; + if(!locpointsptr) + return; + auto& locpoints = *locpointsptr; + auto& loclines = *loclinesptr; + auto& plainpoints = *plainpointsptr; + + if (loclines.Size() != changeval) + { + center = Point<3>(0,0,-5); + rad = 0.1; + + // CalcTransformationMatrices(); + changeval = loclines.Size(); + } + + glClearColor(backcolor, backcolor, backcolor, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + SetLight(); + + // glEnable (GL_COLOR_MATERIAL); + + // glDisable (GL_SHADING); + // glColor3f (0.0f, 1.0f, 1.0f); + // glLineWidth (1.0f); + // glShadeModel (GL_SMOOTH); + + // glCallList (linelists.Get(1)); + + // SetLight(); + + glPushMatrix(); + glMultMatrixd (transformationmat); + + glShadeModel (GL_SMOOTH); + // glDisable (GL_COLOR_MATERIAL); + glEnable (GL_COLOR_MATERIAL); + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + + glEnable (GL_BLEND); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // glEnable (GL_LIGHTING); + + double shine = vispar.shininess; + double transp = vispar.transp; + + glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine); + glLogicOp (GL_COPY); + float mat_col[] = { 0.2, 0.2, 0.8, 1 }; + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); + + glPolygonOffset (1, 1); + glEnable (GL_POLYGON_OFFSET_FILL); + + float mat_colbl[] = { 0.8, 0.2, 0.2, 1 }; + float mat_cololdl[] = { 0.2, 0.8, 0.2, 1 }; + float mat_colnewl[] = { 0.8, 0.8, 0.2, 1 }; + + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + glPolygonOffset (1, -1); + glLineWidth (3); + + for (int i = 1; i <= loclines.Size(); i++) + { + if (i == 1) + { + glEnable (GL_POLYGON_OFFSET_FILL); + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbl); + } + else if (i <= oldnl) + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_cololdl); + else + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colnewl); + + int pi1 = loclines.Get(i).I1(); + int pi2 = loclines.Get(i).I2(); + + if (pi1 >= 1 && pi2 >= 1) + { + Point3d p1 = locpoints.Get(pi1); + Point3d p2 = locpoints.Get(pi2); + + glBegin (GL_LINES); + glVertex3f (p1.X(), p1.Y(), p1.Z()); + glVertex3f (p2.X(), p2.Y(), p2.Z()); + glEnd(); + } + + glDisable (GL_POLYGON_OFFSET_FILL); + } + + + glLineWidth (1); + + + glPointSize (5); + float mat_colp[] = { 1, 0, 0, 1 }; + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); + glBegin (GL_POINTS); + for (int i = 1; i <= locpoints.Size(); i++) + { + Point3d p = locpoints.Get(i); + glVertex3f (p.X(), p.Y(), p.Z()); + } + glEnd(); + + + glPopMatrix(); + + + // float mat_colp[] = { 1, 0, 0, 1 }; + + float mat_col2d1[] = { 1, 0.5, 0.5, 1 }; + float mat_col2d[] = { 1, 1, 1, 1 }; + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); + + glBegin (GL_LINES); + for (int i = 1; i <= loclines.Size(); i++) + { + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d); + if (i == 1) + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col2d1); + + int pi1 = loclines.Get(i).I1(); + int pi2 = loclines.Get(i).I2(); + + if (pi1 >= 1 && pi2 >= 1) + { + const auto& p1 = plainpoints.Get(pi1); + const auto& p2 = plainpoints.Get(pi2); + + glBegin (GL_LINES); + glVertex3f (scalex * p1[0] + shiftx, scaley * p1[1] + shifty, -5); + glVertex3f (scalex * p2[0] + shiftx, scaley * p2[1] + shifty, -5); + glEnd(); + } + } + glEnd (); + + + glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colp); + glBegin (GL_POINTS); + for (int i = 1; i <= plainpoints.Size(); i++) + { + const auto& p = plainpoints.Get(i); + glVertex3f (scalex * p[0] + shiftx, scaley * p[1] + shifty, -5); + } + glEnd(); + + + + + + + glDisable (GL_POLYGON_OFFSET_FILL); + + glPopMatrix(); + DrawCoordinateCross (); + DrawNetgenLogo (); + glFinish(); + + } + + + void VisualSceneSurfaceMeshing :: BuildScene (int zoomall) + { + } + + VisualSceneSurfaceMeshing vssurfacemeshing; + + void Impl_Render (bool blocking) + { + if (blocking && multithread.running) + { + multithread.redraw = 2; + while (multithread.redraw == 2) ; + } + else + multithread.redraw = 1; + } + + void Impl_UpdateVisSurfaceMeshData(int oldnl, + shared_ptr>> locpointsptr, + shared_ptr> loclinesptr, + shared_ptr>> plainpointsptr) + { + vssurfacemeshing.oldnl = oldnl; + if(locpointsptr) vssurfacemeshing.locpointsptr = locpointsptr; + if(loclinesptr) vssurfacemeshing.loclinesptr = loclinesptr; + if(plainpointsptr) vssurfacemeshing.plainpointsptr = plainpointsptr; + } + + static bool set_function_pointers = []() + { + Ptr_Render = Impl_Render; + Ptr_UpdateVisSurfaceMeshData = Impl_UpdateVisSurfaceMeshData; + return true; + }(); + + + #ifdef PARALLELGL void VisualScene :: InitParallelGL () { diff --git a/libsrc/visualization/mvdraw.hpp b/libsrc/visualization/mvdraw.hpp index 39efbc5f..e750edb1 100644 --- a/libsrc/visualization/mvdraw.hpp +++ b/libsrc/visualization/mvdraw.hpp @@ -9,30 +9,32 @@ namespace netgen class VisualScene { protected: - static DLL_HEADER Point3d center; - static DLL_HEADER double rad; + static NGGUI_API Point3d center; + static NGGUI_API double rad; static double lookatmat[16]; static double transmat[16]; static double rotmat[16]; static double centermat[16]; - static DLL_HEADER double transformationmat[16]; + static NGGUI_API double transformationmat[16]; GLdouble clipplane[4]; int changeval; - static DLL_HEADER GLdouble backcolor; + static NGGUI_API GLdouble backcolor; - static int DLL_HEADER selface; + static int NGGUI_API selface; static int selelement; - static PointIndex DLL_HEADER selpoint; + static PointIndex NGGUI_API selpoint; static PointIndex selpoint2; static int locpi; - static int DLL_HEADER seledge; + static int NGGUI_API seledge; - static int selecttimestamp; + static optional> marker; + static int subdivision_timestamp; + static int subdivisions; public: static int viewport[4]; static GLuint coltexname; @@ -41,48 +43,53 @@ namespace netgen public: - DLL_HEADER VisualScene (); - DLL_HEADER virtual ~VisualScene(); + NGGUI_API VisualScene (); + NGGUI_API virtual ~VisualScene(); - DLL_HEADER virtual void BuildScene (int zoomall = 0); - DLL_HEADER virtual void DrawScene (); + NGGUI_API virtual void BuildScene (int zoomall = 0); + NGGUI_API virtual void DrawScene (); - DLL_HEADER void CalcTransformationMatrices(); - DLL_HEADER void StandardRotation (const char * dir); - DLL_HEADER void ArbitraryRotation (const Array & alpha, const Array & vec); - DLL_HEADER void ArbitraryRotation (const double alpha, const Vec3d & vec); + NGGUI_API void CalcTransformationMatrices(); + NGGUI_API void StandardRotation (const char * dir); + NGGUI_API void ArbitraryRotation (const NgArray & alpha, const NgArray & vec); + NGGUI_API void ArbitraryRotation (const double alpha, const Vec3d & vec); - DLL_HEADER void MouseMove(int oldx, int oldy, - int newx, int newy, - char mode); + NGGUI_API virtual void MouseMove(int oldx, int oldy, + int newx, int newy, + char mode); - DLL_HEADER void LookAt (const Point<3> & cam, const Point<3> & obj, + NGGUI_API void LookAt (const Point<3> & cam, const Point<3> & obj, const Point<3> & camup); - DLL_HEADER void SetClippingPlane (); + NGGUI_API void SetClippingPlane (); - DLL_HEADER virtual void MouseDblClick (int px, int py); + NGGUI_API virtual void MouseDblClick (int px, int py); - DLL_HEADER void SetLight (); + NGGUI_API void SetLight (); static void SetBackGroundColor (double col) { backcolor = col; } - DLL_HEADER void CreateTexture (int ncols, int linear, double alpha, int typ); - DLL_HEADER void DrawColorBar (double minval, double maxval, int logscale = 0, bool linear = 1); - DLL_HEADER void DrawCoordinateCross (); - DLL_HEADER void DrawNetgenLogo (); - DLL_HEADER void SetOpenGlColor(double val, double valmin, double valmax, int logscale = 0); + NGGUI_API void CreateTexture (int ncols, int linear, double alpha, int typ); + NGGUI_API void DrawColorBar (double minval, double maxval, int logscale = 0, bool linear = 1, string format="%8.3e", string unit=""); + NGGUI_API void DrawTitle (string title); + NGGUI_API void DrawCoordinateCross (); + NGGUI_API void DrawMarker(); + NGGUI_API void DrawNetgenLogo (); + NGGUI_API void SetOpenGlColor(double val, double valmin, double valmax, int logscale = 0); #ifdef PARALLELGL - DLL_HEADER void InitParallelGL (); - DLL_HEADER void Broadcast (); + NGGUI_API void InitParallelGL (); + NGGUI_API void Broadcast (); #endif }; - DLL_HEADER extern void MyOpenGLText (const char * text); - DLL_HEADER extern void Set_OpenGLText_Callback ( void (*fun) (const char * text) ); + NGGUI_API extern void MyOpenGLText (const char * text); + NGGUI_API extern int MyOpenGLTextWidth (); + NGGUI_API extern void Set_OpenGLText_Callback ( void (*fun) (const char * text), int width ); + NGGUI_API extern VisualScene visual_scene_cross; + NGGUI_API extern VisualScene *visual_scene; @@ -95,14 +102,23 @@ namespace netgen class VisualSceneSurfaceMeshing : public VisualScene { + double scalex = 1., scaley = 1., shiftx = 0., shifty = 0.; public: + shared_ptr>> locpointsptr; + shared_ptr> loclinesptr; + shared_ptr>> plainpointsptr; + int oldnl; + bool clearptr; VisualSceneSurfaceMeshing (); virtual ~VisualSceneSurfaceMeshing (); - virtual void BuildScene (int zoomall = 0); - virtual void DrawScene (); + void BuildScene (int zoomall = 0) override; + void DrawScene () override; + NGGUI_API void MouseMove(int oldx, int oldy, int newx, int newy, + char mode) override; }; + NGGUI_API extern VisualSceneSurfaceMeshing vssurfacemeshing; @@ -111,39 +127,58 @@ namespace netgen class VisualSceneMesh : public VisualScene { - int filledlist; - int linelist; - int edgelist; - int pointnumberlist; + int filledlist = 0; + int linelist = 0; + int edgelist = 0; + int pointnumberlist = 0; - int tetlist; - int prismlist; - int pyramidlist; - int hexlist; + int tetlist = 0; + int prismlist = 0; + int pyramidlist = 0; + int hexlist = 0; - int badellist; - int identifiedlist; - int domainsurflist; + int badellist = 0; + int identifiedlist = 0; + int domainsurflist = 0; - int vstimestamp;//, selecttimestamp; - int filledtimestamp; - int linetimestamp; - int edgetimestamp; - int pointnumbertimestamp; + int vstimestamp = -1; + int filledtimestamp = -1; + int linetimestamp = -1; + int edgetimestamp = -1; + int pointnumbertimestamp = -1; - int tettimestamp; - int prismtimestamp; - int pyramidtimestamp; - int hextimestamp; + int tettimestamp = -1; + int prismtimestamp = -1; + int pyramidtimestamp = -1; + int hextimestamp = -1; - int badeltimestamp; - int identifiedtimestamp; - int domainsurftimestamp; + int badeltimestamp = -1; + int identifiedtimestamp = -1; + int domainsurftimestamp = -1; + struct { + unsigned texture = -1; + int width = 0; + int height = 0; + int size = 0; + } colors; + + struct { + unsigned framebuffer = 0; + unsigned render_buffers[2]; + unsigned width = 0; + unsigned height = 0; + unsigned x = 0; + unsigned y = 0; + int list = 0; + int list_timestamp = -1; + double projmat[16]; + int viewport[4]; + } select; #ifdef PARALLELGL - Array par_linelists; - Array par_filledlists; + NgArray par_linelists; + NgArray par_filledlists; #endif MouseEventHandler * user_me_handler; @@ -159,12 +194,12 @@ namespace netgen // weak_ptr wp_mesh; public: - DLL_HEADER VisualSceneMesh (); - DLL_HEADER virtual ~VisualSceneMesh (); + NGGUI_API VisualSceneMesh (); + NGGUI_API virtual ~VisualSceneMesh (); - DLL_HEADER virtual void BuildScene (int zoomall = 0); - DLL_HEADER virtual void DrawScene (); - DLL_HEADER virtual void MouseDblClick (int px, int py); + NGGUI_API virtual void BuildScene (int zoomall = 0); + NGGUI_API virtual void DrawScene (); + NGGUI_API virtual void MouseDblClick (int px, int py); // void SetMesh (shared_ptr mesh) { wp_mesh = mesh; } // shared_ptr GetMesh () { return shared_ptr(wp_mesh); } @@ -174,18 +209,20 @@ namespace netgen { user_me_handler = handler; } - DLL_HEADER int SelectedFace () const + NGGUI_API int SelectedFace () const { return selface; } - DLL_HEADER void SetSelectedFace (int asf); + NGGUI_API void SetSelectedFace (int asf); // { selface = asf; selecttimestamp = GetTimeStamp(); } - DLL_HEADER int SelectedEdge () const + NGGUI_API int SelectedEdge () const { return seledge; } - DLL_HEADER int SelectedElement () const + NGGUI_API int SelectedElement () const { return selelement; } - DLL_HEADER int SelectedPoint () const + NGGUI_API int SelectedPoint () const { return selpoint; } - void BuildFilledList (bool names); + void BuildFilledList (bool select); + void BuildColorTexture(); + void SelectCenter(int zoomall); // private: void BuildLineList(); void BuildEdgeList(); @@ -199,12 +236,16 @@ namespace netgen void BuildBadelList(); void BuildIdentifiedList(); void BuildDomainSurfList(); + + bool SelectSurfaceElement (int px, int py, Point<3> &p, bool select_on_clipping_plane); + bool Unproject(int px, int py, Point<3> &p); + ngcore::INT<2> Project(Point<3> p); }; - DLL_HEADER extern VisualSceneMesh vsmesh; + NGGUI_API extern VisualSceneMesh vsmesh; - class DLL_HEADER VisualSceneSpecPoints : public VisualScene + class NGGUI_API VisualSceneSpecPoints : public VisualScene { public: VisualSceneSpecPoints (); @@ -239,6 +280,7 @@ namespace netgen PointIndex & selpoint2, int & locpi); + NGGUI_API std::vector Snapshot( int w, int h ); } diff --git a/libsrc/visualization/stlmeshing.cpp b/libsrc/visualization/stlmeshing.cpp deleted file mode 100644 index 452dbd92..00000000 --- a/libsrc/visualization/stlmeshing.cpp +++ /dev/null @@ -1,1076 +0,0 @@ -#include -#include - -#include -#include - -#include -#ifndef NOTCL -#include -#endif - -namespace netgen -{ - -/* -//mmm -#include "stlgeom/modeller.hpp" -*/ - -/* *********************** Draw STL Geometry **************** */ - -extern STLGeometry * stlgeometry; -extern AutoPtr mesh; - - -#ifdef OPENGL - -// #include "../../ngtcltk/mvdraw.hpp" - - -VisualSceneSTLMeshing :: VisualSceneSTLMeshing () - : VisualScene() -{ - selecttrig = 0; - nodeofseltrig = 1; - stlgeometry->SetSelectTrig(selecttrig); - stlgeometry->SetNodeOfSelTrig(nodeofseltrig); -} - -VisualSceneSTLMeshing :: ~VisualSceneSTLMeshing () -{ - ; -} - -void VisualSceneSTLMeshing :: DrawScene () -{ - int i, j, k; - - if (changeval != stlgeometry->GetNT()) - BuildScene(); - changeval = stlgeometry->GetNT(); - - int colormeshsize = vispar.colormeshsize; - - double hmin = 0.0, hmax = 1.0; - - if (colormeshsize) - { - hmax = -1E50; - hmin = +1E50; - double ms; - - for (i = 1; i <= stlgeometry->GetNP(); i++) - { - ms = mesh->GetH (stlgeometry->GetPoint(i)); - hmin = min2(hmin,ms); - hmax = max2(hmax,ms); - } - - //hmax = mparam.maxh; - //hmin = mesh->GetMinH (stlgeometry->GetBoundingBox().PMin(), - // stlgeometry->GetBoundingBox().PMax()); - - if (hmin == 0) hmin = 0.1 * hmax; - //hmax *= 1.1; - } - - - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - - SetLight(); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - SetClippingPlane (); - - glShadeModel (GL_SMOOTH); - glDisable (GL_COLOR_MATERIAL); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - float mat_spec_col[] = { 1, 1, 1, 1 }; - glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_spec_col); - - double shine = vispar.shininess; - // double transp = vispar.transp; - - glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine); - glLogicOp (GL_COPY); - - float mat_colred[] = { 0.9f, 0.0f, 0.0f, 1.0f }; - float mat_colgreen[] = { 0.0f, 0.9f, 0.0f, 1.0f }; - float mat_colblue[] = { 0.1f, 0.1f, 1.0f, 1.0f }; - - float mat_colbluegreen[] = { 0.1f, 0.5f, 0.9f, 1.0f }; - // float mat_colpink[] = { 1.0f, 0.1f, 0.5f, 1.0f }; - float mat_colviolet[] = { 1.0f, 0.1f, 1.0f, 1.0f }; - float mat_colbrown[] = { 0.8f, 0.6f, 0.1f, 1.0f }; - // float mat_colorange[] = { 0.9f, 0.7f, 0.1f, 1.0f }; - // float mat_colturquis[] = { 0.0f, 1.0f, 0.8f, 1.0f }; - - float mat_colgrey[] = { 0.3f, 0.3f, 0.3f, 1.0f }; - - float mat_collred[] = { 1.0f, 0.5f, 0.5f, 1.0f }; - float mat_collgreen[] = { 0.2f, 1.9f, 0.2f, 1.0f }; - float mat_collbrown[] = { 1.0f, 0.8f, 0.3f, 1.0f }; - - float mat_collgrey[] = { 0.8f, 0.8f, 0.8f, 1.0f }; - // float mat_colmgrey[] = { 0.4f, 0.4f, 0.4f, 1.0f }; - - float mat_colstlbody[] = { 0.0f, 0.0f, 0.8f, 1.0f }; - float mat_colseltrig[] = { 0.7f, 0.7f, 0.3f, 1.0f }; - float mat_colseledge[] = { 0.7f, 0.7f, 1.0f, 1.0f }; - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colblue); - - float pgoff = 0.5f; - - glPolygonOffset (pgoff*1, pgoff*1); - glEnable (GL_POLYGON_OFFSET_FILL); - - glEnable (GL_NORMALIZE); - - /* - { - //mmm - //test modeller - Modeller model; - - //MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01); - //model.Add(&z1); - //MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01); - //model.Add(&z2); - - MoZylinder z1(Point3d(0,0,0),Vec3d(100,0,0),20,0.01); - MoZylinder z2(Point3d(50,50,0),Vec3d(0,-100,0),20,0.01); - MoCombine cb1(&z1,&z2); - model.Add(&cb1); - - Array trigs; - model.GetTriangles(trigs); - int i, k; - glBegin (GL_TRIANGLES); - for (i = 1; i <= trigs.Size(); i++) - { - const MoTriangle & tria = trigs.Get(i); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - - for (k = 0; k < 3; k++) - { - glVertex3f (tria.pts[k].X(), - tria.pts[k].Y(), - tria.pts[k].Z()); - } - } - glEnd (); - - - } - -*/ - - - - - if (!stlgeometry->trigsconverted) - { - glBegin (GL_TRIANGLES); - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - /* - if (j % 10 == seltria) - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_colred); - */ - - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - - - for (k = 1; k <= 3; k++) - { - const Point3d & tp = stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k)); - glVertex3f (tp.X(), tp.Y(), tp.Z()); - - } - /* - if (j%10 == seltria) - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_colblue); - */ - } - glEnd (); - - glDisable (GL_POLYGON_OFFSET_FILL); - - int showtrias = vispar.stlshowtrias; - - if (showtrias) - { - float mat_coll[] = { 0.2f, 0.2f, 0.2f, 1.f }; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_coll); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - - glEnable (GL_NORMALIZE); - - glBegin (GL_TRIANGLES); - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle & tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - - for (k = 1; k <= 3; k++) - { - const Point3d & tp = - stlgeometry->GetPoint(stlgeometry->GetTriangle(j).PNum(k)); - glVertex3f (tp.X(), tp.Y(), tp.Z()); - - } - - /* - for (k = 0; k < 3; k++) - { - glVertex3f (tria.pts[k].X(), - tria.pts[k].Y(), - tria.pts[k].Z()); - } - */ - } - glEnd (); - } - } - else - { - int showfilledtrias = vispar.stlshowfilledtrias; - - //(*mycout) << "in " << showfilledtrias << ", NT=" << stlgeometry -> GetNT() << endl; - - int chartnumber; - if (vispar.stlshowmarktrias) - chartnumber = vispar.stlchartnumber + vispar.stlchartnumberoffset; - else - chartnumber = stlgeometry->GetMeshChartNr(); - - if (showfilledtrias) - { - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - if (colormeshsize) - glEnable (GL_COLOR_MATERIAL); - - glPolygonOffset (pgoff*4, pgoff*4); - glEnable (GL_POLYGON_OFFSET_FILL); - glEnable (GL_NORMALIZE); - - - glBegin (GL_TRIANGLES); - - int selt = stlgeometry -> GetSelectTrig(); - if (stldoctor.selectmode != 0) - {selt = 0; } //do not show selected triangle!!!! - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody); - - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;} - - if (j == selt) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseltrig); - } - else if (j == selt+1) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colstlbody); - } - - const STLTriangle& st = stlgeometry -> GetTriangle(j); - - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - const Point3d & p = stlgeometry->GetPoint(st[k]); - if (colormeshsize) - { - SetOpenGlColor (mesh->GetH (p), hmin, hmax, 1); - } - - glVertex3f (p.X(), p.Y(), p.Z()); - } - } - - glEnd (); - } - - int foundseltrig = stlgeometry -> GetSelectTrig(); - if (foundseltrig == 0 || foundseltrig > stlgeometry->GetNT() || - (stldoctor.showvicinity && !stlgeometry->Vicinity(foundseltrig))) - {foundseltrig = 0;} - - if (foundseltrig) - { - - glPolygonOffset (pgoff*0, 0); - glEnable (GL_POLYGON_OFFSET_FILL); - - //glDisable (GL_POLYGON_OFFSET_FILL); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colseledge); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - - glEnable (GL_NORMALIZE); - - if (stldoctor.selectmode == 2) - { - //point - const STLTriangle& st = stlgeometry -> GetTriangle(foundseltrig); - const Point3d & p1 = stlgeometry->GetPoint(st[0]); - const Point3d & p2 = stlgeometry->GetPoint(st[1]); - const Point3d & p3 = stlgeometry->GetPoint(st[2]); - - double cs = (Dist(p1,p2)+Dist(p2,p3)+Dist(p3,p1))/100.; - - const Point3d & p = stlgeometry->GetPoint(st[nodeofseltrig-1]); - - glLineWidth (4); - glBegin (GL_LINES); - glVertex3f(p.X()+cs, p.Y()+cs, p.Z()+cs); - glVertex3f(p.X()-cs, p.Y()-cs, p.Z()-cs); - - glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs); - glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs); - - glVertex3f(p.X()-cs, p.Y()+cs, p.Z()+cs); - glVertex3f(p.X()+cs, p.Y()-cs, p.Z()-cs); - - glVertex3f(p.X()+cs, p.Y()-cs, p.Z()+cs); - glVertex3f(p.X()-cs, p.Y()+cs, p.Z()-cs); - - glEnd (); - glLineWidth (1); - } - else if (stldoctor.selectmode == 1 || - stldoctor.selectmode == 3 || - stldoctor.selectmode == 4) - { - //multiedge - - const Array& me = stlgeometry->SelectedMultiEdge(); - if (stlgeometry->GetSelectTrig() > 0 && - stlgeometry->GetSelectTrig() <= stlgeometry->GetNT() && - me.Size()) - { - - int en = stlgeometry->EdgeDataList().GetEdgeNum(me.Get(1).i1,me.Get(1).i2); - int status = stlgeometry->EdgeDataList().Get(en).GetStatus(); - - switch (status) - { - case ED_CONFIRMED: - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_collgreen); - break; - case ED_CANDIDATE: - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_collbrown); - break; - case ED_EXCLUDED: - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collred); - break; - } - - glLineWidth (2); - glBegin (GL_LINES); - for (j = 1; j <= me.Size(); j++) - { - Point3d p1 = stlgeometry->GetPoint(me.Get(j).i1); - Point3d p2 = stlgeometry->GetPoint(me.Get(j).i2); - - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - } - glEnd (); - glLineWidth (1); - } - } - } - - int showmarktrias = vispar.stlshowmarktrias || vispar.stlshowactivechart; - - if (stldoctor.showmarkedtrigs) - { - //(*mycout) << "marked" << endl; - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); //GL_LINE - glPolygonOffset (pgoff*1, pgoff*1); - glEnable (GL_POLYGON_OFFSET_FILL); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbluegreen); - glEnable (GL_NORMALIZE); - - glBegin (GL_TRIANGLES); - - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) - {continue;} - - if (!stlgeometry->IsMarkedTrig(j)) - {continue;} - - const STLTriangle& st = stlgeometry -> GetTriangle(j); - - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - const Point3d & p = stlgeometry->GetPoint(st[k]); - glVertex3f (p.X(), p.Y(), p.Z()); - } - } - glEnd (); - - //show OpenSegments on original geometry - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colviolet); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - glPolygonOffset (pgoff*1, 1); - - glEnable (GL_NORMALIZE); - - glBegin (GL_LINES); - - if (stlgeometry->GetNMarkedSegs()) - { - Point<3> p1,p2; - for (j = 1; j <= stlgeometry -> GetNMarkedSegs(); j++) - { - stlgeometry->GetMarkedSeg(j,p1,p2); - glVertex3dv(&p1(0)); - glVertex3dv(&p2(0)); - } - } - glEnd (); - } - - - if (stldoctor.showfaces) - { - int facenumber = vispar.stlchartnumber + vispar.stlchartnumberoffset; - - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glPolygonOffset (pgoff*3, 3); - glEnable (GL_POLYGON_OFFSET_FILL); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_collgrey); - glEnable (GL_NORMALIZE); - - glBegin (GL_TRIANGLES); - - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) - {continue;} - - //(*mycout) << " facenum = " << stlgeometry->GetTriangle(j).GetFaceNum() << " "; - if (stlgeometry->GetTriangle(j).GetFaceNum() != facenumber) - {continue;} - - const STLTriangle& st = stlgeometry -> GetTriangle(j); - - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - Point3d p = stlgeometry->GetPoint(st[k]); - glVertex3f (p.X(), p.Y(), p.Z()); - } - } - glEnd (); - } - - if (showmarktrias && stlgeometry->AtlasMade()) - { - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - glPolygonOffset (pgoff*3, 3); - glEnable (GL_POLYGON_OFFSET_FILL); - - glBegin (GL_TRIANGLES); - - if (chartnumber >= 1 && chartnumber <= stlgeometry->GetNOCharts()) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown); - const STLChart& chart = stlgeometry->GetChart(chartnumber); - for (j = 1; j <= chart.GetNChartT(); j++) - { - /* - if (j == charttrignumber) - {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred);} - else - {glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colbrown);} - */ - const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetChartTrig(j)); - - - const Vec3d & n = stlgeometry->GetTriangle(chart.GetChartTrig(j)).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetChartTrig(j)); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - glVertex3f (stlgeometry->GetPoint(st[k])(0), - stlgeometry->GetPoint(st[k])(1), - stlgeometry->GetPoint(st[k])(2)); - } - } - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen); - - for (j = 1; j <= chart.GetNOuterT(); j++) - { - - const STLTriangle& st = stlgeometry -> GetTriangle(chart.GetOuterTrig(j)); - - const Vec3d & n = stlgeometry->GetTriangle(chart.GetOuterTrig(j)).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - - - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(chart.GetOuterTrig(j)); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - glVertex3f (stlgeometry->GetPoint(st[k])(0), - stlgeometry->GetPoint(st[k])(1), - stlgeometry->GetPoint(st[k])(2)); - } - } - } - glEnd (); - } - - int showtrias = vispar.stlshowtrias; - - if (showtrias) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgrey); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - glPolygonOffset (pgoff*2, 2); - glEnable (GL_POLYGON_OFFSET_FILL); - glEnable (GL_NORMALIZE); - - glBegin (GL_TRIANGLES); - - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;} - - const STLTriangle& st = stlgeometry -> GetTriangle(j); - - const Vec3d & n = stlgeometry->GetTriangle(j).Normal(); - glNormal3f (n.X(), n.Y(), n.Z()); - /* - const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j); - glNormal3f (tria.normal.X(), - tria.normal.Y(), - tria.normal.Z()); - */ - for (k = 0; k < 3; k++) - { - glVertex3f (stlgeometry->GetPoint(st[k])(0), - stlgeometry->GetPoint(st[k])(1), - stlgeometry->GetPoint(st[k])(2)); - } - } - glEnd (); - } - - int showedges = vispar.stlshowedges; - - if (showedges) - { - glPolygonOffset (pgoff*1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - //glDisable (GL_POLYGON_OFFSET_FILL); - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen); - glPolygonMode (GL_FRONT_AND_BACK, GL_LINE); - - glEnable (GL_NORMALIZE); - - glBegin (GL_LINES); - - /* - if (stldoctor.useexternaledges) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colorange); - for (j = 1; j <= stlgeometry -> NOExternalEdges(); j++) - { - twoint v = stlgeometry->GetExternalEdge(j); - Point3d p1 = stlgeometry->GetPoint(v.i1); - Point3d p2 = stlgeometry->GetPoint(v.i2); - - Vec3d n1 = stlgeometry->GetNormal(v.i1); - Vec3d n2 = stlgeometry->GetNormal(v.i2); - - glNormal3f(n1.X(), n1.Y(), n1.Z()); - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glNormal3f(n2.X(), n2.Y(), n2.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - } - } - */ - - - if (!stlgeometry->meshlines.Size() || !stldoctor.drawmeshededges) - { - /* - for (j = 1; j <= stlgeometry -> GetNE(); j++) - { - STLEdge v = stlgeometry->GetEdge(j); - Point3d p1 = stlgeometry->GetPoint(v.pts[0]); - Point3d p2 = stlgeometry->GetPoint(v.pts[1]); - - Vec3d n1 = stlgeometry->GetNormal(v.pts[0]); - Vec3d n2 = stlgeometry->GetNormal(v.pts[1]); - - glNormal3f(n1.X(), n1.Y(), n1.Z()); - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glNormal3f(n2.X(), n2.Y(), n2.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - } - */ - const STLEdgeDataList& ed = stlgeometry->EdgeDataList(); - for (i = 1; i <= ed.Size(); i++) - { - if (ed.Get(i).GetStatus() != ED_UNDEFINED) - { - switch (ed.Get(i).GetStatus()) - { - case ED_CONFIRMED: - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_colgreen); - break; - case ED_CANDIDATE: - glMaterialfv (GL_FRONT_AND_BACK, - GL_AMBIENT_AND_DIFFUSE, mat_colbrown); - break; - case ED_EXCLUDED: - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred); - break; - } - - if (ed.Get(i).GetStatus() == ED_EXCLUDED && !stldoctor.showexcluded) continue; - - Point3d p1 = stlgeometry->GetPoint(ed.Get(i).PNum(1)); - Point3d p2 = stlgeometry->GetPoint(ed.Get(i).PNum(2)); - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - } - } - } - - /* - else - if (stlgeometry->meshlines.Size() == 0) - { - for (j = 1; j <= stlgeometry->GetNLines(); j++) - { - STLLine* line = stlgeometry->GetLine(j); - int pn1, pn2; - for (int k = 1; k <= line->NP()-1; k++) - { - pn1 = line->PNum(k); - pn2 = line->PNum(k+1); - - Point3d p1 = stlgeometry->GetPoint(pn1); - Point3d p2 = stlgeometry->GetPoint(pn2); - - Vec3d n1 = stlgeometry->GetNormal(pn1); - Vec3d n2 = stlgeometry->GetNormal(pn2); - - glNormal3f(n1.X(), n1.Y(), n1.Z()); - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glNormal3f(n2.X(), n2.Y(), n2.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - } - } - } - */ - - else if (stlgeometry->meshlines.Size() != 0) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen); - for (j = 1; j <= stlgeometry->meshlines.Size(); j++) - { - STLLine* line = stlgeometry->meshlines.Get(j); - int pn1, pn2; - for (int k = 1; k <= line->NP()-1; k++) - { - pn1 = line->PNum(k); - pn2 = line->PNum(k+1); - - Point3d p1 = stlgeometry->meshpoints.Get(pn1); - Point3d p2 = stlgeometry->meshpoints.Get(pn2); - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colgreen); - glVertex3f(p1.X(), p1.Y(), p1.Z()); - glVertex3f(p2.X(), p2.Y(), p2.Z()); - - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred); - double cs = 0.02*Dist(p1,p2); - glVertex3f(p1.X()+cs, p1.Y()+cs, p1.Z()+cs); - glVertex3f(p1.X()-cs, p1.Y()-cs, p1.Z()-cs); - glVertex3f(p2.X()+cs, p2.Y()+cs, p2.Z()+cs); - glVertex3f(p2.X()-cs, p2.Y()-cs, p2.Z()-cs); - - glVertex3f(p1.X()-cs, p1.Y()+cs, p1.Z()+cs); - glVertex3f(p1.X()+cs, p1.Y()-cs, p1.Z()-cs); - glVertex3f(p2.X()-cs, p2.Y()+cs, p2.Z()+cs); - glVertex3f(p2.X()+cs, p2.Y()-cs, p2.Z()-cs); - - } - } - } - - - glEnd (); - } - - if (stldoctor.showedgecornerpoints && stlgeometry->LineEndPointsSet()) - { - glPointSize (5); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_colred); - glBegin (GL_POINTS); - for (i = 1; i <= stlgeometry->GetNP(); i++) - { - if (stlgeometry->IsLineEndPoint(i)) - { - const Point3d p = stlgeometry->GetPoint(i); - glVertex3f (p.X(), p.Y(), p.Z()); - } - } - glEnd(); - - } - - - } - - - glPopMatrix(); - - if (vispar.colormeshsize) - DrawColorBar (hmin, hmax, 1); - - glFinish(); -} - - -void VisualSceneSTLMeshing :: BuildScene (int zoomall) -{ - if (selecttrig && zoomall == 2) - center = stlgeometry -> GetPoint ( stlgeometry->GetTriangle(selecttrig).PNum(nodeofseltrig)); - else - center = stlgeometry -> GetBoundingBox().Center(); - - rad = stlgeometry -> GetBoundingBox().Diam() / 2; - - CalcTransformationMatrices(); -} - - - -void VisualSceneSTLMeshing :: MouseDblClick (int px, int py) -{ - // (*mycout) << "dblclick: " << px << " - " << py << endl; - - - int i, j, k, hits; - - // select surface triangle by mouse click - - GLuint selbuf[10000]; - glSelectBuffer (10000, selbuf); - - - glRenderMode (GL_SELECT); - - GLint viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - - /* - (*mycout) << "viewport = " << viewport[0] << " " - << viewport[1] << " " << viewport[2] << " " << viewport[3] << endl; - */ - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - - - GLdouble projmat[16]; - glGetDoublev (GL_PROJECTION_MATRIX, projmat); - - glLoadIdentity(); - gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); - glMultMatrixd (projmat); - - - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode (GL_MODELVIEW); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - - glInitNames(); - glPushName (1); - - - glEnable (GL_POLYGON_OFFSET_FILL); - for (j = 1; j <= stlgeometry -> GetNT(); j++) - { - if (stldoctor.showvicinity && !stlgeometry->Vicinity(j)) {continue;} - - const STLTriangle& st = stlgeometry -> GetTriangle(j); - - //const STLReadTriangle& tria = stlgeometry -> GetReadTriangle(j); - //glNormal3f (tria.normal.X(), tria.normal.Y(), tria.normal.Z()); - - if (stldoctor.selectmode == 0) - { - glLoadName (j); - glBegin (GL_TRIANGLES); - for (k = 0; k < 3; k++) - { - Point3d p = stlgeometry->GetPoint(st[k]); - glVertex3f (p.X(), p.Y(), p.Z()); - } - glEnd (); - } - else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3 - || stldoctor.selectmode == 4) - { - Point3d pm = Center(stlgeometry->GetPoint(st[0]), - stlgeometry->GetPoint(st[1]), - stlgeometry->GetPoint(st[2])); - - for (k = 0; k < 3; k++) - { - glLoadName (j*3+k-2); - glBegin (GL_TRIANGLES); - - Point3d p1 = stlgeometry->GetPoint(st[k]); - Point3d p2 = stlgeometry->GetPoint(st[(k+1)%3]); - glVertex3f (p1.X(), p1.Y(), p1.Z()); - glVertex3f (p2.X(), p2.Y(), p2.Z()); - glVertex3f (pm.X(), pm.Y(), pm.Z()); - - glEnd (); - } - } - else - { - Point3d pm1 = Center(stlgeometry->GetPoint(st[0]), - stlgeometry->GetPoint(st[1])); - Point3d pm2 = Center(stlgeometry->GetPoint(st[1]), - stlgeometry->GetPoint(st[2])); - Point3d pm3 = Center(stlgeometry->GetPoint(st[2]), - stlgeometry->GetPoint(st[0])); - - Point3d p1 = stlgeometry->GetPoint(st[0]); - Point3d p2 = stlgeometry->GetPoint(st[1]); - Point3d p3 = stlgeometry->GetPoint(st[2]); - - glLoadName (j*4-3); - glBegin (GL_TRIANGLES); - glVertex3f (p1.X(), p1.Y(), p1.Z()); - glVertex3f (pm1.X(), pm1.Y(), pm1.Z()); - glVertex3f (pm3.X(), pm3.Y(), pm3.Z()); - glEnd (); - - glLoadName (j*4-2); - glBegin (GL_TRIANGLES); - glVertex3f (p2.X(), p2.Y(), p2.Z()); - glVertex3f (pm2.X(), pm2.Y(), pm2.Z()); - glVertex3f (pm1.X(), pm1.Y(), pm1.Z()); - glEnd (); - - glLoadName (j*4-1); - glBegin (GL_TRIANGLES); - glVertex3f (p3.X(), p3.Y(), p3.Z()); - glVertex3f (pm3.X(), pm3.Y(), pm3.Z()); - glVertex3f (pm2.X(), pm2.Y(), pm2.Z()); - glEnd (); - - glLoadName (j*4); - glBegin (GL_TRIANGLES); - glVertex3f (pm1.X(), pm1.Y(), pm1.Z()); - glVertex3f (pm2.X(), pm2.Y(), pm2.Z()); - glVertex3f (pm3.X(), pm3.Y(), pm3.Z()); - glEnd (); - } - } - - glPopName(); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - glFlush(); - - - hits = glRenderMode (GL_RENDER); - - // (*mycout) << "hits = " << hits << endl; - - //int minrec = -1; - int minname = 0; - GLuint mindepth = 0; - for (i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - GLuint curdepth = selbuf[4*i+1]; - - /* - (*mycout) << selbuf[4*i] << " " << selbuf[4*i+1] << " " - << selbuf[4*i+2] << " " << selbuf[4*i+3] << endl; - */ - if (curname && - (curdepth < mindepth || !minname)) - { - //minrec = i; - mindepth = curdepth; - minname = curname; - } - } - - if (!minname) {return;} - - if (stldoctor.selectmode == 0) - { - int oldtrig = selecttrig; - selecttrig = minname; - if (selecttrig == oldtrig) - nodeofseltrig = (nodeofseltrig % 3) + 1; - else - nodeofseltrig = 1; - - stlgeometry->SetSelectTrig(selecttrig); - stlgeometry->SetNodeOfSelTrig(nodeofseltrig); - stlgeometry->PrintSelectInfo(); - - } - else if (stldoctor.selectmode == 1 || stldoctor.selectmode == 3 || stldoctor.selectmode == 4) - { - selecttrig = (minname-1) / 3 + 1; - nodeofseltrig = minname-selecttrig*3+3; - - stlgeometry->SetSelectTrig(selecttrig); - stlgeometry->SetNodeOfSelTrig(nodeofseltrig); - stlgeometry->PrintSelectInfo(); - - if (stldoctor.selectmode == 1) - { - stlgeometry->BuildSelectedEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig), - stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1))); - } - if (stldoctor.selectmode == 3) - { - stlgeometry->BuildSelectedMultiEdge(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig), - stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1))); - } - else if (stldoctor.selectmode == 4) - { - stlgeometry->BuildSelectedCluster(twoint(stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig), - stlgeometry->GetTriangle(selecttrig).PNumMod(nodeofseltrig+1))); - } - - switch (stldoctor.edgeselectmode) - { - case 1: stlgeometry->STLDoctorUndefinedEdge(); break; - case 2: stlgeometry->STLDoctorConfirmEdge(); break; - case 3: stlgeometry->STLDoctorCandidateEdge(); break; - case 4: stlgeometry->STLDoctorExcludeEdge(); break; - default: break; - } - } - else if (stldoctor.selectmode == 2) - { - selecttrig = (minname-1) / 4 + 1; - nodeofseltrig = minname-selecttrig*4+4; - if (nodeofseltrig == 4) {nodeofseltrig = 1;} - - stlgeometry->SetSelectTrig(selecttrig); - stlgeometry->SetNodeOfSelTrig(nodeofseltrig); - stlgeometry->PrintSelectInfo(); - - } - - if (stldoctor.showtouchedtrigchart && stlgeometry->AtlasMade() && stlgeometry->GetSelectTrig()) - { - vispar.stlchartnumber = stlgeometry->GetChartNr(stlgeometry->GetSelectTrig()); - vispar.stlchartnumberoffset = 0; - } - -} - - - - -VisualSceneSTLMeshing vsstlmeshing; - -#endif - - - - -} diff --git a/libsrc/visualization/vispar.hpp b/libsrc/visualization/vispar.hpp index 94026440..93d4425d 100644 --- a/libsrc/visualization/vispar.hpp +++ b/libsrc/visualization/vispar.hpp @@ -120,7 +120,7 @@ public: public: VisualizationParameters(); }; -DLL_HEADER extern VisualizationParameters vispar; +NGGUI_API extern VisualizationParameters vispar; } #endif diff --git a/libsrc/visualization/visual.hpp b/libsrc/visualization/visual.hpp index 493cb739..94dcf87d 100644 --- a/libsrc/visualization/visual.hpp +++ b/libsrc/visualization/visual.hpp @@ -17,10 +17,12 @@ Visualization // #define PARALLELGL // #endif +#include "visual_api.hpp" #include "../include/incopengl.hpp" +#include "../meshing/visual_interface.hpp" +#include "../meshing/soldata.hpp" #include "vispar.hpp" -#include "soldata.hpp" #include "mvdraw.hpp" #include diff --git a/libsrc/visualization/visual_api.hpp b/libsrc/visualization/visual_api.hpp new file mode 100644 index 00000000..8873f8da --- /dev/null +++ b/libsrc/visualization/visual_api.hpp @@ -0,0 +1,10 @@ +#ifndef VISUAL_API_HPP_INCLUDED +#define VISUAL_API_HPP_INCLUDED + +#ifdef nggui_EXPORTS +#define NGGUI_API NGCORE_API_EXPORT +#else +#define NGGUI_API NGCORE_API_IMPORT +#endif + +#endif // VISUAL_API_HPP_INCLUDED diff --git a/libsrc/visualization/visual_dummy.cpp b/libsrc/visualization/visual_dummy.cpp deleted file mode 100644 index 8d82f8d6..00000000 --- a/libsrc/visualization/visual_dummy.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include - -#include -#include "../include/nginterface.h" - -void Ng_ClearSolutionData () { ; } -void Ng_InitSolutionData (Ng_SolutionData * soldata) { ; } -void Ng_SetSolutionData (Ng_SolutionData * soldata) { ; } -void Ng_Redraw (bool blocking) { ; } - - - - - - diff --git a/libsrc/visualization/visualpkg.cpp b/libsrc/visualization/visualpkg.cpp index c660f77f..11881159 100644 --- a/libsrc/visualization/visualpkg.cpp +++ b/libsrc/visualization/visualpkg.cpp @@ -83,7 +83,7 @@ namespace netgen if ( (strlen (vssolution.soldata[i]->name.c_str()) == size_t(pointpos-1)) && (strncmp (vssolution.soldata[i]->name.c_str(), scalname, pointpos-1) == 0) ) { - vssolution.scalfunction = i; + vssolution.SetScalfunction(i); vssolution.scalcomp = atoi (scalname + pointpos); if ( vssolution.scalcomp > vssolution.soldata[i]->components ) vssolution.scalcomp = 1; @@ -91,14 +91,14 @@ namespace netgen for ( int ii = 0; ii < pointpos; ii++ ) newscalname[ii] = scalname[ii]; newscalname[pointpos] = ':'; - sprintf (newscalname+pointpos, "%i", vssolution.scalcomp); + snprintf (newscalname+pointpos, sizeof(newscalname)-pointpos, "%i", vssolution.scalcomp); if (strcmp (scalname, newscalname) != 0) Tcl_SetVar ( interp, "::visoptions.scalfunction", newscalname, TCL_GLOBAL_ONLY ); scalname = Tcl_GetVar (interp, "::visoptions.scalfunction", TCL_GLOBAL_ONLY); } if (strcmp (vssolution.soldata[i]->name.c_str(), vecname) == 0) - vssolution.vecfunction = i; + vssolution.SetVecfunction(i); if (strcmp (vssolution.soldata[i]->name.c_str(), fieldlines_vecname) == 0) vssolution.fieldlines_vecfunction = i; @@ -111,7 +111,7 @@ namespace netgen vssolution.vecfunction = vssolution.fieldlines_vecfunction; } - // reset visoptions.scalfunction and visoptions.vecfunction if not avialable + // reset visoptions.scalfunction and visoptions.vecfunction if not available if ( vssolution.scalfunction == -1 && strcmp (scalname, "none") != 0) Tcl_SetVar ( interp, "::visoptions.scalfunction", "none", TCL_GLOBAL_ONLY ); if ( vssolution.vecfunction == -1 && strcmp (vecname, "none") != 0) @@ -325,22 +325,22 @@ namespace netgen if (strcmp (argv[1], "getnfieldnames") == 0) { - sprintf (buf, "%d", vssolution.GetNSolData()); + snprintf (buf, size(buf), "%d", vssolution.GetNSolData()); } if (strcmp (argv[1], "getfieldname") == 0) { - sprintf (buf, "%s", vssolution.GetSolData(atoi(argv[2])-1)->name.c_str()); + snprintf (buf, size(buf), "%s", vssolution.GetSolData(atoi(argv[2])-1)->name.c_str()); } if (strcmp (argv[1], "iscomplex") == 0) { - sprintf (buf, "%d", vssolution.GetSolData(atoi(argv[2])-1)->iscomplex); + snprintf (buf, size(buf), "%d", vssolution.GetSolData(atoi(argv[2])-1)->iscomplex); } if (strcmp (argv[1], "getfieldcomponents") == 0) { - sprintf (buf, "%d", vssolution.GetSolData(atoi(argv[2])-1)->components); + snprintf (buf, size(buf), "%d", vssolution.GetSolData(atoi(argv[2])-1)->components); } @@ -362,13 +362,13 @@ namespace netgen if (strcmp (argv[1], "getactivefield") == 0) { - sprintf (buf, "1"); + snprintf (buf, size(buf), "1"); } if (strcmp (argv[1], "getdimension") == 0) { auto mesh = vssolution.GetMesh(); - sprintf (buf, "%d", mesh->GetDimension()); + snprintf (buf, size(buf), "%d", mesh->GetDimension()); } } diff --git a/libsrc/visualization/vsfieldlines.cpp b/libsrc/visualization/vsfieldlines.cpp index 5e259b42..64b21981 100644 --- a/libsrc/visualization/vsfieldlines.cpp +++ b/libsrc/visualization/vsfieldlines.cpp @@ -10,6 +10,7 @@ #include #include +#include namespace netgen @@ -17,430 +18,7 @@ namespace netgen // extern shared_ptr mesh; - - - RKStepper :: ~RKStepper() - { - delete a; - } - - RKStepper :: RKStepper(int type) : a(NULL), tolerance(1e100) - { - notrestarted = 0; - - if (type == 0) // explicit Euler - { - c.SetSize(1); c[0] = 0; - b.SetSize(1); b[0] = 1; - steps = order = 1; - } - else if (type == 1) // Euler-Cauchy - { - c.SetSize(2); c[0] = 0; c[1] = 0.5; - b.SetSize(2); b[0] = 0; b[1] = 1; - Array size(2); - size[0] = 0; size[1] = 1; - a = new TABLE(size); - a->Set(2,1,0.5); // Set, Get: 1-based! - steps = order = 2; - } - else if (type == 2) // Simpson - { - c.SetSize(3); c[0] = 0; c[1] = 1; c[2] = 0.5; - b.SetSize(3); b[0] = b[1] = 1./6.; b[2] = 2./3.; - Array size(3); - size[0] = 0; size[1] = 1; size[2] = 2; - a = new TABLE(size); - a->Set(2,1,1); - a->Set(3,1,0.25); a->Set(3,2,0.25); - steps = order = 3; - } - else if (type == 3) // classical Runge-Kutta - { - c.SetSize(4); c[0] = 0; c[1] = c[2] = 0.5; c[3] = 1; - b.SetSize(4); b[0] = b[3] = 1./6.; b[1] = b[2] = 1./3.; - Array size(4); - size[0] = 0; size[1] = 1; size[2] = 2; size[3] = 3; - a = new TABLE(size); - a->Set(2,1,0.5); - a->Set(3,1,0); a->Set(3,2,0.5); - a->Set(4,1,0); a->Set(4,2,0); a->Set(4,3,1); - steps = order = 4; - } - - K.SetSize(steps); - } - - void RKStepper :: StartNextValCalc(const Point3d & astartval, const double astartt, const double ah, const bool aadaptive) - { - //cout << "Starting RK-Step with h=" << ah << endl; - - stepcount = 0; - h = ah; - startt = astartt; - startval = astartval; - adaptive = aadaptive; - adrun = 0; - } - - bool RKStepper :: GetNextData(Point3d & val, double & t, double & ah) - { - bool finished(false); - - - //cout << "stepcount " << stepcount << endl; - - if(stepcount <= steps) - { - t = startt + c[stepcount-1]*h; - val = startval; - for(int i=0; iGet(stepcount,i+1) * K[i]; - } - - - if(stepcount == steps) - { - val = startval; - for(int i=0; i 1.3) fac = 1.3; - - if(fac < 1 || notrestarted >= 2) - ah = 2.*h * fac; - - if(err < tolerance) - { - finished = true; - notrestarted++; - //(*testout) << "finished RK-Step, new h=" << ah << " tolerance " << tolerance << " err " << err << endl; - } - else - { - //ah *= 0.9; - notrestarted = 0; - //(*testout) << "restarting h " << 2.*h << " ah " << ah << " tolerance " << tolerance << " err " << err << endl; - StartNextValCalc(startval_bak,startt_bak, ah, adaptive); - } - } - } - else - { - t = startt + h; - finished = true; - } - - } - - if(stepcount == 0) - { - t = startt + c[stepcount]*h; - val = startval; - for(int i=0; iGet(stepcount,i) * K[i]; - } - - return finished; - } - - - bool RKStepper :: FeedNextF(const Vec3d & f) - { - K[stepcount] = f; - stepcount++; - return true; - } - - - - void FieldLineCalc :: GenerateFieldLines(Array & potential_startpoints, const int numlines, const int gllist, - const double minval, const double maxval, const int logscale, double phaser, double phasei) - { - - - Array points; - Array values; - Array drawelems; - Array dirstart; - - - if(vsol -> iscomplex) - SetPhase(phaser,phasei); - - double crit = 1.0; - - if(randomized) - { - double sum = 0; - double lami[3]; - double values[6]; - Vec3d v; - - for(int i=0; iiscomplex, phaser, phasei); - - sum += v.Length(); - } - - crit = sum/double(numlines); - } - - - int calculated = 0; - - cout << endl; - - - - - for(int i=0; i= numlines) break; - - Calc(potential_startpoints[i],points,values,drawelems,dirstart); - - bool usable = false; - - for(int j=1; j 0) ? rel_length : 0.5; - maxlength *= 2.*rad; - - thickness = (rel_thickness > 0) ? rel_thickness : 0.0015; - thickness *= 2.*rad; - - double auxtolerance = (rel_tolerance > 0) ? rel_tolerance : 1.5e-3; - auxtolerance *= 2.*rad; - - stepper.SetTolerance(auxtolerance); - - direction = adirection; - - - maxpoints = amaxpoints; - - if(direction == 0) - { - maxlength *= 0.5; - maxpoints /= 2; - } - - - phaser = 1; - phasei = 0; - - critical_value = -1; - - randomized = false; - - } - - - - - void FieldLineCalc :: Calc(const Point3d & startpoint, Array & points, Array & vals, Array & drawelems, Array & dirstart) - { - double lami[3], startlami[3]; - double values[6]; - double dummyt(0); - Vec3d v; - Vec3d startv; - Point3d newp; - double h; - - double startval; - bool startdraw; - bool drawelem = false; - int elnr; - - for (int i=0; i<6; i++) values[i]=0.0; - for (int i=0; i<3; i++) lami[i]=0.0; - for (int i=0; i<3; i++) startlami[i]=0.0; - - points.SetSize(0); - vals.SetSize(0); - drawelems.SetSize(0); - - dirstart.SetSize(0); - dirstart.Append(0); - - - int startelnr = mesh.GetElementOfPoint(startpoint,startlami,true) - 1; - (*testout) << "p = " << startpoint << "; elnr = " << startelnr << endl; - if (startelnr == -1) - return; - - mesh.SetPointSearchStartElement(startelnr); - - if (mesh.GetDimension()==3) - startdraw = vss.GetValues ( vsol, startelnr, startlami[0], startlami[1], startlami[2], values); - else - startdraw = vss.GetSurfValues ( vsol, startelnr, -1, startlami[0], startlami[1], values); - - VisualSceneSolution::RealVec3d ( values, startv, vsol->iscomplex, phaser, phasei); - - startval = startv.Length(); - - if(critical_value > 0 && fabs(startval) < critical_value) - return; - - //cout << "p = " << startpoint << "; elnr = " << startelnr << endl; - - - - for(int dir = 1; dir >= -1; dir -= 2) - { - if(dir*direction < 0) continue; - - points.Append(startpoint); - vals.Append(startval); - drawelems.Append(startdraw); - - h = 0.001*rad/startval; // otherwise no nice lines; should be made accessible from outside - - v = startv; - if(dir == -1) v *= -1.; - - elnr = startelnr; - lami[0] = startlami[0]; lami[1] = startlami[1]; lami[2] = startlami[2]; - - - for(double length = 0; length < maxlength; length += h*vals.Last()) - { - if(v.Length() < 1e-12*rad) - { - (*testout) << "Current fieldlinecalculation came to a stillstand at " << points.Last() << endl; - break; - } - - stepper.StartNextValCalc(points.Last(),dummyt,h,true); - stepper.FeedNextF(v); - - while(!stepper.GetNextData(newp,dummyt,h) && elnr != -1) - { - elnr = mesh.GetElementOfPoint(newp,lami,true) - 1; - if(elnr != -1) - { - mesh.SetPointSearchStartElement(elnr); - if (mesh.GetDimension()==3) - drawelem = vss.GetValues (vsol, elnr, lami[0], lami[1], lami[2], values); - else - drawelem = vss.GetSurfValues (vsol, elnr, -1, lami[0], lami[1], values); - - VisualSceneSolution::RealVec3d (values, v, vsol->iscomplex, phaser, phasei); - if(dir == -1) v *= -1.; - stepper.FeedNextF(v); - } - } - - if (elnr == -1) - { - //cout << "direction " < 1) - (*testout) << "Points in current fieldline: " << points.Size() << ", current position: " << newp << endl; - - if(maxpoints > 0 && points.Size() >= maxpoints) - { - break; - } - - //cout << "length " << length << " h " << h << " vals.Last() " << vals.Last() << " maxlength " << maxlength << endl; - } - dirstart.Append(points.Size()); - } - } - - - - - - void VisualSceneSolution :: BuildFieldLinesFromBox(Array & startpoints) + void VisualSceneSolution :: BuildFieldLinesFromBox(Array> & startpoints) { shared_ptr mesh = GetMesh(); if (!mesh) return; @@ -462,7 +40,7 @@ namespace netgen for (int i = 1; i <= startpoints.Size(); i++) { - Point3d p (fieldlines_startarea_parameter[0] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]), + Point<3> p (fieldlines_startarea_parameter[0] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]), fieldlines_startarea_parameter[1] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[4]-fieldlines_startarea_parameter[1]), fieldlines_startarea_parameter[2] + double (rand()) / RAND_MAX * (fieldlines_startarea_parameter[5]-fieldlines_startarea_parameter[2])); @@ -470,7 +48,7 @@ namespace netgen } } - void VisualSceneSolution :: BuildFieldLinesFromLine(Array & startpoints) + void VisualSceneSolution :: BuildFieldLinesFromLine(Array> & startpoints) { shared_ptr mesh = GetMesh(); if (!mesh) return; @@ -480,7 +58,7 @@ namespace netgen { double s = double (rand()) / RAND_MAX; - Point3d p (fieldlines_startarea_parameter[0] + s * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]), + Point<3> p (fieldlines_startarea_parameter[0] + s * (fieldlines_startarea_parameter[3]-fieldlines_startarea_parameter[0]), fieldlines_startarea_parameter[1] + s * (fieldlines_startarea_parameter[4]-fieldlines_startarea_parameter[1]), fieldlines_startarea_parameter[2] + s * (fieldlines_startarea_parameter[5]-fieldlines_startarea_parameter[2])); @@ -489,7 +67,7 @@ namespace netgen } - void VisualSceneSolution :: BuildFieldLinesFromFile(Array & startpoints) + void VisualSceneSolution :: BuildFieldLinesFromFile(Array> & startpoints) { shared_ptr mesh = GetMesh(); if (!mesh) return; @@ -538,7 +116,9 @@ namespace netgen if (keyword == "point") { - (*infile) >> startpoints[numpoints].X(); (*infile) >> startpoints[numpoints].Y(); (*infile) >> startpoints[numpoints].Z(); + (*infile) >> startpoints[numpoints][0]; + (*infile) >> startpoints[numpoints][1]; + (*infile) >> startpoints[numpoints][2]; numpoints++; } else if (keyword == "line" || keyword == "box") @@ -546,7 +126,7 @@ namespace netgen for(int i=0; i<6; i++) (*infile) >> fieldlines_startarea_parameter[i]; (*infile) >> iparam; - Array auxpoints(iparam); + Array> auxpoints(iparam); if (keyword == "box") BuildFieldLinesFromBox(auxpoints); @@ -571,7 +151,7 @@ namespace netgen } - void VisualSceneSolution :: BuildFieldLinesFromFace(Array & startpoints) + void VisualSceneSolution :: BuildFieldLinesFromFace(Array> & startpoints) { shared_ptr mesh = GetMesh(); if (!mesh) return; @@ -592,7 +172,7 @@ namespace netgen int i; for(i=0; iSurfaceElement(elements_2d[i]); + const Element2d & elem = (*mesh)[elements_2d[i]]; v1 = mesh->Point(elem[1]) - mesh->Point(elem[0]); v2 = mesh->Point(elem[2]) - mesh->Point(elem[0]); @@ -613,7 +193,7 @@ namespace netgen while(startpointsp < startpoints.Size()) { - const Element2d & elem = mesh->SurfaceElement(elements_2d[i]); + const Element2d & elem = (*mesh)[elements_2d[i]]; int numtri = (elem.GetNV() == 3) ? 1 : 2; @@ -678,8 +258,25 @@ namespace netgen num_fieldlineslists = (vsol -> iscomplex && !fieldlines_fixedphase) ? 100 : 1; + double phaser=1.0; + double phasei=0.0; + std::function &)> eval_func = [&](int elnr, const double * lami, Vec<3> & vec) + { + double values[6] = {0., 0., 0., 0., 0., 0.}; + bool drawelem; + auto mesh = GetMesh(); + if (mesh->GetDimension()==3) + drawelem = GetValues (vsol, elnr, lami[0], lami[1], lami[2], values); + else + drawelem = GetSurfValues (vsol, elnr, -1, lami[0], lami[1], values); - FieldLineCalc linecalc(*mesh,*this,vsol, + Vec3d v; + RealVec3d (values, v, vsol->iscomplex, phaser, phasei); + vec = v; + return drawelem; + }; + + FieldLineCalc linecalc(*mesh, eval_func, fieldlines_rellength,fieldlines_maxpoints,fieldlines_relthickness,fieldlines_reltolerance,fieldlines_rktype); if(fieldlines_randomstart) @@ -694,7 +291,7 @@ namespace netgen num_startpoints *= 10; - Array startpoints(num_startpoints); + Array> startpoints(num_startpoints); for (int ln = 0; ln < num_fieldlineslists; ln++) @@ -722,17 +319,27 @@ namespace netgen cout << "phi = " << phi << endl; - double phaser = cos(phi), phasei = sin(phi); + phaser = cos(phi); + phasei = sin(phi); + linecalc.GenerateFieldLines(startpoints,num_fieldlines / num_fieldlineslists+1); + + auto & pstart = linecalc.GetPStart(); + auto & pend = linecalc.GetPEnd(); + auto & values = linecalc.GetValues(); + auto nlines = values.Size(); + glNewList(fieldlineslist+ln, GL_COMPILE); - SetTextureMode (usetexture); - linecalc.GenerateFieldLines(startpoints,num_fieldlines / num_fieldlineslists+1, - fieldlineslist+ln,minval,maxval,logscale,phaser,phasei); + + for(auto i : Range(nlines)) + { + SetOpenGlColor (values[i]); + DrawCylinder (pstart[i], pend[i], fieldlines_relthickness); + } glEndList (); - } } diff --git a/libsrc/visualization/vsmesh.cpp b/libsrc/visualization/vsmesh.cpp index 04ec7e7b..c63a8e0a 100644 --- a/libsrc/visualization/vsmesh.cpp +++ b/libsrc/visualization/vsmesh.cpp @@ -23,46 +23,16 @@ namespace netgen VisualSceneMesh :: VisualSceneMesh () : VisualScene() { - filledlist = 0; - linelist = 0; - edgelist = 0; - badellist = 0; - tetlist = 0; - prismlist = 0; - hexlist = 0; - pyramidlist = 0; - identifiedlist = 0; - pointnumberlist = 0; - domainsurflist = 0; - - vstimestamp = -1; // GetTimeStamp(); - selecttimestamp = -1; // GetTimeStamp(); - filledtimestamp = -1; // GetTimeStamp(); - linetimestamp = -1; // GetTimeStamp(); - edgetimestamp = -1; // GetTimeStamp(); - pointnumbertimestamp = -1; // GetTimeStamp(); - - tettimestamp = -1; // GetTimeStamp(); - prismtimestamp = -1; // GetTimeStamp(); - hextimestamp = -1; // GetTimeStamp(); - pyramidtimestamp = -1; // GetTimeStamp(); - - badeltimestamp = -1; // GetTimeStamp(); - identifiedtimestamp = -1; // GetTimeStamp(); - domainsurftimestamp = -1; // GetTimeStamp(); - - selface = -1; selelement = -1; - locpi = 1; - selpoint = -1; - selpoint2 = -1; + locpi = -2; + selpoint = PointIndex::INVALID; + selpoint2 = PointIndex::INVALID; seledge = -1; minh = 0.0; maxh = 0.0; user_me_handler = NULL; - } VisualSceneMesh :: ~VisualSceneMesh () @@ -150,11 +120,7 @@ namespace netgen if (vispar.drawfilledtrigs) { - if (filledtimestamp < mesh->GetTimeStamp () || - filledtimestamp < selecttimestamp) - { - BuildFilledList (false); - } + BuildFilledList (false); #ifdef PARALLELGL @@ -219,8 +185,7 @@ namespace netgen glPolygonOffset (1, 1); glEnable (GL_POLYGON_OFFSET_LINE); - if (linetimestamp < mesh->GetTimeStamp ()) - BuildLineList (); + BuildLineList (); #ifdef PARALLELGL if (ntasks > 1 && vispar.drawtetsdomain > 0 && vispar.drawtetsdomain < ntasks) @@ -256,36 +221,7 @@ namespace netgen glCallList (edgelist); } - if (selpoint > 0 && selpoint <= mesh->GetNP()) - { - /* - glPointSize (3.0); - glColor3d (0, 0, 1); - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolblue); - glBegin (GL_POINTS); - - const Point3d p = mesh->Point(selpoint); - glVertex3f (p.X(), p.Y(), p.Z()); - glEnd(); - */ - - glColor3d (0, 0, 1); - - static GLubyte cross[] = - { - 0xc6, 0xee, 0x7c, 0x38, 0x7c, 0xee, 0xc6 - }; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - glDisable (GL_COLOR_MATERIAL); - glDisable (GL_LIGHTING); - glDisable (GL_CLIP_PLANE0); - - const Point3d p = mesh->Point(selpoint); - glRasterPos3d (p.X(), p.Y(), p.Z()); - glBitmap (7, 7, 3, 3, 0, 0, &cross[0]); - } - + DrawMarker(); glDisable(GL_CLIP_PLANE0); @@ -317,6 +253,47 @@ namespace netgen } + void VisualSceneMesh :: SelectCenter (int zoomall) + { + shared_ptr mesh = GetMesh(); + Point3d pmin, pmax; + mesh->GetBox (pmin, pmax, -1); + + // works in NGSolve, mesh view + if (mesh->GetDimension() == 2) + mesh->GetBox (pmin, pmax); + else // otherwise strange zooms during mesh generation + mesh->GetBox (pmin, pmax, SURFACEPOINT); + + if (vispar.use_center_coords && zoomall==2) + { + center.X() = vispar.centerx; + center.Y() = vispar.centery; + center.Z() = vispar.centerz; + } + else if (selpoint >= 1 && zoomall==2) + center = mesh->Point (selpoint); + else if (marker && zoomall==2) + center = *marker; + else if (vispar.centerpoint >= 1 && zoomall==2) + center = mesh->Point (vispar.centerpoint); + else + center = Center (pmin, pmax); + + double oldrad = rad; + rad = 0.5 * Dist (pmin, pmax); + if(rad == 0) rad = 1e-6; + + if (rad > 1.2 * oldrad || + mesh->GetMajorTimeStamp() > vstimestamp || + zoomall) + { + CalcTransformationMatrices(); + } + + glEnable (GL_NORMALIZE); + + } void VisualSceneMesh :: BuildScene (int zoomall) { @@ -341,48 +318,11 @@ namespace netgen - Point3d pmin, pmax; - static double oldrad = 0; - - Array faces; + NgArray faces; int meshtimestamp = mesh->GetTimeStamp(); if (meshtimestamp > vstimestamp || zoomall) - { - if (mesh->GetDimension() == 2) - { - // works in NGSolve, mesh view - mesh->GetBox (pmin, pmax); - } - else - { - // otherwise strange zooms douring mesh generation - mesh->GetBox (pmin, pmax, SURFACEPOINT); - } - - if (vispar.use_center_coords && zoomall == 2) - { - center.X() = vispar.centerx; center.Y() = vispar.centery; center.Z() = vispar.centerz; - } - else if (selpoint >= 1 && zoomall == 2) - center = mesh->Point (selpoint); - else if (vispar.centerpoint >= 1 && zoomall == 2) - center = mesh->Point (vispar.centerpoint); - else - center = Center (pmin, pmax); - rad = 0.5 * Dist (pmin, pmax); - if(rad == 0) rad = 1e-6; - - if (rad > 1.2 * oldrad || - mesh->GetMajorTimeStamp() > vstimestamp || - zoomall) - { - CalcTransformationMatrices(); - oldrad = rad; - } - } - - glEnable (GL_NORMALIZE); + SelectCenter(zoomall); if (pointnumberlist) { @@ -445,7 +385,7 @@ namespace netgen const Point3d & p = mesh->Point(pi); glRasterPos3d (p.X(), p.Y(), p.Z()); - sprintf (buf, "%d", int(pi)); + snprintf (buf, size(buf), "%d", int(pi)); // glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); @@ -463,7 +403,7 @@ namespace netgen const Point3d p = Center (p1, p2); glRasterPos3d (p.X(), p.Y(), p.Z()); - sprintf (buf, "%d", seg.edgenr); + snprintf (buf, size(buf), "%d", seg.edgenr); glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf); } */ @@ -478,7 +418,7 @@ namespace netgen const Point3d p = Center (p1, p2); glRasterPos3d (p.X(), p.Y(), p.Z()); - sprintf (buf, "%d", i); + snprintf (buf, size(buf), "%d", i); // glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); @@ -490,7 +430,7 @@ namespace netgen if (vispar.drawfacenumbers) { const MeshTopology & top = mesh->GetTopology(); - Array v; + NgArray v; for (int i = 1; i <= top.GetNFaces(); i++) { top.GetFaceVertices (i, v); @@ -511,7 +451,7 @@ namespace netgen } glRasterPos3d (p.X(), p.Y(), p.Z()); - sprintf (buf, "%d", i); + snprintf (buf, size(buf), "%d", i); // glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); } @@ -521,11 +461,11 @@ namespace netgen if (vispar.drawelementnumbers) { - Array v; + NgArray v; for (int i = 1; i <= mesh->GetNE(); i++) { // const ELEMENTTYPE & eltype = mesh->ElementType(i); - Array pnums; + NgArray pnums; Point3d p; const Element & el = mesh->VolumeElement (i); @@ -596,7 +536,7 @@ namespace netgen } glRasterPos3d (p.X(), p.Y(), p.Z()); - sprintf (buf, "%d", i); + snprintf (buf, size(buf), "%d", i); // glCallLists (strlen (buf), GL_UNSIGNED_BYTE, buf); MyOpenGLText (buf); @@ -626,8 +566,8 @@ namespace netgen for (int i = 1; i <= mesh->GetNE(); i++) { - if (mesh->VolumeElement(i).flags.badel || - mesh->VolumeElement(i).flags.illegal || + if (mesh->VolumeElement(i).Flags().badel || + mesh->VolumeElement(i).Flags().illegal || (i == vispar.drawelement)) { // copy to be thread-safe @@ -665,10 +605,10 @@ namespace netgen for (ElementIndex ei : mesh->VolumeElements().Range()) { - if (mesh->VolumeElement(ei).flags.badel) + if ((*mesh)[ei].Flags().badel) { // copy to be thread-safe - Element el = mesh->VolumeElement (ei); + Element el = (*mesh)[ei]; if ( (el.GetNP() == 4) || (el.GetNP() == 10)) { glBegin (GL_LINES); @@ -749,10 +689,12 @@ namespace netgen for (SurfaceElementIndex sei : mesh->SurfaceElements().Range()) { - Element2d el = mesh->SurfaceElement(sei); // copy to be thread-safe + Element2d el = (*mesh)[sei]; // copy to be thread-safe if (!el.BadElement()) continue; + if (el.IsDeleted()) continue; + bool drawel = true; for (int j = 1; j <= el.GetNP(); j++) if (!el.PNum(j).IsValid()) @@ -909,13 +851,45 @@ namespace netgen + void VisualSceneMesh :: BuildColorTexture () + { + shared_ptr mesh = GetMesh(); - void VisualSceneMesh :: BuildFilledList (bool names) + if(colors.texture == -1) + glGenTextures(1, &colors.texture); + + // build color texture + glBindTexture(GL_TEXTURE_2D, colors.texture); + Array data; + for(auto fdi : Range(1, mesh->GetNFD()+1)) + { + auto c = mesh->GetFaceDescriptor(fdi).SurfColour(); + ArrayMem cf{float(c[0]), float(c[1]), float(c[2]), float(c[3])}; + if(fdi==selface) + cf = {1.0f, 0.0f, 0.0f, 1.0f}; + data.Append(cf); + } + int n = data.Size()/4; + colors.width = min2(n, 1024); + colors.height = (n+colors.width-1)/colors.width; + for(auto i: Range(n, colors.width*colors.height)) + data.Append({0.0f, 0.0f, 0.0f, 0.0f}); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, colors.width, colors.height, 0, GL_RGBA, GL_FLOAT, data.Data()); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + void VisualSceneMesh :: BuildFilledList (bool build_select) { shared_ptr mesh = GetMesh(); static int timer = NgProfiler::CreateTimer ("Mesh::BuildFilledList"); NgProfiler::RegionTimer reg (timer); + auto & list = build_select ? select.list : filledlist; + auto & timestamp = build_select ? select.list_timestamp : filledtimestamp; + if (list && timestamp > max(mesh->GetTimeStamp(), subdivision_timestamp)) + return; + #ifdef PARALLELGL if (id == 0 && ntasks > 1) @@ -928,18 +902,18 @@ namespace netgen for ( int dest = 1; dest < ntasks; dest++ ) MyMPI_Recv (par_filledlists[dest], dest, MPI_TAG_VIS); - if (filledlist) - glDeleteLists (filledlist, 1); + if (list) + glDeleteLists (list, 1); - filledlist = glGenLists (1); - glNewList (filledlist, GL_COMPILE); + list = glGenLists (1); + glNewList (list, GL_COMPILE); for ( int dest = 1; dest < ntasks; dest++ ) glCallList (par_filledlists[dest]); glEndList(); - filledtimestamp = NextTimeStamp(); + timestamp = NextTimeStamp(); return; } @@ -952,14 +926,18 @@ namespace netgen lock -> Lock(); } - filledtimestamp = NextTimeStamp(); + timestamp = NextTimeStamp(); - if (filledlist) - glDeleteLists (filledlist, 1); + if(!build_select && !vispar.colormeshsize && colors.texture==-1) + BuildColorTexture(); - filledlist = glGenLists (1); - glNewList (filledlist, GL_COMPILE); + if (list) + glDeleteLists (list, 1); + list = glGenLists (1); + glNewList (list, GL_COMPILE); + + glBindTexture(GL_TEXTURE_2D, colors.texture); #ifdef STLGEOM STLGeometry * stlgeometry = dynamic_cast (ng_geometry); @@ -991,9 +969,21 @@ namespace netgen maxh = 10; } } - else + else if (build_select) + { + glDisable(GL_TEXTURE_1D); + glDisable(GL_TEXTURE_2D); + glDisable(GL_FOG); + glDisable(GL_LIGHTING); glDisable (GL_COLOR_MATERIAL); - + } + else + { + glDisable(GL_TEXTURE_1D); + glEnable(GL_TEXTURE_2D); + glEnable (GL_COLOR_MATERIAL); + glBindTexture(GL_TEXTURE_2D, colors.texture); + } GLfloat matcol[] = { 0, 1, 0, 1 }; GLfloat matcolsel[] = { 1, 0, 0, 1 }; @@ -1003,7 +993,7 @@ namespace netgen CurvedElements & curv = mesh->GetCurvedElements(); - int hoplotn = 1 << vispar.subdivisions; + int hoplotn = 1 << subdivisions; Array seia; @@ -1012,23 +1002,19 @@ namespace netgen { mesh->GetSurfaceElementsOfFace (faceindex, seia); - // Philippose - 06/07/2009 - // Modified the colour system to integrate the face colours into - // the mesh data structure, rather than limit it to the OCC geometry - // structure... allows other geometry types to use face colours too - - matcol[0] = mesh->GetFaceDescriptor(faceindex).SurfColour().X(); - matcol[1] = mesh->GetFaceDescriptor(faceindex).SurfColour().Y(); - matcol[2] = mesh->GetFaceDescriptor(faceindex).SurfColour().Z(); - matcol[3] = 1.0; - - if (faceindex == selface) - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolsel); - else - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcol); + if(!build_select && !vispar.colormeshsize) + { + int i = faceindex-1; + float x = (0.5+i%colors.width)/colors.width; + float y = (0.5+i/colors.width)/colors.height; + glTexCoord2f(x,y); + } + static Point<3> xa[129]; + static Vec<3> na[129]; - + + for (int hi = 0; hi < seia.Size(); hi++) { SurfaceElementIndex sei = seia[hi]; @@ -1046,8 +1032,14 @@ namespace netgen if (!drawel) continue; - if (names) - glLoadName (sei+1); + if (build_select) + { + GLushort r,g,b; + r = (sei+1) % (1<<16); + g = (sei+1) >> 16; + b = 0; + glColor3us(r,g,b); + } switch (el.GetType()) { @@ -1056,8 +1048,6 @@ namespace netgen if (curv.IsHighOrder()) // && curv.IsSurfaceElementCurved(sei)) { if (hoplotn > 128) hoplotn = 128; - Point<3> xa[129]; - Vec<3> na[129]; for (int i = 0; i < hoplotn; i++) { @@ -1289,13 +1279,14 @@ namespace netgen glLoadName (0); + glBindTexture(GL_TEXTURE_2D, 0); glEndList (); #ifdef PARALLELGL glFinish(); if (id > 0) - MyMPI_Send (filledlist, 0, MPI_TAG_VIS); + MyMPI_Send (list, 0, MPI_TAG_VIS); #endif } @@ -1303,6 +1294,8 @@ namespace netgen void VisualSceneMesh :: BuildLineList() { shared_ptr mesh = GetMesh(); + if (linetimestamp > max(mesh->GetTimeStamp (), subdivision_timestamp)) + return; static int timer = NgProfiler::CreateTimer ("Mesh::BuildLineList"); NgProfiler::RegionTimer reg (timer); @@ -1363,7 +1356,7 @@ namespace netgen glLineWidth (1.0f); - int hoplotn = 1 << vispar.subdivisions; + int hoplotn = 1 << subdivisions; // PrintMessage (3, "nse = ", mesh->GetNSE()); for (SurfaceElementIndex sei = 0; sei < mesh->GetNSE(); sei++) @@ -1592,7 +1585,7 @@ namespace netgen lock -> Lock(); } - if (edgetimestamp > mesh->GetTimeStamp () && vispar.drawtetsdomain == 0 + if (edgetimestamp > max(mesh->GetTimeStamp(), subdivision_timestamp) && vispar.drawtetsdomain == 0 && vispar.shrink == 1) return; @@ -1649,7 +1642,7 @@ namespace netgen if (mesh->GetCurvedElements().IsHighOrder()) { - int hoplotn = 1 << vispar.subdivisions; + int hoplotn = 1 << subdivisions; // mesh->GetCurvedElements().GetNVisualSubsecs(); Point<3> x; @@ -1815,9 +1808,9 @@ namespace netgen - Array faces; + NgArray faces; - BitArray shownode(mesh->GetNP()); + NgBitArray shownode(mesh->GetNP()); if (vispar.clipping.enable) { shownode.Clear(); @@ -1860,7 +1853,7 @@ namespace netgen else glShadeModel (GL_SMOOTH); - int hoplotn = max (2, 1 << vispar.subdivisions); + int hoplotn = max (2, 1 << subdivisions); @@ -1955,8 +1948,8 @@ namespace netgen int order = curv.GetOrder(); - Array > ploc ( (order+1)*(order+1) ); - Array > pglob ( (order+1)*(order+1) ); + NgArray > ploc ( (order+1)*(order+1) ); + NgArray > pglob ( (order+1)*(order+1) ); Point<3> fpts[3]; for (int trig = 0; trig < 4; trig++) @@ -2141,9 +2134,10 @@ namespace netgen static float prismcol[] = { 0.0f, 1.0f, 1.0f, 1.0f }; glLineWidth (1.0f); - Array faces; + NgArray faces; + glDisable (GL_COLOR_MATERIAL); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, prismcol); for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++) @@ -2162,7 +2156,7 @@ namespace netgen Point<3> grid[11][11]; Point<3> fpts[4]; - int order = vispar.subdivisions+1; + int order = subdivisions+1; for (int trig = 0; trig < 2; trig++) { @@ -2261,7 +2255,7 @@ namespace netgen /* - int hoplotn = 1 << vispar.subdivisions; + int hoplotn = 1 << subdivisions; // int hoplotn = curv.GetNVisualSubsecs(); const Point3d * facepoint = MeshTopology :: GetVertices (TRIG); @@ -2468,9 +2462,10 @@ namespace netgen static float hexcol[] = { 1.0f, 1.0f, 0.0f, 1.0f }; glLineWidth (1.0f); + glDisable (GL_COLOR_MATERIAL); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, hexcol); - Array faces; + NgArray faces; // int hoplotn = 1 << vispar.subdivisions; for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++) @@ -2549,7 +2544,7 @@ namespace netgen Point<3> grid[11][11]; Point<3> fpts[4]; - int order = vispar.subdivisions+1; + int order = subdivisions+1; for (int quad = 0; quad<6; quad++) { @@ -2679,10 +2674,11 @@ namespace netgen glNewList (pyramidlist, GL_COMPILE); static float pyramidcol[] = { 1.0f, 0.0f, 1.0f, 1.0f }; + glDisable (GL_COLOR_MATERIAL); glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, pyramidcol); glLineWidth (1.0f); - Array faces; + NgArray faces; for (ElementIndex ei = 0; ei < mesh->GetNE(); ei++) { @@ -2700,7 +2696,7 @@ namespace netgen Point<3> grid[11][11]; Point<3> fpts[4]; - int order = vispar.subdivisions+1; + int order = subdivisions+1; for (int trig = 0; trig < 4; trig++) { @@ -3125,46 +3121,105 @@ namespace netgen - - - - void VisualSceneMesh :: MouseDblClick (int px, int py) + bool VisualSceneMesh :: SelectSurfaceElement (int px, int py, Point<3> &p, bool select_on_clipping_plane) { + selelement = -1; + marker = nullopt; shared_ptr mesh = GetMesh(); + if(px != select.x || py != select.y) + { + select.x = px; + select.y = py; + locpi = -2; + } - BuildFilledList (true); - - MouseDblClickSelect(px,py,clipplane,backcolor,transformationmat,center,rad, - filledlist,selelement,selface,seledge,selpoint,selpoint2,locpi); - - - GLdouble /* modelview[16], */ projection[16]; - GLint viewport[4]; - GLdouble result[3]; - - glGetDoublev(GL_PROJECTION_MATRIX, &projection[0]); - glGetIntegerv(GL_VIEWPORT, &viewport[0]); - - int hy = viewport[3]-py; - - GLfloat pz; - // cout << "x, y = " << px << ", " << hy << endl; - glReadPixels (px, hy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pz); - 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; - - - if (user_me_handler && pz < 1.0) + glGetIntegerv (GL_VIEWPORT, select.viewport); + GLenum err; + if(select.framebuffer == 0 || select.viewport[2] != select.width || select.viewport[3] != select.height) + { + select.width = select.viewport[2]; + select.height = select.viewport[3]; + if(select.framebuffer != 0) { - if (selelement != -1) - user_me_handler -> DblClick (selelement-1, result[0], result[1], result[2]); + glDeleteRenderbuffers(2, select.render_buffers); + glDeleteFramebuffers(1, &select.framebuffer); } - selecttimestamp = NextTimeStamp(); + glGenFramebuffers(1, &select.framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, select.framebuffer); + + // create, reserve and attach color and depth renderbuffer + glGenRenderbuffers(2, select.render_buffers); + glBindRenderbuffer(GL_RENDERBUFFER, select.render_buffers[0]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB16, select.width, select.height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, select.render_buffers[0]); + + glBindRenderbuffer(GL_RENDERBUFFER, select.render_buffers[1]); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, select.width, select.height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, select.render_buffers[1]); + + // check if framebuffer status is complete + if(int fbstatus; (fbstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) + cerr << "no frame buffer " << fbstatus << endl; + + } + glFlush(); + + glBindFramebuffer(GL_FRAMEBUFFER, select.framebuffer); + BuildFilledList (true); + + glEnable(GL_DEPTH_TEST); + glClearColor(0, 0, 0, 1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glMultMatrixd (transformationmat); + + glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + auto hy = select.viewport[3] - py; + + SetClippingPlane(); + if (vispar.clipping.enable) + { + Vec<3> n(clipplane[0], clipplane[1], clipplane[2]); + double len = Abs(n); + double mu = -clipplane[3] / (len*len); + Point<3> p (mu * n); + n /= len; + Vec<3> t1 = n.GetNormal (); + Vec<3> t2 = Cross (n, t1); + + double xi1mid = (center - p) * t1; + double xi2mid = (center - p) * t2; + + if(select_on_clipping_plane) + { + glColor3us(0,0,0); + glBegin (GL_QUADS); + glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2); + glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2); + glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2); + glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2); + glEnd (); + } + } + glCallList (select.list); + glFinish(); + + glGetDoublev (GL_PROJECTION_MATRIX, select.projmat); + auto found = Unproject(px, py, p); + if(found) + { + marker = p; + GLushort numbers[3]; + glReadPixels (px, hy, 1, 1, GL_RGB, GL_UNSIGNED_SHORT, numbers); + selelement = numbers[0] + numbers[1]*(1<<16); + locpi++; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glPopMatrix(); if(lock) { @@ -3173,355 +3228,104 @@ namespace netgen lock = NULL; } - /* - int i, hits; - - // select surface triangle by mouse click - - GLuint selbuf[10000]; - glSelectBuffer (10000, selbuf); - - - glRenderMode (GL_SELECT); - - GLint viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - - GLdouble projmat[16]; - glGetDoublev (GL_PROJECTION_MATRIX, projmat); - - glLoadIdentity(); - gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); - glMultMatrixd (projmat); - - - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode (GL_MODELVIEW); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - - // SetClippingPlane(); - - glInitNames(); - glPushName (1); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - glDisable(GL_CLIP_PLANE0); - - if (vispar.clipenable) - { - Vec<3> n(clipplane[0], clipplane[1], clipplane[2]); - double len = Abs(n); - double mu = -clipplane[3] / (len*len); - Point<3> p (mu * n); - n /= len; - Vec<3> t1 = n.GetNormal (); - Vec<3> t2 = Cross (n, t1); - - double xi1mid = (center - p) * t1; - double xi2mid = (center - p) * t2; - - glLoadName (0); - glBegin (GL_QUADS); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2); - glEnd (); - } - - // SetClippingPlane(); - - glCallList (filledlist); - - glDisable (GL_POLYGON_OFFSET_FILL); - - glPopName(); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - glFlush(); - - - hits = glRenderMode (GL_RENDER); - - // cout << "hits = " << hits << endl; - - int minname = 0; - GLuint mindepth = 0; - - // find clippingplane - GLuint clipdepth = 0; // GLuint(-1); - - for (i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - if (!curname) clipdepth = selbuf[4*i+1]; - } - - for (i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - GLuint curdepth = selbuf[4*i+1]; - - if (curname && (curdepth > clipdepth) && - (curdepth < mindepth || !minname)) - { - mindepth = curdepth; - minname = curname; - } - } - - seledge = -1; - if (minname) - { - 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++) - { - const Segment & seg = mesh->LineSegment(i); - if (seg[0] == selpoint && seg[1] == selpoint2 || - seg[1] == selpoint && seg[0] == selpoint2) - { - seledge = seg.edgenr; - cout << "seledge = " << seledge << endl; - } - } - - } - else - { - selface = -1; - selelement = -1; - selpoint = -1; - selpoint2 = -1; - } - - glDisable(GL_CLIP_PLANE0); - - selecttimestamp = NextTimeStamp(); - */ - + return found; } - - - - - void MouseDblClickSelect (const int px, const int py, - const GLdouble * clipplane, const GLdouble backcolor, - const double * transformationmat, - const Point3d & center, - const double rad, - const int displaylist, - int & selelement, int & selface, int & seledge, PointIndex & selpoint, - PointIndex & selpoint2, int & locpi) + bool VisualSceneMesh :: Unproject(int px, int py, Point<3> &p) { - auto mesh = vsmesh.GetMesh(); - - int i, hits; - - // select surface triangle by mouse click - - GLuint selbuf[10000]; - glSelectBuffer (10000, selbuf); - - - glRenderMode (GL_SELECT); - - GLint viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - - GLdouble projmat[16]; - glGetDoublev (GL_PROJECTION_MATRIX, projmat); - - glLoadIdentity(); - gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); - glMultMatrixd (projmat); - - - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode (GL_MODELVIEW); - - glPushMatrix(); - glMultMatrixd (transformationmat); - - - // SetClippingPlane(); - - glInitNames(); - glPushName (1); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - glDisable(GL_CLIP_PLANE0); - - if (vispar.clipping.enable) - { - Vec<3> n(clipplane[0], clipplane[1], clipplane[2]); - double len = Abs(n); - double mu = -clipplane[3] / (len*len); - Point<3> p (mu * n); - n /= len; - Vec<3> t1 = n.GetNormal (); - Vec<3> t2 = Cross (n, t1); - - double xi1mid = (center - p) * t1; - double xi2mid = (center - p) * t2; - - glLoadName (0); - glBegin (GL_QUADS); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2); - glEnd (); - } - - // SetClippingPlane(); - glCallList (displaylist); - - glDisable (GL_POLYGON_OFFSET_FILL); - - glPopName(); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - glFlush(); - - - hits = glRenderMode (GL_RENDER); - //cout << "hits = " << hits << endl; - - int minname = 0; - GLuint mindepth = 0; - - // find clippingplane - GLuint clipdepth = 0; // GLuint(-1); - - for (i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - if (!curname) clipdepth = selbuf[4*i+1]; - } - - for (i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - GLuint curdepth = selbuf[4*i+1]; - /* - cout << selbuf[4*i] << " " << selbuf[4*i+1] << " " - << selbuf[4*i+2] << " " << selbuf[4*i+3] << endl; - */ - if (curname && (curdepth > clipdepth) && - (curdepth < mindepth || !minname)) - { - mindepth = curdepth; - minname = curname; - } - } - - seledge = -1; - if (minname) - { - 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++) - { - const Segment & seg = mesh->LineSegment(i); - if ( (seg[0] == selpoint && seg[1] == selpoint2) || - (seg[1] == selpoint && seg[0] == selpoint2) ) - { - seledge = seg.edgenr; - cout << "seledge = " << seledge << endl; - } - } - } - else - { - selface = -1; - selelement = -1; - selpoint = -1; - selpoint2 = -1; - } - - glDisable(GL_CLIP_PLANE0); - - - -#ifdef PARALLELGL - vsmesh.Broadcast (); -#endif + auto hy = select.viewport[3] - py; + float pz; + glReadPixels (px, hy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pz); + if(pz<1 && pz>0) + gluUnProject(px, hy, pz, transformationmat, select.projmat, select.viewport, + &p[0], &p[1], &p[2]); + return pz<1 && pz>0; } + ngcore::INT<2> VisualSceneMesh :: Project(Point<3> p) + { + Point<3> pwin; + gluProject(p[0], p[1], p[2], transformationmat, select.projmat, select.viewport, + &pwin[0], &pwin[1], &pwin[2]); + + return ngcore::INT<2>(pwin[0]+0.5, select.viewport[3]-pwin[1]+0.5); + } + + + void VisualSceneMesh :: MouseDblClick (int px, int py) + { + Point<3> p; + bool found_point = SelectSurfaceElement(px, py, p, false); + + if(selelement>0) + { + const Element2d & sel = GetMesh()->SurfaceElement(selelement); + SetSelectedFace(sel.GetIndex()); + + auto pi_nearest = sel[0]; + double min_dist = 1e99; + for(auto pi : sel.PNums()) + if(Dist2(GetMesh()->Point(pi), p) < min_dist) + { + min_dist = Dist2(GetMesh()->Point(pi), p); + pi_nearest = pi; + } + auto p_win = Project(GetMesh()->Point(pi_nearest)); + if(abs(p_win[0]-px) < 5 && abs(p_win[1]-py) < 5) + { + marker = GetMesh()->Point(pi_nearest); + selpoint = pi_nearest; + cout << "select point " << pi_nearest << " at " << *marker << endl; + locpi = -2; + } + else + { + if(locpi < 0) + { + cout << endl << "select element " << selelement + << " on face " << sel.GetIndex(); + // output face name + auto name = GetMesh()->GetFaceDescriptor(sel.GetIndex()).GetBCName(); + if(name != "") + cout << " with name " << name; + cout << endl; + cout << "\tpoint: " << p << endl;; + cout << "\tnodes: "; + for (int i = 1; i <= sel.GetNP(); i++) + cout << sel.PNum(i) << " "; + cout << endl; + } + else { + auto pi = sel[locpi%sel.GetNP()]; + marker = GetMesh()->Points()[pi]; + cout << "select point " << pi << " at " << *marker << endl; + } + } + } + + if(found_point && user_me_handler) + { + if (selelement != -1) + user_me_handler -> DblClick (selelement-1, p[0], p[1], p[2]); + } + + if(lock) + { + lock->UnLock(); + delete lock; + lock = NULL; + } + } + + void VisualSceneMesh :: SetSelectedFace (int asf) { - selface = asf; - selecttimestamp = NextTimeStamp(); + if(selface != asf) + { + selface = asf; + BuildColorTexture(); + } } @@ -3534,8 +3338,9 @@ namespace netgen #ifdef NG_PYTHON #include <../general/ngpython.hpp> +#include "../include/nginterface.h" -DLL_HEADER void ExportMeshVis(py::module &m) +NGGUI_API void ExportMeshVis(py::module &m) { using namespace netgen; vispar.drawcolorbar = true; diff --git a/libsrc/visualization/vsocc.cpp b/libsrc/visualization/vsocc.cpp deleted file mode 100644 index c16984cc..00000000 --- a/libsrc/visualization/vsocc.cpp +++ /dev/null @@ -1,762 +0,0 @@ -#ifndef NOTCL - -#ifdef OCCGEOMETRY - -#include -#include -#include - -#include - -#include "TopoDS_Shape.hxx" -#include "TopoDS_Vertex.hxx" -#include "TopExp_Explorer.hxx" -#include "BRep_Tool.hxx" -#include "TopoDS.hxx" -#include "gp_Pnt.hxx" -#include "Geom_Curve.hxx" -#include "Poly_Triangulation.hxx" -#include "Poly_Array1OfTriangle.hxx" -#include "TColgp_Array1OfPnt2d.hxx" -#include "Poly_Triangle.hxx" -#include "Poly_Polygon3D.hxx" -#include "Poly_PolygonOnTriangulation.hxx" - -#include - -namespace netgen -{ - extern OCCGeometry * occgeometry; - - /* *********************** Draw OCC Geometry **************** */ - - VisualSceneOCCGeometry :: VisualSceneOCCGeometry () - : VisualScene() - { - trilists.SetSize(0); - linelists.SetSize(1); - - } - - VisualSceneOCCGeometry :: ~VisualSceneOCCGeometry () - { - ; - } - - void VisualSceneOCCGeometry :: DrawScene () - { - if ( occgeometry->changed ) - { - BuildScene(); - occgeometry -> changed = 0; - } - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - SetLight(); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - glShadeModel (GL_SMOOTH); - glDisable (GL_COLOR_MATERIAL); - glPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - - glEnable (GL_BLEND); - glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // glEnable (GL_LIGHTING); - - double shine = vispar.shininess; - // double transp = vispar.transp; - - glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, shine); - glLogicOp (GL_COPY); - - glEnable (GL_NORMALIZE); - - float mat_col[] = { 0.2f, 0.2f, 0.8f, 1.0f}; - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - // Philippose - 30/01/2009 - // Added clipping planes to Geometry view - SetClippingPlane(); - - GLfloat matcoledge[] = { 0, 0, 1, 1}; - GLfloat matcolhiedge[] = { 1, 0, 0, 1}; - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcoledge); - glLineWidth (1.0f); - - if (vispar.occshowedges) glCallList (linelists.Get(1)); - if (vispar.occshowsurfaces) glCallList (trilists.Get(1)); - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge); - glLineWidth (5.0f); - - if (vispar.occshowedges) glCallList (linelists.Get(2)); - - for (int i = 1; i <= occgeometry->vmap.Extent(); i++) - if (occgeometry->vvispar[i-1].IsHighlighted()) - { - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, matcolhiedge); - glLineWidth (5.0f); - - glBegin (GL_LINES); - - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(occgeometry->vmap(i))); - double d = rad/100; - glVertex3f (p.X()-d, p.Y(), p.Z()); - glVertex3f (p.X()+d, p.Y(), p.Z()); - glVertex3f (p.X(), p.Y()-d, p.Z()); - glVertex3f (p.X(), p.Y()+d, p.Z()); - glVertex3f (p.X(), p.Y(), p.Z()-d); - glVertex3f (p.X(), p.Y(), p.Z()+d); - glEnd(); - } - - glDisable (GL_POLYGON_OFFSET_FILL); - - glPopMatrix(); - // DrawCoordinateCross (); - // DrawNetgenLogo (); - glFinish(); - - glDisable (GL_POLYGON_OFFSET_FILL); - } - - /* - void VisualSceneOCCGeometry :: BuildScene (int zoomall) - { - int i = 0, j, k; - - TopExp_Explorer ex, ex_edge; - - if (vispar.occvisproblemfaces || (occgeometry -> changed != 2)) - { - Box<3> bb = occgeometry -> GetBoundingBox(); - - center = bb.Center(); - rad = bb.Diam() / 2; - - - - if (vispar.occvisproblemfaces) - { - for (i = 1; i <= occgeometry->fmap.Extent(); i++) - if (occgeometry->facemeshstatus[i-1] == -1) - { - GProp_GProps system; - BRepGProp::LinearProperties(occgeometry->fmap(i), system); - gp_Pnt pnt = system.CentreOfMass(); - center = Point<3> (pnt.X(), pnt.Y(), pnt.Z()); - cout << "Setting center to mid of face " << i << " = " << center << endl; - } - } - - - CalcTransformationMatrices(); - } - - - for (i = 1; i <= linelists.Size(); i++) - glDeleteLists (linelists.Elem(i), 1); - linelists.SetSize(0); - - linelists.Append (glGenLists (1)); - glNewList (linelists.Last(), GL_COMPILE); - - i = 0; - for (ex_edge.Init(occgeometry -> shape, TopAbs_EDGE); - ex_edge.More(); ex_edge.Next()) - { - if (BRep_Tool::Degenerated(TopoDS::Edge(ex_edge.Current()))) continue; - i++; - - - TopoDS_Edge edge = TopoDS::Edge(ex_edge.Current()); - - Handle(Poly_PolygonOnTriangulation) aEdgePoly; - Handle(Poly_Triangulation) T; - TopLoc_Location aEdgeLoc; - BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc); - - if(aEdgePoly.IsNull()) - { - cout << "cannot visualize edge " << i << endl; - continue; - } - - glBegin (GL_LINE_STRIP); - - int nbnodes = aEdgePoly -> NbNodes(); - for (j = 1; j <= nbnodes; j++) - { - gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); - glVertex3f (p.X(), p.Y(), p.Z()); - } - - glEnd (); - - - } - - glEndList (); - - for (i = 1; i <= trilists.Size(); i++) - glDeleteLists (trilists.Elem(i), 1); - trilists.SetSize(0); - - - trilists.Append (glGenLists (1)); - glNewList (trilists.Last(), GL_COMPILE); - - i = 0; - - TopExp_Explorer exp0, exp1, exp2, exp3; - int shapenr = 0; - for (exp0.Init(occgeometry -> shape, TopAbs_SOLID); exp0.More(); exp0.Next()) - { - shapenr++; - - if (vispar.occshowvolumenr != 0 && - vispar.occshowvolumenr != shapenr) continue; - - float mat_col[4]; - mat_col[3] = 1; - switch (shapenr) - { - case 1: - mat_col[0] = 0.2; - mat_col[1] = 0.2; - mat_col[2] = 0.8; - break; - case 2: - mat_col[0] = 0.8; - mat_col[1] = 0.2; - mat_col[2] = 0.8; - break; - case 3: - mat_col[0] = 0.2; - mat_col[1] = 0.8; - mat_col[2] = 0.8; - break; - case 4: - mat_col[0] = 0.8; - mat_col[1] = 0.2; - mat_col[2] = 0.2; - break; - case 5: - mat_col[0] = 0.8; - mat_col[1] = 0.8; - mat_col[2] = 0.8; - break; - case 6: - mat_col[0] = 0.6; - mat_col[1] = 0.6; - mat_col[2] = 0.6; - break; - case 7: - mat_col[0] = 0.2; - mat_col[1] = 0.8; - mat_col[2] = 0.2; - break; - case 8: - mat_col[0] = 0.8; - mat_col[1] = 0.8; - mat_col[2] = 0.2; - break; - default: - // mat_col[0] = 1-(1.0/double(shapenr)); - // mat_col[1] = 0.5; - mat_col[0] = 0.5+double((shapenr*shapenr*shapenr*shapenr) % 10)/20.0; - mat_col[1] = 0.5+double(int(shapenr*shapenr*shapenr*shapenr*sin(double(shapenr))) % 10)/20.0; - mat_col[2] = 0.5+double((shapenr*shapenr*shapenr) % 10)/20.0; - } - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - for (exp1.Init(exp0.Current(), TopAbs_SHELL); exp1.More(); exp1.Next()) - for (exp2.Init(exp1.Current().Composed(exp0.Current().Orientation()), TopAbs_FACE); exp2.More(); exp2.Next()) - { - TopoDS_Face face = TopoDS::Face (exp2.Current().Composed(exp1.Current().Orientation())); - - i = occgeometry->fmap.FindIndex(face); - - TopLoc_Location loc; - Handle(Geom_Surface) surf = BRep_Tool::Surface (face); - BRepAdaptor_Surface sf(face, Standard_False); - BRepLProp_SLProps prop(sf, 1, 1e-5); - Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc); - - if (triangulation.IsNull()) - { - cout << "cannot visualize face " << i << endl; - continue; - } - - if (vispar.occvisproblemfaces) - { - switch (occgeometry->facemeshstatus[i-1]) - { - case 0: - mat_col[0] = 0.2; - mat_col[1] = 0.2; - mat_col[2] = 0.8; - break; - case 1: - mat_col[0] = 0.2; - mat_col[1] = 0.8; - mat_col[2] = 0.2; - break; - case -1: - mat_col[0] = 0.8; - mat_col[1] = 0.2; - mat_col[2] = 0.2; - break; - } - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - } - glBegin (GL_TRIANGLES); - - int ntriangles = triangulation -> NbTriangles(); - for (j = 1; j <= ntriangles; j++) - { - Poly_Triangle triangle = (triangulation -> Triangles())(j); - for (k = 1; k <= 3; k++) - { - gp_Pnt2d uv = (triangulation -> UVNodes())(triangle(k)); - gp_Pnt pnt; - gp_Vec du, dv; - prop.SetParameters (uv.X(), uv.Y()); - surf->D0 (uv.X(), uv.Y(), pnt); - gp_Vec n; - - if (prop.IsNormalDefined()) - n = prop.Normal(); - else - n = gp_Vec (0,0,0); - - if (face.Orientation() == TopAbs_REVERSED) n *= -1; - glNormal3f (n.X(), n.Y(), n.Z()); - glVertex3f (pnt.X(), pnt.Y(), pnt.Z()); - } - } - glEnd (); - - } - } - - - glEndList (); - - } - */ - - void VisualSceneOCCGeometry :: BuildScene (int zoomall) - { - if (occgeometry -> changed == OCCGEOMETRYVISUALIZATIONFULLCHANGE) - { - occgeometry -> BuildVisualizationMesh (vispar.occdeflection); - - center = occgeometry -> Center(); - rad = occgeometry -> GetBoundingBox().Diam() / 2; - - if (vispar.occzoomtohighlightedentity) - { - bool hilite = false; - bool hiliteonepoint = false; - Bnd_Box bb; - - for (int i = 1; i <= occgeometry->fmap.Extent(); i++) - if (occgeometry->fvispar[i-1].IsHighlighted()) - { - hilite = true; - BRepBndLib::Add (occgeometry->fmap(i), bb); - } - - for (int i = 1; i <= occgeometry->emap.Extent(); i++) - if (occgeometry->evispar[i-1].IsHighlighted()) - { - hilite = true; - BRepBndLib::Add (occgeometry->emap(i), bb); - } - - for (int i = 1; i <= occgeometry->vmap.Extent(); i++) - if (occgeometry->vvispar[i-1].IsHighlighted()) - { - hiliteonepoint = true; - BRepBndLib::Add (occgeometry->vmap(i), bb); - } - - if (hilite || hiliteonepoint) - { - double x1,y1,z1,x2,y2,z2; - bb.Get (x1,y1,z1,x2,y2,z2); - Point<3> p1 = Point<3> (x1,y1,z1); - Point<3> p2 = Point<3> (x2,y2,z2); - Box<3> boundingbox(p1,p2); - - center = boundingbox.Center(); - if (hiliteonepoint) - rad = occgeometry -> GetBoundingBox().Diam() / 100; - else - rad = boundingbox.Diam() / 2; - } - } - - CalcTransformationMatrices(); - } - - // Clear lists - - for (int i = 1; i <= linelists.Size(); i++) - glDeleteLists (linelists.Elem(i), 1); - linelists.SetSize(0); - - for (int i = 1; i <= trilists.Size(); i++) - glDeleteLists (trilists.Elem(i), 1); - trilists.SetSize(0); - - // Total wireframe - - linelists.Append (glGenLists (1)); - glNewList (linelists.Last(), GL_COMPILE); - - for (int i = 1; i <= occgeometry->emap.Extent(); i++) - { - TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i)); - if (BRep_Tool::Degenerated(edge)) continue; - if (occgeometry->evispar[i-1].IsHighlighted()) continue; - - Handle(Poly_PolygonOnTriangulation) aEdgePoly; - Handle(Poly_Triangulation) T; - TopLoc_Location aEdgeLoc; - BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc); - - if(aEdgePoly.IsNull()) - { - (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge) - << " without using the occ visualization triangulation" << endl; - - double s0, s1; - Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); - - glBegin (GL_LINE_STRIP); - for (int i = 0; i<=50; i++) - { - gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0); - glVertex3f (p.X(),p.Y(),p.Z()); - } - glEnd (); - - continue; - } - - int nbnodes = aEdgePoly -> NbNodes(); - glBegin (GL_LINE_STRIP); - for (int j = 1; j <= nbnodes; j++) - { - gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); - glVertex3f (p.X(), p.Y(), p.Z()); - } - glEnd (); - } - - glEndList (); - - // Highlighted edge list - - linelists.Append (glGenLists (1)); - glNewList (linelists.Last(), GL_COMPILE); - - for (int i = 1; i <= occgeometry->emap.Extent(); i++) - if (occgeometry->evispar[i-1].IsHighlighted()) - { - TopoDS_Edge edge = TopoDS::Edge(occgeometry->emap(i)); - if (BRep_Tool::Degenerated(edge)) continue; - - Handle(Poly_PolygonOnTriangulation) aEdgePoly; - Handle(Poly_Triangulation) T; - TopLoc_Location aEdgeLoc; - BRep_Tool::PolygonOnTriangulation(edge, aEdgePoly, T, aEdgeLoc); - - if(aEdgePoly.IsNull()) - { - (*testout) << "visualizing edge " << occgeometry->emap.FindIndex (edge) - << " without using the occ visualization triangulation" << endl; - - double s0, s1; - Handle(Geom_Curve) c = BRep_Tool::Curve(edge, s0, s1); - - glBegin (GL_LINE_STRIP); - for (int i = 0; i<=50; i++) - { - gp_Pnt p = c->Value (s0 + i*(s1-s0)/50.0); - glVertex3f (p.X(),p.Y(),p.Z()); - } - glEnd (); - - continue; - } - - int nbnodes = aEdgePoly -> NbNodes(); - glBegin (GL_LINE_STRIP); - for (int j = 1; j <= nbnodes; j++) - { - gp_Pnt p = (T -> Nodes())(aEdgePoly->Nodes()(j)).Transformed(aEdgeLoc); - glVertex3f (p.X(), p.Y(), p.Z()); - } - glEnd (); - } - - glEndList (); - - // display faces - - trilists.Append (glGenLists (1)); - glNewList (trilists.Last(), GL_COMPILE); - - for (int i = 1; i <= occgeometry->fmap.Extent(); i++) - { - if (!occgeometry->fvispar[i-1].IsVisible()) continue; - - glLoadName (i); - float mat_col[4]; - mat_col[3] = 1; - - TopoDS_Face face = TopoDS::Face(occgeometry->fmap(i)); - - if (!occgeometry->fvispar[i-1].IsHighlighted()) - { - // Philippose - 30/01/2009 - // OpenCascade XDE Support - Quantity_Color face_colour; - // Philippose - 23/02/2009 - // Check to see if colours have been extracted first!! - // Forum bug-fox (Jean-Yves - 23/02/2009) - if(!(occgeometry->face_colours.IsNull()) - && (occgeometry->face_colours->GetColor(face,XCAFDoc_ColorSurf,face_colour))) - { - mat_col[0] = face_colour.Red(); - mat_col[1] = face_colour.Green(); - mat_col[2] = face_colour.Blue(); - } - else - { - mat_col[0] = 0.0; - mat_col[1] = 1.0; - mat_col[2] = 0.0; - } - } - else - { - mat_col[0] = 0.8; - mat_col[1] = 0.2; - mat_col[2] = 0.2; - } - - glMaterialfv (GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_col); - - TopLoc_Location loc; - Handle(Geom_Surface) surf = BRep_Tool::Surface (face); - BRepAdaptor_Surface sf(face, Standard_False); - BRepLProp_SLProps prop(sf, 1, 1e-5); - Handle(Poly_Triangulation) triangulation = BRep_Tool::Triangulation (face, loc); - - if (triangulation.IsNull()) - { - cout << "cannot visualize face " << i << endl; - occgeometry->fvispar[i-1].SetNotDrawable(); - continue; - } - - gp_Pnt2d uv; - gp_Pnt pnt; - gp_Vec n; - - glBegin (GL_TRIANGLES); - - int ntriangles = triangulation -> NbTriangles(); - for (int j = 1; j <= ntriangles; j++) - { - Poly_Triangle triangle = (triangulation -> Triangles())(j); - gp_Pnt p[3]; - for (int k = 1; k <= 3; k++) - p[k-1] = (triangulation -> Nodes())(triangle(k)).Transformed(loc); - - for (int k = 1; k <= 3; k++) - { - uv = (triangulation -> UVNodes())(triangle(k)); - prop.SetParameters (uv.X(), uv.Y()); - - // surf->D0 (uv.X(), uv.Y(), pnt); - - if (prop.IsNormalDefined()) - n = prop.Normal(); - else - { - (*testout) << "Visualization of face " << i - << ": Normal vector not defined" << endl; - // n = gp_Vec (0,0,0); - gp_Vec a(p[0],p[1]); - gp_Vec b(p[0],p[2]); - n = b^a; - } - - if (face.Orientation() == TopAbs_REVERSED) n *= -1; - glNormal3f (n.X(), n.Y(), n.Z()); - glVertex3f (p[k-1].X(), p[k-1].Y(), p[k-1].Z()); - } - } - glEnd (); - - } - glEndList (); - - } - - void SelectFaceInOCCDialogTree (int facenr); - - void VisualSceneOCCGeometry :: MouseDblClick (int px, int py) - { - int hits; - - // select surface triangle by mouse click - - GLuint selbuf[10000]; - glSelectBuffer (10000, selbuf); - - glRenderMode (GL_SELECT); - - GLint viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - - glMatrixMode (GL_PROJECTION); - glPushMatrix(); - - GLdouble projmat[16]; - glGetDoublev (GL_PROJECTION_MATRIX, projmat); - - glLoadIdentity(); - gluPickMatrix (px, viewport[3] - py, 1, 1, viewport); - glMultMatrixd (projmat); - - glClearColor(backcolor, backcolor, backcolor, 1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode (GL_MODELVIEW); - - glPushMatrix(); - glMultMatrixf (transformationmat); - - glInitNames(); - glPushName (1); - - glPolygonOffset (1, 1); - glEnable (GL_POLYGON_OFFSET_FILL); - - glDisable(GL_CLIP_PLANE0); - - // Philippose - 30/01/2009 - // Enable clipping planes for Selection mode in OCC Geometry - if (vispar.clipenable) - { - Vec<3> n(clipplane[0], clipplane[1], clipplane[2]); - double len = Abs(n); - double mu = -clipplane[3] / (len*len); - Point<3> p (mu * n); - n /= len; - Vec<3> t1 = n.GetNormal (); - Vec<3> t2 = Cross (n, t1); - - double xi1mid = (center - p) * t1; - double xi2mid = (center - p) * t2; - - glLoadName (0); - glBegin (GL_QUADS); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid-rad) * t2); - glVertex3dv (p + (xi1mid+rad) * t1 + (xi2mid+rad) * t2); - glVertex3dv (p + (xi1mid-rad) * t1 + (xi2mid+rad) * t2); - glEnd (); - } - - glCallList (trilists.Get(1)); - - glDisable (GL_POLYGON_OFFSET_FILL); - - glPopName(); - - glMatrixMode (GL_PROJECTION); - glPopMatrix(); - - glMatrixMode (GL_MODELVIEW); - glPopMatrix(); - - glFlush(); - - hits = glRenderMode (GL_RENDER); - - int minname = 0; - GLuint mindepth = 0; - - // find clippingplane - GLuint clipdepth = 0; // GLuint(-1); - - for (int i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - if (!curname) clipdepth = selbuf[4*i+1]; - } - - for (int i = 0; i < hits; i++) - { - int curname = selbuf[4*i+3]; - GLuint curdepth = selbuf[4*i+1]; - if (curname && (curdepth> clipdepth) && - (curdepth < mindepth || !minname)) - { - mindepth = curdepth; - minname = curname; - } - } - - occgeometry->LowLightAll(); - - if (minname) - { - occgeometry->fvispar[minname-1].Highlight(); - - if (vispar.occzoomtohighlightedentity) - occgeometry->changed = OCCGEOMETRYVISUALIZATIONFULLCHANGE; - else - occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE; - cout << "Selected face: " << minname << endl; - } - else - { - occgeometry->changed = OCCGEOMETRYVISUALIZATIONHALFCHANGE; - } - - glDisable(GL_CLIP_PLANE0); - - SelectFaceInOCCDialogTree (minname); - - // Philippose - 30/01/2009 - // Set the currently selected face in the array - // for local face mesh size definition - occgeometry->SetSelectedFace(minname); - - // selecttimestamp = NextTimeStamp(); - } - -} - -#endif - -#endif // NOTCL diff --git a/libsrc/visualization/vssolution.cpp b/libsrc/visualization/vssolution.cpp index 6e758687..baaaf448 100644 --- a/libsrc/visualization/vssolution.cpp +++ b/libsrc/visualization/vssolution.cpp @@ -9,11 +9,12 @@ // #include #include #include + namespace netgen { - DLL_HEADER VisualSceneSolution & GetVSSolution() + VisualSceneSolution & GetVSSolution() { static VisualSceneSolution vssolution; return vssolution; @@ -23,14 +24,6 @@ namespace netgen // extern shared_ptr mesh; extern VisualSceneMesh vsmesh; - - void AddUserVisualizationObject (UserVisualizationObject * vis) - { - // vssolution.AddUserVisualizationObject (vis); - GetVSSolution().AddUserVisualizationObject (vis); - } - - VisualSceneSolution :: SolData :: SolData () : data (0), solclass(0) { ; } @@ -56,6 +49,7 @@ namespace netgen clipplane_isolinelist = 0; surface_vector_list = 0; isosurface_list = 0; + numtexturecols = 8; fieldlineslist = 0; pointcurvelist = 0; @@ -208,7 +202,7 @@ namespace netgen case SOL_SURFACE_NONCONTINUOUS: ost << " -type=surfacenoncontinuous"; break; default: - cerr << "save solution data, case not handeld" << endl; + cerr << "save solution data, case not handled" << endl; } ost << endl; @@ -340,7 +334,7 @@ namespace netgen { double values[3], sumvalues[3] = { 0, 0, 0 }; - FlatArray els = mesh->GetTopology().GetVertexElements(pi); + NgFlatArray els = mesh->GetTopology().GetVertexElements(pi); for (int j = 0; j < els.Size(); j++) { @@ -382,7 +376,7 @@ namespace netgen BuildScene(); - CreateTexture (numtexturecols, lineartexture, 0.5, GL_MODULATE); + CreateTexture (GetVSSolution().numtexturecols, lineartexture, 0.5, GL_MODULATE); glClearColor(backcolor, backcolor, backcolor, 1); // glClearColor(backcolor, backcolor, backcolor, 0); @@ -451,8 +445,8 @@ namespace netgen glCallList (surfellist); #ifdef USE_BUFFERS - static int timer = NgProfiler::CreateTimer ("Solution::drawing - DrawSurfaceElements VBO"); - NgProfiler::StartTimer(timer); + // static int timer = NgProfiler::CreateTimer ("Solution::drawing - DrawSurfaceElements VBO"); + // NgProfiler::StartTimer(timer); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); @@ -460,7 +454,7 @@ namespace netgen glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); - NgProfiler::StopTimer(timer); + // NgProfiler::StopTimer(timer); #endif glDisable(GL_BLEND); @@ -629,10 +623,13 @@ namespace netgen for (int i = 0; i < user_vis.Size(); i++) user_vis[i] -> Draw(); + DrawMarker(); + glPopMatrix(); glDisable(GL_CLIP_PLANE0); - DrawColorBar (minval, maxval, logscale, lineartexture); + DrawColorBar (minval, maxval, logscale, lineartexture, number_format, unit); + DrawTitle (title); if (vispar.drawcoordinatecross) DrawCoordinateCross (); @@ -794,23 +791,7 @@ namespace netgen if (mesh->GetTimeStamp() > surfeltimestamp || zoomall) { // mesh has changed - - Point3d pmin, pmax; - static double oldrad = 0; - - mesh->GetBox (pmin, pmax, -1); - center = Center (pmin, pmax); - rad = 0.5 * Dist (pmin, pmax); - - glEnable (GL_NORMALIZE); - - if (rad > 1.5 * oldrad || - mesh->GetMajorTimeStamp() > surfeltimestamp || - zoomall) - { - CalcTransformationMatrices(); - oldrad = rad; - } + vsmesh.SelectCenter(zoomall); } DrawSurfaceElements(); @@ -881,7 +862,7 @@ namespace netgen if (autoscale) GetMinMax (vecfunction, 0, minval, maxval); - Array cpp; + NgArray cpp; GetClippingPlaneGrid (cpp); for (int i = 0; i < cpp.Size(); i++) @@ -1101,19 +1082,20 @@ namespace netgen glEndList (); if (clipplane_isolinelist) glDeleteLists (clipplane_isolinelist, 1); - + if (vispar.clipping.enable && clipsolution == 1 && sol) { clipplane_isolinelist = glGenLists (1); glNewList (clipplane_isolinelist, GL_COMPILE); - Array cpt; - Array pts; + NgArray cpt; + NgArray pts; GetClippingPlaneTrigs (cpt, pts); bool drawelem; glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]); - + glBegin (GL_LINES); + if (numisolines) for (int i = 0; i < cpt.Size(); i++) { @@ -1129,13 +1111,11 @@ namespace netgen DrawIsoLines (pts[trig.points[0].pnr].p, pts[trig.points[1].pnr].p, pts[trig.points[2].pnr].p, - // trig.points[1].p, - // trig.points[2].p, vali[0], vali[1], vali[2]); } + glEnd(); glEndList (); } - glEnd(); } clipplanetimestamp = max2 (vispar.clipping.timestamp, solutiontimestamp); @@ -1158,8 +1138,8 @@ namespace netgen glNewList (element1dlist, GL_COMPILE); int npt = (1 << subdivisions) + 1; - Array pref(npt), values(npt); - Array > points(npt); + NgArray pref(npt), values(npt); + NgArray > points(npt); const SolData * sol = NULL; if (scalfunction != -1) sol = soldata[scalfunction]; @@ -1170,12 +1150,12 @@ namespace netgen int ncomp = 0; if (sol) ncomp = sol->components; if (vsol) ncomp = vsol->components; - Array mvalues(ncomp); + NgArray mvalues(ncomp); for (int i = 0; i < npt; i++) pref[i] = double(i) / (npt-1); - + int meshdim = mesh->GetDimension(); for (SegmentIndex i = 0; i < mesh -> GetNSeg(); i++) { // mesh->GetCurvedElements(). @@ -1190,8 +1170,10 @@ namespace netgen { vsol->solclass->GetSegmentValue (i, pref[j], &mvalues[0]); // values[j] = ExtractValue (sol, scalcomp, &mvalues[0]); - points[j](0) += scaledeform * mvalues[0]; - points[j](1) += scaledeform * mvalues[1]; + for (int k = 0; k < min(ncomp, 3); k++) + points[j](k) += scaledeform * mvalues[k]; + // points[j](0) += scaledeform * mvalues[0]; + // points[j](1) += scaledeform * mvalues[1]; } } else if (sol) @@ -1200,7 +1182,8 @@ namespace netgen { sol->solclass->GetSegmentValue (i, pref[j], &mvalues[0]); values[j] = ExtractValue (sol, scalcomp, &mvalues[0]); - points[j](1) += scaledeform * values[j]; + if (meshdim <= 2) + points[j](meshdim) += scaledeform * values[j]; } } @@ -1218,6 +1201,7 @@ namespace netgen shared_ptr mesh = GetMesh(); static int timer = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements"); + /* static int timerstart = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements start"); static int timerloops = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements loops"); static int timerlist = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements list"); @@ -1229,6 +1213,7 @@ namespace netgen static int timer2 = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements 2"); static int timer2a = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements 2a"); static int timer2b = NgProfiler::CreateTimer ("Solution::DrawSurfaceElements 2b"); + */ NgProfiler::RegionTimer reg (timer); @@ -1260,7 +1245,7 @@ namespace netgen } #endif - NgProfiler::StartTimer(timerstart); + // NgProfiler::StartTimer(timerstart); if (surfellist) glDeleteLists (surfellist, 1); @@ -1296,29 +1281,29 @@ namespace netgen int n = 1 << subdivisions; int npt = sqr(n+1); - Array > pref (npt); - Array > points (npt); - Array > dxdxis (npt); - Array > nvs(npt); - Array values(npt); + NgArray > pref (npt); + NgArray > points (npt); + NgArray > dxdxis (npt); + NgArray > nvs(npt); + NgArray values(npt); - Array mvalues(npt); + NgArray mvalues(npt); int sol_comp = (sol && sol->draw_surface) ? sol->components : 0; - Array> > simd_pref ( (npt+SIMD::Size()-1)/SIMD::Size() ); - Array> > simd_points ( (npt+SIMD::Size()-1)/SIMD::Size() ); - Array> > simd_dxdxis ( (npt+SIMD::Size()-1)/SIMD::Size() ); - Array> > simd_nvs( (npt+SIMD::Size()-1)/SIMD::Size() ); - Array> simd_values( (npt+SIMD::Size()-1)/SIMD::Size() * sol_comp); + NgArray> > simd_pref ( (npt+SIMD::Size()-1)/SIMD::Size() ); + NgArray> > simd_points ( (npt+SIMD::Size()-1)/SIMD::Size() ); + NgArray> > simd_dxdxis ( (npt+SIMD::Size()-1)/SIMD::Size() ); + NgArray> > simd_nvs( (npt+SIMD::Size()-1)/SIMD::Size() ); + NgArray> simd_values( (npt+SIMD::Size()-1)/SIMD::Size() * sol_comp); - // Array> glob_pnts; - // Array> glob_nvs; - // Array glob_values; + // NgArray> glob_pnts; + // NgArray> glob_nvs; + // NgArray glob_values; if (sol && sol->draw_surface) mvalues.SetSize (npt * sol->components); - Array > valuesc(npt); + NgArray > valuesc(npt); #ifdef USE_BUFFERS if (has_surfel_vbo) @@ -1351,7 +1336,7 @@ namespace netgen #endif - NgProfiler::StopTimer(timerstart); + // NgProfiler::StopTimer(timerstart); for (SurfaceElementIndex sei = 0; sei < nse; sei++) { @@ -1506,7 +1491,7 @@ namespace netgen n = 1 << subdivisions; double invn = 1.0 / n; npt = (n+1)*(n+2)/2; - NgProfiler::StartTimer(timerloops); + // NgProfiler::StartTimer(timerloops); size_t base_pi = 0; for (int iy = 0, ii = 0; iy <= n; iy++) @@ -1522,7 +1507,7 @@ namespace netgen simd_pref[i](1) = [&] (size_t j) { size_t ii = i*simd_size+j; return (ii < npt) ? pref[ii](1) : 0; }; } - Array ind_reftrig; + NgArray ind_reftrig; for (int iy = 0, ii = 0; iy < n; iy++,ii++) for (int ix = 0; ix < n-iy; ix++, ii++) { @@ -1532,7 +1517,7 @@ namespace netgen for (int j = 0; j < nv; j++) ind_reftrig.Append (ind[j]); } - Array glob_ind; + NgArray glob_ind; glob_ind.SetSize(ind_reftrig.Size()); @@ -1558,7 +1543,7 @@ namespace netgen if ( el.GetType() == TRIG || el.GetType() == TRIG6 ) { - NgProfiler::StartTimer(timer1); + // NgProfiler::StartTimer(timer1); #ifdef __AVX_try_it_out__ // NgProfiler::StartTimer(timer1a); bool curved = curv.IsSurfaceElementCurved(sei); @@ -1642,7 +1627,7 @@ namespace netgen // NgProfiler::StopTimer(timer1c); #else - bool curved = curv.IsSurfaceElementCurved(sei); + bool curved = (*mesh)[sei].IsCurved(); for (int iy = 0, ii = 0; iy <= n; iy++) for (int ix = 0; ix <= n-iy; ix++, ii++) @@ -1699,7 +1684,7 @@ namespace netgen for (int ii = 0; ii < npt; ii++) points[ii] += GetSurfDeformation (sei, -1, pref[ii](0), pref[ii](1)); #endif - NgProfiler::StopTimer(timer1); + // NgProfiler::StopTimer(timer1); int save_usetexture = usetexture; if (!drawelem) @@ -1708,7 +1693,7 @@ namespace netgen SetTextureMode (usetexture); } - NgProfiler::StartTimer(timer2); + // NgProfiler::StartTimer(timer2); #ifdef USE_BUFFERS if (drawelem && usetexture == 1 && !logscale) @@ -1759,7 +1744,7 @@ namespace netgen glEnd(); } - NgProfiler::StopTimer(timer2); + // NgProfiler::StopTimer(timer2); @@ -1771,15 +1756,15 @@ namespace netgen } } } - NgProfiler::StopTimer(timerloops); + // NgProfiler::StopTimer(timerloops); - NgProfiler::StartTimer(timerbuffer); + // NgProfiler::StartTimer(timerbuffer); // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surfel_vbo[3]); // glBufferData(GL_ELEMENT_ARRAY_BUFFER, glob_ind.Size()*sizeof(int), &glob_ind[0], GL_STATIC_DRAW); // surfel_vbo_size = glob_ind.Size(); - NgProfiler::StopTimer(timerbuffer); + // NgProfiler::StopTimer(timerbuffer); // glDrawElements(GL_TRIANGLES, surfel_vbo_size, GL_UNSIGNED_INT, 0); @@ -1793,9 +1778,9 @@ namespace netgen // glDeleteBuffers (4, &vboId[0]); - NgProfiler::StartTimer(timerlist); + // NgProfiler::StartTimer(timerlist); glEndList (); - NgProfiler::StopTimer(timerlist); + // NgProfiler::StopTimer(timerlist); #ifdef PARALLELGL glFinish(); @@ -1848,8 +1833,8 @@ namespace netgen CurvedElements & curv = mesh->GetCurvedElements(); int n = 1 << subdivisions; - ArrayMem, 65> ptsloc(n+1); - ArrayMem, 65> ptsglob(n+1); + NgArrayMem, 65> ptsloc(n+1); + NgArrayMem, 65> ptsglob(n+1); double trigpts[3][2] = { { 0, 0 }, { 0, 1 }, { 1, 0} }; double trigvecs[3][2] = { { 1, 0 }, { 0, -1 }, { -1, 1} }; @@ -1946,12 +1931,12 @@ namespace netgen int n = 1 << subdivisions; int n3 = (n+1)*(n+1)*(n+1); - Array > grid(n3); - Array > locgrid(n3); - Array > trans(n3); - Array val1(n3*sol->components); - Array > grads1(n3); - Array compress(n3); + NgArray > grid(n3); + NgArray > locgrid(n3); + NgArray > trans(n3); + NgArray val1(n3*sol->components); + NgArray > grads1(n3); + NgArray compress(n3); MatrixFixWidth<3> pointmat(8); grads1 = Vec<3> (0.0); @@ -2007,7 +1992,7 @@ namespace netgen double(iz)/n); break; default: - cerr << "case not implementd 878234" << endl; + cerr << "case not implemented 878234" << endl; ploc = 0.0; } if (compress[ii] != -1) @@ -2187,9 +2172,9 @@ namespace netgen - void VisualSceneSolution :: DrawTrigSurfaceVectors(const Array< Point<3> > & lp, + void VisualSceneSolution :: DrawTrigSurfaceVectors(const NgArray< Point<3> > & lp, const Point<3> & pmin, const Point<3> & pmax, - const int sei, const SolData * vsol) + const int sei, const SolData * vsol, bool swap_lam) { shared_ptr mesh = GetMesh(); @@ -2254,12 +2239,17 @@ namespace netgen if (lam1 >= 0 && lam2 >= 0 && lam1+lam2 <= 1) { + if(swap_lam) + { + Swap(lam1, lam2); + lam1 = 1.0-lam1; + lam2 = 1.0-lam2; + } Point<3> cp; for (k = 0; k < 3; k++) cp(k) = lp[0](k) + lam1 * (lp[1](k)-lp[0](k)) + lam2 * (lp[2](k)-lp[0](k)); - Point<2> xref(lam1, lam2); if (mesh->GetCurvedElements().IsHighOrder()) mesh->GetCurvedElements(). @@ -2341,7 +2331,7 @@ namespace netgen if (el.GetType() == TRIG || el.GetType() == TRIG6) { - Array< Point<3> > lp(3); + NgArray< Point<3> > lp(3); lp[0] = mesh->Point(el[2]); lp[1] = mesh->Point(el[0]); @@ -2447,22 +2437,21 @@ namespace netgen } else if (el.GetType() == QUAD) { - /* - Array < Point<3> > lp(3); + NgArray < Point<3> > lp(3); lp[0] = mesh->Point(el[0]); lp[1] = mesh->Point(el[1]); - lp[2] = mesh->Point(el[2]); - - DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol); - - lp[0] = mesh->Point(el[0]); - lp[1] = mesh->Point(el[2]); lp[2] = mesh->Point(el[3]); DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol); - */ + + lp[0] = mesh->Point(el[2]); + lp[1] = mesh->Point(el[1]); + lp[2] = mesh->Point(el[3]); + + DrawTrigSurfaceVectors(lp,pmin,pmax,sei,vsol, true); + /* Point<3> lp[4]; Point<2> p2d[4]; @@ -2561,6 +2550,7 @@ namespace netgen } } + */ } } } @@ -2646,10 +2636,10 @@ namespace netgen { shared_ptr mesh = GetMesh(); - static int timer1 = NgProfiler::CreateTimer ("getminmax, vol"); - static int timer2 = NgProfiler::CreateTimer ("getminmax, surf"); + // static int timer1 = NgProfiler::CreateTimer ("getminmax, vol"); + // static int timer2 = NgProfiler::CreateTimer ("getminmax, surf"); -#ifdef PARALLEL +#ifdef PARALLELGL auto comm = mesh->GetCommunicator(); if (comm.Size() > 1) { @@ -2680,7 +2670,7 @@ namespace netgen if (sol->draw_volume) { - NgProfiler::RegionTimer reg1 (timer1); + // NgProfiler::RegionTimer reg1 (timer1); int ne = mesh->GetNE(); @@ -2719,12 +2709,13 @@ namespace netgen if (sol->draw_surface) { - NgProfiler::RegionTimer reg2 (timer2); + // NgProfiler::RegionTimer reg2 (timer2); - int nse = mesh->GetNSE(); - for (int i = 0; i < nse; i++) + // int nse = mesh->GetNSE(); + // for (int i = 0; i < nse; i++) + for (SurfaceElementIndex i : mesh->SurfaceElements().Range()) { - ELEMENT_TYPE type = mesh->SurfaceElement(i+1).GetType(); + ELEMENT_TYPE type = (*mesh)[i].GetType(); double val; bool considerElem = (type == QUAD) ? GetSurfValue (sol, i, -1, 0.5, 0.5, comp, val) @@ -2825,7 +2816,7 @@ namespace netgen if (comp == 0) { - ArrayMem values(data->components); + NgArrayMem values(data->components); ok = GetValues (data, elnr, xref, x, dxdxref, &values[0]); val = ExtractValue (data, 0, &values[0]); @@ -2876,7 +2867,7 @@ namespace netgen break; } default: - cerr << "case not implementd 23523" << endl; + cerr << "case not implemented 23523" << endl; } for (int i = 0; i < np; i++) @@ -2996,7 +2987,7 @@ namespace netgen if (comp == 0) { - ArrayMem values(data->components); + NgArrayMem values(data->components); ok = GetValues (data, elnr, lam1, lam2, lam3, &values[0]); val = ExtractValue (data, 0, &values[0]); return ok; @@ -3379,7 +3370,7 @@ namespace netgen { case SOL_VIRTUALFUNCTION: { - ArrayMem values(data->components); + NgArrayMem values(data->components); bool ok; ok = data->solclass->GetSurfValue (selnr, facetnr, lam1, lam2, &values[0]); @@ -3395,7 +3386,7 @@ namespace netgen return ok; } default: - cerr << "case not implementd 6565" << endl; + cerr << "case not implemented 6565" << endl; } return 0; } @@ -3409,7 +3400,7 @@ namespace netgen if (comp == 0) { val = 0; - ArrayMem values(data->components); + NgArrayMem values(data->components); ok = GetSurfValues (data, selnr, facetnr, lam1, lam2, &values[0]); val = ExtractValue (data, 0, &values[0]); return ok; @@ -3421,7 +3412,7 @@ namespace netgen case SOL_VIRTUALFUNCTION: { - ArrayMem values(data->components); + NgArrayMem values(data->components); bool ok; ok = data->solclass->GetSurfValue (selnr, facetnr, lam1, lam2, &values[0]); @@ -3562,7 +3553,7 @@ namespace netgen break; } default: - cerr << "case not implementd 2342" << endl; + cerr << "case not implemented 2342" << endl; } break; } @@ -3651,7 +3642,7 @@ namespace netgen if (comp == 0) { val = 0; - ArrayMem values(data->components); + NgArrayMem values(data->components); ok = GetSurfValues (data, selnr, facetnr, xref, x, dxdxref, &values[0]); val = ExtractValue (data, 0, &values[0]); return ok; @@ -3662,7 +3653,7 @@ namespace netgen { case SOL_VIRTUALFUNCTION: { - ArrayMem values(data->components); + NgArrayMem values(data->components); bool ok; // ok = data->solclass->GetSurfValue (selnr, lam1, lam2, &values[0]); @@ -3831,7 +3822,7 @@ namespace netgen break; } default: - cerr << "case not implented 3234" << endl; + cerr << "case not implemented 3234" << endl; } break; } @@ -3957,18 +3948,18 @@ namespace netgen - void VisualSceneSolution :: GetClippingPlaneTrigs (Array & trigs, - Array & pts) + void VisualSceneSolution :: GetClippingPlaneTrigs (NgArray & trigs, + NgArray & pts) { shared_ptr mesh = GetMesh(); - static int timer_vals = NgProfiler::CreateTimer ("ClipPlaneTrigs - vertex values"); + // static int timer_vals = NgProfiler::CreateTimer ("ClipPlaneTrigs - vertex values"); static int timer1 = NgProfiler::CreateTimer ("ClipPlaneTrigs1"); // static int timer1a = NgProfiler::CreateTimer ("ClipPlaneTrigs1a"); // static int timer2 = NgProfiler::CreateTimer ("ClipPlaneTrigs2"); - static int timer3 = NgProfiler::CreateTimer ("ClipPlaneTrigs3"); - static int timer4 = NgProfiler::CreateTimer ("ClipPlaneTrigs4"); - static int timer4b = NgProfiler::CreateTimer ("ClipPlaneTrigs4b"); + // static int timer3 = NgProfiler::CreateTimer ("ClipPlaneTrigs3"); + // static int timer4 = NgProfiler::CreateTimer ("ClipPlaneTrigs4"); + // static int timer4b = NgProfiler::CreateTimer ("ClipPlaneTrigs4b"); NgProfiler::RegionTimer reg1 (timer1); @@ -3987,24 +3978,25 @@ namespace netgen int cntce; int cpe1 = 0, cpe2 = 0, cpe3 = 0; - // Array loctets; - // Array loctetsloc; - // Array > pointsloc; + // NgArray loctets; + // NgArray loctetsloc; + // NgArray > pointsloc; int n = 1 << subdivisions; int n3 = (n+1)*(n+1)*(n+1); - Array > grid(n3); - Array > locgrid(n3); - Array > trans(n3); - Array val(n3); - Array locposval(n3); - Array compress(n3); + NgArray > grid(n3); + NgArray > locgrid(n3); + NgArray > trans(n3); + NgArray val(n3); + NgArray locposval(n3); + NgArray compress(n3); - NgProfiler::StartTimer (timer_vals); - Array vertval(mesh->GetNV()); - Array posval(mesh->GetNV()); - for (PointIndex pi = vertval.Begin(); pi < vertval.End(); pi++) + // NgProfiler::StartTimer (timer_vals); + NgArray vertval(mesh->GetNP()); + NgArray posval(mesh->GetNP()); + // for (PointIndex pi = vertval.Begin(); pi < vertval.End(); pi++) + for (PointIndex pi : vertval.Range()) { Point<3> vert = (*mesh)[pi]; vertval[pi] = @@ -4014,7 +4006,7 @@ namespace netgen clipplane[3]; posval[pi] = vertval[pi] > 0; } - NgProfiler::StopTimer (timer_vals); + // NgProfiler::StopTimer (timer_vals); INDEX_2_CLOSED_HASHTABLE edges(8*n3); // point nr of edge @@ -4127,13 +4119,13 @@ namespace netgen if (mesh->GetCurvedElements().IsHighOrder()) { - NgProfiler::RegionTimer reg4(timer4); + // NgProfiler::RegionTimer reg4(timer4); mesh->GetCurvedElements(). CalcMultiPointElementTransformation (&locgrid, ei, &grid, 0); } else { - NgProfiler::RegionTimer reg4(timer4b); + // NgProfiler::RegionTimer reg4(timer4b); Vector shape(el.GetNP()); MatrixFixWidth<3> pointmat(el.GetNP()); @@ -4155,7 +4147,7 @@ namespace netgen } } - NgProfiler::RegionTimer reg3(timer3); + // NgProfiler::RegionTimer reg3(timer3); bool has_pos = false, all_pos = true; @@ -4305,7 +4297,7 @@ namespace netgen } } - void VisualSceneSolution :: GetClippingPlaneGrid (Array & pts) + void VisualSceneSolution :: GetClippingPlaneGrid (NgArray & pts) { shared_ptr mesh = GetMesh(); @@ -4373,7 +4365,7 @@ namespace netgen { InitParallelGL(); - Array parlists (ntasks); + NgArray parlists (ntasks); MyMPI_SendCmd ("redraw"); MyMPI_SendCmd ("clipplanetrigs"); @@ -4406,8 +4398,8 @@ namespace netgen glNewList (clipplanelist_scal, GL_COMPILE); - Array trigs; - Array points; + NgArray trigs; + NgArray points; GetClippingPlaneTrigs (trigs, points); glNormal3d (-clipplane[0], -clipplane[1], -clipplane[2]); @@ -4429,14 +4421,14 @@ namespace netgen for (int j = 0; j < 3; j++) maxlpnr = max2 (maxlpnr, trigs[i].points[j].locpnr); - Array vals(maxlpnr+1); - Array > valsc(maxlpnr+1); - Array elnrs(maxlpnr+1); - Array trigok(maxlpnr+1); - Array > locpoints(maxlpnr+1); - Array > globpoints(maxlpnr+1); - Array > jacobi(maxlpnr+1); - Array mvalues( (maxlpnr+1) * sol->components); + NgArray vals(maxlpnr+1); + NgArray > valsc(maxlpnr+1); + NgArray elnrs(maxlpnr+1); + NgArray trigok(maxlpnr+1); + NgArray > locpoints(maxlpnr+1); + NgArray > globpoints(maxlpnr+1); + NgArray > jacobi(maxlpnr+1); + NgArray mvalues( (maxlpnr+1) * sol->components); trigok = false; elnrs = -1; @@ -4521,7 +4513,7 @@ namespace netgen glEndList (); -#ifdef PARALLELGL +#ifdef PARALLELGLGL glFinish(); if (id > 0) MyMPI_Send (clipplanelist_scal, 0, MPI_TAG_VIS); @@ -4743,9 +4735,159 @@ namespace netgen void VisualSceneSolution :: MouseDblClick (int px, int py) { - vsmesh.SetClippingPlane(); - // vsmesh.BuildFilledList(); - vsmesh.MouseDblClick(px,py); + auto mesh = GetMesh(); + auto dim = mesh->GetDimension(); + marker = nullopt; + + 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) + { + cout << '\t'; + 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; + } + }; + + Point<3> p; + bool found_point = vsmesh.SelectSurfaceElement(px, py, p, showclipsolution && clipsolution); + if(!found_point) + return; + + // marker = p; + + // found point on clipping plane + if(selelement==0) + { + GLint viewport[4]; + GLdouble projection[16]; + glGetDoublev(GL_PROJECTION_MATRIX, &projection[0]); + + glGetIntegerv(GL_VIEWPORT, &viewport[0]); + + 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-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 = eye + lam*view; + + double lami[3]; + if(auto el3d = mesh->GetElementOfPoint( p, lami )) + { + cout << endl << "Selected point " << p << " on clipping plane" << endl; + // marker = p; + + 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; + } + } + } + + 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(selelement>0 && 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); + } } @@ -4809,7 +4951,7 @@ namespace netgen #include "../include/nginterface.h" -void Ng_ClearSolutionData () +void Impl_Ng_ClearSolutionData () { #ifdef OPENGL // if (nodisplay) return; @@ -4818,7 +4960,7 @@ void Ng_ClearSolutionData () #endif } -void Ng_InitSolutionData (Ng_SolutionData * soldata) +void Impl_Ng_InitSolutionData (Ng_SolutionData * soldata) { // soldata -> name = NULL; soldata -> data = NULL; @@ -4832,7 +4974,7 @@ void Ng_InitSolutionData (Ng_SolutionData * soldata) soldata -> solclass = 0; } -void Ng_SetSolutionData (Ng_SolutionData * soldata) +void Impl_Ng_SetSolutionData (Ng_SolutionData * soldata) { #ifdef OPENGL // if (nodisplay) return; @@ -4842,6 +4984,9 @@ void Ng_SetSolutionData (Ng_SolutionData * soldata) // vss->name = new char[strlen (soldata->name)+1]; // strcpy (vss->name, soldata->name); vss->name = soldata->name; + vss->title = soldata->title; + vss->number_format = soldata->number_format; + vss->unit = soldata->unit; vss->data = soldata->data; vss->components = soldata->components; vss->dist = soldata->dist; @@ -4858,12 +5003,7 @@ void Ng_SetSolutionData (Ng_SolutionData * soldata) -namespace netgen -{ - extern void Render (bool blocking); -} - -void Ng_Redraw (bool blocking) +void Impl_Ng_Redraw (bool blocking) { #ifdef OPENGL //netgen::vssolution.UpdateSolutionTimeStamp(); @@ -4879,7 +5019,18 @@ void (*glDeleteBuffers) (GLsizei a, const GLuint *b); void (*glGenBuffers) (GLsizei a, GLuint *b); void (*glBufferData) (GLenum a, GLsizeiptr b, const GLvoid *c, GLenum d); void (*glBufferSubData) (GLenum a, GLintptr b, GLsizeiptr c, const GLvoid *d); -DLL_HEADER void LoadOpenGLFunctionPointers() { + +GLenum (*glCheckFramebufferStatus) (GLenum target); +void (*glBindFramebuffer) (GLenum target, GLuint framebuffer); +void (*glBindRenderbuffer) (GLenum target, GLuint renderbuffer); +void (*glDeleteFramebuffers) (GLsizei n, const GLuint *framebuffers); +void (*glDeleteRenderbuffers) (GLsizei n, const GLuint *renderbuffers); +void (*glGenFramebuffers) (GLsizei n, GLuint *framebuffers); +void (*glGenRenderbuffers) (GLsizei n, GLuint *renderbuffers); +void (*glRenderbufferStorage) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +void (*glFramebufferRenderbuffer) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); + +NGGUI_API void LoadOpenGLFunctionPointers() { #ifdef USE_BUFFERS glBindBuffer = (decltype(glBindBuffer)) wglGetProcAddress("glBindBuffer"); glBufferSubData = (decltype(glBufferSubData)) wglGetProcAddress("glBufferSubData"); @@ -4888,8 +5039,27 @@ DLL_HEADER void LoadOpenGLFunctionPointers() { glGenBuffers = (decltype(glGenBuffers)) wglGetProcAddress("glGenBuffers"); if(!glBindBuffer) throw std::runtime_error("Could not load OpenGL functions!"); #endif + + glCheckFramebufferStatus = (decltype(glCheckFramebufferStatus )) wglGetProcAddress("glCheckFramebufferStatus"); + glBindFramebuffer = (decltype(glBindFramebuffer )) wglGetProcAddress("glBindFramebuffer"); + glBindRenderbuffer = (decltype(glBindRenderbuffer )) wglGetProcAddress("glBindRenderbuffer"); + glDeleteFramebuffers = (decltype(glDeleteFramebuffers )) wglGetProcAddress("glDeleteFramebuffers"); + glDeleteRenderbuffers = (decltype(glDeleteRenderbuffers )) wglGetProcAddress("glDeleteRenderbuffers"); + glGenFramebuffers = (decltype(glGenFramebuffers )) wglGetProcAddress("glGenFramebuffers"); + glGenRenderbuffers = (decltype(glGenRenderbuffers )) wglGetProcAddress("glGenRenderbuffers"); + glRenderbufferStorage = (decltype(glRenderbufferStorage )) wglGetProcAddress("glRenderbufferStorage"); + glFramebufferRenderbuffer = (decltype(glFramebufferRenderbuffer )) wglGetProcAddress("glFramebufferRenderbuffer"); } #else // WIN32 -DLL_HEADER void LoadOpenGLFunctionPointers() { } +NGGUI_API void LoadOpenGLFunctionPointers() { } #endif // WIN32 #endif // OPENGL + +// set function pointers +static bool dummy_init = [](){ + Ptr_Ng_ClearSolutionData = Impl_Ng_ClearSolutionData; + Ptr_Ng_InitSolutionData = Impl_Ng_InitSolutionData; + Ptr_Ng_SetSolutionData = Impl_Ng_SetSolutionData; + Ptr_Ng_Redraw = Impl_Ng_Redraw; + return true; +}(); diff --git a/libsrc/visualization/vssolution.hpp b/libsrc/visualization/vssolution.hpp index 56d757df..66684ed4 100644 --- a/libsrc/visualization/vssolution.hpp +++ b/libsrc/visualization/vssolution.hpp @@ -1,6 +1,8 @@ #ifndef FILE_VSSOLUTION #define FILE_VSSOLUTION +#include "visual_api.hpp" +#include "mvdraw.hpp" typedef void * ClientData; struct Tcl_Interp; @@ -9,18 +11,16 @@ struct Tcl_Interp; namespace netgen { -DLL_HEADER extern void ImportSolution (const char * filename); +NGGUI_API extern void ImportSolution (const char * filename); -class FieldLineCalc; - extern int Ng_Vis_Set (ClientData clientData, Tcl_Interp * interp, int argc, const char *argv[]); -class DLL_HEADER VisualSceneSolution : public VisualScene +class NGGUI_API VisualSceneSolution : public VisualScene { friend class FieldLineCalc; @@ -76,7 +76,7 @@ class DLL_HEADER VisualSceneSolution : public VisualScene int fieldlineslist; int num_fieldlineslists; int fieldlines_startarea; - Array fieldlines_startarea_parameter; + NgArray fieldlines_startarea_parameter; int fieldlines_startface; string fieldlines_filename; double fieldlines_reltolerance; @@ -94,19 +94,23 @@ class DLL_HEADER VisualSceneSolution : public VisualScene int fieldlinestimestamp, surface_vector_timestamp; int pointcurve_timestamp; int isosurface_timestamp; - int subdivision_timestamp; int timetimestamp; double minval, maxval; + int scalfunction, vecfunction; + string number_format = "%8.3e"; + string unit = ""; + string title = ""; + NgLock *lock; #ifdef PARALLELGL - Array par_linelists; - Array par_surfellists; + NgArray par_linelists; + NgArray par_surfellists; #endif - Array user_vis; + NgArray user_vis; public: @@ -137,6 +141,9 @@ public: ~SolData (); string name; + string number_format = "%8.3e"; + string unit = ""; + string title = ""; double * data; int components; int dist; @@ -154,19 +161,18 @@ public: - Array soldata; + NgArray soldata; int usetexture; // 0..no, 1..1D texture (standard), 2..2D-texture (complex) int clipsolution; // 0..no, 1..scal, 2..vec - int scalfunction, scalcomp, vecfunction; + int scalcomp; int gridsize; double xoffset, yoffset; int autoscale, logscale; double mminval, mmaxval; int numisolines; - int subdivisions; bool showclipsolution; bool showsurfacesolution; @@ -183,10 +189,10 @@ public: bool imag_part; private: - void BuildFieldLinesFromFile(Array & startpoints); - void BuildFieldLinesFromFace(Array & startpoints); - void BuildFieldLinesFromBox(Array & startpoints); - void BuildFieldLinesFromLine(Array & startpoints); + void BuildFieldLinesFromFile(Array> & startpoints); + void BuildFieldLinesFromFace(Array> & startpoints); + void BuildFieldLinesFromBox(Array> & startpoints); + void BuildFieldLinesFromLine(Array> & startpoints); // weak_ptr wp_mesh; public: VisualSceneSolution (); @@ -233,11 +239,16 @@ public: { user_vis.Append (vis); } - + void DeleteUserVisualizationObject (UserVisualizationObject * vis) + { + int pos = user_vis.Pos(vis); + if (pos >= 0) + user_vis.Delete(pos); + } private: - void GetClippingPlaneTrigs (Array & trigs, Array & pts); - void GetClippingPlaneGrid (Array & pts); + void GetClippingPlaneTrigs (NgArray & trigs, NgArray & pts); + void GetClippingPlaneGrid (NgArray & pts); void DrawCone (const Point<3> & p1, const Point<3> & p2, double r); void DrawCylinder (const Point<3> & p1, const Point<3> & p2, double r); @@ -314,8 +325,8 @@ public: void Draw1DElements(); void DrawSurfaceVectors (); - void DrawTrigSurfaceVectors(const Array< Point<3> > & lp, const Point<3> & pmin, const Point<3> & pmax, - const int sei, const SolData * vsol); + void DrawTrigSurfaceVectors(const NgArray< Point<3> > & lp, const Point<3> & pmin, const Point<3> & pmax, + const int sei, const SolData * vsol, bool swap_lam=false); void DrawIsoSurface(const SolData * sol, const SolData * grad, int comp); void DrawIsoLines (const Point<3> & p1, @@ -343,6 +354,19 @@ public: Tcl_Interp * interp, int argc, const char *argv[]); + void SetScalfunction( int i ) { + scalfunction = i; + title = soldata[i]->title; + number_format = soldata[i]->number_format; + unit = soldata[i]->unit; + } + + void SetVecfunction( int i ) { + vecfunction = i; + title = soldata[i]->title; + number_format = soldata[i]->number_format; + unit = soldata[i]->unit; + } #ifdef PARALLELGL void Broadcast (); @@ -351,104 +375,17 @@ public: }; +NGGUI_API VisualSceneSolution & GetVSSolution(); - - -class RKStepper +inline void AddUserVisualizationObject (UserVisualizationObject * vis) { -private: - Array c,b; - TABLE *a; - int steps; - int order; - - double tolerance; - - Array K; - - int stepcount; - - double h; - double startt; - double startt_bak; - Point3d startval; - Point3d startval_bak; - - bool adaptive; - int adrun; - Point3d valh; - - int notrestarted; + GetVSSolution().AddUserVisualizationObject (vis); +} -public: - - ~RKStepper(); - - RKStepper(int type = 0); - - void SetTolerance(const double tol){tolerance = tol;} - - void StartNextValCalc(const Point3d & astartval, const double astartt, const double ah, const bool aadaptive = false); - - bool GetNextData(Point3d & val, double & t, double & ah); - - bool FeedNextF(const Vec3d & f); -}; - - - - - -class FieldLineCalc +inline void DeleteUserVisualizationObject (UserVisualizationObject * vis) { -private: - const Mesh & mesh; - - VisualSceneSolution & vss; - - const VisualSceneSolution::SolData * vsol; - - RKStepper stepper; - - double maxlength; - - int maxpoints; - - int direction; - - Point3d pmin, pmax; - double rad; - double phaser, phasei; - - double critical_value; - - bool randomized; - - double thickness; - -public: - FieldLineCalc(const Mesh & amesh, VisualSceneSolution & avss, const VisualSceneSolution::SolData * solution, - const double rel_length, const int amaxpoints = -1, - const double rel_thickness = -1, const double rel_tolerance = -1, const int rk_type = 0, const int adirection = 0); - - void SetPhase(const double real, const double imag) { phaser = real; phasei = imag; } - - void SetCriticalValue(const double val) { critical_value = val; } - - void Randomized(void) { randomized = true; } - void NotRandomized(void) { randomized = false; } - - void Calc(const Point3d & startpoint, Array & points, Array & vals, Array & drawelems, Array & dirstart); - - void GenerateFieldLines(Array & potential_startpoints, const int numlines, const int gllist, - const double minval, const double maxval, const int logscale, double phaser, double phasei); -}; - - - - - // DLL_HEADER extern VisualSceneSolution vssolution; -DLL_HEADER extern VisualSceneSolution & GetVSSolution(); + GetVSSolution().DeleteUserVisualizationObject (vis); +} } diff --git a/ng/CMakeLists.txt b/ng/CMakeLists.txt index 2705aecf..3ad3d8be 100644 --- a/ng/CMakeLists.txt +++ b/ng/CMakeLists.txt @@ -4,54 +4,39 @@ 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) +target_sources(nglib PRIVATE onetcl.cpp) - add_library(gui SHARED - gui.cpp ngpkg.cpp demoview.cpp parallelfunc.cpp - ../libsrc/stlgeom/stlpkg.cpp ../libsrc/visualization/visualpkg.cpp - ../libsrc/csg/csgpkg.cpp ../libsrc/geom2d/geom2dpkg.cpp - ../libsrc/occ/occpkg.cpp ../libsrc/occ/vsocc.cpp +if(USE_GUI) + target_sources(nggui PRIVATE + gui.cpp ngpkg.cpp demoview.cpp parallelfunc.cpp ngtcl.cpp ) - add_executable(netgen ngappinit.cpp onetcl.cpp) - - 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} ) - if(NOT APPLE) - target_link_libraries( gui PRIVATE ${TCL_LIBRARY} ${TK_LIBRARY}) - endif(NOT APPLE) - - target_link_libraries( netgen nglib gui ${LIBTOGL} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} ${FFMPEG_LIBRARIES} ${X11_Xmu_LIB} ${X11_X11_LIB} ${OCC_LIBRARIES} ${TK_LIBRARY} ${TCL_LIBRARY}) - - if(NOT WIN32) - target_link_libraries( netgen mesh stlvis stl geom2dvis interface geom2d csg stl visual csgvis ) - target_link_libraries( gui PUBLIC mesh stlvis stl geom2dvis interface geom2d csg stl visual csgvis ) - endif(NOT WIN32) - + if(NOT BUILD_FOR_CONDA) + add_executable(netgen ngappinit.cpp) + if(WIN32) + target_sources(netgen PRIVATE ../windows/netgen.rc) + endif(WIN32) + target_link_libraries( netgen nglib nggui netgen_python netgen_gui) install(TARGETS netgen ${NG_INSTALL_DIR}) - install(TARGETS gui ${NG_INSTALL_DIR}) - if(APPLE) set_target_properties(netgen PROPERTIES OUTPUT_NAME netgen) endif(APPLE) - if(WIN32) - set_target_properties( gui PROPERTIES OUTPUT_NAME libgui ) - endif(WIN32) + target_link_libraries( netgen ${PYTHON_LIBRARIES} ${TCL_LIBRARY} ${TK_LIBRARY}) + endif(NOT BUILD_FOR_CONDA) + install(TARGETS nggui ${NG_INSTALL_DIR}) endif(USE_GUI) if(USE_PYTHON) add_library(ngpy SHARED netgenpy.cpp) - target_link_libraries( ngpy PUBLIC nglib PRIVATE ${PYTHON_LIBRARIES}) + target_link_libraries( ngpy PUBLIC nglib PRIVATE "$" ) if(APPLE) set_target_properties( ngpy PROPERTIES SUFFIX ".so") elseif(WIN32) @@ -60,6 +45,19 @@ if(USE_PYTHON) endif() set_target_properties(ngpy PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/../${NETGEN_PYTHON_RPATH}") install(TARGETS ngpy DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} COMPONENT netgen) + + if(USE_GUI) + add_library(ngguipy SHARED ngguipy.cpp) + target_link_libraries( ngguipy PUBLIC nglib nggui PRIVATE "$" $) + if(APPLE) + set_target_properties( ngguipy PROPERTIES SUFFIX ".so") + elseif(WIN32) + set_target_properties( ngguipy PROPERTIES SUFFIX ".pyd") + set_target_properties( ngguipy PROPERTIES OUTPUT_NAME "libngguipy") + endif() + set_target_properties(ngguipy PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/../${NETGEN_PYTHON_RPATH}") + install(TARGETS ngguipy DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} COMPONENT netgen) + endif(USE_GUI) endif(USE_PYTHON) if(USE_GUI) diff --git a/ng/Togl-1.7/README.stubs b/ng/Togl-1.7/README.stubs index 2950a981..4c1b8186 100644 --- a/ng/Togl-1.7/README.stubs +++ b/ng/Togl-1.7/README.stubs @@ -4,7 +4,7 @@ interface, witch means that the same binary works with any stubs-aware wish (i.e. version >= 8.1) It has been tested on Windows NT/2000 and Linux for several Tcl/Tk versions up -to 8.4a3. I haven't been able to test the Mac port, it propably needs mending +to 8.4a3. I haven't been able to test the Mac port, it probably needs mending but I can't see why it shouldn't work in principle. Implementation wise, what differs from Togl 1.5 is that Togl_MakeWindowExist() @@ -16,6 +16,6 @@ same binary runs on all versions of Wish from 8.1 and up. For this to work you need to compile against the headers from Tcl/Tk 8.4a3 or later, or the binary will only work for Tcl/Tk 8.1-8.4a2. The tk8.4a3 public headers (tk8.4a3.h + tkDecls.h) are included for -conveniance, and they are used if the flag -DUSE_LOCAL_TK_H is specified. +convenience, and they are used if the flag -DUSE_LOCAL_TK_H is specified. Jonas Beskow, December 2001 \ No newline at end of file diff --git a/ng/Togl-1.7/Togl.html b/ng/Togl-1.7/Togl.html index fa451e24..5df48136 100644 --- a/ng/Togl-1.7/Togl.html +++ b/ng/Togl-1.7/Togl.html @@ -844,7 +844,7 @@ make -C Togl install
pathName swapbuffers
Causes front/back buffers to be swapped if in double buffer mode. - And flushs the OpenGL command buffer if in single buffer mode. + And flushes the OpenGL command buffer if in single buffer mode. (So this is appropriate to call after every frame is drawn.) @@ -900,7 +900,7 @@ make -C Togl install type make demos. The demos are compiled into shared libraries, that can are loaded into the Tcl interpreter as Tcl/Tk-extensions. - Demos are started by running the corrsponding Tcl script. + Demos are started by running the corresponding Tcl script. To run a demo just type ./double.tcl or ./texture.tcl etc. diff --git a/ng/Togl2.1/CMakeLists.txt b/ng/Togl2.1/CMakeLists.txt index bcade1f7..5d149129 100644 --- a/ng/Togl2.1/CMakeLists.txt +++ b/ng/Togl2.1/CMakeLists.txt @@ -5,13 +5,21 @@ if(APPLE) endif(APPLE) if(WIN32) - add_definitions("-DBUILD_togl -DUNICODE -D_UNICODE -DTOGL_USE_FONTS=0 -DSTDC_HEADERS -DSTDC_HEADER") - add_library(togl SHARED togl.c toglProcAddr.c toglStubInit.c) - install(TARGETS togl DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) - target_link_libraries(togl ${TCL_LIBRARY} ${TK_LIBRARY}) + set(TOGL_LIBRARY_TYPE SHARED) else(WIN32) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer -Wno-implicit-int") - add_definitions("-DPACKAGE_NAME=\"Togl\" -DPACKAGE_TARNAME=\"togl\" -DPACKAGE_VERSION=\"2.1\" -DPACKAGE_STRING=\"Togl\ 2.1\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE_URL=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=0 -DHAVE_LIMITS_H=1 -DHAVE_SYS_PARAM_H=1 -DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1 -DTCL_THREADS=1 -DMODULE_SCOPE=extern\ __attribute__\(\(__visibility__\(\"hidden\"\)\)\) -D_LARGEFILE64_SOURCE=1 -DTCL_WIDE_INT_IS_LONG=1 -DUSE_TCL_STUBS=1 -DUSE_TK_STUBS=1 -DAUTOSTEREOD=\"\"") + set(TOGL_LIBRARY_TYPE STATIC) +endif(WIN32) + +add_library(togl ${TOGL_LIBRARY_TYPE} togl.c toglProcAddr.c toglStubInit.c) +target_link_libraries( togl PUBLIC $) + +target_compile_definitions(togl PRIVATE -DBUILD_togl=1 -DSTDC_HEADERS=1) + +if(WIN32) + target_compile_definitions(togl PRIVATE -DUNICODE -D_UNICODE -DTOGL_USE_FONTS=0 -DSTDC_HEADER) +else(WIN32) + target_compile_options(togl PRIVATE -fomit-frame-pointer -Wno-implicit-int) + target_compile_definitions(togl PRIVATE -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=0 -DHAVE_LIMITS_H=1 -DHAVE_SYS_PARAM_H=1 -DUSE_THREAD_ALLOC=1 -D_REENTRANT=1 -D_THREAD_SAFE=1 -DTCL_THREADS=1 -D_LARGEFILE64_SOURCE=1 -DTCL_WIDE_INT_IS_LONG=1) include_directories(BEFORE "${TCL_INCLUDE_PATH}/tcl-private/generic" "${TCL_INCLUDE_PATH}/tcl-private/unix") include_directories(BEFORE "${TK_INCLUDE_PATH}/tk-private/generic" "${TK_INCLUDE_PATH}/tk-private/unix" "${TK_INCLUDE_PATH}/tk-private") @@ -20,9 +28,9 @@ else(WIN32) include_directories(BEFORE "${TCL_INCLUDE_PATH}") include_directories(BEFORE "${TK_INCLUDE_PATH}") - add_library(togl togl.c toglProcAddr.c toglStubInit.c) - target_link_libraries(togl -ldl) endif(WIN32) -target_link_libraries(togl ${OPENGL_LIBRARIES}) +target_include_directories(togl PUBLIC ${OPENGL_INCLUDE_DIR}) +target_link_libraries(togl PUBLIC ${OPENGL_LIBRARY}) set_target_properties(togl PROPERTIES POSITION_INDEPENDENT_CODE ON ) +install(TARGETS togl DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) diff --git a/ng/Togl2.1/README.bin b/ng/Togl2.1/README.bin index 49e820b2..a555b7c9 100644 --- a/ng/Togl2.1/README.bin +++ b/ng/Togl2.1/README.bin @@ -67,7 +67,7 @@ File hierarchy: *.html Start with index.html The contents of the include and lib directories can be placed verbatim -in the Tcl installataion hierarchy. +in the Tcl installation hierarchy. Documentation is in the doc directory. Start with doc/index.html in your web browser. diff --git a/ng/Togl2.1/README.stubs b/ng/Togl2.1/README.stubs index 2950a981..4c1b8186 100644 --- a/ng/Togl2.1/README.stubs +++ b/ng/Togl2.1/README.stubs @@ -4,7 +4,7 @@ interface, witch means that the same binary works with any stubs-aware wish (i.e. version >= 8.1) It has been tested on Windows NT/2000 and Linux for several Tcl/Tk versions up -to 8.4a3. I haven't been able to test the Mac port, it propably needs mending +to 8.4a3. I haven't been able to test the Mac port, it probably needs mending but I can't see why it shouldn't work in principle. Implementation wise, what differs from Togl 1.5 is that Togl_MakeWindowExist() @@ -16,6 +16,6 @@ same binary runs on all versions of Wish from 8.1 and up. For this to work you need to compile against the headers from Tcl/Tk 8.4a3 or later, or the binary will only work for Tcl/Tk 8.1-8.4a2. The tk8.4a3 public headers (tk8.4a3.h + tkDecls.h) are included for -conveniance, and they are used if the flag -DUSE_LOCAL_TK_H is specified. +convenience, and they are used if the flag -DUSE_LOCAL_TK_H is specified. Jonas Beskow, December 2001 \ No newline at end of file diff --git a/ng/Togl2.1/aclocal.m4 b/ng/Togl2.1/aclocal.m4 index 3a839212..6fdd332b 100644 --- a/ng/Togl2.1/aclocal.m4 +++ b/ng/Togl2.1/aclocal.m4 @@ -48,7 +48,7 @@ AC_DEFUN(TOGL_ENABLE_STUBS, [ #------------------------------------------------------------------------ # TOGL_UNDEF_GET_PROC_ADDRESS -- # -# Does defining GLX_GLXEXT_LEGACY interfer with including GL/glxext.h? +# Does defining GLX_GLXEXT_LEGACY interfere with including GL/glxext.h? # # Arguments: # none @@ -59,7 +59,7 @@ AC_DEFUN(TOGL_ENABLE_STUBS, [ # #------------------------------------------------------------------------ AC_DEFUN(TOGL_UNDEF_GET_PROC_ADDRESS, [ - AC_MSG_CHECKING([if GLX_GLXEXT_LEGACY interfers with including GL/glxext.h]) + AC_MSG_CHECKING([if GLX_GLXEXT_LEGACY interferes with including GL/glxext.h]) AC_LANG_PUSH(C) ac_save_CFLAGS=$CFLAGS CFLAGS=$TK_XINCLUDES diff --git a/ng/Togl2.1/configure b/ng/Togl2.1/configure index e11028e9..08b05afd 100755 --- a/ng/Togl2.1/configure +++ b/ng/Togl2.1/configure @@ -6028,8 +6028,8 @@ fi LIBGLU=-lGLU - { $as_echo "$as_me:${as_lineno-$LINENO}: checking if GLX_GLXEXT_LEGACY interfers with including GL/glxext.h" >&5 -$as_echo_n "checking if GLX_GLXEXT_LEGACY interfers with including GL/glxext.h... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if GLX_GLXEXT_LEGACY interferes with including GL/glxext.h" >&5 +$as_echo_n "checking if GLX_GLXEXT_LEGACY interferes with including GL/glxext.h... " >&6; } ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' diff --git a/ng/Togl2.1/doc/stereo.html b/ng/Togl2.1/doc/stereo.html index 97d02187..09410ef2 100644 --- a/ng/Togl2.1/doc/stereo.html +++ b/ng/Togl2.1/doc/stereo.html @@ -55,7 +55,7 @@ should be replaced with the Togl Tcl or C versions - for seemless stereo rendering. + for seamless stereo rendering.

The various stereo modes are:

diff --git a/ng/Togl2.1/doc/tclapi.html b/ng/Togl2.1/doc/tclapi.html index 6d6c9a97..3d510749 100644 --- a/ng/Togl2.1/doc/tclapi.html +++ b/ng/Togl2.1/doc/tclapi.html @@ -136,7 +136,7 @@ if {[lsearch [pathName extensions] GL_EXT_bgra] != -1]} {
pathName swapbuffers
Causes front/back buffers to be swapped if in double buffer mode. - And flushs the OpenGL command buffer if in single buffer mode. + And flushes the OpenGL command buffer if in single buffer mode. (So this is appropriate to call after every frame is drawn.)
diff --git a/ng/Togl2.1/doc/using.html b/ng/Togl2.1/doc/using.html index e72eaf38..c16e871c 100644 --- a/ng/Togl2.1/doc/using.html +++ b/ng/Togl2.1/doc/using.html @@ -95,7 +95,7 @@ in the Togl source directory. The C packages are compiled into shared libraries that are loaded into the Tcl interpreter as Tcl/Tk-extensions. - The examples are started by running the corrsponding Tcl script: + The examples are started by running the corresponding Tcl script: just type ./double.tcl (or ./texture.tcl etc.) or run under one of the Tcl interpreters, i.e., diff --git a/ng/Togl2.1/togl.c b/ng/Togl2.1/togl.c index ee1f16e5..35ab8356 100644 --- a/ng/Togl2.1/togl.c +++ b/ng/Togl2.1/togl.c @@ -14,6 +14,15 @@ * Currently we support X11, Win32 and Mac OS X only */ +#ifndef MODULE_SCOPE +# ifdef __cplusplus +# define MODULE_SCOPE extern "C" +# else +# define MODULE_SCOPE extern +# endif +#endif + + #define USE_TOGL_STUB_PROCS #include "togl.h" #include // don't need it on osx ??? @@ -5009,7 +5018,7 @@ Togl_Frustum(const Togl *togl, GLdouble left, GLdouble right, eyeOffset = togl->EyeSeparation / 2; /* for right eye */ eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence); - /* compenstate for altered viewports */ + /* compensate for altered viewports */ switch (togl->Stereo) { default: break; @@ -5044,7 +5053,7 @@ Togl_Ortho(const Togl *togl, GLdouble left, GLdouble right, eyeOffset = togl->EyeSeparation / 2; /* for right eye */ eyeShift = (togl->Convergence - zNear) * (eyeOffset / togl->Convergence); - /* compenstate for altered viewports */ + /* compensate for altered viewports */ switch (togl->Stereo) { default: break; @@ -5103,7 +5112,7 @@ static int ObjectIsEmpty(Tcl_Obj *objPtr); * * GetStereo - * - * Converts an internal int into a a Tcl string obj. + * Converts an internal int into a Tcl string obj. * * Results: * Tcl_Obj containing the string representation of the stereo value. @@ -5273,7 +5282,7 @@ RestoreStereo(ClientData clientData, Tk_Window tkwin, char *internalPtr, * * GetWideInt - * - * Converts an internal wide integer into a a Tcl WideInt obj. + * Converts an internal wide integer into a Tcl WideInt obj. * * Results: * Tcl_Obj containing the wide int value. diff --git a/ng/Togl2.1/togl.h b/ng/Togl2.1/togl.h index ba71cb35..618784bf 100644 --- a/ng/Togl2.1/togl.h +++ b/ng/Togl2.1/togl.h @@ -48,6 +48,7 @@ # if defined(TOGL_AGL) # include # elif defined(TOGL_NSOPENGL) +#define GL_SILENCE_DEPRECATION # include # include # else diff --git a/ng/csgeom.tcl b/ng/csgeom.tcl index d3b1c7d3..bc596d7d 100644 --- a/ng/csgeom.tcl +++ b/ng/csgeom.tcl @@ -222,7 +222,7 @@ proc editprimitivedialog2 { name } { # # -# Select primitve to edit +# Select primitive to edit # # diff --git a/ng/demoview.cpp b/ng/demoview.cpp index 81fb329f..996082ce 100644 --- a/ng/demoview.cpp +++ b/ng/demoview.cpp @@ -20,7 +20,6 @@ #include namespace netgen { - extern VisualScene *vs; #include "demoview.hpp" @@ -422,7 +421,7 @@ namespace netgen { */ - vs -> LookAt ( Point<3>( campos.Evaluate (time)), + visual_scene -> LookAt ( Point<3>( campos.Evaluate (time)), Point<3>(campoint.Evaluate (time)), Point<3>( camup.Evaluate (time)) ); diff --git a/ng/demoview.hpp b/ng/demoview.hpp index 91ae8fe3..907d3f2a 100644 --- a/ng/demoview.hpp +++ b/ng/demoview.hpp @@ -98,7 +98,7 @@ template class InterpolationSpline { protected: - // Array < InterpolationPoint[3] > ip; + // NgArray < InterpolationPoint[3] > ip; class intpts { @@ -106,7 +106,7 @@ protected: InterpolationPoint pts[3]; InterpolationPoint & operator[](int i) { return pts[i]; } }; - Array < intpts > ip; + NgArray < intpts > ip; int finished; diff --git a/ng/dialog.tcl b/ng/dialog.tcl index 8faac639..3d118574 100644 --- a/ng/dialog.tcl +++ b/ng/dialog.tcl @@ -183,7 +183,7 @@ proc meshingoptionsdialog { } { ttk::labelframe $f.bts -borderwidth 3 -relief groove -text "Additional meshing options" pack $f.bts -fill x -pady 15 ttk::frame $f.bts.btnframe - ttk::checkbutton $f.bts.btnframe.parthread -text "Parallel meshing thread" \ + ttk::checkbutton $f.bts.btnframe.parthread -text "Separate meshing thread" \ -variable options.parthread ttk::checkbutton $f.bts.btnframe.second -text "Second order elements" \ -variable options.secondorder @@ -214,9 +214,21 @@ proc meshingoptionsdialog { } { #ttk::frame $f.bts.sbox #pack $f.bts.sbox -anchor w -pady 10 - ttk::label $f.bts.btnframe.l -text "Element order" - ttk::spinbox $f.bts.btnframe.elementorder2 -from 1 -to 20 -textvariable options.elementorder -width 2 - pack $f.bts.btnframe.elementorder2 $f.bts.btnframe.l -anchor w -side left + ttk::frame $f.bts.btnframe.elorder + ttk::label $f.bts.btnframe.elorder.l -text "Element order" + ttk::spinbox $f.bts.btnframe.elorder.elementorder2 -from 1 -to 20 -textvariable options.elementorder -width 2 + pack $f.bts.btnframe.elorder -fill x + pack $f.bts.btnframe.elorder.elementorder2 $f.bts.btnframe.elorder.l -anchor w -side left + + ttk::frame $f.bts.btnframe.pm + ttk::checkbutton $f.bts.btnframe.pm.parallel_meshing -text "Parallel meshing" \ + -variable options.parallel_meshing + pack $f.bts.btnframe.pm -fill x -pady 5 + pack $f.bts.btnframe.pm.parallel_meshing -anchor w + + ttk::label $f.bts.btnframe.pm.lnthreads -text "Number of meshing threads" + ttk::spinbox $f.bts.btnframe.pm.nthreads -from 1 -to 128 -textvariable options.nthreads -width 2 + pack $f.bts.btnframe.pm.nthreads $f.bts.btnframe.pm.lnthreads -anchor w -side left @@ -664,7 +676,7 @@ proc meshingoptionsdialog { } { ttk::checkbutton $f.cb1.slowchecks -text "Slow checks" \ -variable debug.slowchecks -command { Ng_SetDebugParameters } - ttk::checkbutton $f.cb1.debugoutput -text "Debugging outout" \ + ttk::checkbutton $f.cb1.debugoutput -text "Debugging output" \ -variable debug.debugoutput -command { Ng_SetDebugParameters } ttk::checkbutton $f.cb1.haltexline -text "Halt on existing line" \ -variable debug.haltexistingline -command { Ng_SetDebugParameters } @@ -677,23 +689,23 @@ proc meshingoptionsdialog { } { ttk::checkbutton $f.cb1.haltlargequal -text "Halt on large quality class" \ -variable debug.haltlargequalclass -command { Ng_SetDebugParameters } ttk::checkbutton $f.cb1.haltseg -text "Halt on Segment:" \ - -variable debug.haltsegment -command "enable_cb %W $f.cb1.segs.ent1 $f.cb1.segs.ent2" + -variable debug.haltsegment -command { Ng_SetDebugParameters } ttk::checkbutton $f.cb1.haltnode -text "Halt on Node:" \ - -variable debug.haltnode -command "enable_cb %W $f.cb1.segs.ent1 $f.cb1.segs.ent2" + -variable debug.haltnode -command { Ng_SetDebugParameters } ttk::frame $f.cb1.fr ttk::checkbutton $f.cb1.fr.cb -text "Halt on Face:" \ - -variable debug.haltface -command "enable_cb %W $f.cb1.fr.ent $f.cb1.fr.ent" - ttk::entry $f.cb1.fr.ent -textvariable debug.haltfacenr -width 3 -state disabled + -variable debug.haltface -command { Ng_SetDebugParameters } + ttk::entry $f.cb1.fr.ent -textvariable debug.haltfacenr -width 3 pack $f.cb1.fr.cb $f.cb1.fr.ent -side left ttk::frame $f.cb1.segs ttk::label $f.cb1.segs.lab1 -text "P1:" ttk::entry $f.cb1.segs.ent1 -width 6 \ - -textvariable debug.haltsegmentp1 -state disabled + -textvariable debug.haltsegmentp1 ttk::label $f.cb1.segs.lab2 -text "P2:" ttk::entry $f.cb1.segs.ent2 -width 6 \ - -textvariable debug.haltsegmentp2 -state disabled + -textvariable debug.haltsegmentp2 pack $f.cb1.segs.lab1 $f.cb1.segs.ent1 $f.cb1.segs.lab2 $f.cb1.segs.ent2 -side left @@ -944,8 +956,14 @@ proc viewingoptionsdialog { } { -textvariable stloptions.chartnumberoffset -validate focus -takefocus 0 \ -validatecommand "my_validate %W 0 1e9 %P 0" \ -invalidcommand "my_invalid %W" + + ttk::button $f.fn.btn_write_chart -text "Write selected chart to chart.stlb" -command { + Ng_STLDoctor writechart + } + grid $f.fn.lab -sticky ne -padx 4 grid $f.fn.ent -sticky nw -padx 4 -row 1 -column 1 + grid $f.fn.btn_write_chart -padx 4 -row 2 -column 1 grid anchor $f.fn center ttk::labelframe $f.advstl -text "Advanced STL options" -relief groove -borderwidth 3 @@ -1418,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 @@ -2228,7 +2246,7 @@ proc stloptionsdialog { } { } proc stldoctordialog { } { - Ng_STLDoctor 0 0 + Ng_STLDoctor set wd .stldoctor_dlg if {[winfo exists .stldoctor_dlg] == 1} { @@ -2642,17 +2660,17 @@ proc stldoctordialog { } { #tixControl $f.gtol -label "load-geometry tolerance factor" -integer false \ - -variable stldoctor.geom_tol_fact \ - -options { + # -variable stldoctor.geom_tol_fact \ + # -options { # entry.width 8 # label.width 30 # label.anchor e #} - ttk::spinbox $f.gtol -from 1 -to 20 -textvariable stldoctor.geom_tol_fact -width 8 - pack $f.gtol + ttk::label $f.gtol_lbl -text "LoadSTL tolerance factor" + ttk::spinbox $f.gtol -from 1e-15 -to 0.001 -textvariable stldoctor.geom_tol_fact -width 8 + pack $f.gtol_lbl $f.gtol ttk::button $f.adap -text "Apply" -command { - .stldoctor_dlg.nb.advanced.gtol invoke Ng_STLDoctor; } pack $f.adap -expand yes diff --git a/ng/drawing.tcl b/ng/drawing.tcl index aebcdab0..3bc7c652 100644 --- a/ng/drawing.tcl +++ b/ng/drawing.tcl @@ -40,7 +40,7 @@ if { [Ng_GetToglVersion] == 2 } { if { $toglok == 1} { # - pack .ndraw -expand true -fill both -padx 10 -pady 10 + pack .ndraw -expand true -fill both -padx 0 -pady 0 catch { tkdnd::drop_target register .ndraw DND_Files } # bind .ndraw { @@ -81,6 +81,18 @@ if { $toglok == 1} { .ndraw render set oldmousex %x; set oldmousey %y; } + + bind .ndraw { + Ng_MouseMove $oldmousex $oldmousey %x %y Move2d + .ndraw render + set oldmousex %x; set oldmousey %y; + } + + bind .ndraw { + Ng_MouseMove $oldmousex $oldmousey %x %y Zoom2d + .ndraw render + set oldmousex %x; set oldmousey %y; + } } diff --git a/ng/drawing_togl17.tcl b/ng/drawing_togl17.tcl index be52dfab..36dfe6d2 100644 --- a/ng/drawing_togl17.tcl +++ b/ng/drawing_togl17.tcl @@ -54,6 +54,18 @@ if {[catch {togl .ndraw -width 400 -height 300 -rgba true -double true -depth t .ndraw render set oldmousex %x; set oldmousey %y; } + + bind .ndraw { + Ng_MouseMove $oldmousex $oldmousey %x %y Move2d + .ndraw render + set oldmousex %x; set oldmousey %y; + } + + bind .ndraw { + Ng_MouseMove $oldmousex $oldmousey %x %y Zoom2d + .ndraw render + set oldmousex %x; set oldmousey %y; + } } diff --git a/ng/encoding.hpp b/ng/encoding.hpp index 4f2b5fd2..9ea2ffb5 100644 --- a/ng/encoding.hpp +++ b/ng/encoding.hpp @@ -5,6 +5,7 @@ extern "C" { #include +#include #include #include #include @@ -131,17 +132,17 @@ class Mpeg { int ret; int i; - av_register_all(); + // av_register_all(); avformat_alloc_output_context2(&oc, NULL, NULL, filename.c_str()); // oc->preload= (int)(0.5*AV_TIME_BASE); oc->max_delay= (int)(0.7*AV_TIME_BASE); - fmt = oc->oformat; + fmt = (AVOutputFormat*) oc->oformat; if (fmt->video_codec != AV_CODEC_ID_NONE) { /* find the encoder */ - video_codec = avcodec_find_encoder(fmt->video_codec); + video_codec = (AVCodec*) avcodec_find_encoder(fmt->video_codec); if (!(video_codec)) { cerr << "Could not find encoder for '" << avcodec_get_name(fmt->video_codec) << "'" << endl; return 1; @@ -235,6 +236,7 @@ class Mpeg { sws_ctx = sws_getContext( width, height, AV_PIX_FMT_RGB24, width, height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL ); + return 0; } void Stop() { diff --git a/ng/fonts.hpp b/ng/fonts.hpp index 9443ae01..c4d47a2e 100644 --- a/ng/fonts.hpp +++ b/ng/fonts.hpp @@ -2312,6 +2312,8 @@ namespace netgen { return list_base; } + int Width() { return w; }; + int Height() { return h; }; }; // create Fonts statically and return pointer on selecting diff --git a/ng/gui.cpp b/ng/gui.cpp index 680a3417..88927aad 100644 --- a/ng/gui.cpp +++ b/ng/gui.cpp @@ -1,30 +1,24 @@ #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) { + Tcl_InitStubs( interp, TCL_VERSION, 0 ); + Tk_InitStubs( interp, TK_VERSION, 0 ); if (Ng_Init(interp) == TCL_ERROR) { cerr << "Problem in Ng_Init: " << endl; cout << "result = " << Tcl_GetStringResult (interp) << endl; diff --git a/ng/menustat.tcl b/ng/menustat.tcl index 79ae9f99..5be804c2 100644 --- a/ng/menustat.tcl +++ b/ng/menustat.tcl @@ -190,7 +190,7 @@ proc AddRecentMeshFile { filename } { } loadmeshinifile; -# astrid ende +# astrid end .ngmenu.file add command -label "Save Mesh..." -accelerator "" \ @@ -231,7 +231,9 @@ loadmeshinifile; {"Universal format" {.unv} } {"Olaf format" {.emt} } {"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 != ""} { @@ -244,6 +246,8 @@ loadmeshinifile; } +set meshexportformats [Ng_GetExportFormats] + .ngmenu.file add command -label "Export Mesh..." \ -command { @@ -261,8 +265,11 @@ loadmeshinifile; } elseif { $exportfiletype == "OpenFOAM 1.5+ Compressed"} { set file [file nativename [tk_chooseDirectory -title "OpenFOAM 1.5+ Mesh Export - Select Case Directory"]] } else { -# set file [tk_getSaveFile -filetypes "{ \"$exportfiletype\" {$extension} }" ] - set file [tk_getSaveFile -filetypes "{ \"$exportfiletype\" {*}}" ] + # set file [tk_getSaveFile -filetypes "{ \"$exportfiletype\" {$extension} }" ] + # set file [tk_getSaveFile -filetypes "{ \"$exportfiletype\" {*}}" ] + set file [tk_getSaveFile -filetypes $meshexportformats -typevariable exportfiletype] + puts "type = $exportfiletype" + puts "filename = $file" } if {$file != ""} { @@ -271,9 +278,12 @@ loadmeshinifile; } .ngmenu.file add cascade -label "Export Filetype" -menu .ngmenu.file.filetype - menu .ngmenu.file.filetype +foreach exportformat $meshexportformats { + .ngmenu.file.filetype add radio -label [lindex $exportformat 0] -variable exportfiletype -command { .ngmenu.file invoke "Export Mesh..." } +} + .ngmenu.file add separator @@ -1122,7 +1132,7 @@ proc timer2 { } { } } - after 10 { timer2 } + after 40 { timer2 } } # after 1000 { timer2 } timer2 diff --git a/ng/netgenpy.cpp b/ng/netgenpy.cpp index a4e7a8d1..1a1d1524 100644 --- a/ng/netgenpy.cpp +++ b/ng/netgenpy.cpp @@ -2,27 +2,19 @@ #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 ExportCSG(py::module &m); +void NGCORE_API_IMPORT ExportGeom2d(py::module &m); +void NGCORE_API_IMPORT ExportSTL(py::module &m); #ifdef OCCGEOMETRY -void DLL_HEADER ExportNgOCC(py::module &m); +void NGCORE_API_IMPORT ExportNgOCC(py::module &m); #endif // OCCGEOMETRY PYBIND11_MODULE(libngpy, ngpy) { + py::module::import("pyngcore"); py::module meshing = ngpy.def_submodule("_meshing", "pybind meshing module"); ExportNetgenMeshing(meshing); py::module csg = ngpy.def_submodule("_csg", "pybind csg module"); @@ -35,22 +27,4 @@ PYBIND11_MODULE(libngpy, ngpy) py::module NgOCC = ngpy.def_submodule("_NgOCC", "pybind NgOCC module"); ExportNgOCC(NgOCC); #endif // OCCGEOMETRY -#ifdef OPENGL - py::module meshvis = ngpy.def_submodule("meshvis", "pybind meshvis module"); - ExportMeshVis(meshvis); - py::module csgvis = ngpy.def_submodule("csgvis", "pybind csgvis module"); - ExportCSGVis(csgvis); - py::module stlvis = ngpy.def_submodule("stlvis", "pybind stlvis module"); - ExportSTLVis(stlvis); -#endif // OPENGL -} - -// Force linking libnglib to libnetgenpy -namespace netgen -{ - void MyBeep (int i); - void MyDummyToForceLinkingNGLib() - { - MyBeep(0); - } } diff --git a/ng/ng.tcl b/ng/ng.tcl index 7343eae0..73b8eb6d 100644 --- a/ng/ng.tcl +++ b/ng/ng.tcl @@ -2,7 +2,7 @@ catch {lappend auto_path $env(NETGENDIR) } catch {lappend auto_path $env(NETGENDIR)/../lib } if {[catch {Ng_GetCommandLineParameter batchmode} result ]} { - load libgui[info sharedlibextension] gui + load libnggui[info sharedlibextension] gui } set batchmode [Ng_GetCommandLineParameter batchmode] diff --git a/ng/ngappinit.cpp b/ng/ngappinit.cpp index 43ff1afd..5fa8c248 100644 --- a/ng/ngappinit.cpp +++ b/ng/ngappinit.cpp @@ -3,6 +3,9 @@ This file is a modification of tkAppInit.c from the Tcl/Tk package */ +#undef USE_TCL_STUBS +#undef USE_TK_STUBS + #include #include #include @@ -10,14 +13,9 @@ #ifdef PARALLEL #include -extern void ParallelRun(); -namespace netgen -{ - MPI_Comm mesh_comm; -} +// extern void ParallelRun(); #endif - #include "../libsrc/interface/writeuser.hpp" namespace netgen @@ -32,7 +30,7 @@ DLL_HEADER extern bool nodisplay; using netgen::parameters; using netgen::ngdir; using netgen::verbose; -using netgen::Array; +using netgen::NgArray; using netgen::RegisterUserFormats; @@ -83,7 +81,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 @@ -146,8 +144,11 @@ int main(int argc, char ** argv) if ( netgen::id == 0 ) { - if (parameters.StringFlagDefined ("testout")) - netgen::testout = new ofstream (parameters.GetStringFlag ("testout", "test.out")); + if (parameters.StringFlagDefined ("testout")) + { + delete ngcore::testout; + ngcore::testout = new ofstream (parameters.GetStringFlag ("testout", "test.out")); + } #ifdef SOCKETS @@ -204,7 +205,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) @@ -247,10 +248,10 @@ int main(int argc, char ** argv) exit (1); } - + /* // lookup user file formats and insert into format list: - Array userformats; - Array extensions; + NgArray userformats; + NgArray extensions; RegisterUserFormats (userformats, extensions); ostringstream fstr; @@ -259,13 +260,13 @@ int main(int argc, char ** argv) for (int i = 1; i <= userformats.Size(); i++) { fstr << ".ngmenu.file.filetype add radio -label \"" - << userformats.Get(i) << "\" -variable exportfiletype\n"; + << userformats.Get(i) << "\" -variable exportfiletype -command { .ngmenu.file invoke \"Export Mesh...\" } \n"; fstr << "lappend meshexportformats { {" << userformats.Get(i) << "} {" << extensions.Get(i) << "} }\n"; } - Tcl_Eval (myinterp, (char*)fstr.str().c_str()); + Tcl_Eval (myinterp, (char*)fstr.str().c_str()); Tcl_SetVar (myinterp, "exportfiletype", exportft, 0); - + */ #ifdef SOCKETS Ng_ServerSocketManagerRun(); @@ -280,7 +281,7 @@ int main(int argc, char ** argv) #ifdef PARALLEL else { - ParallelRun(); + // ParallelRun(); MPI_Finalize(); } diff --git a/ng/ngguipy.cpp b/ng/ngguipy.cpp new file mode 100644 index 00000000..3bcd11b3 --- /dev/null +++ b/ng/ngguipy.cpp @@ -0,0 +1,23 @@ +#include +#include <../general/ngpython.hpp> +#include + +void NGCORE_API_IMPORT ExportMeshVis(py::module &m); +void NGCORE_API_IMPORT ExportCSGVis(py::module &m); +void NGCORE_API_IMPORT ExportSTLVis(py::module &m); +namespace netgen +{ + std::vector NGCORE_API_IMPORT Snapshot( int w, int h ); +} + +PYBIND11_MODULE(libngguipy, ngpy) +{ + py::module::import("pyngcore"); + py::module meshvis = ngpy.def_submodule("meshvis", "pybind meshvis module"); + ExportMeshVis(meshvis); + py::module csgvis = ngpy.def_submodule("csgvis", "pybind csgvis module"); + ExportCSGVis(csgvis); + py::module stlvis = ngpy.def_submodule("stlvis", "pybind stlvis module"); + ExportSTLVis(stlvis); + ngpy.def("Snapshot", netgen::Snapshot); +} diff --git a/ng/ngpkg.cpp b/ng/ngpkg.cpp index af9c1ea6..12bdeaef 100644 --- a/ng/ngpkg.cpp +++ b/ng/ngpkg.cpp @@ -22,6 +22,8 @@ The interface between the GUI and the netgen library #include "../libsrc/sockets/socketmanager.hpp" #endif +#include "../libsrc/general/gzstream.h" + // to be sure to include the 'right' togl-version #include "Togl2.1/togl.h" @@ -94,8 +96,8 @@ namespace netgen #ifdef SOCKETS AutoPtr clientsocket; ServerSocketManager serversocketmanager; - //Array< AutoPtr < ServerInfo > > servers; - Array< ServerInfo* > servers; + //NgArray< AutoPtr < ServerInfo > > servers; + NgArray< ServerInfo* > servers; AutoPtr serversocketusernetgen; #endif @@ -103,7 +105,6 @@ namespace netgen // visualization scenes, pointer vs selects which one is drawn: - static VisualScene vscross; DLL_HEADER extern VisualSceneSurfaceMeshing vssurfacemeshing; DLL_HEADER extern VisualSceneMeshDoctor vsmeshdoc; @@ -111,7 +112,8 @@ namespace netgen - VisualScene *vs = &vscross; + DLL_HEADER extern VisualScene *visual_scene; + DLL_HEADER extern VisualScene visual_scene_cross; @@ -122,39 +124,6 @@ namespace netgen -#ifndef SMALLLIB -// // Destination for messages, errors, ... -#ifndef WIN32 - DLL_HEADER void Ng_PrintDest(const char * s) - { - /* -#ifdef PARALLEL - int id, ntasks; - MPI_Comm_size(MPI_COMM_WORLD, &ntasks); - MPI_Comm_rank(MPI_COMM_WORLD, &id); -#else - int id = 0; int ntasks = 1; -#endif - */ - - if (id == 0) - (*mycout) << s << flush; - - /* - if ( ntasks == 1 ) - (*mycout) << s << flush; - else - (*mycout) << "p" << id << ": " << s << flush ; - */ - } -#endif - void MyError2(const char * ch) - { - cout << ch; - (*testout) << "Error !!! " << ch << endl << flush; - } -#endif - static clock_t starttimea; void ResetTime2 () { @@ -203,9 +172,9 @@ namespace netgen Tcl_Interp * interp, int argc, tcl_const char *argv[]) { - string filename (argv[1]); + auto filename = filesystem::u8path(argv[1]); - if (filename.find(".vol") == string::npos) + if (filename.string().find(".vol") == string::npos) { return Ng_ImportMesh(clientData,interp,argc,argv); } @@ -215,48 +184,15 @@ namespace netgen mesh = make_shared(); try { - istream * infile; - // if (filename.substr (filename.length()-3, 3) == ".gz") - if (filename.find(".vol.gz") != string::npos) - infile = new igzstream (filename.c_str()); - else - infile = new ifstream (filename.c_str()); - - // ifstream infile(filename.c_str()); - mesh -> Load(*infile); - // vsmesh.SetMesh (mesh); + mesh -> Load(filename); SetGlobalMesh (mesh); -#ifdef PARALLEL +#ifdef PARALLEL_NETGEN MyMPI_SendCmd ("mesh"); mesh -> Distribute(); #endif - for (int i = 0; i < geometryregister.Size(); i++) - { - NetgenGeometry * hgeom = geometryregister[i]->LoadFromMeshFile (*infile); - if (hgeom) - { - ng_geometry = shared_ptr(hgeom); - break; - } - } - delete infile; - - /* - string auxstring; - if(infile.good()) - { - infile >> auxstring; - if(auxstring == "csgsurfaces") - { - CSGeometry * geometry = new CSGeometry (""); - geometry -> LoadSurfaces(infile); - - delete ng_geometry; - ng_geometry = geometry; - } - } - */ + if(mesh->GetGeometry()) + ng_geometry = mesh->GetGeometry(); } catch (NgException e) { @@ -346,7 +282,21 @@ namespace netgen } - + int Ng_GetExportFormats (ClientData clientData, + Tcl_Interp * interp, + int argc, tcl_const char *argv[]) + { + NgArray userformats; + NgArray extensions; + RegisterUserFormats (userformats, extensions); + + ostringstream fstr; + for (int i = 1; i <= userformats.Size(); i++) + fstr << "{ {" << userformats.Get(i) << "} {" << extensions.Get(i) << "} }\n"; + + Tcl_SetResult (interp, const_cast(fstr.str().c_str()), TCL_VOLATILE); + return TCL_OK; + } int Ng_ExportMesh (ClientData clientData, @@ -391,6 +341,7 @@ namespace netgen PrintMessage (2, mesh->GetNP(), " Points, ", mesh->GetNE(), " Elements."); + SetGlobalMesh (mesh); mesh->SetGlobalH (mparam.maxh); mesh->CalcLocalH(mparam.grading); @@ -522,6 +473,7 @@ namespace netgen // delete ng_geometry; // ng_geometry = hgeom; ng_geometry = shared_ptr (hgeom); + geometryregister[i]->SetParameters(interp); mesh.reset(); return TCL_OK; @@ -664,40 +616,96 @@ namespace netgen int argc, tcl_const char *argv[]) { char buf[20], lstring[200]; + static int prev_np = -1; + static int prev_ne = -1; + static int prev_nse = -1; + if (mesh) { - sprintf (buf, "%d", mesh->GetNP()); - Tcl_SetVar (interp, "::status_np", buf, 0); - sprintf (buf, "%d", mesh->GetNE()); - Tcl_SetVar (interp, "::status_ne", buf, 0); - sprintf (buf, "%d", mesh->GetNSE()); - Tcl_SetVar (interp, "::status_nse", buf, 0); + if (prev_np != mesh->GetNP()) + { + snprintf (buf, size(buf), "%u", unsigned(mesh->GetNP())); + Tcl_SetVar (interp, "::status_np", buf, 0); + prev_np = mesh->GetNP(); + } + + if (prev_ne != mesh->GetNE()) + { + snprintf (buf, size(buf), "%u", unsigned(mesh->GetNE())); + Tcl_SetVar (interp, "::status_ne", buf, 0); + prev_ne = mesh->GetNE(); + } + + if (prev_nse != mesh->GetNSE()) + { + snprintf (buf, size(buf), "%u", unsigned(mesh->GetNSE())); + Tcl_SetVar (interp, "::status_nse", buf, 0); + prev_nse = mesh->GetNSE(); + } + + auto tets_in_qualclass = mesh->GetQualityHistogram(); + lstring[0] = 0; + for (int i = 0; i < tets_in_qualclass.Size(); i++) + { + snprintf (buf, size(buf), " %d", tets_in_qualclass[i]); + strcat (lstring, buf); + } + for (int i = tets_in_qualclass.Size(); i < 20; i++) + strcat (lstring, " 0"); + Tcl_SetVar (interp, "::status_tetqualclasses", lstring, 0); } else { - Tcl_SetVar (interp, "::status_np", "0", 0); - Tcl_SetVar (interp, "::status_ne", "0", 0); - Tcl_SetVar (interp, "::status_nse", "0", 0); + if (prev_np != 0) + { + Tcl_SetVar (interp, "::status_np", "0", 0); + prev_np = 0; + } + + if (prev_ne != 0) + { + Tcl_SetVar (interp, "::status_ne", "0", 0); + prev_ne = 0; + } + + if (prev_nse != 0) + { + Tcl_SetVar (interp, "::status_nse", "0", 0); + prev_nse = 0; + } + Tcl_SetVar (interp, "::status_tetqualclasses", "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", 0); } + static string prev_working; + string working = multithread.running ? "working" : " "; + if (working != prev_working) + { + Tcl_SetVar (interp, "::status_working", working.c_str(), 0); + prev_working = working; + } + + /* if (multithread.running) Tcl_SetVar (interp, "::status_working", "working", 0); else Tcl_SetVar (interp, "::status_working", " ", 0); - - Tcl_SetVar (interp, "::status_task", const_cast(multithread.task), 0); - sprintf (buf, "%lf", multithread.percent); - Tcl_SetVar (interp, "::status_percent", buf, 0); - - lstring[0] = 0; - for (int i = 1; i <= tets_in_qualclass.Size(); i++) + */ + + static string prev_task; + if (prev_task != string(multithread.task)) { - sprintf (buf, " %d", tets_in_qualclass.Get(i)); - strcat (lstring, buf); + prev_task = multithread.task; + Tcl_SetVar (interp, "::status_task", prev_task.c_str(), 0); } - for (int i = tets_in_qualclass.Size()+1; i <= 20; i++) - strcat (lstring, " 0"); - Tcl_SetVar (interp, "::status_tetqualclasses", lstring, 0); + + static double prev_percent = -1; + if (prev_percent != multithread.percent) + { + prev_percent = multithread.percent; + snprintf (buf, size(buf), "%lf", prev_percent); + Tcl_SetVar (interp, "::status_percent", buf, 0); + } + { lock_guard guard(tcl_todo_mutex); @@ -772,7 +780,7 @@ namespace netgen int facenr = atoi (argv[2]); if (mesh && facenr >= 1 && facenr <= mesh->GetNFD()) { - sprintf (buf, "%d", mesh->GetFaceDescriptor(facenr).BCProperty()); + snprintf (buf, size(buf), "%d", mesh->GetFaceDescriptor(facenr).BCProperty()); } else { @@ -786,7 +794,7 @@ namespace netgen int facenr = atoi (argv[2]); if (mesh && facenr >= 1 && facenr <= mesh->GetNFD()) { - sprintf (buf, "%s", mesh->GetFaceDescriptor(facenr).GetBCName().c_str()); + snprintf (buf, size(buf), "%s", mesh->GetFaceDescriptor(facenr).GetBCName().c_str()); } else { @@ -798,7 +806,7 @@ namespace netgen if (strcmp (argv[1], "getactive") == 0) { - sprintf (buf, "%d", vsmesh.SelectedFace()); + snprintf (buf, size(buf), "%d", vsmesh.SelectedFace()); Tcl_SetResult (interp, buf, TCL_STATIC); } @@ -814,9 +822,9 @@ namespace netgen if (strcmp (argv[1], "getnfd") == 0) { if (mesh) - sprintf (buf, "%d", mesh->GetNFD()); + snprintf (buf, size(buf), "%d", mesh->GetNFD()); else - sprintf (buf, "0"); + snprintf (buf, size(buf), "0"); Tcl_SetResult (interp, buf, TCL_STATIC); } @@ -1100,8 +1108,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; @@ -1117,9 +1125,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; } @@ -1204,11 +1217,18 @@ namespace netgen mparam.autozrefine = atoi (Tcl_GetVar (interp, "::options.autozrefine", 0)); - extern int printmessage_importance; + // extern int printmessage_importance; extern int printdots; printmessage_importance = atoi (Tcl_GetVar (interp, "::options.printmsg", 0)); printdots = (printmessage_importance >= 4); + mparam.parallel_meshing = atoi (Tcl_GetVar (interp, "::options.parallel_meshing", 0)); + mparam.nthreads = atoi (Tcl_GetVar (interp, "::options.nthreads", 0)); + if(atoi(Tcl_GetVar (interp, "::stloptions.resthcloseedgeenable", 0))) + mparam.closeedgefac = atof(Tcl_GetVar (interp, "::stloptions.resthcloseedgefac", 0)); + else + mparam.closeedgefac = {}; + //BaseMoveableMem::totalsize = 0; // 1048576 * atoi (Tcl_GetVar (interp, "::options.memory", 0)); if (mesh) @@ -1217,7 +1237,7 @@ namespace netgen mesh->SetMinimalH (mparam.minh); } -#ifdef PARALLEL +#ifdef PARALLELGL MyMPI_SendCmd ("bcastparthread"); MyMPI_Bcast (mparam.parthread, MPI_COMM_WORLD); #endif @@ -1249,6 +1269,28 @@ namespace netgen + int Ng_SetCommandLineParameter (ClientData clientData, + Tcl_Interp * interp, + int argc, tcl_const char *argv[]) + { + if (argc != 2) + { + Tcl_SetResult (interp, (char*)"Ng_SetCommandLineParameter needs 1 parameter", + TCL_STATIC); + return TCL_ERROR; + } + + if (argv[1][0] == '-') + parameters.SetCommandLineFlag (argv[1]); + else + { + if (strstr(argv[1], ".py")) + parameters.SetFlag ("py", argv[1]); + else + parameters.SetFlag ("geofile", argv[1]); + } + return TCL_OK; + } int Ng_GetCommandLineParameter (ClientData clientData, @@ -1265,11 +1307,11 @@ namespace netgen static char buf[10]; if (parameters.StringFlagDefined (argv[1])) - Tcl_SetResult (interp, - (char*)parameters.GetStringFlag (argv[1], NULL), TCL_STATIC); + Tcl_SetResult (interp, + const_cast(parameters.GetStringFlag (argv[1], NULL).c_str()), TCL_VOLATILE); else if (parameters.NumFlagDefined (argv[1])) { - sprintf (buf, "%lf", parameters.GetNumFlag (argv[1], 0)); + snprintf (buf, size(buf), "%lf", parameters.GetNumFlag (argv[1], 0)); Tcl_SetResult (interp, buf, TCL_STATIC); } else if (parameters.GetDefineFlag (argv[1])) @@ -1314,12 +1356,19 @@ namespace netgen #endif if (ng_geometry) { - mesh = make_shared (); - // vsmesh.SetMesh (mesh); - SetGlobalMesh (mesh); - mesh -> SetGeometry(ng_geometry); + if (perfstepsstart == 1) + { + mesh = make_shared (); + // vsmesh.SetMesh (mesh); + SetGlobalMesh (mesh); + mesh -> SetGeometry(ng_geometry); + } + if(!mesh) + throw Exception("Need existing global mesh"); mparam.perfstepsstart = perfstepsstart; mparam.perfstepsend = perfstepsend; + if(optstring) + mparam.optimize3d = *optstring; int res = ng_geometry -> GenerateMesh (mesh, mparam); if (res != MESHING3_OK) @@ -1329,6 +1378,14 @@ namespace netgen return 0; } } + else if (mesh) + { + if(perfstepsstart > 1 && perfstepsstart < 5) + throw Exception("Need geometry for surface mesh operations!"); + MeshVolume(mparam, *mesh); + OptimizeVolume(mparam, *mesh); + return 0; + } else // no ng_geometry { multithread.task = savetask; @@ -1529,13 +1586,13 @@ namespace netgen { angles[0] = angles[1] = angles[2] = angles[3] = 0; } - sprintf (buf, "%5.1lf", angles[0]); + snprintf (buf, size(buf), "%5.1lf", angles[0]); Tcl_SetVar (interp, argv[1], buf, 0); - sprintf (buf, "%5.1lf", angles[1]); + snprintf (buf, size(buf), "%5.1lf", angles[1]); Tcl_SetVar (interp, argv[2], buf, 0); - sprintf (buf, "%5.1lf", angles[2]); + snprintf (buf, size(buf), "%5.1lf", angles[2]); Tcl_SetVar (interp, argv[3], buf, 0); - sprintf (buf, "%5.1lf", angles[3]); + snprintf (buf, size(buf), "%5.1lf", angles[3]); Tcl_SetVar (interp, argv[4], buf, 0); return TCL_OK; @@ -1857,7 +1914,8 @@ namespace netgen { const char * vismode = vispar.selectvisual; // Tcl_GetVar (interp, "selectvisual", 0); - vs = &vscross; + VisualScene *& vs = visual_scene; + vs = &visual_scene_cross; if (GetVisualizationScenes().Used(vismode)) { vs = GetVisualizationScenes()[vismode]; @@ -1890,7 +1948,7 @@ namespace netgen vs = &vsmeshdoc; } - // if (strcmp (vismode, "surfmeshing") == 0) vs = &vssurfacemeshing; + if (strcmp (vismode, "surfmeshing") == 0) vs = &vssurfacemeshing; if (strcmp (vismode, "specpoints") == 0) vs = &vsspecpoints; if (strcmp (vismode, "solution") == 0) vs = &netgen::GetVSSolution(); } @@ -1933,8 +1991,8 @@ namespace netgen glMatrixMode(GL_MODELVIEW); SetVisualScene (Togl_Interp(togl)); - vs->DrawScene(); - Set_OpenGLText_Callback (&MyOpenGLText_GUI); + visual_scene->DrawScene(); + Set_OpenGLText_Callback (&MyOpenGLText_GUI, font->Width()); return TCL_OK; } @@ -1953,7 +2011,7 @@ namespace netgen glPushMatrix(); glLoadIdentity(); - vs->DrawScene(); + visual_scene->DrawScene(); Togl_SwapBuffers(togl); glPopMatrix(); @@ -2007,7 +2065,7 @@ namespace netgen int w = Togl_PixelScale(togl)*Togl_Width (togl); int h = Togl_PixelScale(togl)*Togl_Height (togl); - Array buffer(w*h*3); + NgArray buffer(w*h*3); glPixelStorei(GL_UNPACK_ALIGNMENT,1); glPixelStorei(GL_PACK_ALIGNMENT,1); glReadPixels (0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, &buffer[0]); @@ -2174,7 +2232,7 @@ namespace netgen newy = atoi (argv[4]); SetVisualScene(interp); - vs->MouseMove (oldx, oldy, newx, newy, argv[5][0]); + visual_scene->MouseMove (oldx, oldy, newx, newy, argv[5][0]); return TCL_OK; } @@ -2188,7 +2246,7 @@ namespace netgen int py = Togl_PixelScale(togl)*atoi (argv[2]); SetVisualScene(interp); - vs->MouseDblClick (px, py); + visual_scene->MouseDblClick (px, py); return TCL_OK; } @@ -2199,7 +2257,7 @@ namespace netgen int argc, tcl_const char *argv[]) { SetVisualScene(interp); - vs->BuildScene (1); + visual_scene->BuildScene (1); return TCL_OK; } @@ -2210,7 +2268,7 @@ namespace netgen int argc, tcl_const char *argv[]) { SetVisualScene(interp); - vs->BuildScene (2); + visual_scene->BuildScene (2); return TCL_OK; } @@ -2221,7 +2279,7 @@ namespace netgen int argc, tcl_const char *argv[]) { SetVisualScene(interp); - vs->StandardRotation (argv[1]); + visual_scene->StandardRotation (argv[1]); return TCL_OK; } @@ -2231,8 +2289,8 @@ namespace netgen int argc, tcl_const char *argv[]) { SetVisualScene(interp); - Array alpha; - Array vec; + NgArray alpha; + NgArray vec; for(int i=1; iArbitraryRotation (alpha,vec); + visual_scene->ArbitraryRotation (alpha,vec); return TCL_OK; } @@ -2261,7 +2319,7 @@ namespace netgen int nparts = atoi (argv[1]); ntasks = nparts+1; cout << "calling metis ... " << flush; - mesh->ParallelMetis(); + mesh->ParallelMetis(ntasks); cout << "done" << endl; ntasks = 1; @@ -2357,7 +2415,7 @@ namespace netgen void SelectFaceInOCCDialogTree (int facenr) { char script[50]; - sprintf (script, "selectentity {Face %i}", facenr); + snprintf (script, size(script), "selectentity {Face %i}", facenr); Tcl_GlobalEval (tcl_interp, script); } @@ -2390,7 +2448,7 @@ namespace netgen // #ifdef OPENGL // #ifndef NOTCL char buf[100]; - sprintf (buf, "visoptions.%s", name); + snprintf (buf, size(buf), "visoptions.%s", name); if (printmessage_importance>0) { cout << "name = " << name << ", value = " << value << endl; @@ -2446,7 +2504,7 @@ void PlayAnimFile(const char* name, int speed, int maxcnt) int rti = (animcnt%(maxcnt-1)) + 1; animcnt+=speed; - sprintf(str2,"%05i.sol",rti); + snprintf(str2, sizeof(str2), "%05i.sol",rti); strcpy(str,"mbssol/"); strcat(str,name); strcat(str,str2); @@ -2703,7 +2761,7 @@ void PlayAnimFile(const char* name, int speed, int maxcnt) //cout << "stopped acis, outcome = " << res.ok() << endl; #endif -#ifdef PARALLEL +#ifdef PARALLELGL if (id == 0) MyMPI_SendCmd ("end"); MPI_Finalize(); #endif @@ -2815,6 +2873,10 @@ void PlayAnimFile(const char* name, int speed, int maxcnt) (ClientData)NULL, (Tcl_CmdDeleteProc*) NULL); + Tcl_CreateCommand (interp, "Ng_GetExportFormats", Ng_GetExportFormats, + (ClientData)NULL, + (Tcl_CmdDeleteProc*) NULL); + Tcl_CreateCommand (interp, "Ng_ExportMesh", Ng_ExportMesh, (ClientData)NULL, (Tcl_CmdDeleteProc*) NULL); @@ -3014,6 +3076,11 @@ void PlayAnimFile(const char* name, int speed, int maxcnt) (ClientData)NULL, (Tcl_CmdDeleteProc*) NULL); + Tcl_CreateCommand (interp, "Ng_SetCommandLineParameter", + Ng_SetCommandLineParameter, + (ClientData)NULL, + (Tcl_CmdDeleteProc*) NULL); + Tcl_CreateCommand (interp, "Ng_GetCommandLineParameter", Ng_GetCommandLineParameter, (ClientData)NULL, diff --git a/ng/ngtcl.cpp b/ng/ngtcl.cpp new file mode 100644 index 00000000..a9dee430 --- /dev/null +++ b/ng/ngtcl.cpp @@ -0,0 +1,20 @@ +#include +#include +#include "../libsrc/meshing/visual_interface.hpp" + + +static void Impl_Ng_Tcl_SetResult(Tcl_Interp *interp, char *result, Tcl_FreeProc *freeProc) +{ + Tcl_SetResult(interp, result, freeProc); +} + +static void Impl_Ng_Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc) +{ + Tcl_CreateCommand(interp, cmdName, proc, nullptr, nullptr); +} + +static bool dummy_init_pointers = [](){ + netgen::Ptr_Ng_Tcl_SetResult = Impl_Ng_Tcl_SetResult; + netgen::Ptr_Ng_Tcl_CreateCommand = Impl_Ng_Tcl_CreateCommand; + return true; +}(); diff --git a/ng/ngvisual.tcl b/ng/ngvisual.tcl index f2dcc498..7da7eca6 100644 --- a/ng/ngvisual.tcl +++ b/ng/ngvisual.tcl @@ -1282,6 +1282,13 @@ proc visual_dialog { } { -validatecommand "Ng_Vis_Set parameters; redraw;my_validatespinbox %W %P 10" \ -command "Ng_Vis_Set parameters; redraw;" \ -invalidcommand "my_invalidspinbox %W;Ng_Vis_Set parameters; redraw" -from -1e10 -to 1e10 -increment 0.001 + + + ttk::label $w.as.lncols -text "N Colors" + ttk::spinbox $w.as.snumcols -textvariable visoptions.numtexturecols -width 5 -validate focus \ + -command "Ng_Vis_Set parameters; redraw;" \ + -from 2 -to 31 -increment 1 + #tixControl $w.as.minval -label "Min-value: " -integer false \ -variable visoptions.mminval \ @@ -1301,7 +1308,7 @@ proc visual_dialog { } { # } pack $w.as -fill x -pady 5 -ipady 3 - grid $w.as.autoscale $w.as.lmin $w.as.smin $w.as.lmax $w.as.smax -sticky nw -padx 4 + grid $w.as.autoscale $w.as.lmin $w.as.smin $w.as.lmax $w.as.smax $w.as.lncols $w.as.snumcols -sticky nw -padx 4 grid columnconfigure $w.as 0 -pad 20 grid columnconfigure $w.as 2 -pad 20 grid anchor $w.as center @@ -1473,7 +1480,7 @@ proc visual_dialog { } { global visoptions.scalfunction foreach { name textval } $scalentries { $f.m add command -label "$textval" -command \ - "$f.b configure -text \"$textval\" ; set visoptions.scalfunction $name ; Ng_Vis_Set parameters ; redraw ; " + "$f.b configure -text \"$textval\" ; set visoptions.scalfunction \"$name\" ; Ng_Vis_Set parameters ; redraw ; " } pack $f.b $f.l -side right @@ -1592,11 +1599,6 @@ proc visual_dialog { } { ttk::checkbutton $w.fcb.cb.lineartexture -text "Use Linear Texture" \ -variable visoptions.lineartexture \ -command { Ng_Vis_Set parameters; redraw } - - scale $w.numcols -orient horizontal -length 100 -from 0 -to 50 \ - -resolution 1 \ - -variable visoptions.numtexturecols \ - -command { popupcheckredraw visual_dialog_pop1 } ttk::checkbutton $w.fcb.cb.showclipsolution -text "Draw Clipping Plane Solution" \ -variable visoptions.showclipsolution \ diff --git a/ng/occgeom.tcl b/ng/occgeom.tcl index 9201cc64..667b84e7 100644 --- a/ng/occgeom.tcl +++ b/ng/occgeom.tcl @@ -101,7 +101,7 @@ proc occdialogbuildtree {} { set nrfaces [expr [llength $faces]] if {$nrfaces >= 2} { #$hlist add ErrorFaces -itemtype text -text "Faces with surface meshing error" - $w.tree insert {} -id ErrorFaces -text "Faces with surface meshing error" + $w.tree insert {} end -id "ErrorFaces" -text "Faces with surface meshing error" #$w.mtre open ErrorFaces $w.tree item ErrorFaces -open true set i [expr 0] @@ -109,12 +109,12 @@ proc occdialogbuildtree {} { set entity [lindex $faces [expr $i]] set myroot [string range $entity 0 [string last / $entity]-1] if { [string length $myroot] == 0 } { - set myroot ErrorFaces - } + set myroot "ErrorFaces" + } incr i 1 set entityname [lindex $faces [expr $i]] #$hlist add ErrorFaces/$entity -text $entityname -data $entityname - $w.tree insert {myroot} end -id $entity -text $entityname -value 0 + $w.tree insert $myroot end -id $entity -text $entityname -value 0 incr i 1 } } diff --git a/ng/onetcl.cpp b/ng/onetcl.cpp index 591af1a0..d25549e4 100644 --- a/ng/onetcl.cpp +++ b/ng/onetcl.cpp @@ -1,8 +1,9 @@ -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" -,"load libgui[info sharedlibextension] gui\n" +,"load libnggui[info sharedlibextension] gui\n" ,"}\n" ,"set batchmode [Ng_GetCommandLineParameter batchmode]\n" ,"if {$batchmode==\"undefined\"} {\n" @@ -84,6 +85,8 @@ const char * ngscript[] = {"" ,"set options.opterrpow 2\n" ,"set options.grading 0.5\n" ,"set options.printmsg 2\n" +,"set options.parallel_meshing 1\n" +,"set options.nthreads 4\n" ,"set debug.slowchecks 0\n" ,"set debug.debugoutput 0\n" ,"set debug.haltexistingline 0\n" @@ -276,7 +279,7 @@ const char * ngscript[] = {"" ,"set visoptions.invcolor 0\n" ,"set visoptions.imaginary 0\n" ,"set visoptions.lineartexture 0\n" -,"set visoptions.numtexturecols 16\n" +,"set visoptions.numtexturecols 8\n" ,"set visoptions.showclipsolution 1\n" ,"set visoptions.showsurfacesolution 0\n" ,"set visoptions.drawfieldlines 0\n" @@ -403,6 +406,8 @@ const char * ngscript[] = {"" ,"puts $datei \"options.opterrpow ${options.opterrpow}\"\n" ,"puts $datei \"options.grading ${options.grading}\"\n" ,"puts $datei \"options.printmsg ${options.printmsg}\"\n" +,"puts $datei \"options.parallel_meshing ${options.parallel_meshing}\"\n" +,"puts $datei \"options.nthreads ${options.nthreads}\"\n" ,"puts $datei \"geooptions.drawcsg ${geooptions.drawcsg}\"\n" ,"puts $datei \"geooptions.detail ${geooptions.detail}\"\n" ,"puts $datei \"geooptions.accuracy ${geooptions.accuracy}\"\n" @@ -861,7 +866,9 @@ const char * ngscript[] = {"" ,"{\"Universal format\" {.unv} }\n" ,"{\"Olaf format\" {.emt} }\n" ,"{\"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" @@ -872,6 +879,7 @@ const char * ngscript[] = {"" ,"Ng_ReadStatus;\n" ,"}\n" ,"}\n" +,"set meshexportformats [Ng_GetExportFormats]\n" ,".ngmenu.file add command -label \"Export Mesh...\" \\\n" ,"-command {\n" ,"foreach exportformat $meshexportformats {\n" @@ -886,7 +894,9 @@ const char * ngscript[] = {"" ,"} elseif { $exportfiletype == \"OpenFOAM 1.5+ Compressed\"} {\n" ,"set file [file nativename [tk_chooseDirectory -title \"OpenFOAM 1.5+ Mesh Export - Select Case Directory\"]]\n" ,"} else {\n" -,"set file [tk_getSaveFile -filetypes \"{ \\\"$exportfiletype\\\" {*}}\" ]\n" +,"set file [tk_getSaveFile -filetypes $meshexportformats -typevariable exportfiletype]\n" +,"puts \"type = $exportfiletype\"\n" +,"puts \"filename = $file\"\n" ,"}\n" ,"if {$file != \"\"} {\n" ,"Ng_ExportMesh $file $exportfiletype\n" @@ -894,6 +904,9 @@ const char * ngscript[] = {"" ,"}\n" ,".ngmenu.file add cascade -label \"Export Filetype\" -menu .ngmenu.file.filetype\n" ,"menu .ngmenu.file.filetype\n" +,"foreach exportformat $meshexportformats {\n" +,".ngmenu.file.filetype add radio -label [lindex $exportformat 0] -variable exportfiletype -command { .ngmenu.file invoke \"Export Mesh...\" }\n" +,"}\n" ,".ngmenu.file add separator\n" ,".ngmenu.file add command -label \"Save Solution...\" \\\n" ,"-command {\n" @@ -1412,7 +1425,7 @@ const char * ngscript[] = {"" ,"}\n" ,"}\n" ,"}\n" -,"after 10 { timer2 }\n" +,"after 40 { timer2 }\n" ,"}\n" ,"timer2\n" ,"proc bgerror { error } {\n" @@ -1533,7 +1546,7 @@ const char * ngscript[] = {"" ,"ttk::labelframe $f.bts -borderwidth 3 -relief groove -text \"Additional meshing options\"\n" ,"pack $f.bts -fill x -pady 15\n" ,"ttk::frame $f.bts.btnframe\n" -,"ttk::checkbutton $f.bts.btnframe.parthread -text \"Parallel meshing thread\" \\\n" +,"ttk::checkbutton $f.bts.btnframe.parthread -text \"Separate meshing thread\" \\\n" ,"-variable options.parthread\n" ,"ttk::checkbutton $f.bts.btnframe.second -text \"Second order elements\" \\\n" ,"-variable options.secondorder\n" @@ -1551,9 +1564,19 @@ const char * ngscript[] = {"" ,"-variable options.autozrefine\n" ,"pack $f.bts.btnframe -anchor center\n" ,"pack $f.bts.btnframe.parthread $f.bts.btnframe.second $f.bts.btnframe.quad $f.bts.btnframe.invtets $f.bts.btnframe.invtrigs $f.bts.btnframe.azref -anchor w\n" -,"ttk::label $f.bts.btnframe.l -text \"Element order\"\n" -,"ttk::spinbox $f.bts.btnframe.elementorder2 -from 1 -to 20 -textvariable options.elementorder -width 2\n" -,"pack $f.bts.btnframe.elementorder2 $f.bts.btnframe.l -anchor w -side left\n" +,"ttk::frame $f.bts.btnframe.elorder\n" +,"ttk::label $f.bts.btnframe.elorder.l -text \"Element order\"\n" +,"ttk::spinbox $f.bts.btnframe.elorder.elementorder2 -from 1 -to 20 -textvariable options.elementorder -width 2\n" +,"pack $f.bts.btnframe.elorder -fill x\n" +,"pack $f.bts.btnframe.elorder.elementorder2 $f.bts.btnframe.elorder.l -anchor w -side left\n" +,"ttk::frame $f.bts.btnframe.pm\n" +,"ttk::checkbutton $f.bts.btnframe.pm.parallel_meshing -text \"Parallel meshing\" \\\n" +,"-variable options.parallel_meshing\n" +,"pack $f.bts.btnframe.pm -fill x -pady 5\n" +,"pack $f.bts.btnframe.pm.parallel_meshing -anchor w\n" +,"ttk::label $f.bts.btnframe.pm.lnthreads -text \"Number of meshing threads\"\n" +,"ttk::spinbox $f.bts.btnframe.pm.nthreads -from 1 -to 128 -textvariable options.nthreads -width 2\n" +,"pack $f.bts.btnframe.pm.nthreads $f.bts.btnframe.pm.lnthreads -anchor w -side left\n" ,"set f $w.nb.meshsize\n" ,"ttk::frame $f.f2\n" ,"pack $f.f2 -pady 10\n" @@ -1829,7 +1852,7 @@ const char * ngscript[] = {"" ,"pack $f.cb1 -fill x -pady 15\n" ,"ttk::checkbutton $f.cb1.slowchecks -text \"Slow checks\" \\\n" ,"-variable debug.slowchecks -command { Ng_SetDebugParameters }\n" -,"ttk::checkbutton $f.cb1.debugoutput -text \"Debugging outout\" \\\n" +,"ttk::checkbutton $f.cb1.debugoutput -text \"Debugging output\" \\\n" ,"-variable debug.debugoutput -command { Ng_SetDebugParameters }\n" ,"ttk::checkbutton $f.cb1.haltexline -text \"Halt on existing line\" \\\n" ,"-variable debug.haltexistingline -command { Ng_SetDebugParameters }\n" @@ -1842,21 +1865,21 @@ const char * ngscript[] = {"" ,"ttk::checkbutton $f.cb1.haltlargequal -text \"Halt on large quality class\" \\\n" ,"-variable debug.haltlargequalclass -command { Ng_SetDebugParameters }\n" ,"ttk::checkbutton $f.cb1.haltseg -text \"Halt on Segment:\" \\\n" -,"-variable debug.haltsegment -command \"enable_cb %W $f.cb1.segs.ent1 $f.cb1.segs.ent2\"\n" +,"-variable debug.haltsegment -command { Ng_SetDebugParameters }\n" ,"ttk::checkbutton $f.cb1.haltnode -text \"Halt on Node:\" \\\n" -,"-variable debug.haltnode -command \"enable_cb %W $f.cb1.segs.ent1 $f.cb1.segs.ent2\"\n" +,"-variable debug.haltnode -command { Ng_SetDebugParameters }\n" ,"ttk::frame $f.cb1.fr\n" ,"ttk::checkbutton $f.cb1.fr.cb -text \"Halt on Face:\" \\\n" -,"-variable debug.haltface -command \"enable_cb %W $f.cb1.fr.ent $f.cb1.fr.ent\"\n" -,"ttk::entry $f.cb1.fr.ent -textvariable debug.haltfacenr -width 3 -state disabled\n" +,"-variable debug.haltface -command { Ng_SetDebugParameters }\n" +,"ttk::entry $f.cb1.fr.ent -textvariable debug.haltfacenr -width 3\n" ,"pack $f.cb1.fr.cb $f.cb1.fr.ent -side left\n" ,"ttk::frame $f.cb1.segs\n" ,"ttk::label $f.cb1.segs.lab1 -text \"P1:\"\n" ,"ttk::entry $f.cb1.segs.ent1 -width 6 \\\n" -,"-textvariable debug.haltsegmentp1 -state disabled\n" +,"-textvariable debug.haltsegmentp1\n" ,"ttk::label $f.cb1.segs.lab2 -text \"P2:\"\n" ,"ttk::entry $f.cb1.segs.ent2 -width 6 \\\n" -,"-textvariable debug.haltsegmentp2 -state disabled\n" +,"-textvariable debug.haltsegmentp2\n" ,"pack $f.cb1.segs.lab1 $f.cb1.segs.ent1 $f.cb1.segs.lab2 $f.cb1.segs.ent2 -side left\n" ,"grid $f.cb1.slowchecks $f.cb1.debugoutput -sticky nw\n" ,"grid $f.cb1.haltexline $f.cb1.haltoverlap -sticky nw\n" @@ -1989,8 +2012,12 @@ const char * ngscript[] = {"" ,"-textvariable stloptions.chartnumberoffset -validate focus -takefocus 0 \\\n" ,"-validatecommand \"my_validate %W 0 1e9 %P 0\" \\\n" ,"-invalidcommand \"my_invalid %W\"\n" +,"ttk::button $f.fn.btn_write_chart -text \"Write selected chart to chart.stlb\" -command {\n" +,"Ng_STLDoctor writechart\n" +,"}\n" ,"grid $f.fn.lab -sticky ne -padx 4\n" ,"grid $f.fn.ent -sticky nw -padx 4 -row 1 -column 1\n" +,"grid $f.fn.btn_write_chart -padx 4 -row 2 -column 1\n" ,"grid anchor $f.fn center\n" ,"ttk::labelframe $f.advstl -text \"Advanced STL options\" -relief groove -borderwidth 3\n" ,"pack $f.advstl -fill x -pady 15\n" @@ -2289,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" @@ -2797,7 +2824,7 @@ const char * ngscript[] = {"" ,"}\n" ,"}\n" ,"proc stldoctordialog { } {\n" -,"Ng_STLDoctor 0 0\n" +,"Ng_STLDoctor\n" ,"set wd .stldoctor_dlg\n" ,"if {[winfo exists .stldoctor_dlg] == 1} {\n" ,"wm withdraw $wd\n" @@ -3048,10 +3075,10 @@ const char * ngscript[] = {"" ,"-variable stldoctor.conecheck \\\n" ,"-command {Ng_STLDoctor;}\n" ,"pack $f.sc.bu $f.sc.bu2\n" -,"ttk::spinbox $f.gtol -from 1 -to 20 -textvariable stldoctor.geom_tol_fact -width 8\n" -,"pack $f.gtol\n" +,"ttk::label $f.gtol_lbl -text \"LoadSTL tolerance factor\"\n" +,"ttk::spinbox $f.gtol -from 1e-15 -to 0.001 -textvariable stldoctor.geom_tol_fact -width 8\n" +,"pack $f.gtol_lbl $f.gtol\n" ,"ttk::button $f.adap -text \"Apply\" -command {\n" -,".stldoctor_dlg.nb.advanced.gtol invoke\n" ,"Ng_STLDoctor;\n" ,"}\n" ,"pack $f.adap -expand yes\n" @@ -3329,7 +3356,7 @@ const char * ngscript[] = {"" ,"}\n" ,"}\n" ,"if { $toglok == 1} {\n" -,"pack .ndraw -expand true -fill both -padx 10 -pady 10\n" +,"pack .ndraw -expand true -fill both -padx 0 -pady 0\n" ,"catch { tkdnd::drop_target register .ndraw DND_Files }\n" ,"bind .ndraw {\n" ,"set oldmousex %x; set oldmousey %y;\n" @@ -3366,6 +3393,16 @@ const char * ngscript[] = {"" ,".ndraw render\n" ,"set oldmousex %x; set oldmousey %y;\n" ,"}\n" +,"bind .ndraw {\n" +,"Ng_MouseMove $oldmousex $oldmousey %x %y Move2d\n" +,".ndraw render\n" +,"set oldmousex %x; set oldmousey %y;\n" +,"}\n" +,"bind .ndraw {\n" +,"Ng_MouseMove $oldmousex $oldmousey %x %y Zoom2d\n" +,".ndraw render\n" +,"set oldmousex %x; set oldmousey %y;\n" +,"}\n" ,"}\n" ,"proc popupcheckredraw { vari { x 0 } } {\n" ,"upvar $vari varname\n" @@ -3955,18 +3992,18 @@ const char * ngscript[] = {"" ,"set faces [Ng_OCCCommand getunmeshedfaceinfo]\n" ,"set nrfaces [expr [llength $faces]]\n" ,"if {$nrfaces >= 2} {\n" -,"$w.tree insert {} -id ErrorFaces -text \"Faces with surface meshing error\"\n" +,"$w.tree insert {} end -id \"ErrorFaces\" -text \"Faces with surface meshing error\"\n" ,"$w.tree item ErrorFaces -open true\n" ,"set i [expr 0]\n" ,"while {$i < $nrfaces} {\n" ,"set entity [lindex $faces [expr $i]]\n" ,"set myroot [string range $entity 0 [string last / $entity]-1]\n" ,"if { [string length $myroot] == 0 } {\n" -,"set myroot ErrorFaces\n" +,"set myroot \"ErrorFaces\"\n" ,"}\n" ,"incr i 1\n" ,"set entityname [lindex $faces [expr $i]]\n" -,"$w.tree insert {myroot} end -id $entity -text $entityname -value 0\n" +,"$w.tree insert $myroot end -id $entity -text $entityname -value 0\n" ,"incr i 1\n" ,"}\n" ,"}\n" @@ -5181,8 +5218,12 @@ const char * ngscript[] = {"" ,"-validatecommand \"Ng_Vis_Set parameters; redraw;my_validatespinbox %W %P 10\" \\\n" ,"-command \"Ng_Vis_Set parameters; redraw;\" \\\n" ,"-invalidcommand \"my_invalidspinbox %W;Ng_Vis_Set parameters; redraw\" -from -1e10 -to 1e10 -increment 0.001\n" +,"ttk::label $w.as.lncols -text \"N Colors\"\n" +,"ttk::spinbox $w.as.snumcols -textvariable visoptions.numtexturecols -width 5 -validate focus \\\n" +,"-command \"Ng_Vis_Set parameters; redraw;\" \\\n" +,"-from 2 -to 31 -increment 1\n" ,"pack $w.as -fill x -pady 5 -ipady 3\n" -,"grid $w.as.autoscale $w.as.lmin $w.as.smin $w.as.lmax $w.as.smax -sticky nw -padx 4\n" +,"grid $w.as.autoscale $w.as.lmin $w.as.smin $w.as.lmax $w.as.smax $w.as.lncols $w.as.snumcols -sticky nw -padx 4\n" ,"grid columnconfigure $w.as 0 -pad 20\n" ,"grid columnconfigure $w.as 2 -pad 20\n" ,"grid anchor $w.as center\n" @@ -5284,7 +5325,7 @@ const char * ngscript[] = {"" ,"global visoptions.scalfunction\n" ,"foreach { name textval } $scalentries {\n" ,"$f.m add command -label \"$textval\" -command \\\n" -,"\"$f.b configure -text \\\"$textval\\\" ; set visoptions.scalfunction $name ; Ng_Vis_Set parameters ; redraw ; \"\n" +,"\"$f.b configure -text \\\"$textval\\\" ; set visoptions.scalfunction \\\"$name\\\" ; Ng_Vis_Set parameters ; redraw ; \"\n" ,"}\n" ,"pack $f.b $f.l -side right\n" ,"foreach { name textval } $scalentries {\n" @@ -5373,10 +5414,6 @@ const char * ngscript[] = {"" ,"ttk::checkbutton $w.fcb.cb.lineartexture -text \"Use Linear Texture\" \\\n" ,"-variable visoptions.lineartexture \\\n" ,"-command { Ng_Vis_Set parameters; redraw }\n" -,"scale $w.numcols -orient horizontal -length 100 -from 0 -to 50 \\\n" -,"-resolution 1 \\\n" -,"-variable visoptions.numtexturecols \\\n" -,"-command { popupcheckredraw visual_dialog_pop1 }\n" ,"ttk::checkbutton $w.fcb.cb.showclipsolution -text \"Draw Clipping Plane Solution\" \\\n" ,"-variable visoptions.showclipsolution \\\n" ,"-command { Ng_Vis_Set parameters; redraw }\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/ng/parallelfunc.cpp b/ng/parallelfunc.cpp index 6c7498ac..37f9a608 100644 --- a/ng/parallelfunc.cpp +++ b/ng/parallelfunc.cpp @@ -1,3 +1,6 @@ +#ifdef OLDFILE + + #ifdef PARALLEL #include "dlfcn.h" @@ -71,8 +74,11 @@ void ParallelRun() MPI_Comm_size(MPI_COMM_WORLD, &ntasks); MPI_Comm_rank(MPI_COMM_WORLD, &id); - if (parameters.StringFlagDefined ("testout")) - testout = new ofstream (string("testout_proc") + id ); + if (parameters.StringFlagDefined ("testout")) + { + delete testout; + testout = new ofstream (string("testout_proc") + id ); + } @@ -255,7 +261,7 @@ void ParallelRun() //get all possible confs int nconfs; auto cptr = glXGetFBConfigs (display,0, &nconfs); - Array conf_ids(nconfs); + NgArray conf_ids(nconfs); for(int k=0;k - $ - $ - $ - $ - $ +if(USE_OCC) + target_sources(nglib PRIVATE nglib_occ.cpp) + install(FILES nglib_occ.h DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel) +endif(USE_OCC) - $ - $ - ) - if(USE_GUI) - set(nglib_objects ${nglib_objects} - $ - $ - $ - ) - endif(USE_GUI) -endif(WIN32) - -add_library(nglib SHARED nglib.cpp ${nglib_objects}) -if(NOT WIN32) - target_link_libraries( nglib PUBLIC mesh stl interface geom2d csg stl visual) - if(USE_GUI) - target_link_libraries( nglib PUBLIC stlvis geom2dvis csgvis ) - endif(USE_GUI) -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 ${MPI_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${JPEG_LIBRARIES} ${MKL_LIBRARIES} ${ZLIB_LIBRARIES} ${OCC_LIBRARIES} netgen_cgns ) -if(USE_OCC AND NOT WIN32) - target_link_libraries(nglib PUBLIC occ) -endif(USE_OCC AND NOT WIN32) - -if(USE_PYTHON) - target_link_libraries(nglib PRIVATE ${PYTHON_LIBRARIES}) -endif(USE_PYTHON) - -install(TARGETS nglib ${NG_INSTALL_DIR}) +install(TARGETS nglib netgen_cgns ${NG_INSTALL_DIR}) install(FILES nglib.h DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel) diff --git a/nglib/ng_stl.cpp b/nglib/ng_stl.cpp index af8a2ed8..ef13549c 100644 --- a/nglib/ng_stl.cpp +++ b/nglib/ng_stl.cpp @@ -113,10 +113,10 @@ int main (int argc, char ** argv) Ng_SaveMesh(mesh,"test.vol"); - // refinement without geomety adaption: + // refinement without geometry adaption: // Ng_Uniform_Refinement (mesh); - // refinement with geomety adaption: + // refinement with geometry adaption: Ng_STL_Uniform_Refinement (stl_geom, mesh); cout << "elements after refinement: " << Ng_GetNE(mesh) << endl; diff --git a/nglib/nglib.cpp b/nglib/nglib.cpp index b692ce46..d75bb529 100644 --- a/nglib/nglib.cpp +++ b/nglib/nglib.cpp @@ -17,12 +17,8 @@ #include #include #include -#include <../visualization/soldata.hpp> -#include <../interface/writeuser.hpp> -#ifdef OCCGEOMETRY -#include -#endif +#include <../interface/writeuser.hpp> namespace netgen { @@ -30,6 +26,8 @@ namespace netgen { shared_ptr & mesh, MeshingParameters & mp); extern void Optimize2d(Mesh & mesh, MeshingParameters & mp); + extern MeshingParameters mparam; + DLL_HEADER extern STLParameters stlparam; } @@ -37,11 +35,6 @@ namespace netgen { #ifdef PARALLEL #include -namespace netgen -{ - // int id = 0, ntasks = 1; - MPI_Comm mesh_comm; -} #endif /* @@ -63,8 +56,8 @@ namespace netgen { // Bryn Lloyd - get rid of warning about macro redefinition (previously defined in mydefs.hpp) -#if defined(DLL_HEADER) - #undef DLL_HEADER +#if defined(NGLIB_API) + #undef NGLIB_API #endif namespace nglib { @@ -91,7 +84,7 @@ namespace nglib }; // initialize, deconstruct Netgen library: - DLL_HEADER void Ng_Init (bool cout_to_null, bool cerr_to_null, bool testout_to_null) + NGLIB_API void Ng_Init (bool cout_to_null, bool cerr_to_null, bool testout_to_null) { static ostream* null_stream = new ostream(new NullStreambuf); mycout = cout_to_null ? null_stream : &cout; @@ -100,7 +93,7 @@ namespace nglib } - DLL_HEADER void Ng_GetStatus(char ** str, double & percent) + NGLIB_API void Ng_GetStatus(char ** str, double & percent) { ::netgen::MyStr s; ::netgen::GetStatus(s, percent); @@ -109,14 +102,14 @@ namespace nglib } - DLL_HEADER void Ng_SetTerminate(bool abort) + NGLIB_API void Ng_SetTerminate(bool abort) { ::netgen::multithread.terminate = abort ? 1 : 0; } // Clean-up functions before ending usage of nglib - DLL_HEADER void Ng_Exit () + NGLIB_API void Ng_Exit () { ; } @@ -125,7 +118,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)); @@ -136,7 +129,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) { @@ -155,7 +148,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); } @@ -164,7 +157,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); @@ -173,7 +166,7 @@ namespace nglib - DLL_HEADER void Ng_ExportMesh(Ng_Mesh * ng_mesh, Ng_Export_Formats format, const char* filename) + NGLIB_API void Ng_ExportMesh(Ng_Mesh * ng_mesh, Ng_Export_Formats format, const char* filename) { Mesh * mesh = (Mesh*)ng_mesh; switch (format) @@ -194,7 +187,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; @@ -235,7 +228,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; } @@ -244,7 +237,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])); @@ -252,21 +245,21 @@ namespace nglib // Manually lock a specific point - DLL_HEADER void Ng_AddLockedPoint(Ng_Mesh * mesh, int pi) + NGLIB_API void Ng_AddLockedPoint(Ng_Mesh * mesh, int pi) { Mesh * m = (Mesh*)mesh; m->AddLockedPoint(pi); } - DLL_HEADER void Ng_ClearFaceDescriptors (Ng_Mesh * ng_mesh) + NGLIB_API void Ng_ClearFaceDescriptors (Ng_Mesh * ng_mesh) { Mesh * mesh = (Mesh*)ng_mesh; mesh->ClearFaceDescriptors(); } - DLL_HEADER int Ng_AddFaceDescriptor (Ng_Mesh * ng_mesh, int surfnr, int domin, int domout, int bcp) + NGLIB_API int Ng_AddFaceDescriptor (Ng_Mesh * ng_mesh, int surfnr, int domin, int domout, int bcp) { Mesh * mesh = (Mesh*)ng_mesh; int nfd = mesh->GetNFD(); @@ -293,7 +286,7 @@ namespace nglib } - DLL_HEADER void Ng_SetupFacedescriptors (Ng_Mesh * mesh, int maxbc) + NGLIB_API void Ng_SetupFacedescriptors (Ng_Mesh * mesh, int maxbc) { Mesh * m = (Mesh*)mesh; m->ClearFaceDescriptors(); @@ -303,7 +296,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, int facenr) { int n = 3; @@ -334,7 +327,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, int domain) { int n = 4; @@ -363,7 +356,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(); } @@ -372,7 +365,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(); } @@ -381,7 +374,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(); } @@ -390,7 +383,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(); @@ -399,7 +392,7 @@ namespace nglib } - DLL_HEADER bool Ng_GetFaceDescriptor (Ng_Mesh * mesh, int facenr, int &surfnr, int &domin, int &domout, int &bcp) + NGLIB_API bool Ng_GetFaceDescriptor (Ng_Mesh * mesh, int facenr, int &surfnr, int &domin, int &domout, int &bcp) { Mesh * m = (Mesh*)mesh; if (facenr <= m->GetNFD()) @@ -414,7 +407,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, int * facenr) { const Element2d & el = ((Mesh*)mesh)->SurfaceElement(num); @@ -447,7 +440,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, int * domain) { const Element & el = ((Mesh*)mesh)->VolumeElement(num); @@ -473,7 +466,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); } @@ -482,7 +475,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); } @@ -491,7 +484,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) @@ -503,7 +496,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; @@ -526,7 +519,7 @@ namespace nglib // Optimize existing mesh - DLL_HEADER Ng_Result Ng_OptimizeVolume(Ng_Mesh *mesh, Ng_Meshing_Parameters *mp) + NGLIB_API Ng_Result Ng_OptimizeVolume(Ng_Mesh *mesh, Ng_Meshing_Parameters *mp) { Mesh * m = (Mesh*)mesh; @@ -544,7 +537,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; @@ -554,7 +547,7 @@ namespace nglib - DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2, int domain_in, int domain_out) + NGLIB_API void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2, int domain_in, int domain_out) { Mesh * m = (Mesh*)mesh; @@ -569,7 +562,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(); @@ -578,7 +571,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(); @@ -587,7 +580,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(); @@ -596,7 +589,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; @@ -608,7 +601,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); @@ -643,7 +636,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]; @@ -656,7 +649,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); @@ -664,13 +657,13 @@ namespace nglib } - DLL_HEADER Ng_Geometry_2D * Ng_NewGeometry_2D () + NGLIB_API Ng_Geometry_2D * Ng_NewGeometry_2D () { SplineGeometry2d * geom = new SplineGeometry2d(); return (Ng_Geometry_2D *)geom; } - DLL_HEADER void Ng_DeleteGeometry_2D (Ng_Geometry_2D * geom) + NGLIB_API void Ng_DeleteGeometry_2D (Ng_Geometry_2D * geom) { if (geom) { @@ -680,7 +673,7 @@ namespace nglib } } - DLL_HEADER void Ng_AppendPoint_2D (Ng_Geometry_2D* geom, double * x, double h) + NGLIB_API void Ng_AppendPoint_2D (Ng_Geometry_2D* geom, double * x, double h) { if (geom) { @@ -690,7 +683,7 @@ namespace nglib } } - DLL_HEADER void Ng_AppendLineSegment_2D (Ng_Geometry_2D* geom, int n1, int n2, + NGLIB_API void Ng_AppendLineSegment_2D (Ng_Geometry_2D* geom, int n1, int n2, int leftdomain, int rightdomain, double h) { if (geom) @@ -708,14 +701,14 @@ namespace nglib } } - DLL_HEADER void Ng_AppendSplinSegment_2D (Ng_Geometry_2D* geom, int n1, int n2, int n3, + NGLIB_API void Ng_AppendSplinSegment_2D (Ng_Geometry_2D* geom, int n1, int n2, int n3, int leftdomain, int rightdomain, double h) { if (geom) { SplineGeometry2d* spline_geom = (SplineGeometry2d*)geom; // zero-offset! - Array > pts; + NgArray > pts; pts.Append(spline_geom->geompoints[n1-1]); pts.Append(spline_geom->geompoints[n2-1]); pts.Append(spline_geom->geompoints[n3-1]); @@ -731,7 +724,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) { @@ -747,7 +740,7 @@ namespace nglib } - DLL_HEADER Ng_Result Ng_OptimizeMesh_2D(Ng_Mesh *mesh, Ng_Meshing_Parameters * mp) + NGLIB_API Ng_Result Ng_OptimizeMesh_2D(Ng_Mesh *mesh, Ng_Meshing_Parameters * mp) { Mesh * m = (Mesh*)mesh; @@ -761,33 +754,33 @@ 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) { - Refinement2d ref(*(SplineGeometry2d*)geom); + Refinement ref(*(SplineGeometry2d*)geom); HPRefinement (*(Mesh*)mesh, &ref, levels); } - 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) { - Refinement2d ref(*(SplineGeometry2d*)geom); + Refinement ref(*(SplineGeometry2d*)geom); HPRefinement (*(Mesh*)mesh, &ref, levels, parameter); } - Array readtrias; //only before initstlgeometry - Array > readedges; //only before init stlgeometry + NgArray readtrias; //only before initstlgeometry + 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; @@ -837,13 +830,13 @@ 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; } - DLL_HEADER void Ng_STL_DeleteGeometry (Ng_STL_Geometry * geom) + NGLIB_API void Ng_STL_DeleteGeometry (Ng_STL_Geometry * geom) { if (geom) { @@ -856,7 +849,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); @@ -881,13 +874,14 @@ 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, Ng_STL_Parameters * stlp) { STLGeometry* stlgeometry = (STLGeometry*)geom; Mesh* me = (Mesh*)mesh; + me->SetGeometry( shared_ptr(stlgeometry, &NOOP_Deleter) ); // Philippose - 27/07/2009 // Do not locally re-define "mparam" here... "mparam" is a global @@ -914,7 +908,7 @@ namespace nglib } */ - STLMeshing (*stlgeometry, *me); + STLMeshing (*stlgeometry, *me, mparam, stlparam); stlgeometry->edgesfound = 1; stlgeometry->surfacemeshed = 0; @@ -928,13 +922,14 @@ 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, Ng_STL_Parameters * stlp) { STLGeometry* stlgeometry = (STLGeometry*)geom; Mesh* me = (Mesh*)mesh; + me->SetGeometry( shared_ptr(stlgeometry, &NOOP_Deleter) ); // Philippose - 27/07/2009 // Do not locally re-define "mparam" here... "mparam" is a global @@ -958,7 +953,7 @@ namespace nglib stlgeometry->surfaceoptimized = 0; stlgeometry->volumemeshed = 0; */ - int retval = STLSurfaceMeshing (*stlgeometry, *me); + int retval = STLSurfaceMeshing (*stlgeometry, *me, mparam, stlparam); if (retval == MESHING3_OK) { (*mycout) << "Success !!!!" << endl; @@ -991,7 +986,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) { @@ -1010,7 +1005,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])); @@ -1020,202 +1015,13 @@ namespace nglib -#ifdef OCCGEOMETRY - // --------------------- OCC Geometry / Meshing Utility Functions ------------------- - // Create new OCC Geometry Object - DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry () - { - return (Ng_OCC_Geometry*)(void*)new OCCGeometry; - } - - - - - // Delete the OCC Geometry Object - DLL_HEADER Ng_Result Ng_OCC_DeleteGeometry(Ng_OCC_Geometry * geom) - { - if (geom != NULL) - { - delete (OCCGeometry*)geom; - geom = NULL; - return NG_OK; - } - - return NG_ERROR; - } - - - - - // Loads geometry from STEP File - DLL_HEADER 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 - OCCGeometry * occgeo = LoadOCC_STEP(filename); - - return ((Ng_OCC_Geometry *)occgeo); - } - - - - - // Loads geometry from IGES File - DLL_HEADER 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 - OCCGeometry * occgeo = LoadOCC_IGES(filename); - - return ((Ng_OCC_Geometry *)occgeo); - } - - - - - // Loads geometry from BREP File - DLL_HEADER 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 - OCCGeometry * occgeo = LoadOCC_BREP(filename); - - return ((Ng_OCC_Geometry *)occgeo); - } - - - - - // 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, - Ng_Mesh * mesh, - Ng_Meshing_Parameters * mp) - { - OCCGeometry * occgeom = (OCCGeometry*)geom; - Mesh * me = (Mesh*)mesh; - - me->geomtype = Mesh::GEOM_OCC; - - mp->Transfer_Parameters(); - - occparam.resthcloseedgeenable = mp->closeedgeenable; - occparam.resthcloseedgefac = mp->closeedgefact; - - // Delete the mesh structures in order to start with a clean - // slate - me->DeleteMesh(); - - OCCSetLocalMeshSize(*occgeom, *me); - - return(NG_OK); - } - - - - - // Mesh the edges and add Face descriptors to prepare for surface meshing - DLL_HEADER Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, - Ng_Mesh * mesh, - Ng_Meshing_Parameters * mp) - { - OCCGeometry * occgeom = (OCCGeometry*)geom; - Mesh * me = (Mesh*)mesh; - - mp->Transfer_Parameters(); - - OCCFindEdges(*occgeom, *me); - - if((me->GetNP()) && (me->GetNFD())) - { - return NG_OK; - } - else - { - return NG_ERROR; - } - } - - - - - // Mesh the edges and add Face descriptors to prepare for surface meshing - DLL_HEADER Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, - Ng_Mesh * mesh, - Ng_Meshing_Parameters * mp) - { - int numpoints = 0; - - OCCGeometry * occgeom = (OCCGeometry*)geom; - Mesh * me = (Mesh*)mesh; - - // Set the internal meshing parameters structure from the nglib meshing - // parameters structure - mp->Transfer_Parameters(); - - - // Only go into surface meshing if the face descriptors have already been added - if(!me->GetNFD()) - return NG_ERROR; - - numpoints = me->GetNP(); - - // Initially set up only for surface meshing without any optimisation - int perfstepsend = MESHCONST_MESHSURFACE; - - // Check and if required, enable surface mesh optimisation step - if(mp->optsurfmeshenable) - { - perfstepsend = MESHCONST_OPTSURFACE; - } - - OCCMeshSurface(*occgeom, *me, perfstepsend); - - me->CalcSurfacesOfNode(); - - if(me->GetNP() <= numpoints) - return NG_ERROR; - - if(me->GetNSE() <= 0) - return NG_ERROR; - - return NG_OK; - } - - - - - // 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, - Ng_OCC_TopTools_IndexedMapOfShape * FMap) - { - OCCGeometry* occgeom = (OCCGeometry*)geom; - TopTools_IndexedMapOfShape *occfmap = (TopTools_IndexedMapOfShape *)FMap; - - // Copy the face map from the geometry to the given variable - occfmap->Assign(occgeom->fmap); - - if(occfmap->Extent()) - { - return NG_OK; - } - else - { - return NG_ERROR; - } - } - - // ------------------ End - OCC Geometry / Meshing Utility Functions ---------------- -#endif // ------------------ 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; @@ -1259,7 +1065,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() { (*this) = Ng_Meshing_Parameters(); } @@ -1268,7 +1074,7 @@ namespace nglib // - DLL_HEADER void Ng_Meshing_Parameters :: Transfer_Parameters() + NGLIB_API void Ng_Meshing_Parameters :: Transfer_Parameters() { mparam.uselocalh = uselocalh; @@ -1301,7 +1107,7 @@ namespace nglib - DLL_HEADER Ng_STL_Parameters :: Ng_STL_Parameters() + NGLIB_API Ng_STL_Parameters :: Ng_STL_Parameters() { yangle = 30; contyangle = 20; @@ -1334,7 +1140,7 @@ namespace nglib - DLL_HEADER void Ng_STL_Parameters :: Transfer_Parameters() + NGLIB_API void Ng_STL_Parameters :: Transfer_Parameters() { stlparam.yangle = yangle; stlparam.contyangle = contyangle; @@ -1361,8 +1167,8 @@ namespace nglib stlparam.resthlinelengthenable = resthlinelengthenable; stlparam.resthlinelengthfac = resthlinelengthfac; - stlparam.resthcloseedgeenable = resthcloseedgeenable; - stlparam.resthcloseedgefac = resthcloseedgefac; + // stlparam.resthcloseedgeenable = resthcloseedgeenable; + // stlparam.resthcloseedgefac = resthcloseedgefac; } // ------------------ End - Meshing Parameters related functions -------------------- @@ -1370,16 +1176,16 @@ 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; + Refinement ref(*((Mesh*) mesh)->GetGeometry()); ref.MakeSecondOrder(*(Mesh*) mesh); } - 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 ); @@ -1388,7 +1194,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); @@ -1397,7 +1203,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); @@ -1406,31 +1212,20 @@ namespace nglib -#ifdef OCCGEOMETRY - DLL_HEADER void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, - Ng_Mesh * mesh) - { - ((OCCGeometry*)geom )->GetRefinement().MakeSecondOrder(*(Mesh*) mesh); - } -#endif // ------------------ End - Second Order Mesh generation functions ------------------ // ------------------ Begin - Uniform Mesh Refinement functions --------------------- - DLL_HEADER void Ng_Uniform_Refinement (Ng_Mesh * ng_mesh) + NGLIB_API void Ng_Uniform_Refinement (Ng_Mesh * mesh) { - Mesh * mesh = (Mesh*) ng_mesh; - - if (auto geom = mesh->GetGeometry()) - geom->GetRefinement().Refine (*mesh); - else - Refinement().Refine (*mesh); + Refinement ref(*((Mesh*)mesh)->GetGeometry()); + ref.Refine ( * (Mesh*) mesh ); } - DLL_HEADER void Ng_SetRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag) + NGLIB_API void Ng_SetRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag) { Mesh * mesh = (Mesh*) ng_mesh; @@ -1447,7 +1242,7 @@ namespace nglib } - DLL_HEADER void Ng_SetSurfaceRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag) + NGLIB_API void Ng_SetSurfaceRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag) { Mesh * mesh = (Mesh*) ng_mesh; @@ -1459,7 +1254,7 @@ namespace nglib } - DLL_HEADER void Ng_Refine (Ng_Mesh * ng_mesh) + NGLIB_API void Ng_Refine (Ng_Mesh * ng_mesh) { Mesh * mesh = (Mesh*) ng_mesh; BisectionOptions biopt; @@ -1467,10 +1262,8 @@ namespace nglib biopt.refine_p = 0; // only h-refinement biopt.refine_hp = 0; - if (auto geom = mesh->GetGeometry()) - geom->GetRefinement().Bisect (*mesh, biopt); - else - Refinement().Bisect (*mesh, biopt); + Refinement ref(*((Mesh*)mesh)->GetGeometry()); + ref.Bisect(*(Mesh*)mesh, biopt); // \todo not sure if this is needed? //mesh -> UpdateTopology(); @@ -1479,7 +1272,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 ); @@ -1487,7 +1280,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 ); @@ -1496,7 +1289,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 ); @@ -1505,13 +1298,6 @@ namespace nglib -#ifdef OCCGEOMETRY - DLL_HEADER void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, - Ng_Mesh * mesh) - { - ( (OCCGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); - } -#endif // ------------------ End - Uniform Mesh Refinement functions ----------------------- } // End of namespace nglib @@ -1523,7 +1309,7 @@ namespace netgen { char geomfilename[255]; - DLL_HEADER void MyError2 (const char * ch) + NGLIB_API void MyError2 (const char * ch) { cerr << ch; } @@ -1532,7 +1318,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; @@ -1544,7 +1330,7 @@ namespace netgen /* - DLL_HEADER double GetTime () + NGLIB_API double GetTime () { return 0; } @@ -1584,10 +1370,3 @@ void Ng_SetSolutionData (Ng_SolutionData * soldata) } void Ng_InitSolutionData (Ng_SolutionData * soldata) { ; } */ - -// Force linking libinterface to libnglib -void MyDummyToForceLinkingLibInterface(Mesh &mesh, NetgenGeometry &geom) -{ - netgen::WriteUserFormat("", mesh, /* geom, */ ""); -} - diff --git a/nglib/nglib.h b/nglib/nglib.h index 913b66a5..b272786e 100644 --- a/nglib/nglib.h +++ b/nglib/nglib.h @@ -7,6 +7,8 @@ /* Date: 7. May. 2000 */ /**************************************************************************/ +#include + /*! \file nglib.h \brief Library interface to the netgen meshing kernel @@ -25,16 +27,15 @@ // Modifications for creating a DLL in Windows #if defined(WIN32) #if defined(NGLIB_EXPORTS) || defined(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 + #define NGLIB_API __attribute__((visibility("default"))) #endif - // ** Constants used within Netgen ********************* /// Maximum allowed number of nodes per volume element #define NG_VOLUME_ELEMENT_MAXPOINTS 10 @@ -57,11 +58,6 @@ typedef void * Ng_Geometry_2D; /// Data type for NETGEN STL geometry typedef void * Ng_STL_Geometry; -#ifdef OCCGEOMETRY -/// Data type for NETGEN OpenCascade geometry -typedef void * Ng_OCC_Geometry; -typedef void * Ng_OCC_TopTools_IndexedMapOfShape; -#endif // *** Special Enum types used within Netgen *********** @@ -158,7 +154,7 @@ public: - #check_overlap: 1 - #check_overlapping_boundary: 1 */ - DLL_HEADER Ng_Meshing_Parameters(); + NGLIB_API Ng_Meshing_Parameters(); @@ -168,7 +164,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(); @@ -179,11 +175,11 @@ 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(); }; -class DLL_HEADER Ng_STL_Parameters +class NGLIB_API Ng_STL_Parameters { public: // Algorithm may be somewhat like Canny edge detector @@ -239,7 +235,7 @@ public: program before beginning to use the other Netgen specific functions. */ -DLL_HEADER void Ng_Init (bool cout_to_null = false, bool cerr_to_null = false, bool testout_to_null = false); +NGLIB_API void Ng_Init (bool cout_to_null = false, bool cerr_to_null = false, bool testout_to_null = false); /*! \brief Exit the Netgen meshing kernel in a clean manner @@ -247,17 +243,17 @@ DLL_HEADER void Ng_Init (bool cout_to_null = false, bool cerr_to_null = false, b 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 Get current status, e.g. info string and percent */ -DLL_HEADER void Ng_GetStatus(char ** str, double & percent); +NGLIB_API void Ng_GetStatus(char ** str, double & percent); /*! \brief Set abort flag */ -DLL_HEADER void Ng_SetTerminate(bool abort); +NGLIB_API void Ng_SetTerminate(bool abort); /*! \brief Create a new (and empty) Netgen Mesh Structure @@ -270,7 +266,7 @@ DLL_HEADER void Ng_SetTerminate(bool abort); \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 @@ -281,7 +277,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 @@ -298,7 +294,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 @@ -311,14 +307,14 @@ 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 Save mesh in various external formats, e.g. fluent, gmsh, gmsh2, vtk, ... */ enum Ng_Export_Formats { NG_GMSH = 1, NG_GMSH2 = 2, NG_VTK = 3, NG_FLUENT = 4, NG_ABAQUS = 5 }; -DLL_HEADER void Ng_ExportMesh(Ng_Mesh * mesh, Ng_Export_Formats format, const char* filename); +NGLIB_API void Ng_ExportMesh(Ng_Mesh * mesh, Ng_Export_Formats format, const char* filename); /*! \brief Merge a Netgen VOL Mesh from disk into an existing mesh in memory @@ -331,7 +327,7 @@ DLL_HEADER void Ng_ExportMesh(Ng_Mesh * mesh, Ng_Export_Formats format, const ch 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 @@ -348,7 +344,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); // ------------------------------------------------------------------ @@ -372,28 +368,28 @@ 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); /*! Add locked point which should not get modified by optimization routines */ -DLL_HEADER void Ng_AddLockedPoint(Ng_Mesh * mesh, int pi); +NGLIB_API void Ng_AddLockedPoint(Ng_Mesh * mesh, int pi); /*! \brief Remove any existing face descriptors */ -DLL_HEADER int Ng_AddFaceDescriptor (Ng_Mesh * ng_mesh, int surfnr, int domin, int domout, int bcp); +NGLIB_API int Ng_AddFaceDescriptor (Ng_Mesh * ng_mesh, int surfnr, int domin, int domout, int bcp); /*! \brief Remove any existing face descriptors */ -DLL_HEADER void Ng_ClearFaceDescriptors (Ng_Mesh * ng_mesh); +NGLIB_API void Ng_ClearFaceDescriptors (Ng_Mesh * ng_mesh); /*! \brief Generate simple facedescriptors, with facenr==bc, from 1...maxbc */ -DLL_HEADER void Ng_SetupFacedescriptors(Ng_Mesh * mesh, int maxbc); +NGLIB_API void Ng_SetupFacedescriptors(Ng_Mesh * mesh, int maxbc); /*! \brief Add a surface element to a given Netgen Mesh Structure @@ -417,7 +413,7 @@ DLL_HEADER void Ng_SetupFacedescriptors(Ng_Mesh * mesh, int maxbc); points which constitute the surface element being added \param facenr Index of face descriptor. Used e.g. to attach boundary condition types to surface elements */ -DLL_HEADER void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi, int facenr=1); +NGLIB_API void Ng_AddSurfaceElement (Ng_Mesh * mesh, Ng_Surface_Element_Type et, int * pi, int facenr=1); /*! \brief Add a volume element to a given Netgen Mesh Structure @@ -441,7 +437,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, int domain=1); +NGLIB_API void Ng_AddVolumeElement (Ng_Mesh * mesh, Ng_Volume_Element_Type et, int * pi, int domain=1); // ------------------------------------------------------------------ @@ -468,7 +464,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 @@ -492,7 +488,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 @@ -523,7 +519,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); // ------------------------------------------------------------------ @@ -554,13 +550,13 @@ 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); /*! \brief Improve quality of an existing 3D Volume Mesh */ -DLL_HEADER Ng_Result Ng_OptimizeVolume(Ng_Mesh *mesh, Ng_Meshing_Parameters *mp); +NGLIB_API Ng_Result Ng_OptimizeVolume(Ng_Mesh *mesh, Ng_Meshing_Parameters *mp); // ------------------------------------------------------------------ @@ -579,7 +575,7 @@ DLL_HEADER Ng_Result Ng_OptimizeVolume(Ng_Mesh *mesh, Ng_Meshing_Parameters *mp) \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 @@ -593,7 +589,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 @@ -607,7 +603,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); // ------------------------------------------------------------------ @@ -621,17 +617,17 @@ 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 bcp and surfnr for specified face descriptor (facenr) -DLL_HEADER bool Ng_GetFaceDescriptor (Ng_Mesh * mesh, int facenr, int &surfnr, int &domin, int &domout, int &bcp); +NGLIB_API bool Ng_GetFaceDescriptor (Ng_Mesh * mesh, int facenr, int &surfnr, int &domin, int &domout, int &bcp); // 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, int * facenr = nullptr); -DLL_HEADER Ng_Volume_Element_Type +NGLIB_API Ng_Volume_Element_Type Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi, int * domain = nullptr); // ------------------------------------------------------------------ @@ -645,49 +641,49 @@ Ng_GetVolumeElement (Ng_Mesh * mesh, int num, int * pi, int * domain = nullptr); // feeds points and boundary to mesh -DLL_HEADER Ng_Geometry_2D * Ng_NewGeometry_2D(); +NGLIB_API Ng_Geometry_2D * Ng_NewGeometry_2D(); -DLL_HEADER void Ng_DeleteGeometry_2D(Ng_Geometry_2D * geom); +NGLIB_API void Ng_DeleteGeometry_2D(Ng_Geometry_2D * geom); -DLL_HEADER void Ng_AppendPoint_2D(Ng_Geometry_2D* geom, double * x, double h); +NGLIB_API void Ng_AppendPoint_2D(Ng_Geometry_2D* geom, double * x, double h); -DLL_HEADER void Ng_AppendLineSegment_2D(Ng_Geometry_2D* geom, int pi1, int pi2, +NGLIB_API void Ng_AppendLineSegment_2D(Ng_Geometry_2D* geom, int pi1, int pi2, int leftdomain, int rightdomain, double h); -DLL_HEADER void Ng_AppendSplinSegment_2D(Ng_Geometry_2D* geom, int pi1, int pi2, int pi3, +NGLIB_API void Ng_AppendSplinSegment_2D(Ng_Geometry_2D* geom, int pi1, int pi2, int pi3, int leftdomain, int rightdomain, double h); -DLL_HEADER void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x); -DLL_HEADER void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2, int domain_in = -1, int domain_out = -1); +NGLIB_API void Ng_AddPoint_2D (Ng_Mesh * mesh, double * x); +NGLIB_API void Ng_AddBoundarySeg_2D (Ng_Mesh * mesh, int pi1, int pi2, int domain_in = -1, int domain_out = -1); // 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 Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = nullptr); +NGLIB_API Ng_Surface_Element_Type Ng_GetElement_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = nullptr); // return 2d boundary segment -DLL_HEADER void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = nullptr); +NGLIB_API void Ng_GetSegment_2D (Ng_Mesh * mesh, int num, int * pi, int * matnum = nullptr); // 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); // functions added to make Optimize2d mesh accessible from nglib -DLL_HEADER Ng_Result Ng_OptimizeMesh_2D(Ng_Mesh *mesh, Ng_Meshing_Parameters * mp); +NGLIB_API Ng_Result Ng_OptimizeMesh_2D(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); @@ -701,38 +697,38 @@ 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 (); -DLL_HEADER void Ng_STL_DeleteGeometry (Ng_STL_Geometry * geom); +NGLIB_API void Ng_STL_DeleteGeometry (Ng_STL_Geometry * geom); // 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 = nullptr); // 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, Ng_STL_Parameters * stlp = nullptr); // 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, Ng_STL_Parameters * stlp = nullptr); @@ -748,10 +744,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); @@ -760,47 +756,6 @@ DLL_HEADER Ng_Result Ng_ACIS_GenerateSurfaceMesh (Ng_ACIS_Geometry * geom, -#ifdef OCCGEOMETRY - -// ********************************************************** -// ** OpenCascade Geometry / Meshing Utilities ** -// ********************************************************** - -// Create new OCC Geometry Object -DLL_HEADER Ng_OCC_Geometry * Ng_OCC_NewGeometry (); - -// Delete an OCC Geometry Object -DLL_HEADER 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); - -// Loads geometry from IGES file -DLL_HEADER 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); - -// Set the local mesh size based on geometry / topology -DLL_HEADER 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, - 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, - 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, - Ng_OCC_TopTools_IndexedMapOfShape * FMap); - -#endif // OCCGEOMETRY @@ -809,32 +764,28 @@ 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); // non-uniform mesh refinement -DLL_HEADER void Ng_SetRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag); +NGLIB_API void Ng_SetRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag); -DLL_HEADER void Ng_SetSurfaceRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag); +NGLIB_API void Ng_SetSurfaceRefinementFlag (Ng_Mesh * ng_mesh, int ei, int flag); -DLL_HEADER void Ng_Refine (Ng_Mesh * ng_mesh); +NGLIB_API void Ng_Refine (Ng_Mesh * ng_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, - Ng_Mesh * mesh); -#endif @@ -843,24 +794,23 @@ 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, - Ng_Mesh * mesh); -#endif - - +#include "nglib_occ.h" +#endif // OCCGEOMETRY + // #endif // NGLIB diff --git a/nglib/nglib_occ.cpp b/nglib/nglib_occ.cpp new file mode 100644 index 00000000..58ee4d8f --- /dev/null +++ b/nglib/nglib_occ.cpp @@ -0,0 +1,212 @@ +#include +#include +#include + +#define OCCGEOMETRY 1 +namespace nglib { +#include "nglib.h" +} + + +namespace netgen +{ + inline void NOOP_Deleter(void *) { ; } + extern MeshingParameters mparam; + DLL_HEADER extern OCCParameters occparam; +} // namespace netgen + +using namespace netgen; + +namespace nglib +{ + + // --------------------- OCC Geometry / Meshing Utility Functions ------------------- + // Create new OCC Geometry Object + NGLIB_API Ng_OCC_Geometry * Ng_OCC_NewGeometry () + { + return (Ng_OCC_Geometry*)(void*)new OCCGeometry; + } + + + // Delete the OCC Geometry Object + NGLIB_API Ng_Result Ng_OCC_DeleteGeometry(Ng_OCC_Geometry * geom) + { + if (geom != NULL) + { + delete (OCCGeometry*)geom; + geom = NULL; + return NG_OK; + } + + return NG_ERROR; + } + + + // Loads geometry from STEP File + 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 + OCCGeometry * occgeo = LoadOCC_STEP(filename); + + return ((Ng_OCC_Geometry *)occgeo); + } + + + // Loads geometry from IGES File + 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 + OCCGeometry * occgeo = LoadOCC_IGES(filename); + + return ((Ng_OCC_Geometry *)occgeo); + } + + + // Loads geometry from BREP File + 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 + OCCGeometry * occgeo = LoadOCC_BREP(filename); + + return ((Ng_OCC_Geometry *)occgeo); + } + + + // Locally limit the size of the mesh to be generated at various points + // based on the topology of the geometry + NGLIB_API Ng_Result Ng_OCC_SetLocalMeshSize (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh, + Ng_Meshing_Parameters * mp) + { + OCCGeometry * occgeom = (OCCGeometry*)geom; + Mesh * me = (Mesh*)mesh; + me->SetGeometry( shared_ptr(occgeom, &NOOP_Deleter) ); + + me->geomtype = Mesh::GEOM_OCC; + + mp->Transfer_Parameters(); + + if(mp->closeedgeenable) + mparam.closeedgefac = mp->closeedgefact; + + // Delete the mesh structures in order to start with a clean + // slate + me->DeleteMesh(); + + OCCSetLocalMeshSize(*occgeom, *me, mparam, occparam); + + return(NG_OK); + } + + + + + // Mesh the edges and add Face descriptors to prepare for surface meshing + NGLIB_API Ng_Result Ng_OCC_GenerateEdgeMesh (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh, + Ng_Meshing_Parameters * mp) + { + OCCGeometry * occgeom = (OCCGeometry*)geom; + Mesh * me = (Mesh*)mesh; + me->SetGeometry( shared_ptr(occgeom, &NOOP_Deleter) ); + + mp->Transfer_Parameters(); + + occgeom->FindEdges(*me, mparam); + + if((me->GetNP())) + { + return NG_OK; + } + else + { + return NG_ERROR; + } + } + + + + + // Mesh the edges and add Face descriptors to prepare for surface meshing + NGLIB_API Ng_Result Ng_OCC_GenerateSurfaceMesh (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh, + Ng_Meshing_Parameters * mp) + { + int numpoints = 0; + + OCCGeometry * occgeom = (OCCGeometry*)geom; + Mesh * me = (Mesh*)mesh; + me->SetGeometry( shared_ptr(occgeom, &NOOP_Deleter) ); + + // Set the internal meshing parameters structure from the nglib meshing + // parameters structure + mp->Transfer_Parameters(); + + numpoints = me->GetNP(); + + // Initially set up only for surface meshing without any optimisation + int perfstepsend = MESHCONST_MESHSURFACE; + + // Check and if required, enable surface mesh optimisation step + if(mp->optsurfmeshenable) + { + perfstepsend = MESHCONST_OPTSURFACE; + } + + occgeom->MeshSurface(*me, mparam); + occgeom->OptimizeSurface(*me, mparam); + + me->CalcSurfacesOfNode(); + + if(me->GetNP() <= numpoints) + return NG_ERROR; + + if(me->GetNSE() <= 0) + return NG_ERROR; + + return NG_OK; + } + + + + + // 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 + NGLIB_API Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, + Ng_OCC_TopTools_IndexedMapOfShape * FMap) + { + OCCGeometry* occgeom = (OCCGeometry*)geom; + TopTools_IndexedMapOfShape *occfmap = (TopTools_IndexedMapOfShape *)FMap; + + // Copy the face map from the geometry to the given variable + occfmap->Assign(occgeom->fmap); + + if(occfmap->Extent()) + { + return NG_OK; + } + else + { + return NG_ERROR; + } + } + + // ------------------ End - OCC Geometry / Meshing Utility Functions ---------------- + + NGLIB_API void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh) + { + ((OCCGeometry*)geom )->GetRefinement().MakeSecondOrder(*(Mesh*) mesh); + } + + NGLIB_API void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh) + { + ( (OCCGeometry*)geom ) -> GetRefinement().Refine ( * (Mesh*) mesh ); + } + +} // namespace nglib diff --git a/nglib/nglib_occ.h b/nglib/nglib_occ.h new file mode 100644 index 00000000..ebb75a4f --- /dev/null +++ b/nglib/nglib_occ.h @@ -0,0 +1,50 @@ +#ifndef NGLIB_OCC_HPP_INCLUDED +#define NGLIB_OCC_HPP_INCLUDED + +/// Data type for NETGEN OpenCascade geometry +typedef void * Ng_OCC_Geometry; +typedef void * Ng_OCC_TopTools_IndexedMapOfShape; + +// ********************************************************** +// ** OpenCascade Geometry / Meshing Utilities ** +// ********************************************************** + +// Create new OCC Geometry Object +NGLIB_API Ng_OCC_Geometry * Ng_OCC_NewGeometry (); + +// Delete an OCC Geometry Object +NGLIB_API Ng_Result Ng_OCC_DeleteGeometry (Ng_OCC_Geometry * geom); + +// Loads geometry from STEP file +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_STEP (const char * filename); + +// Loads geometry from IGES file +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_IGES (const char * filename); + +// Loads geometry from BREP file +NGLIB_API Ng_OCC_Geometry * Ng_OCC_Load_BREP (const char * filename); + +// Set the local mesh size based on geometry / topology +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 +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 +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 +NGLIB_API Ng_Result Ng_OCC_GetFMap(Ng_OCC_Geometry * geom, + Ng_OCC_TopTools_IndexedMapOfShape * FMap); + +NGLIB_API void Ng_OCC_Uniform_Refinement (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh); +NGLIB_API void Ng_OCC_Generate_SecondOrder (Ng_OCC_Geometry * geom, + Ng_Mesh * mesh); +#endif // NGLIB_OCC_HPP_INCLUDED 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/py_tutorials/merge.py b/py_tutorials/merge.py index abacd38e..1633e808 100644 --- a/py_tutorials/merge.py +++ b/py_tutorials/merge.py @@ -16,9 +16,9 @@ m2 = geo2.GenerateMesh (maxh=0.05) m2.Refine() m2.Refine() -print ("***************************") -print ("** merging suface meshes **") -print ("***************************") +print ("****************************") +print ("** merging surface meshes **") +print ("****************************") # create an empty mesh mesh = Mesh() diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 00000000..98527864 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1 @@ +version.py diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 96a9cdc7..ccab5494 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,13 +1,38 @@ -if(USE_GUI) - set(IMPORT_TKINTER True) -else() - set(IMPORT_TKINTER False) -endif() -configure_file(__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY) +get_target_property(ngcore_compile_definitions ngcore INTERFACE_COMPILE_DEFINITIONS) +get_property(have_options TARGET ngcore PROPERTY INTERFACE_COMPILE_OPTIONS SET) +if(have_options) + get_target_property(ngcore_compile_options ngcore INTERFACE_COMPILE_OPTIONS) +endif(have_options) + +configure_file(config_template.py ${CMAKE_CURRENT_BINARY_DIR}/config.py @ONLY) +configure_file(version_template.py ${CMAKE_CURRENT_BINARY_DIR}/version.py @ONLY) install(FILES - ${CMAKE_CURRENT_BINARY_DIR}/__init__.py - meshing.py csg.py geom2d.py stl.py gui.py NgOCC.py read_gmsh.py + ${CMAKE_CURRENT_BINARY_DIR}/config.py + ${CMAKE_CURRENT_BINARY_DIR}/version.py + __main__.py __init__.py + meshing.py csg.py geom2d.py stl.py gui.py NgOCC.py occ.py + read_gmsh.py read_meshio.py + webgui.py DESTINATION ${NG_INSTALL_DIR_PYTHON}/${NG_INSTALL_SUFFIX} COMPONENT netgen ) + +install(FILES + pyngcore/__init__.py + DESTINATION ${NG_INSTALL_DIR_PYTHON}/pyngcore + COMPONENT netgen + ) + +# build stub files for pybind11 packages +if(BUILD_STUB_FILES) +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.") +else() + message("-- Found pybind11-stubgen: ${stubgen_path}") + install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pybind11_stubgen --no-setup-py --ignore-invalid=all 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/NgOCC.py b/python/NgOCC.py index 40f0a51d..ebc7fd51 100644 --- a/python/NgOCC.py +++ b/python/NgOCC.py @@ -1,10 +1,7 @@ -from netgen.libngpy._NgOCC import * -from netgen.libngpy._meshing import MeshingParameters -def NgOCC_meshing_func (geom, **args): - if "mp" in args: - return GenerateMesh (geom, args["mp"]) - else: - return GenerateMesh (geom, MeshingParameters (**args)) +import logging +logger = logging.getLogger(__name__) -OCCGeometry.GenerateMesh = NgOCC_meshing_func +logger.warning("This module is deprecated and just a wrapper for netgen.occ, import netgen.occ instead") + +from .occ import * diff --git a/python/__init__.py b/python/__init__.py index 5c8f2dc3..527a2ed2 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -1,17 +1,49 @@ import os import sys -# import tkinter only if Netgen was configured with USE_GUI=ON -if @IMPORT_TKINTER@: - import tkinter +from . import config +_netgen_bin_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..',config.NETGEN_PYTHON_RPATH_BIN)) +_netgen_lib_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..',config.NETGEN_PYTHON_RPATH)) -_netgen_bin_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..','@NETGEN_PYTHON_RPATH_BIN@')) -_netgen_lib_dir=os.path.realpath(os.path.join(os.path.dirname(__file__),'..','@NETGEN_PYTHON_RPATH@')) +__diagnostics_template = """ +Netgen diagnostics: + sys.platform: {sys.platform} + sys.executable: {sys.executable} + sys.version: {sys.version} + Netgen python version: {config.PYTHON_VERSION} + Netgen path {__file__} + Netgen config {config.__file__} + Netgen version {config.NETGEN_VERSION} + sys.path: {sys.path} +""" + +def _get_diagnostics(): + return __diagnostics_template.format(sys=sys, config=config, __file__=__file__) + +# compare compile-time and run-time python version +def _check_python_version(): + sys_version = f"{sys.version_info.major}.{sys.version_info.minor}" + compile_version = f"{config.PYTHON_VERSION_MAJOR}.{config.PYTHON_VERSION_MINOR}" + + if sys_version != compile_version: + print(_get_diagnostics(), file=sys.stderr) + raise RuntimeError(f"Python version mismatch: compile-time version is {compile_version}, run-time version is {sys_version}") + +_check_python_version() if sys.platform.startswith('win'): - os.environ['PATH'] += ';'+os.path.realpath(os.path.join(os.path.dirname(__file__),'../../../bin')) + v = sys.version_info + if v.major == 3 and v.minor >= 8: + os.add_dll_directory(_netgen_bin_dir) + else: + os.environ['PATH'] += ';'+_netgen_bin_dir del sys del os from . import libngpy + +from netgen.libngpy._meshing import _Redraw + +def Redraw(*args, **kwargs): + return _Redraw(*args, **kwargs) diff --git a/python/__main__.py b/python/__main__.py new file mode 100644 index 00000000..585107a6 --- /dev/null +++ b/python/__main__.py @@ -0,0 +1,53 @@ +import importlib.util, threading, sys, os + +def _py_handler(f): + spec = importlib.util.spec_from_file_location(os.path.basename(f), os.path.abspath(f)) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + +def _geo_handler(f): + from netgen.csg import CSGeometry + print("load", f) + geo = CSGeometry(f) + geo.Draw() + +def _step_handler(f): + from netgen.occ import OCCGeometry + print("load", f) + geo = OCCGeometry(f) + geo.Draw() + +def _stl_handler(f): + from netgen.stl import STLGeometry + print("load", f) + geo = STLGeometry(f) + geo.Draw() + +_file_handler = {} +_file_handler['.py'] = _py_handler +_file_handler['.geo'] = _geo_handler +_file_handler['.step'] = _step_handler +_file_handler['.stl'] = _stl_handler + +def handle_arguments(): + import __main__ + import os.path + argv = sys.argv + if len(argv)>1: + _, ext = os.path.splitext(argv[1]) + if ext in _file_handler: + _file_handler[ext](argv[1]) + +def main(): + import netgen + # Use Redraw without event handling + netgen.Redraw = netgen._Redraw + + from .gui import win + th = threading.Thread(target=handle_arguments) + th.start() + win.tk.mainloop() + +if __name__ == "__main__": + sys.exit(main()) diff --git a/python/config_template.py b/python/config_template.py new file mode 100644 index 00000000..38991567 --- /dev/null +++ b/python/config_template.py @@ -0,0 +1,59 @@ +def _cmake_to_bool(s): + return s.upper() not in ['', '0','FALSE','OFF','N','NO','IGNORE','NOTFOUND'] + +is_python_package = _cmake_to_bool("@SKBUILD@") + +BUILD_FOR_CONDA = _cmake_to_bool("@BUILD_FOR_CONDA@") +BUILD_STUB_FILES = _cmake_to_bool("@BUILD_STUB_FILES@") +CHECK_RANGE = _cmake_to_bool("@CHECK_RANGE@") +DEBUG_LOG = _cmake_to_bool("@DEBUG_LOG@") +ENABLE_CPP_CORE_GUIDELINES_CHECK = _cmake_to_bool("@ENABLE_CPP_CORE_GUIDELINES_CHECK@") +ENABLE_UNIT_TESTS = _cmake_to_bool("@ENABLE_UNIT_TESTS@") +INSTALL_PROFILES = _cmake_to_bool("@INSTALL_PROFILES@") +INTEL_MIC = _cmake_to_bool("@INTEL_MIC@") +TRACE_MEMORY = _cmake_to_bool("@TRACE_MEMORY@") +USE_CCACHE = _cmake_to_bool("@USE_CCACHE@") +USE_CGNS = _cmake_to_bool("@USE_CGNS@") +USE_GUI = _cmake_to_bool("@USE_GUI@") +USE_INTERNAL_TCL = _cmake_to_bool("@USE_INTERNAL_TCL@") +USE_JPEG = _cmake_to_bool("@USE_JPEG@") +USE_MPEG = _cmake_to_bool("@USE_MPEG@") +USE_MPI = _cmake_to_bool("@USE_MPI@") +USE_MPI4PY = _cmake_to_bool("@USE_MPI4PY@") +USE_NATIVE_ARCH = _cmake_to_bool("@USE_NATIVE_ARCH@") +USE_NUMA = _cmake_to_bool("@USE_NUMA@") +USE_OCC = _cmake_to_bool("@USE_OCC@") +USE_PYTHON = _cmake_to_bool("@USE_PYTHON@") +USE_SPDLOG = _cmake_to_bool("@USE_SPDLOG@") + +CMAKE_INSTALL_PREFIX = "@CMAKE_INSTALL_PREFIX@" +NG_INSTALL_DIR_PYTHON = "@NG_INSTALL_DIR_PYTHON@" +NG_INSTALL_DIR_BIN = "@NG_INSTALL_DIR_BIN@" +NG_INSTALL_DIR_LIB = "@NG_INSTALL_DIR_LIB@" +NG_INSTALL_DIR_INCLUDE = "@NG_INSTALL_DIR_INCLUDE@" +NG_INSTALL_DIR_CMAKE = "@NG_INSTALL_DIR_CMAKE@" +NG_INSTALL_DIR_RES = "@NG_INSTALL_DIR_RES@" + +NETGEN_PYTHON_RPATH_BIN = "@NETGEN_PYTHON_RPATH_BIN@" +NETGEN_PYTHON_RPATH = "@NETGEN_PYTHON_RPATH@" +NETGEN_PYTHON_PACKAGE_NAME = "@NETGEN_PYTHON_PACKAGE_NAME@" + +NG_COMPILE_FLAGS = "@NG_COMPILE_FLAGS@" +ngcore_compile_options = "@ngcore_compile_options@" +ngcore_compile_definitions = "@ngcore_compile_definitions@" + +NETGEN_VERSION = "@NETGEN_VERSION@" +NETGEN_VERSION_GIT = "@git_version_string@" +NETGEN_VERSION_PYTHON = "@NETGEN_VERSION_PYTHON@" + +NETGEN_VERSION_MAJOR = "@NETGEN_VERSION_MAJOR@" +NETGEN_VERSION_MINOR = "@NETGEN_VERSION_MINOR@" +NETGEN_VERSION_TWEAK = "@NETGEN_VERSION_TWEAK@" +NETGEN_VERSION_PATCH = "@NETGEN_VERSION_PATCH@" +NETGEN_VERSION_HASH = "@NETGEN_VERSION_HASH@" + +PYTHON_VERSION = "@PYTHON_VERSION@" +PYTHON_VERSION_MAJOR = "@PYTHON_VERSION_MAJOR@" +PYTHON_VERSION_MINOR = "@PYTHON_VERSION_MINOR@" + +version = NETGEN_VERSION_GIT diff --git a/python/csg.py b/python/csg.py index f9333179..f6b37f1c 100644 --- a/python/csg.py +++ b/python/csg.py @@ -1,34 +1,19 @@ -from netgen.libngpy._csg import * -from netgen.libngpy._meshing import MeshingParameters -from netgen.libngpy._meshing import Pnt -from netgen.libngpy._meshing import Vec -from netgen.libngpy._meshing import Trafo - +from .libngpy._csg import * +from .libngpy._meshing import Pnt, Vec, Trafo +from .meshing import meshsize try: - import libngpy.csgvis as csgvis - from libngpy.csgvis import MouseMove + from . import csgvis + from .csgvis import MouseMove CSGeometry.VS = csgvis.VS SetBackGroundColor = csgvis.SetBackGroundColor del csgvis def VS (obj): return obj.VS() - except: pass - -def csg_meshing_func (geom, **args): - if "mp" in args: - return GenerateMesh (geom, args["mp"]) - else: - return GenerateMesh (geom, MeshingParameters (**args)) -# return GenerateMesh (geom, MeshingParameters (**args)) - -CSGeometry.GenerateMesh = csg_meshing_func - - unit_cube = CSGeometry() p1 = Plane(Pnt(0,0,0),Vec(-1,0,0)).bc("back") p2 = Plane(Pnt(1,1,1),Vec(1,0,0)).bc("front") @@ -37,5 +22,4 @@ p4 = Plane(Pnt(1,1,1),Vec(0,1,0)).bc("right") p5 = Plane(Pnt(0,0,0),Vec(0,0,-1)).bc("bottom") p6 = Plane(Pnt(1,1,1),Vec(0,0,1)).bc("top") unit_cube.Add (p1*p2*p3*p4*p5*p6, col=(0,0,1)) -# unit_cube.Add (OrthoBrick(Pnt(0,0,0), Pnt(1,1,1))) diff --git a/python/geom2d.py b/python/geom2d.py index 34b37870..3bd69d14 100644 --- a/python/geom2d.py +++ b/python/geom2d.py @@ -1,24 +1,13 @@ -from netgen.libngpy._geom2d import * -from netgen.libngpy._meshing import * - -tmp_generate_mesh = SplineGeometry.GenerateMesh - -def geom2d_meshing_func (geom, **args): - if "mp" in args: - return tmp_generate_mesh (geom, args["mp"]) - else: - return tmp_generate_mesh (geom, MeshingParameters (**args)) - - -SplineGeometry.GenerateMesh = geom2d_meshing_func - +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) ] -lines = [ (0,1,1,"bottom"), (1,2,2,"right"), (2,3,3,"top"), (3,0,4,"left") ] -pnums = [unit_square.AppendPoint(*p) for p in pnts] -for l1,l2,bc,bcname in lines: - unit_square.Append( ["line", pnums[l1], pnums[l2]], bc=bcname) +_pnts = [ (0,0), (1,0), (1,1), (0,1) ] +_lines = [ (0,1,1,"bottom"), (1,2,2,"right"), (2,3,3,"top"), (3,0,4,"left") ] +_pnums = [unit_square.AppendPoint(*p) for p in _pnts] +for l1,l2,bc,bcname in _lines: + unit_square.Append( ["line", _pnums[l1], _pnums[l2]], bc=bcname) def MakeRectangle (geo, p1, p2, bc=None, bcs=None, **args): @@ -91,6 +80,8 @@ Returned is a dict with information to create the pml layer: start_ndoms = ndoms = geo.GetNDomains() + 1 new_spline_domains = [] normals = {} + duplicate_cnt = 0 + for i, spline in enumerate(border): if i == 0: global_start = Start(spline) + pml_size * spline.GetNormal(0) @@ -98,6 +89,12 @@ Returned is a dict with information to create the pml layer: next_spline = border[(i+1)%len(border)] new_end = End(spline) + pml_size * spline.GetNormal(1) spline_name = geo.GetBCName(spline.bc) + + if "pml_" + spline_name in normals \ + and normals["pml_" + spline_name] != spline.GetNormal(0): + duplicate_cnt += 1 + spline_name = spline_name + "_duplicate_" + str(duplicate_cnt) + if (new_end - global_start).Norm() < tol: new_spline_domains.append(ndoms) geo.Append(["line", current_start, global_start_pnt], bc="outer_" + spline_name, leftdomain = ndoms) @@ -141,8 +138,41 @@ SplineGeometry.AddSegment = lambda *args, **kwargs : SplineGeometry.Append(*args SplineGeometry.AddPoint = lambda *args, **kwargs : SplineGeometry.AppendPoint(*args, **kwargs) SplineGeometry.CreatePML = CreatePML -__all__ = ['SplineGeometry', 'unit_square'] - +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/gui.py b/python/gui.py index 35fa1f34..5c29ab10 100644 --- a/python/gui.py +++ b/python/gui.py @@ -1,14 +1,33 @@ import netgen +from . import libngguipy +from . import libngpy + def StartGUI(): from tkinter import Tk + from netgen import config + import sys, os + + try: + # the GUI tries to load ngsolve.tcl (which loads ngsolve shared libraries) + # BUT might fail to load dependencies (like mkl), these are handled by the + # ngsolve __init__.py script, so import ngsolve from python already here + import ngsolve + except: + pass global win win = Tk() win.tk.eval('lappend ::auto_path ' + netgen._netgen_lib_dir) win.tk.eval('lappend ::auto_path ' + netgen._netgen_bin_dir) # load with absolute path to avoid issues on MacOS - win.tk.eval('load "'+netgen._netgen_lib_dir.replace('\\','/')+'/libgui[info sharedlibextension]" gui') + win.tk.eval('load "'+netgen._netgen_lib_dir.replace('\\','/')+'/libnggui[info sharedlibextension]" gui') + + if config.is_python_package and 'darwin' in sys.platform: + # libngsolve and other libraries are installed into netgen python dir to keep relative installation paths, but tcl won't find them there automatically + netgen_dir = os.path.abspath(os.path.dirname(netgen.__file__)) + win.tk.eval(f'set netgen_library_dir {netgen_dir}') + win.tk.eval( netgen.libngpy._meshing._ngscript) try: @@ -17,6 +36,47 @@ def StartGUI(): ipython.magic('gui tk') except: pass + + def _Redraw(*args, **kwargs): + if libngpy._meshing._Redraw(*args, **kwargs): + import netgen + import tkinter + cnt = 0 + while(win.tk.dooneevent(tkinter._tkinter.DONT_WAIT) and cnt < 100): + cnt += 1 + + netgen._Redraw = _Redraw + _Redraw(blocking=True) + -if not netgen.libngpy._meshing._netgen_executable_started: + +def Snapshot(w,h, filename=None): + netgen.Redraw(blocking=True) + import numpy + image = netgen.libngguipy.Snapshot(w, h) + image = numpy.array(image, dtype=numpy.uint8).reshape(h, w, 3) + image = image[::-1,:,:] + if filename: + import PIL.Image + im = PIL.Image.fromarray(image) + im.save(filename) + return image + +def run_pyfile(filename): + with open(filename) as f: + exec(f.read(), {}) + +if __name__ == "__main__": + import sys, threading StartGUI() + if(len(sys.argv) > 1): + if(sys.argv[1].endswith(".py")): + t = threading.Thread(target=run_pyfile, args=(sys.argv[1],), + daemon=True) + t.start() + win.mainloop() +else: + if not netgen.libngpy._meshing._netgen_executable_started: + import os + if not "NETGEN_DOCUMENTATION_RST_FORMAT" in os.environ: + StartGUI() diff --git a/python/meshing.py b/python/meshing.py index f6ad65ce..9b912b66 100644 --- a/python/meshing.py +++ b/python/meshing.py @@ -1 +1,64 @@ -from netgen.libngpy._meshing import * +from .libngpy._meshing import * + +class _MeshsizeObject: + @property + def very_coarse(self): + return MeshingParameters(curvaturesafety=1, + segmentsperedge=0.3, + grading=0.7, + chartdistfac=0.8, + linelengthfac=0.2, + closeedgefac=0.5, + minedgelen=0.002, + surfmeshcurvfac=1., + optsteps3d=5) + @property + def coarse(self): + return MeshingParameters(curvaturesafety=1.5, + segmentsperedge=0.5, + grading=0.5, + chartdistfac=1, + linelengthfac=0.35, + closeedgefac=1, + minedgelen=0.02, + surfmeshcurvfac=1.5, + optsteps3d=5) + @property + def moderate(self): + return MeshingParameters(curvaturesafety=2, + segmentsperedge=1, + grading=0.3, + chartdistfac=1.5, + linelengthfac=0.5, + closeedgefac=2, + minedgelen=0.2, + surfmeshcurvfac=2., + optsteps3d=5) + @property + def fine(self): + return MeshingParameters(curvaturesafety=3, + segmentsperedge=2, + grading=0.2, + chartdistfac=2, + linelengthfac=1.5, + closeedgefac=3.5, + minedgelen=1., + surfmeshcurvfac=3., + optsteps3d=5) + + @property + def very_fine(self): + return MeshingParameters(curvaturesafety=5, + segmentsperedge=3, + grading=0.1, + chartdistfac=5, + linelengthfac=3, + closeedgefac=5, + minedgelen=2., + surfmeshcurvfac=5., + optsteps3d=5) + +meshsize = _MeshsizeObject() + + +clearsol = ClearSolutionClass() diff --git a/python/occ.py b/python/occ.py new file mode 100644 index 00000000..666c91ed --- /dev/null +++ b/python/occ.py @@ -0,0 +1,52 @@ +""" Netgen OCC documentation + +This module provides the Netgen OCCGeometry class, as well as Python wrappers +for OpenCascade functionality. This allows the construction from scratch, as well as +editing geometries imported via STEP format. + +Most of the classes are directly py-wrapped from the OCCT classes. +For more detailed documentation consult the OCCT docs, a good starting point is +https://dev.opencascade.org/doc/refman/html/class_b_rep_builder_a_p_i___make_shape.html +""" + +from .config import USE_OCC +if not USE_OCC: + raise ImportError("Netgen was not built with Opencascade support") + +from .libngpy._NgOCC import * +from .meshing import meshsize + + +gp_Ax3 = Axes +gp_Ax1 = Axis + +Translation = gp_Trsf.Translation +Rotation = gp_Trsf.Rotation +Mirror = gp_Trsf.Mirror + + +def Rectangle(l,w): return WorkPlane().Rectangle(l,w) +def MoveTo(x,y): return WorkPlane().MoveTo(x,y) +def LineTo(x,y): return WorkPlane().LineTo(x,y) +def Line(l): return WorkPlane().Line(l) + + +unit_square_shape = WorkPlane().Line(1, name="bottom").Rotate(90) \ + .Line(1, name="right").Rotate(90) \ + .Line(1, name="top").Rotate(90) \ + .Line(1, name="left").Rotate(90).Face() + +unit_square = OCCGeometry(unit_square_shape, dim=2) + + + +uc_shape = Box((0,0,0),(1,1,1)) +uc_shape.faces.Max(X).name = "front" +uc_shape.faces.Min(X).name = "back" +uc_shape.faces.Max(Y).name = "right" +uc_shape.faces.Min(Y).name = "left" +uc_shape.faces.Max(Z).name = "top" +uc_shape.faces.Min(Z).name = "bottom" + +unit_cube = OCCGeometry(uc_shape) + diff --git a/python/pyngcore/__init__.py b/python/pyngcore/__init__.py new file mode 100644 index 00000000..29580174 --- /dev/null +++ b/python/pyngcore/__init__.py @@ -0,0 +1 @@ +from .pyngcore import * diff --git a/python/read_meshio.py b/python/read_meshio.py new file mode 100644 index 00000000..5cd8a4f1 --- /dev/null +++ b/python/read_meshio.py @@ -0,0 +1,22 @@ +from netgen.meshing import * + +def ReadViaMeshIO(filename): + import meshio + import numpy as np + + # print ("reading via meshio:", filename) + + m = meshio.read(filename) + pts = m.points + + mesh = Mesh(dim=pts.shape[1]) + # mesh.AddPoints ( np.asarray(pts, dtype=np.float64) ) # needed for correct little/big endian + mesh.AddPoints ( pts ) + + fd = mesh.Add (FaceDescriptor(bc=1)) + for cb in m.cells: + # mesh.AddElements(dim=cb.dim, index=1, data=np.asarray(cb.data, dtype=np.int32), base=0) + mesh.AddElements(dim=cb.dim, index=1, data=cb.data, base=0) + + return mesh + diff --git a/python/stl.py b/python/stl.py index 032f2b69..40f4421a 100644 --- a/python/stl.py +++ b/python/stl.py @@ -1,12 +1,2 @@ from netgen.libngpy._stl import * -from netgen.libngpy._meshing import MeshingParameters - - -def stl_meshing_func (geom, **args): - if "mp" in args: - return GenerateMesh (geom, args["mp"]) - else: - return GenerateMesh (geom, MeshingParameters (**args)) -# return GenerateMesh (geom, MeshingParameters (**args)) - -STLGeometry.GenerateMesh = stl_meshing_func +from .meshing import meshsize diff --git a/python/version_template.py b/python/version_template.py new file mode 100644 index 00000000..1a0977fe --- /dev/null +++ b/python/version_template.py @@ -0,0 +1,2 @@ +__version__ = "@NETGEN_VERSION_PYTHON@" +__package_name__ = "@NETGEN_PYTHON_PACKAGE_NAME@" diff --git a/python/webgui.py b/python/webgui.py new file mode 100644 index 00000000..a093b635 --- /dev/null +++ b/python/webgui.py @@ -0,0 +1,250 @@ +import math +import numpy as np +from time import time +import os + +from webgui_jupyter_widgets import BaseWebGuiScene, encodeData, WebGuiDocuWidget +import webgui_jupyter_widgets.widget as wg + +class WebGLScene(BaseWebGuiScene): + def __init__(self, mesh, clipping, on_init): + from IPython.display import display, Javascript + import threading + self.mesh = mesh + self.clipping = clipping + self.on_init = on_init + + def GetData(self, set_minmax=True): + import json + # d = BuildRenderData(self.mesh, self.cf, self.order, draw_surf=self.draw_surf, draw_vol=self.draw_vol, deformation=self.deformation, region=self.region) + d = self.mesh._webgui_data() + bp = d['Bezier_trig_points'] + for i in range(len(bp)): + bp[i] = encodeData(np.array(bp[i], dtype=np.float32)) + + ep = d['edges'] + for i in range(len(ep)): + ep[i] = encodeData(np.array(ep[i], dtype=np.float32)) + + if self.clipping is not None: + d['clipping'] = True + if isinstance(self.clipping, dict): + allowed_args = ("x", "y", "z", "dist", "function", "pnt", "vec") + if "vec" in self.clipping: + vec = self.clipping["vec"] + self.clipping["x"] = vec[0] + self.clipping["y"] = vec[1] + self.clipping["z"] = vec[2] + if "pnt" in self.clipping: + d['mesh_center'] = list(self.clipping["pnt"]) + for name, val in self.clipping.items(): + if not (name in allowed_args): + raise Exception('Only {} allowed as arguments for clipping!'.format(", ".join(allowed_args))) + d['clipping_' + name] = val + + if self.on_init: + d['on_init'] = self.on_init + + return d + + +bezier_trig_trafos = { } # cache trafos for different orders + +def BuildRenderData(mesh, func, order=2, draw_surf=True, draw_vol=True, deformation=None, region=True): + d = {} + d['ngsolve_version'] = "Netgen" + d['mesh_dim'] = 3 # mesh.dim TODO + + d['order2d'] = 1 + d['order3d'] = 0 + + d['draw_vol'] = False + d['draw_surf'] = True + d['funcdim'] = 1 + + func2 = None + if func and func.is_complex: + d['is_complex'] = True + func1 = func[0].real + func2 = ngs.CoefficientFunction( (func[0].imag, 0.0) ) + elif func and func.dim>1: + func1 = func[0] + func2 = ngs.CoefficientFunction( tuple(func[i] if i we are just drawing a mesh, eval mesh element index instead + mats = mesh.GetMaterials() + bnds = mesh.GetBoundaries() + nmats = len(mesh.GetMaterials()) + nbnds = len(mesh.GetBoundaries()) + n = max(nmats, nbnds) + func1 = ngs.CoefficientFunction(list(range(n))) + n_regions = [0, 0, nmats, nbnds] + d['mesh_regions_2d'] = n_regions[mesh.dim] + d['mesh_regions_3d'] = nmats if mesh.dim==3 else 0 + d['funcdim'] = 0 + func1 = ngs.CoefficientFunction( (ngs.x, ngs.y, ngs.z, func1 ) ) + func0 = ngs.CoefficientFunction( (ngs.x, ngs.y, ngs.z, 0.0 ) ) + if deformation is not None: + func1 += ngs.CoefficientFunction((deformation, 0.0)) + func0 += ngs.CoefficientFunction((deformation, 0.0)) + + d['show_wireframe'] = False + d['show_mesh'] = True + if order2d>0: + og = order2d + d['show_wireframe'] = True + d['show_mesh'] = True + timer2.Start() + + timer3Bvals.Start() + + # transform point-values to Bernsteinbasis + def Binomial(n,i): return math.factorial(n) / math.factorial(i) / math.factorial(n-i) + def Bernstein(x, i, n): return Binomial(n,i) * x**i*(1-x)**(n-i) + Bvals = ngs.Matrix(og+1,og+1) + for i in range(og+1): + for j in range(og+1): + Bvals[i,j] = Bernstein(i/og, j, og) + iBvals = Bvals.I + timer3Bvals.Stop() + # print (Bvals) + # print (iBvals) + + + Bezier_points = [] + + ipts = [(i/og,0) for i in range(og+1)] + [(0, i/og) for i in range(og+1)] + [(i/og,1.0-i/og) for i in range(og+1)] + ir_trig = ngs.IntegrationRule(ipts, [0,]*len(ipts)) + ipts = [(i/og,0) for i in range(og+1)] + [(0, i/og) for i in range(og+1)] + [(i/og,1.0) for i in range(og+1)] + [(1.0, i/og) for i in range(og+1)] + ir_quad = ngs.IntegrationRule(ipts, [0,]*len(ipts)) + + vb = [ngs.VOL, ngs.BND][mesh.dim-2] + if region and region.VB() == vb: + vb = region + cf = func1 if draw_surf else func0 + timer2map.Start() + pts = mesh.MapToAllElements({ngs.ET.TRIG: ir_trig, ngs.ET.QUAD: ir_quad}, vb) + timer2map.Stop() + pmat = cf(pts) + + timermult.Start() + pmat = pmat.reshape(-1, og+1, 4) + if False: + BezierPnts = np.tensordot(iBvals.NumPy(), pmat, axes=(1,1)) + else: + BezierPnts = np.zeros( (og+1, pmat.shape[0], 4) ) + for i in range(4): + ngsmat = ngs.Matrix(pmat[:,:,i].transpose()) + BezierPnts[:,:,i] = iBvals * ngsmat + timermult.Stop() + + timer2list.Start() + for i in range(og+1): + Bezier_points.append(encodeData(BezierPnts[i], dtype=np.float32)) + timer2list.Stop() + + d['Bezier_points'] = Bezier_points + + ipts = [(i/og,0) for i in range(og+1)] + ir_seg = ngs.IntegrationRule(ipts, [0,]*len(ipts)) + vb = [ngs.VOL, ngs.BND, ngs.BBND][mesh.dim-1] + if region and region.VB() == vb: + vb = region + pts = mesh.MapToAllElements(ir_seg, vb) + pmat = func0(pts) + pmat = pmat.reshape(-1, og+1, 4) + edge_data = np.tensordot(iBvals.NumPy(), pmat, axes=(1,1)) + edges = [] + for i in range(og+1): + edges.append(encodeData(edge_data[i], dtype=np.float32)) + d['edges'] = edges + + ndtrig = int((og+1)*(og+2)/2) + + if og in bezier_trig_trafos.keys(): + iBvals_trig = bezier_trig_trafos[og] + else: + def BernsteinTrig(x, y, i, j, n): + return math.factorial(n)/math.factorial(i)/math.factorial(j)/math.factorial(n-i-j) \ + * x**i*y**j*(1-x-y)**(n-i-j) + Bvals = ngs.Matrix(ndtrig, ndtrig) + ii = 0 + for ix in range(og+1): + for iy in range(og+1-ix): + jj = 0 + for jx in range(og+1): + for jy in range(og+1-jx): + Bvals[ii,jj] = BernsteinTrig(ix/og, iy/og, jx, jy, og) + jj += 1 + ii += 1 + iBvals_trig = Bvals.I + bezier_trig_trafos[og] = iBvals_trig + + + # Bezier_points = [ [] for i in range(ndtrig) ] + Bezier_points = [] + + ipts = [(i/og,j/og) for j in range(og+1) for i in range(og+1-j)] + ir_trig = ngs.IntegrationRule(ipts, [0,]*len(ipts)) + ipts = ([(i/og,j/og) for j in range(og+1) for i in range(og+1-j)] + + [(1-i/og,1-j/og) for j in range(og+1) for i in range(og+1-j)]) + ir_quad = ngs.IntegrationRule(ipts, [0,]*len(ipts)) + + vb = [ngs.VOL, ngs.BND][mesh.dim-2] + if region and region.VB() == vb: + vb = region + pts = mesh.MapToAllElements({ngs.ET.TRIG: ir_trig, ngs.ET.QUAD: ir_quad}, vb) + + pmat = ngs.CoefficientFunction( func1 if draw_surf else func0 ) (pts) + + + funcmin = np.min(pmat[:,3]) + funcmax = np.max(pmat[:,3]) + pmin = np.min(pmat[:,0:3], axis=0) + pmax = np.max(pmat[:,0:3], axis=0) + + mesh_center = 0.5*(pmin+pmax) + mesh_radius = np.linalg.norm(pmax-pmin)/2 + + pmat = pmat.reshape(-1, len(ir_trig), 4) + + BezierPnts = np.tensordot(iBvals_trig.NumPy(), pmat, axes=(1,1)) + + for i in range(ndtrig): + Bezier_points.append(encodeData(BezierPnts[i], dtype=np.float32)) + + + d['Bezier_trig_points'] = Bezier_points + d['mesh_center'] = list(mesh_center) + d['mesh_radius'] = mesh_radius + + + + if func: + d['funcmin'] = funcmin + d['funcmax'] = funcmax + return d + +def Draw(shape, clipping=None, js_code=None, filename=""): + # todo: also handle occ geometry, list of shapes, etc. + + scene = WebGLScene(shape, clipping=clipping, on_init=js_code) + + if wg._IN_IPYTHON: + if wg._IN_GOOGLE_COLAB: + from IPython.display import display, HTML + html = scene.GenerateHTML() + display(HTML(html)) + else: + # render scene using widgets.DOMWidget + scene.Draw() + return scene + else: + if filename: + scene.GenerateHTML(filename=filename) + return scene + diff --git a/rules/CMakeLists.txt b/rules/CMakeLists.txt new file mode 100644 index 00000000..82dad7e7 --- /dev/null +++ b/rules/CMakeLists.txt @@ -0,0 +1,29 @@ +# this file is included from the parent directory (otherwise generated source files are not recognized properly by cmake) + +# generate .cpp files containing the string of the .rls meshing rule files +add_executable(makerls rules/makerlsfile.cpp) + +set(rules + hexrules + prismrules2 + pyramidrules + pyramidrules2 + quadrules + tetrules + triarules +) + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/rules/) + +foreach(rule ${rules}) + list(APPEND rules_sources ${CMAKE_CURRENT_BINARY_DIR}/rules/rule_${rule}.cpp) + set(rule_file ${CMAKE_CURRENT_SOURCE_DIR}/rules/${rule}.rls) + set(rule_cpp ${CMAKE_CURRENT_BINARY_DIR}/rules/rule_${rule}.cpp) + + add_custom_command(OUTPUT ${rule_cpp} + COMMAND makerls ${rule_file} ${rule_cpp} ${rule} + DEPENDS makerls ${rule_file} + ) +endforeach() + +target_sources(nglib PRIVATE ${rules_sources}) diff --git a/rules/hexa.rls b/rules/hexrules.rls similarity index 100% rename from rules/hexa.rls rename to rules/hexrules.rls diff --git a/rules/makerlsfile.cpp b/rules/makerlsfile.cpp index 00c253a2..822c721d 100644 --- a/rules/makerlsfile.cpp +++ b/rules/makerlsfile.cpp @@ -1,6 +1,7 @@ #include #include #include +#include using namespace std; @@ -8,9 +9,9 @@ using namespace std; int main (int argc, char ** argv) { - if (argc != 3) + if (argc != 4) { - cout << "use: makerlsfile infile outfile" << endl; + cout << "use: makerlsfile infile outfile rulename" << endl; exit(1); } @@ -28,8 +29,10 @@ int main (int argc, char ** argv) ifstream inf (argv[1]); ofstream outf (argv[2]); + string rulename = argv[3]; - outf << "const char * ngscript[] = {" << endl; + outf << "namespace netgen" << endl << '{' << endl; + outf << "const char * " << rulename << "[] = {" << endl; while (inf.good()) { i = 0; @@ -57,9 +60,10 @@ int main (int argc, char ** argv) inf.get(ch); } line[i] = 0; - cout << line << endl; + // cout << line << endl; outf << "\"" << line << "\\n\",\\" << endl; } outf << "0};" << endl; + outf << '}' << endl; return 0; } diff --git a/rules/prisms2.rls b/rules/prismrules2.rls similarity index 100% rename from rules/prisms2.rls rename to rules/prismrules2.rls diff --git a/rules/pyramids.rls b/rules/pyramidrules.rls similarity index 100% rename from rules/pyramids.rls rename to rules/pyramidrules.rls diff --git a/rules/pyramids2.rls b/rules/pyramidrules2.rls similarity index 85% rename from rules/pyramids2.rls rename to rules/pyramidrules2.rls index 8e659bce..bdce3340 100644 --- a/rules/pyramids2.rls +++ b/rules/pyramidrules2.rls @@ -100,53 +100,6 @@ freeset endrule - - -rule "connect pyramid" - -quality 100 - -mappoints -(0, 0, 0); -(1, 0, 0); -(1, 1, 0); -(0, 1, 0); -(0.5, 0.5, -0.5); - -mapfaces -(1, 2, 3, 4) del; - -newpoints - -newfaces -(1, 2, 5); -(2, 3, 5); -(3, 4, 5); -(4, 1, 5); - -elements -(1, 2, 3, 4, 5); - -freezone2 -{ 1 P1 }; -{ 1 P2 }; -{ 1 P3 }; -{ 1 P4 }; -{ 1 P5 }; - -freeset -1 2 3 5; - -freeset -1 3 4 5; - -endrule - - - - - - rule "pyramid with one trig" quality 1 @@ -210,6 +163,69 @@ freeset endrule +rule "pyramid with one trig, left" + +quality 20 + +mappoints +(0, 0, 0); +(1, 0, 0); +(1, 1, 0); +(0, 1, 0); +(0.5, 0.5, -0.5); + +mapfaces +(1, 2, 3, 4) del; +(3, 2, 5) del; + +newpoints + +newfaces +(1, 2, 5); +(3, 4, 5); +(4, 1, 5); + +elements +(1, 2, 3, 4, 5); + +freezone2 +{ 1 P1 }; +{ 1 P2 }; +{ 1 P3 }; +{ 1 P4 }; +{ 1 P5 }; +{ 0.34 P1, 0.34 P2, 0.34 P5, -0.01 P3 }; +{ 0.34 P3, 0.34 P4, 0.34 P5, -0.02 P1 }; +{ 0.34 P1, 0.34 P4, 0.34 P5, -0.02 P2 }; + +freezonelimit +{ 1 P1 }; +{ 1 P2 }; +{ 1 P3 }; +{ 1 P4 }; +{ 1 P5 }; +{ 0.333 P1, 0.333 P2, 0.334 P5, 0 P3 }; +{ 0.333 P3, 0.333 P4, 0.334 P5, 0 P1 }; +{ 0.333 P1, 0.333 P4, 0.334 P5, 0 P2 }; + +orientations +(1, 2, 3, 5); +(1, 3, 4, 5); + + +freeset +1 2 3 5; +freeset +1 3 4 5; +freeset +1 2 5 6; +freeset +3 4 5 7; +freeset +1 4 5 8; +endrule + + diff --git a/rules/quad.rls b/rules/quadrules.rls similarity index 84% rename from rules/quad.rls rename to rules/quadrules.rls index b2ef50be..1716d308 100644 --- a/rules/quad.rls +++ b/rules/quadrules.rls @@ -160,6 +160,49 @@ orientations endrule +rule "Quad P Right (150)" + +quality 150 + +mappoints +(0, 0); +(1, 0); +(1, 1); + +maplines +(1, 2) del; + +newpoints +(0, 1) { 1 X2, -1 X3 } { 1 Y3 }; + +newlines +(1, 4); +(4, 3); +(3, 2); + +freearea +(0, 0); +(1, 0) { 1 X2 } { }; +(1.2, 0.5) { 0.7 X2, 0.5 X3 } { 0.5 Y3 }; +(1, 1) { 1 X3 } { 1 Y3 }; +(-0.5, 1.5) { -2 X2, 1.5 X3 } { 1.5 Y3 }; + +freearea2 +(0, 0); +(1, 0) { 1 X2 } { }; +(1, 0.5) { 0.5 X2, 0.5 X3 } { 0.5 Y3 }; +(1, 1) { 1 X3 } { 1 Y3 }; +(0, 1) { 1 X2, -1 X3 } { 1 Y3 }; + +elements +(1, 2, 3, 4); + +orientations +(1, 2, 3); + +endrule + + rule "Quad Right PL (2)" quality 2 @@ -297,6 +340,48 @@ endrule +rule "Left P Quad (150)" + +quality 150 + +mappoints +(0, 0); +(1, 0); +(0, 1); + +maplines +(1, 2) del; + +newpoints +(1, 1) { 1 X2, -1 X3 } { 1 Y3 }; + +newlines +(1, 3); +(3, 4); +(4, 2); + +freearea +(0, 0); +(1, 0) { 1 X2 } { }; +(1.5, 1.5) { 1.5 X2, 1.5 X3 } { 1.5 Y3 }; +(0, 1) { 1 X3 } { 1 Y3 }; +(-0.2, 0.6) { -0.2 X2, 0.6 X3 } { 0.6 Y3 }; + +freearea2 +(0, 0); +(1, 0) { 1 X2 } { }; +(1, 1) { 1 X2, -1 X3 } { 1 Y3 }; +(0, 1) { 1 X3 } { 1 Y3 }; +(0, 0.5) { 0.5 X3 } { 0.5 Y3 }; + +elements +(1, 2, 4, 3); + +endrule + + + + rule "Left Quad RP (2)" @@ -714,6 +799,43 @@ endrule +rule "Triangle Vis A Vis (200)" + +quality 200 + +mappoints +(0, 0); +(1, 0); +(0.5, 0.866); + +maplines +(1, 2) del; + +newpoints + +newlines +(1, 3); +(3, 2); + +freearea +(0, 0); +(1, 0) { 1 X2 } { }; +(1.2, 0.693) { 0.8 X2, 0.8 X3 } { 0.8 Y2, 0.8 Y3 }; +(0.5, 0.866) { 1 X3 } { 1 Y3 }; +(-0.2, 0.693) { -0.6 X2, 0.8 X3 } { -0.6 Y2, 0.8 Y3 }; + +freearea2 +(0, 0); +(1, 0) { 1 X2 } { }; +(0.75, 0.433) { 0.5 X2, 0.5 X3 } { 0.5 Y2, 0.5 Y3 }; +(0.5, 0.866) { 1 X3 } { 1 Y3 }; +(0.25, 0.433) { 0.5 X3 } { 0.5 Y3 }; + +elements +(1, 2, 3); + +endrule + rule "2 h Vis A Vis (1)" diff --git a/rules/tetra.rls b/rules/tetrules.rls similarity index 98% rename from rules/tetra.rls rename to rules/tetrules.rls index 81301fcd..faad6c43 100644 --- a/rules/tetra.rls +++ b/rules/tetrules.rls @@ -13,8 +13,8 @@ mapfaces (1, 2, 3) del; newpoints -(0.5, 0.288, -0.816) - { 0.333 X1, 0.333 X2, 0.333 X3 } +(0.5, 0.288, -0.816) + { 0.333 X1, 0.333 X2, 0.333 X3 } { 0.333 Y1, 0.333 Y2, 0.333 Y3 } { }; newfaces @@ -30,9 +30,9 @@ freezone2 { 1 P2 }; { 1 P3 }; { 1.6 P4, -0.2 P1, -0.2 P2, -0.2 P3 }; -{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 }; -{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 }; -{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 }; +{ -0.5 P1, 0.5 P2, 0.5 P3, 0.5 P4 }; +{ 0.5 P1, -0.5 P2, 0.5 P3, 0.5 P4 }; +{ 0.5 P1, 0.5 P2, -0.5 P3, 0.5 P4 }; endrule @@ -139,7 +139,7 @@ endrule rule "Tetrahedron Vis a Vis Point (1)" -quality 1 +quality 100 mappoints (0, 0, 0); @@ -356,7 +356,7 @@ mappoints (0.5, 0.866, 0) { 0.5 }; (0, 0, -0.816) { 0.5 }; (1, 0, -0.816) { 0.5 }; -(0.5, 0.866, -0.816) { 0.5 }; +(0.5, 0.866, -0.816) { 0.5 }; mapfaces (1, 2, 3) del; @@ -505,10 +505,10 @@ mapfaces (3, 2, 5) del; newpoints -(0, 0.578, -0.816) - { -1 X2, 1 X3, 1 X4 } - { -1 Y2, 1 Y3, 1 Y4 } - { -1 Z2, 1 Z3, 1 Z4 }; +(0, 0.578, -0.816) + { -1 X2, 1 X3, 1 X4 } + { -1 Y2, 1 Y3, 1 Y4 } + { -1 Z2, 1 Z3, 1 Z4 }; newfaces (1, 2, 4); @@ -686,7 +686,7 @@ mapfaces newpoints (0.5, 0.288, -0.816) - { -0.5 X1, -0.5 X2, 1 X3, 1 X4 } + { -0.5 X1, -0.5 X2, 1 X3, 1 X4 } { -0.5 Y1, -0.5 Y2, 1 Y3, 1 Y4} { -0.5 Z1, -0.5 Z2, 1 Z3, 1 Z4}; @@ -1459,4 +1459,3 @@ freezone (0.5, 0.288, -0.15) { 0.333 X2, 0.333 X3 } { 0.333 Y3 } { }; endrule - diff --git a/rules/triangle.rls b/rules/triarules.rls similarity index 100% rename from rules/triangle.rls rename to rules/triarules.rls diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..bea48af3 --- /dev/null +++ b/setup.py @@ -0,0 +1,125 @@ +import glob +import os +import sys +import pathlib + +from skbuild import setup +import skbuild.cmaker +from subprocess import check_output +from distutils.sysconfig import get_python_lib; + +setup_requires = [] + +def install_filter(cmake_manifest): + print(cmake_manifest) + return cmake_manifest + +def _patched_parse_manifests(self): + paths = \ + glob.glob(os.path.join(skbuild.cmaker.CMAKE_BUILD_DIR(), "netgen", "install_manifest*.txt")) + try: + return [self._parse_manifest(path) for path in paths][0] + except IndexError: + return [] + +# we are using the netgen superbuild (to download and build some dependencies) +# patch the parse_manifests function to point to the actual netgen cmake project within the superbuild +skbuild.cmaker.CMaker._parse_manifests = _patched_parse_manifests + +git_version = check_output(['git', 'describe', '--tags']).decode('utf-8').strip() +version = git_version[1:].split('-') +if len(version)>2: + version = version[:2] +if len(version)>1: + version = '.post'.join(version) + '.dev' +else: + version = version[0] + +py_install_dir = get_python_lib(1,0,'').replace('\\','/') + +name = "netgen-mesher" +arch = None +cmake_args = [ + f'-DNETGEN_VERSION_GIT={git_version}', + f'-DNETGEN_VERSION_PYTHON={version}', + ] + +if 'NETGEN_ARCH' in os.environ and os.environ['NETGEN_ARCH'] == 'avx2': + # build for avx2 architecture + if 'darwin' in sys.platform: + flag = "'-Xarch_x86_64;-march=core-avx2'" + elif 'win' in sys.platform: + flag = '/AVX2' + else: + flag = '-march=core-avx2' + cmake_args += [f'-DNG_COMPILE_FLAGS={flag}'] + +if 'NETGEN_CCACHE' in os.environ: + cmake_args += [f'-DUSE_CCACHE=ON'] + +packages = ['netgen', 'pyngcore'] + +if 'darwin' in sys.platform: + cmake_args += [ + '-DNG_INSTALL_DIR_LIB=netgen', + '-DNG_INSTALL_DIR_PYTHON=.', + '-DNG_INSTALL_DIR_BIN=bin', + '-DNG_INSTALL_DIR_CMAKE=netgen/cmake', + '-DNG_INSTALL_DIR_INCLUDE=netgen/include', + '-DNG_INSTALL_DIR_RES=share', + ] +elif 'win' in sys.platform: + cmake_args += [ + '-A Win64', + '-DNG_INSTALL_DIR_BIN=netgen', + '-DNG_INSTALL_DIR_PYTHON=.', + '-DNG_INSTALL_DIR_LIB=netgen/lib', + '-DNG_INSTALL_DIR_CMAKE=netgen/cmake', + '-DNG_INSTALL_DIR_INCLUDE=netgen/include', + ] +elif 'linux' in sys.platform: + name_dir = name.replace('-','_') + cmake_args += [ + f'-DNG_INSTALL_DIR_LIB={py_install_dir}/{name_dir}.libs', + '-DNG_INSTALL_DIR_BIN=bin', + '-DNG_INSTALL_DIR_INCLUDE=include/netgen', + '-DTCL_INCLUDE_PATH=/usr/include', + '-DTK_INCLUDE_PATH=/usr/include', + ] + packages = [] + +cmake_args += [ + '-DUSE_SUPERBUILD:BOOL=ON', + '-DUSE_CCACHE:BOOL=ON', + '-DUSE_GUI=ON', + '-DUSE_NATIVE_ARCH=OFF', + '-DBUILD_ZLIB=ON', + '-DBUILD_OCC=ON', + '-DUSE_OCC=ON', + '-DBUILD_FOR_CONDA=ON', + f'-DNETGEN_PYTHON_PACKAGE_NAME={name}', + '-DBUILD_STUB_FILES=OFF', +] + +pyprefix = pathlib.Path(sys.prefix).as_posix() +cmake_args += [f'-DCMAKE_PREFIX_PATH={pyprefix}'] + +setup( + name=name, + version=version, + description="Netgen", + author='The Netgen team', + license="LGPL2.1", + packages=packages, + #package_dir={'netgen': 'python'}, + tests_require=['pytest'], + #include_package_data=True, + cmake_process_manifest_hook=install_filter, + cmake_args = cmake_args, + setup_requires=setup_requires, + entry_points={ + 'console_scripts': [ + 'netgen = netgen.__main__:main', + ], +}, +) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a0783363..55fcd1ee 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,2 +1,7 @@ add_subdirectory(catch) add_subdirectory(pytest) + +# this code goes here, because tests is the last add_subdirectory (otherwise it gets executed too early) +if(APPLE AND BUILD_FOR_CONDA) + install(CODE "execute_process(COMMAND sh -c \"codesign --force -s - netgen/*.so netgen/*.dylib pyngcore/*.so\" WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX})") +endif(APPLE AND BUILD_FOR_CONDA) diff --git a/tests/build.sh b/tests/build.sh deleted file mode 100755 index 68aea3ad..00000000 --- a/tests/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -cd -mkdir -p build/netgen -cd build/netgen -cmake ../../src/netgen -DUSE_CCACHE=ON -make -j12 -make install diff --git a/tests/build_debug.sh b/tests/build_debug.sh new file mode 100755 index 00000000..b89f3145 --- /dev/null +++ b/tests/build_debug.sh @@ -0,0 +1,15 @@ +cd +mkdir -p build/netgen +cd build/netgen +cmake \ + -DCMAKE_CXX_FLAGS="-Og -Wall -Wno-sign-compare -DDebug" \ + -DUSE_CCACHE=ON \ + -DCMAKE_BUILD_TYPE=DEBUG \ + -DENABLE_UNIT_TESTS=ON \ + -DUSE_OCC=ON \ + -DCHECK_RANGE=ON \ + -DUSE_CGNS=ON \ + -DCMAKE_INSTALL_PREFIX=/usr \ + ../../src/netgen +make -j12 +make install diff --git a/tests/build_mpi.sh b/tests/build_mpi.sh new file mode 100644 index 00000000..0e26af94 --- /dev/null +++ b/tests/build_mpi.sh @@ -0,0 +1,10 @@ +cd +mkdir -p build/netgen +cd build/netgen +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 new file mode 100755 index 00000000..d452d2da --- /dev/null +++ b/tests/build_ngsolve.sh @@ -0,0 +1,13 @@ +cd ~/src +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 \ + -DCMAKE_BUILD_TYPE=DEBUG \ + ~/src/ngsolve +make -j12 +make install diff --git a/tests/build_pip.ps1 b/tests/build_pip.ps1 new file mode 100644 index 00000000..909b5b3e --- /dev/null +++ b/tests/build_pip.ps1 @@ -0,0 +1,15 @@ +if (test-path _skbuild) { + cmd.exe /c rd /s /q _skbuild +} +if (test-path dist) { + cmd.exe /c rd /s /q dist +} + +$env:NETGEN_CCACHE = 1 +$env:NETGEN_ARCH = 'avx2' + +$pydir=$args[0] +& $pydir\python.exe --version +& $pydir\python.exe -m pip install scikit-build wheel numpy twine pybind11-stubgen +& $pydir\python setup.py bdist_wheel -G"Visual Studio 16 2019" +& $pydir\python -m twine upload dist\*.whl diff --git a/tests/build_pip.sh b/tests/build_pip.sh new file mode 100755 index 00000000..09c6d8d2 --- /dev/null +++ b/tests/build_pip.sh @@ -0,0 +1,29 @@ +set -e +ulimit -n 1024000 # lower open file limit, seems to affect performance +yum -y update +yum -y install ninja-build fontconfig-devel tk-devel tcl-devel libXmu-devel mesa-libGLU-devel ccache + +rm -rf wheelhouse +export NETGEN_CCACHE=1 + +/opt/python/cp39-cp39/bin/python tests/fix_auditwheel_policy.py + +for pyversion in 38 39 310 311 +do + export PYDIR="/opt/python/cp${pyversion}-cp${pyversion}/bin" + echo $PYDIR + $PYDIR/pip install -U pytest-check numpy wheel scikit-build pybind11-stubgen + + rm -rf _skbuild + NETGEN_ARCH=avx2 $PYDIR/pip wheel . + auditwheel repair netgen_mesher*-cp${pyversion}-*.whl + rm netgen_mesher-*.whl + + $PYDIR/pip install wheelhouse/netgen_mesher*-cp${pyversion}-*.whl + $PYDIR/python3 -c 'import netgen' + #cd ../tests/pytest + #$PYDIR/python3 -m pytest +done + +$PYDIR/pip install -U twine +$PYDIR/twine upload wheelhouse/*manylinux*.whl diff --git a/tests/build_pip_mac.sh b/tests/build_pip_mac.sh new file mode 100755 index 00000000..22ea0d4f --- /dev/null +++ b/tests/build_pip_mac.sh @@ -0,0 +1,14 @@ +set -e +rm -rf _skbuild dist + +export PATH=/Applications/CMake.app/Contents/bin:$PATH +export NETGEN_CCACHE=1 + +export PYDIR=/Library/Frameworks/Python.framework/Versions/$1/bin +$PYDIR/python3 --version +$PYDIR/pip3 install --user numpy twine scikit-build wheel pybind11-stubgen + +export CMAKE_OSX_ARCHITECTURES='arm64;x86_64' +export NETGEN_ARCH='avx2' +$PYDIR/python3 setup.py bdist_wheel --plat-name macosx-10.15-universal2 -j10 +$PYDIR/python3 -m twine upload dist/*.whl diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index f0a25744..fce02cdc 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -14,7 +14,7 @@ add_test(NAME unit_tests_built COMMAND ${CMAKE_COMMAND} --build . --target unit_ macro(add_unit_test name sources) add_executable(test_${name} ${sources} ) - target_link_libraries(test_${name} ngcore catch_main) + target_link_libraries(test_${name} ngcore catch_main nglib) add_dependencies(unit_tests test_${name}) add_test(NAME unit_${name} COMMAND test_${name}) @@ -26,7 +26,12 @@ macro(add_unit_test name sources) endmacro() add_unit_test(archive archive.cpp) +# RegisterForArchive needs Python.h if built with Python +target_link_libraries(test_archive netgen_python) +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) endif(ENABLE_UNIT_TESTS) diff --git a/tests/catch/archive.cpp b/tests/catch/archive.cpp index 96c2087c..8c436697 100644 --- a/tests/catch/archive.cpp +++ b/tests/catch/archive.cpp @@ -1,6 +1,7 @@ -#include "catch.hpp" +#include #include <../core/ngcore.hpp> +#include using namespace ngcore; using namespace std; @@ -264,6 +265,28 @@ void testEnum(Archive& in, Archive& out) CHECK(enin == CASE2); } +void testOptional(Archive& in, Archive& out) + { + { + std::optional no_value; + std::optional myint = 42; + std::optional mystr = "have an optional string"; + out & no_value & myint & mystr; + out.FlushBuffer(); + } + { + std::optional no_value_; + std::optional myint_; + std::optional mystr_; + in & no_value_ & myint_ & mystr_; + CHECK(no_value_.has_value() == false); + CHECK(myint_.has_value() == true); + CHECK(*myint_ == 42); + CHECK(mystr_.has_value() == true); + CHECK(*mystr_ == "have an optional string"); + } + } + void testArchive(Archive& in, Archive& out) { SECTION("Empty String") @@ -321,6 +344,10 @@ void testArchive(Archive& in, Archive& out) { testEnum(in, out); } + SECTION("optional") + { + testOptional(in, out); + } } TEST_CASE("BinaryArchive") diff --git a/tests/catch/array.cpp b/tests/catch/array.cpp new file mode 100644 index 00000000..c21aa800 --- /dev/null +++ b/tests/catch/array.cpp @@ -0,0 +1,93 @@ + +#include +#include +using namespace ngcore; +using namespace std; + +#include "meshing.hpp" + +template +class ClsWithIndexType +{ + size_t size; +public: + ClsWithIndexType(size_t asize) : size(asize) {} + using index_type = TIND; + size_t Size() const { return size; } +}; + +template +class ClsWithRange : public ClsWithIndexType +{ +public: + ClsWithRange(size_t size) : ClsWithIndexType(size) {} + T_Range Range() const { return {1, 1+this->Size()}; } +}; + + +TEST_CASE("Array") +{ + Array array; +#ifdef DEBUG + CHECK_THROWS_AS(array[0], RangeException); + CHECK_THROWS_AS(array.DeleteLast(), RangeException); + CHECK_THROWS_AS(array.Last(), RangeException); +#endif // DEBUG + Array a_initlst = { 1., 2., 3.}; + CHECK(a_initlst[1] == 2.); + CHECK(a_initlst.Size() == 3); + FlatArray fa_a = a_initlst; + CHECK(typeid(fa_a) == typeid(FlatArray)); + CHECK(fa_a.Size() == 3); + CHECK(fa_a.Last() == 3.); + a_initlst.DeleteLast(); + CHECK(a_initlst.Last() == 2.); + CHECK(a_initlst.Size() == 2); +#ifdef DEBUG + CHECK_THROWS_AS(fa_a[5], RangeException); +#endif // DEBUG + Array b = Array(4); + b = 2; + int count = 0; + for(auto val : b) + { + count++; + CHECK(val == 2); + } + CHECK(count == 4); + + // range tests + CHECK(typeid(array.Range()) == typeid(T_Range)); + Array intarray; + CHECK(typeid(intarray.Range()) == typeid(T_Range)); + CHECK(typeid(Range(intarray)) == typeid(T_Range)); + int i = 0; + for(auto j : Range(b)) + CHECK(j == i++); + i = 0; + for(auto j : b.Range()) + CHECK(j == i++); + + // pointindex is still 1 based + Array piarray(2); + i = 1; + for(auto j : Range(piarray)) + CHECK(j == i++); + i = 1; + for(auto j : piarray.Range()) + CHECK(j == i++); + // a class can implement index_type and Size as well. + ClsWithIndexType clsi(3); + CHECK(typeid(Range(clsi)) == typeid(T_Range)); + i = 0; + for(auto j : Range(clsi)) + CHECK(j == i++); + // if the class has a Range function prefer that one + ClsWithRange clsr(3); + CHECK(typeid(Range(clsr)) == typeid(T_Range)); + i=1; + for(auto j : Range(clsr)) + CHECK(j == i++); + CHECK(typeid(Range(size_t(4))) == typeid(T_Range)); + CHECK(typeid(Range(4)) == typeid(T_Range)); +} diff --git a/tests/catch/main.cpp b/tests/catch/main.cpp index de419564..ad4c8431 100644 --- a/tests/catch/main.cpp +++ b/tests/catch/main.cpp @@ -1,3 +1,3 @@ - #define CATCH_CONFIG_MAIN -#include +#define DO_NOT_USE_WMAIN +#include diff --git a/tests/catch/ranges.cpp b/tests/catch/ranges.cpp new file mode 100644 index 00000000..aeca69b8 --- /dev/null +++ b/tests/catch/ranges.cpp @@ -0,0 +1,18 @@ + +#include + +#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/catch/symboltable.cpp b/tests/catch/symboltable.cpp index 5e6ecd2c..10bdff12 100644 --- a/tests/catch/symboltable.cpp +++ b/tests/catch/symboltable.cpp @@ -1,5 +1,5 @@ -#include "catch.hpp" +#include #include <../core/ngcore.hpp> using namespace ngcore; using namespace std; diff --git a/tests/catch/utils.cpp b/tests/catch/utils.cpp new file mode 100644 index 00000000..74b6dd37 --- /dev/null +++ b/tests/catch/utils.cpp @@ -0,0 +1,67 @@ + +#include +#include +using namespace ngcore; +using namespace std; + + +uint64_t shuffle(uint64_t N, uint64_t i) { + // Shuffle the numbers using multiplication with a prime number to force many updates of min, max + constexpr uint64_t P = 101; + return (N/2 + i*P) % N; +} + +void testThreading(int n_threads) +{ + TaskManager::SetNumThreads(n_threads); + n_threads = EnterTaskManager(); + + constexpr uint64_t N = 100000; + + + SECTION( "atomic operations" ) { + uint64_t i_min = 2*N; + uint64_t i_max = 0; + uint64_t i_sum = 0; + + double d_min = 1e100; + double d_max = 0.0; + double d_sum = 0.0; + + ParallelFor( Range(N), [&] (uint64_t i) { + AtomicMin(i_min, shuffle(N,i)); + }); + REQUIRE( i_min==0 ); + + ParallelFor( Range(N), [&] (uint64_t i) { + AtomicMax(i_max, shuffle(N,i)); + }); + REQUIRE( i_max==N-1 ); + + ParallelFor( Range(N), [&] (uint64_t i) { + AsAtomic(i_sum) += i; + }); + REQUIRE( i_sum==N*(N-1)/2 ); + + ParallelFor( Range(N), [&] (double i) { + AtomicMin(d_min, static_cast(shuffle(N,i))); + }); + REQUIRE( d_min==0 ); + + ParallelFor( Range(N), [&] (double i) { + AtomicMax(d_max, static_cast(shuffle(N,i))); + }); + REQUIRE( d_max==N-1 ); + + ParallelFor( Range(N), [&] (double i) { + AtomicAdd(d_sum, i); + }); + REQUIRE( d_sum==N*(N-1)/2 ); + + } + ExitTaskManager(n_threads); +} + +TEST_CASE("Threading - 1 Thread") { testThreading(1); } +TEST_CASE("Threading - 2 Thread") { testThreading(2); } +TEST_CASE("Threading - 8 Thread") { testThreading(8); } diff --git a/tests/catch/version.cpp b/tests/catch/version.cpp index 5655ab23..873636c2 100644 --- a/tests/catch/version.cpp +++ b/tests/catch/version.cpp @@ -1,5 +1,5 @@ -#include "catch.hpp" +#include #include <../core/ngcore.hpp> using namespace ngcore; using namespace std; diff --git a/tests/dockerfile b/tests/dockerfile index ebed3dea..a44bd3e7 100644 --- a/tests/dockerfile +++ b/tests/dockerfile @@ -1,5 +1,33 @@ -FROM ubuntu:18.04 +FROM ubuntu:22.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 +RUN apt-get update && apt-get -y install \ + ccache \ + clang occt-misc \ + clang-tidy \ + cmake \ + g++ \ + git \ + libcgns-dev \ + libglu1-mesa-dev \ + libhdf5-dev \ + libocct-ocaf-dev \ + libocct-visualization-dev \ + libocct-data-exchange-dev \ + libocct-draw-dev \ + libpython3-dev \ + libtbb-dev \ + libxi-dev \ + libxmu-dev \ + occt-misc \ + python3 \ + python3-distutils \ + python3-numpy \ + python3-pip \ + python3-pytest \ + python3-tk \ + tcl-dev \ + tk-dev + +RUN python3 -m pip install pytest-check pybind11-stubgen ADD . /root/src/netgen diff --git a/tests/dockerfile_mpi b/tests/dockerfile_mpi new file mode 100644 index 00000000..df905b17 --- /dev/null +++ b/tests/dockerfile_mpi @@ -0,0 +1,27 @@ +FROM ubuntu:22.04 +ENV DEBIAN_FRONTEND=noninteractive +MAINTAINER Matthias Hochsteger +RUN apt-get update && apt-get -y install \ + ccache \ + clang \ + clang-tidy \ + cmake \ + g++ \ + gfortran \ + git \ + libglu1-mesa-dev \ + libopenmpi-dev \ + libpython3-dev \ + libxmu-dev \ + openmpi-bin \ + python3 \ + python3-distutils \ + python3-mpi4py \ + python3-numpy \ + python3-pip \ + python3-tk \ + tcl-dev \ + tk-dev + +RUN python3 -m pip install pytest-mpi pytest-check pytest pybind11-stubgen +ADD . /root/src/netgen diff --git a/tests/fix_auditwheel_policy.py b/tests/fix_auditwheel_policy.py new file mode 100644 index 00000000..1b0329f9 --- /dev/null +++ b/tests/fix_auditwheel_policy.py @@ -0,0 +1,26 @@ +import json + +policy_file = "/opt/_internal/pipx/venvs/auditwheel/lib/python3.10/site-packages/auditwheel/policy/manylinux-policy.json" +data = json.load(open(policy_file)) +additional_libs = [ + "libbz2.so.1.0.6", + "libfontconfig.so.1.11.1", + "libfreetype.so.6.14.0", + "libGLU.so.1.3.1", + "libpng15.so.15.13.0", + "libtcl8.so", + "libtk8.so", + "libuuid.so.1.3.0", + "libz.so.1.2.7", + "libXmu.so.6", + "libOpenGL.so.0", + "libGLdispatch.so.0", + "libGLX.so.0", + "libGLU.so.1", + ] + +for entry in data: + if 'manylinux' in entry['name']: + entry['lib_whitelist'] += additional_libs + +json.dump(data, open(policy_file, 'w')) diff --git a/tests/pytest/CMakeLists.txt b/tests/pytest/CMakeLists.txt index 9997622a..26c7d22f 100644 --- a/tests/pytest/CMakeLists.txt +++ b/tests/pytest/CMakeLists.txt @@ -1,4 +1,8 @@ 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/compare_results.py b/tests/pytest/compare_results.py new file mode 100644 index 00000000..1015be34 --- /dev/null +++ b/tests/pytest/compare_results.py @@ -0,0 +1,118 @@ +import json +import sys +import subprocess +import statistics + +def readData(a, files): + amin=[] + amax=[] + amin1=[] + amax1=[] + bad=[] + ne1d=[] + ne2d=[] + ne3d=[] + file=[] + for f in files: + for t in a[f]: + if t['ne1d']>0: + ne1d.append(t['ne1d']) + if t['ne2d']>0: + ne2d.append(t['ne2d']) + if t['ne3d']>0: + ne3d.append(t['ne3d']) + if t['total_badness']>0.0: + bad.append(t['total_badness']) + file.append(f) + if 'angles_tet' in t: + amin.append(t['angles_tet'][0]) + amax.append(t['angles_tet'][1]) + if 'angles_trig' in t: + amin1.append(t['angles_trig'][0]) + amax1.append(t['angles_trig'][1]) + return { + "min tet angle":amin, + "max tet angle" : amax, + "min trig angle":amin1, + "max trig angle" : amax1, + "badness" : bad, + "#edges" : ne1d, + "#trigs" : ne2d, + "#tets" : ne3d, + "file" : file, + } + +import matplotlib.pyplot as plt + +ref = 'master' +if len(sys.argv)>1: + ref = sys.argv[1] + +res = subprocess.run(['git','show','{}:./results.json'.format(ref)], capture_output=True) +s = json.loads(res.stdout.decode()) + +if len(sys.argv) > 2: + ref2 = sys.argv[2] + res = subprocess.run(['git','show','{}:./results.json'.format(ref2)], capture_output=True) + s2 = res.stdout.decode() +else: + ref2 = 'current' + s2 = open('results.json','r').read() +s2 = json.loads(s2) + +filenames = [f for f in s if f in s2] +data = readData(s, filenames) +data2 = readData(s2, filenames) + +assert(len(data) == len(data2)) + +w = 90 +GREEN = '\033[92m' +RED = '\033[91m' +RESET = '\033[0m' + +for bad1,bad2, f1, f2 in zip(data['badness'], data2['badness'], data['file'], data2['file']): + assert f1==f2 + + diff = f"{100*(bad2-bad1)/bad1:+.2f}%" + if bad2>0 and bad2>1.2*bad1: + print(f"{RED}badness {f1} got worse: {bad1} -> {bad2}".ljust(w) + diff + RESET) + if bad2>0 and bad2<0.8*bad1: + print(f"{GREEN}badness {f1} got better: {bad1} -> {bad2}".ljust(w) + diff + RESET) + +for bad1,bad2, f1, f2 in zip(data['#trigs'], data2['#trigs'], data['file'], data2['file']): + assert f1==f2 + diff = f"{100*(bad2-bad1)/bad1:+.2f}%" + if bad2>0 and bad2>1.2*bad1: + print(f"{RED}ntrigs {f1} got worse: {bad1} -> {bad2}".ljust(w) + diff + RESET) + if bad2>0 and bad2<0.8*bad1: + print(f"{GREEN}ntrigs {f1} got better: {bad1} -> {bad2}".ljust(w) + diff + RESET) + +n = len(data)+1 +fig,ax = plt.subplots(figsize=(10,7)) +for i,d in enumerate(['min trig angle','min tet angle','max trig angle','max tet angle']): + ax = plt.subplot(2,5,i+1) + plt.title(d) + ax.set_xticks([1,2]) + if len(data[d])==0 or len(data2[d])==0: + continue + + plt.violinplot([data[d],data2[d]], showmedians=True) + med = statistics.median(data[d]) + plt.hlines(med, 1,2, linestyle='dotted') + if d=='badness': + ax.set_yscale('log') + ax.set_xticklabels([ref, ref2]) + + +for i,d in enumerate(['badness','#edges','#trigs','#tets']): + ax = plt.subplot(2,5,6+i) + plt.title('difference '+d+' (in %)') +# plt.violinplot([(y-x)/x for x,y in zip(data[d],data2[d])], showmedians=True) + plt.boxplot([100.0*(y-x)/x for x,y in zip(data[d],data2[d])]) + plt.hlines(0.0, 0.5,1.5, linestyle='dotted') + + +# plt.savefig('comparison.png', dpi=100) +plt.show() + diff --git a/tests/pytest/conftest.py b/tests/pytest/conftest.py new file mode 100644 index 00000000..bb692b22 --- /dev/null +++ b/tests/pytest/conftest.py @@ -0,0 +1,28 @@ +""" +Pytest configuration file to run slow test only if pytest is started with option --runslow or if +environment variable RUN_SLOW_TESTS is set. +""" + +import pytest, os + +def pytest_addoption(parser): + parser.addoption("--runslow", action="store_true", default=False, help="run slow tests") + +def pytest_configure(config): + config.addinivalue_line("markers", "slow: mark test as slow to run") + + +def pytest_collection_modifyitems(config, items): + import platform + + run_slow_tests = config.getoption("--runslow") + + # gitlab-ci: run slow tests only on Linux + if platform.system()=='Linux' and 'RUN_SLOW_TESTS' in os.environ and os.environ['RUN_SLOW_TESTS']: + run_slow_tests = True + + if not run_slow_tests: + skip_slow = pytest.mark.skip(reason="need --runslow option to run") + for item in items: + if "slow" in item.keywords: + item.add_marker(skip_slow) diff --git a/tests/pytest/geofiles/plane.stl b/tests/pytest/geofiles/plane.stl new file mode 100644 index 00000000..841c3426 --- /dev/null +++ b/tests/pytest/geofiles/plane.stl @@ -0,0 +1,15682 @@ +solid Exported from Blender-2.80 (sub 74) +facet normal 0.191110 0.013657 0.981474 +outer loop +vertex 21.194145 2.557368 9.374599 +vertex 21.148577 1.562913 9.397309 +vertex 22.736126 1.562173 9.088195 +endloop +endfacet +facet normal 0.185636 0.004848 0.982607 +outer loop +vertex 21.194145 2.557368 9.374599 +vertex 22.736126 1.562173 9.088195 +vertex 22.751976 2.556384 9.080295 +endloop +endfacet +facet normal 0.204879 0.041043 0.977926 +outer loop +vertex 21.148577 1.562913 9.397309 +vertex 21.028854 0.738857 9.456977 +vertex 22.697634 0.738462 9.107379 +endloop +endfacet +facet normal 0.191110 0.013928 0.981470 +outer loop +vertex 21.148577 1.562913 9.397309 +vertex 22.697634 0.738462 9.107379 +vertex 22.736126 1.562173 9.088195 +endloop +endfacet +facet normal 0.217324 0.004292 0.976090 +outer loop +vertex 22.751976 2.556384 9.080295 +vertex 22.736126 1.562173 9.088195 +vertex 24.224312 1.561327 8.756858 +endloop +endfacet +facet normal 0.214909 0.000543 0.976634 +outer loop +vertex 22.751976 2.556384 9.080295 +vertex 24.224312 1.561327 8.756858 +vertex 24.226292 2.555258 8.755870 +endloop +endfacet +facet normal 0.222975 0.012281 0.974747 +outer loop +vertex 22.736126 1.562173 9.088195 +vertex 22.697634 0.738462 9.107379 +vertex 24.219498 0.738010 8.759255 +endloop +endfacet +facet normal 0.217324 0.001572 0.976098 +outer loop +vertex 22.736126 1.562173 9.088195 +vertex 24.219498 0.738010 8.759255 +vertex 24.224312 1.561327 8.756858 +endloop +endfacet +facet normal 0.135843 0.002594 0.990727 +outer loop +vertex 19.769468 3.808219 9.566667 +vertex 19.760412 2.558099 9.571181 +vertex 21.194145 2.557368 9.374599 +endloop +endfacet +facet normal 0.135217 0.001868 0.990814 +outer loop +vertex 19.769468 3.808219 9.566667 +vertex 21.194145 2.557368 9.374599 +vertex 21.200653 3.807420 9.371354 +endloop +endfacet +facet normal 0.140126 0.022514 0.989878 +outer loop +vertex 19.760412 2.558099 9.571181 +vertex 19.697014 1.563463 9.602778 +vertex 21.148577 1.562913 9.397309 +endloop +endfacet +facet normal 0.135833 0.016398 0.990596 +outer loop +vertex 19.760412 2.558099 9.571181 +vertex 21.148577 1.562913 9.397309 +vertex 21.194145 2.557368 9.374599 +endloop +endfacet +facet normal 0.155681 0.077323 0.984776 +outer loop +vertex 19.697014 1.563463 9.602778 +vertex 19.506817 0.739151 9.697570 +vertex 21.028854 0.738857 9.456977 +endloop +endfacet +facet normal 0.139988 0.051260 0.988825 +outer loop +vertex 19.697014 1.563463 9.602778 +vertex 21.028854 0.738857 9.456977 +vertex 21.148577 1.562913 9.397309 +endloop +endfacet +facet normal 0.219664 0.209516 0.952812 +outer loop +vertex 19.506817 0.739151 9.697570 +vertex 18.900000 0.000000 10.000000 +vertex 20.833847 0.000000 9.554167 +endloop +endfacet +facet normal 0.155540 0.088368 0.983869 +outer loop +vertex 19.506817 0.739151 9.697570 +vertex 20.833847 0.000000 9.554167 +vertex 21.028854 0.738857 9.456977 +endloop +endfacet +facet normal 0.225734 0.068252 0.971795 +outer loop +vertex 21.028854 0.738857 9.456977 +vertex 20.833847 0.000000 9.554167 +vertex 22.645555 0.000000 9.133333 +endloop +endfacet +facet normal 0.205006 0.019935 0.978558 +outer loop +vertex 21.028854 0.738857 9.456977 +vertex 22.645555 0.000000 9.133333 +vertex 22.697634 0.738462 9.107379 +endloop +endfacet +facet normal 0.230193 0.017963 0.972979 +outer loop +vertex 22.697634 0.738462 9.107379 +vertex 22.645555 0.000000 9.133333 +vertex 24.212990 0.000000 8.762500 +endloop +endfacet +facet normal 0.222988 0.002319 0.974818 +outer loop +vertex 22.697634 0.738462 9.107379 +vertex 24.212990 0.000000 8.762500 +vertex 24.219498 0.738010 8.759255 +endloop +endfacet +facet normal 0.239169 0.002159 0.970976 +outer loop +vertex 24.219498 0.738010 8.759255 +vertex 24.212990 0.000000 8.762500 +vertex 25.414011 0.000000 8.466667 +endloop +endfacet +facet normal 0.237910 0.000000 0.971287 +outer loop +vertex 24.219498 0.738010 8.759255 +vertex 25.414011 0.000000 8.466667 +vertex 25.414009 0.737545 8.466667 +endloop +endfacet +facet normal 0.237910 0.001437 0.971286 +outer loop +vertex 24.224312 1.561327 8.756858 +vertex 24.219498 0.738010 8.759255 +vertex 25.414009 0.737545 8.466667 +endloop +endfacet +facet normal 0.236973 0.000002 0.971516 +outer loop +vertex 24.224312 1.561327 8.756858 +vertex 25.414009 0.737545 8.466667 +vertex 25.414009 1.560457 8.466666 +endloop +endfacet +facet normal 0.236973 0.000494 0.971516 +outer loop +vertex 24.226292 2.555258 8.755870 +vertex 24.224312 1.561327 8.756858 +vertex 25.414009 1.560457 8.466666 +endloop +endfacet +facet normal 0.236582 -0.000001 0.971611 +outer loop +vertex 24.226292 2.555258 8.755870 +vertex 25.414009 1.560457 8.466666 +vertex 25.414009 2.554101 8.466667 +endloop +endfacet +facet normal 0.236583 0.000056 0.971611 +outer loop +vertex 24.226574 3.805111 8.755730 +vertex 24.226292 2.555258 8.755870 +vertex 25.414009 2.554101 8.466667 +endloop +endfacet +facet normal 0.236526 -0.000001 0.971625 +outer loop +vertex 24.226574 3.805111 8.755730 +vertex 25.414009 2.554101 8.466667 +vertex 25.414011 3.803846 8.466667 +endloop +endfacet +facet normal 0.214909 0.000492 0.976634 +outer loop +vertex 22.754240 3.806343 9.079166 +vertex 22.751976 2.556384 9.080295 +vertex 24.226292 2.555258 8.755870 +endloop +endfacet +facet normal 0.214560 0.000062 0.976711 +outer loop +vertex 22.754240 3.806343 9.079166 +vertex 24.226292 2.555258 8.755870 +vertex 24.226574 3.805111 8.755730 +endloop +endfacet +facet normal 0.185636 0.001584 0.982617 +outer loop +vertex 21.200653 3.807420 9.371354 +vertex 21.194145 2.557368 9.374599 +vertex 22.751976 2.556384 9.080295 +endloop +endfacet +facet normal 0.184832 0.000552 0.982770 +outer loop +vertex 21.200653 3.807420 9.371354 +vertex 22.751976 2.556384 9.080295 +vertex 22.754240 3.806343 9.079166 +endloop +endfacet +facet normal -0.091368 0.009363 0.995773 +outer loop +vertex 18.613682 2.558479 9.619390 +vertex 17.733503 2.558462 9.538629 +vertex 17.717653 1.563737 9.546528 +endloop +endfacet +facet normal -0.111632 0.027793 0.993361 +outer loop +vertex 18.613682 2.558479 9.619390 +vertex 17.717653 1.563737 9.546528 +vertex 18.568115 1.563749 9.642101 +endloop +endfacet +facet normal -0.247479 0.001455 0.968892 +outer loop +vertex 17.733503 2.558462 9.538629 +vertex 17.054708 2.558016 9.365246 +vertex 17.052727 1.563401 9.366233 +endloop +endfacet +facet normal -0.261688 0.011834 0.965080 +outer loop +vertex 17.733503 2.558462 9.538629 +vertex 17.052727 1.563401 9.366233 +vertex 17.717653 1.563737 9.546528 +endloop +endfacet +facet normal -0.111632 0.028326 0.993346 +outer loop +vertex 18.568115 1.563749 9.642101 +vertex 17.717653 1.563737 9.546528 +vertex 17.679161 0.739297 9.565712 +endloop +endfacet +facet normal -0.173365 0.096112 0.980157 +outer loop +vertex 18.568115 1.563749 9.642101 +vertex 17.679161 0.739297 9.565712 +vertex 18.448393 0.739303 9.701769 +endloop +endfacet +facet normal -0.261698 0.004334 0.965140 +outer loop +vertex 17.717653 1.563737 9.546528 +vertex 17.052727 1.563401 9.366233 +vertex 17.047916 0.739118 9.368630 +endloop +endfacet +facet normal -0.297839 0.036104 0.953933 +outer loop +vertex 17.717653 1.563737 9.546528 +vertex 17.047916 0.739118 9.368630 +vertex 17.679161 0.739297 9.565712 +endloop +endfacet +facet normal 0.043013 0.002369 0.999072 +outer loop +vertex 19.769468 3.808219 9.566667 +vertex 18.620193 3.808635 9.616146 +vertex 18.613682 2.558479 9.619390 +endloop +endfacet +facet normal 0.042005 0.003304 0.999112 +outer loop +vertex 19.769468 3.808219 9.566667 +vertex 18.613682 2.558479 9.619390 +vertex 19.760412 2.558099 9.571181 +endloop +endfacet +facet normal -0.088572 0.001059 0.996069 +outer loop +vertex 18.620193 3.808635 9.616146 +vertex 17.735767 3.808617 9.537500 +vertex 17.733503 2.558462 9.538629 +endloop +endfacet +facet normal -0.091374 0.003061 0.995812 +outer loop +vertex 18.620193 3.808635 9.616146 +vertex 17.733503 2.558462 9.538629 +vertex 18.613682 2.558479 9.619390 +endloop +endfacet +facet normal -0.245484 0.000165 0.969401 +outer loop +vertex 17.735767 3.808617 9.537500 +vertex 17.054991 3.808129 9.365105 +vertex 17.054708 2.558016 9.365246 +endloop +endfacet +facet normal -0.247484 0.001322 0.968891 +outer loop +vertex 17.735767 3.808617 9.537500 +vertex 17.054708 2.558016 9.365246 +vertex 17.733503 2.558462 9.538629 +endloop +endfacet +facet normal -0.395446 0.000000 0.918489 +outer loop +vertex 17.054991 3.808129 9.365105 +vertex 16.516666 3.807135 9.133333 +vertex 16.516666 2.557108 9.133333 +endloop +endfacet +facet normal -0.395828 0.000193 0.918324 +outer loop +vertex 17.054991 3.808129 9.365105 +vertex 16.516666 2.557108 9.133333 +vertex 17.054708 2.558016 9.365246 +endloop +endfacet +facet normal -0.395827 0.000000 0.918325 +outer loop +vertex 17.054708 2.558016 9.365246 +vertex 16.516666 2.557108 9.133333 +vertex 16.516666 1.562718 9.133333 +endloop +endfacet +facet normal -0.398483 0.001704 0.917174 +outer loop +vertex 17.054708 2.558016 9.365246 +vertex 16.516666 1.562718 9.133333 +vertex 17.052727 1.563401 9.366233 +endloop +endfacet +facet normal -0.398481 0.000000 0.917176 +outer loop +vertex 17.052727 1.563401 9.366233 +vertex 16.516666 1.562718 9.133333 +vertex 16.516666 0.738753 9.133333 +endloop +endfacet +facet normal -0.404967 0.005022 0.914317 +outer loop +vertex 17.052727 1.563401 9.366233 +vertex 16.516666 0.738753 9.133333 +vertex 17.047916 0.739118 9.368630 +endloop +endfacet +facet normal -0.404970 0.000000 0.914330 +outer loop +vertex 17.047916 0.739118 9.368630 +vertex 16.516666 0.738753 9.133333 +vertex 16.516666 0.000000 9.133333 +endloop +endfacet +facet normal -0.413823 0.007640 0.910325 +outer loop +vertex 17.047916 0.739118 9.368630 +vertex 16.516666 0.000000 9.133333 +vertex 17.041407 0.000000 9.371875 +endloop +endfacet +facet normal -0.298019 0.006815 0.954536 +outer loop +vertex 17.679161 0.739297 9.565712 +vertex 17.047916 0.739118 9.368630 +vertex 17.041407 0.000000 9.371875 +endloop +endfacet +facet normal -0.350771 0.057525 0.934693 +outer loop +vertex 17.679161 0.739297 9.565712 +vertex 17.041407 0.000000 9.371875 +vertex 17.627083 0.000000 9.591667 +endloop +endfacet +facet normal -0.173979 0.046789 0.983637 +outer loop +vertex 18.448393 0.739303 9.701769 +vertex 17.679161 0.739297 9.565712 +vertex 17.627083 0.000000 9.591667 +endloop +endfacet +facet normal -0.307649 0.203345 0.929518 +outer loop +vertex 18.448393 0.739303 9.701769 +vertex 17.627083 0.000000 9.591667 +vertex 18.253386 0.000000 9.798959 +endloop +endfacet +facet normal 0.003953 0.129314 0.991596 +outer loop +vertex 19.506817 0.739151 9.697570 +vertex 18.448393 0.739303 9.701769 +vertex 18.253386 0.000000 9.798959 +endloop +endfacet +facet normal -0.250697 0.535724 0.806319 +outer loop +vertex 19.506817 0.739151 9.697570 +vertex 18.253386 0.000000 9.798959 +vertex 18.900000 0.000000 10.000000 +endloop +endfacet +facet normal 0.034751 0.067120 0.997140 +outer loop +vertex 19.697014 1.563463 9.602778 +vertex 18.568115 1.563749 9.642101 +vertex 18.448393 0.739303 9.701769 +endloop +endfacet +facet normal 0.003957 0.113339 0.993548 +outer loop +vertex 19.697014 1.563463 9.602778 +vertex 18.448393 0.739303 9.701769 +vertex 19.506817 0.739151 9.697570 +endloop +endfacet +facet normal 0.042000 0.020882 0.998899 +outer loop +vertex 19.760412 2.558099 9.571181 +vertex 18.613682 2.558479 9.619390 +vertex 18.568115 1.563749 9.642101 +endloop +endfacet +facet normal 0.034805 0.029516 0.998958 +outer loop +vertex 19.760412 2.558099 9.571181 +vertex 18.568115 1.563749 9.642101 +vertex 19.697014 1.563463 9.602778 +endloop +endfacet +facet normal 0.288243 0.045314 -0.956485 +outer loop +vertex 26.129168 11.849633 8.070150 +vertex 26.005474 12.920404 8.083603 +vertex 26.240326 12.930573 8.154860 +endloop +endfacet +facet normal 0.274405 0.047052 -0.960462 +outer loop +vertex 26.129168 11.849633 8.070150 +vertex 26.240326 12.930573 8.154860 +vertex 26.372263 11.851037 8.139670 +endloop +endfacet +facet normal 0.318796 0.178510 -0.930862 +outer loop +vertex 26.005474 12.920404 8.083603 +vertex 25.675266 13.692222 8.118527 +vertex 25.882959 13.725335 8.196007 +endloop +endfacet +facet normal 0.278988 0.174332 -0.944338 +outer loop +vertex 26.005474 12.920404 8.083603 +vertex 25.882959 13.725335 8.196007 +vertex 26.240326 12.930573 8.154860 +endloop +endfacet +facet normal 0.468854 0.044890 0.882134 +outer loop +vertex 26.372263 11.851037 8.139670 +vertex 26.240326 12.930573 8.154860 +vertex 26.005474 12.920482 8.280198 +endloop +endfacet +facet normal 0.458197 0.040470 0.887929 +outer loop +vertex 26.372263 11.851037 8.139670 +vertex 26.005474 12.920482 8.280198 +vertex 26.129166 11.849897 8.265168 +endloop +endfacet +facet normal 0.487982 0.175154 0.855099 +outer loop +vertex 26.240326 12.930573 8.154860 +vertex 25.882959 13.725335 8.196007 +vertex 25.675266 13.692232 8.321312 +endloop +endfacet +facet normal 0.460435 0.150400 0.874859 +outer loop +vertex 26.240326 12.930573 8.154860 +vertex 25.675266 13.692232 8.321312 +vertex 26.005474 12.920482 8.280198 +endloop +endfacet +facet normal 0.047924 0.001828 -0.998849 +outer loop +vertex 25.414011 10.480907 8.033333 +vertex 25.400063 11.848212 8.035166 +vertex 26.129168 11.849633 8.070150 +endloop +endfacet +facet normal 0.047563 0.002017 -0.998866 +outer loop +vertex 25.414011 10.480907 8.033333 +vertex 26.129168 11.849633 8.070150 +vertex 26.146835 10.481279 8.068229 +endloop +endfacet +facet normal 0.050335 0.016690 -0.998593 +outer loop +vertex 25.400063 11.848212 8.035166 +vertex 25.302427 12.910231 8.047994 +vertex 26.005474 12.920404 8.083603 +endloop +endfacet +facet normal 0.047885 0.018078 -0.998689 +outer loop +vertex 25.400063 11.848212 8.035166 +vertex 26.005474 12.920404 8.083603 +vertex 26.129168 11.849633 8.070150 +endloop +endfacet +facet normal 0.057660 0.062379 -0.996386 +outer loop +vertex 25.302427 12.910231 8.047994 +vertex 25.061609 13.654374 8.080646 +vertex 25.675266 13.692222 8.118527 +endloop +endfacet +facet normal 0.049517 0.066280 -0.996572 +outer loop +vertex 25.302427 12.910231 8.047994 +vertex 25.675266 13.692222 8.118527 +vertex 26.005474 12.920404 8.083603 +endloop +endfacet +facet normal 0.095358 0.176334 -0.979701 +outer loop +vertex 25.061609 13.654374 8.080646 +vertex 24.762611 14.062502 8.125000 +vertex 25.047661 14.199250 8.177359 +endloop +endfacet +facet normal 0.049852 0.175785 -0.983165 +outer loop +vertex 25.061609 13.654374 8.080646 +vertex 25.047661 14.199250 8.177359 +vertex 25.675266 13.692222 8.118527 +endloop +endfacet +facet normal 0.320723 0.490980 -0.809985 +outer loop +vertex 25.675266 13.692222 8.118527 +vertex 25.047661 14.199250 8.177359 +vertex 25.190845 14.267941 8.275694 +endloop +endfacet +facet normal 0.249862 0.445005 -0.859965 +outer loop +vertex 25.675266 13.692222 8.118527 +vertex 25.190845 14.267941 8.275694 +vertex 25.882959 13.725335 8.196007 +endloop +endfacet +facet normal 0.459444 0.475901 0.749953 +outer loop +vertex 25.882959 13.725335 8.196007 +vertex 25.190845 14.267941 8.275694 +vertex 25.047661 14.199251 8.407001 +endloop +endfacet +facet normal 0.428374 0.392727 0.813794 +outer loop +vertex 25.882959 13.725335 8.196007 +vertex 25.047661 14.199251 8.407001 +vertex 25.675266 13.692232 8.321312 +endloop +endfacet +facet normal 0.290536 0.201539 0.935399 +outer loop +vertex 25.675266 13.692232 8.321312 +vertex 25.047661 14.199251 8.407001 +vertex 24.762611 14.062502 8.525001 +endloop +endfacet +facet normal 0.269544 0.140295 0.952714 +outer loop +vertex 25.675266 13.692232 8.321312 +vertex 24.762611 14.062502 8.525001 +vertex 25.061607 13.654394 8.500504 +endloop +endfacet +facet normal 0.275863 0.066949 0.958863 +outer loop +vertex 26.005474 12.920482 8.280198 +vertex 25.675266 13.692232 8.321312 +vertex 25.061607 13.654394 8.500504 +endloop +endfacet +facet normal 0.269238 0.057685 0.961344 +outer loop +vertex 26.005474 12.920482 8.280198 +vertex 25.061607 13.654394 8.500504 +vertex 25.302429 12.910394 8.477700 +endloop +endfacet +facet normal 0.270178 0.017701 0.962648 +outer loop +vertex 26.129166 11.849897 8.265168 +vertex 26.005474 12.920482 8.280198 +vertex 25.302429 12.910394 8.477700 +endloop +endfacet +facet normal 0.268016 0.015887 0.963283 +outer loop +vertex 26.129166 11.849897 8.265168 +vertex 25.302429 12.910394 8.477700 +vertex 25.400064 11.848762 8.468046 +endloop +endfacet +facet normal 0.268070 0.001951 0.963397 +outer loop +vertex 26.146837 10.481909 8.263021 +vertex 26.129166 11.849897 8.265168 +vertex 25.400064 11.848762 8.468046 +endloop +endfacet +facet normal 0.267745 0.001759 0.963488 +outer loop +vertex 26.146837 10.481909 8.263021 +vertex 25.400064 11.848762 8.468046 +vertex 25.414009 10.482211 8.466667 +endloop +endfacet +facet normal 0.458701 0.004906 0.888577 +outer loop +vertex 26.391113 10.481605 8.137500 +vertex 26.372263 11.851037 8.139670 +vertex 26.129166 11.849897 8.265168 +endloop +endfacet +facet normal 0.457041 0.004507 0.889434 +outer loop +vertex 26.391113 10.481605 8.137500 +vertex 26.129166 11.849897 8.265168 +vertex 26.146837 10.481909 8.263021 +endloop +endfacet +facet normal 0.274929 0.004899 -0.961452 +outer loop +vertex 26.146835 10.481279 8.068229 +vertex 26.129168 11.849633 8.070150 +vertex 26.372263 11.851037 8.139670 +endloop +endfacet +facet normal 0.272809 0.005281 -0.962054 +outer loop +vertex 26.146835 10.481279 8.068229 +vertex 26.372263 11.851037 8.139670 +vertex 26.391113 10.481605 8.137500 +endloop +endfacet +facet normal 0.094174 0.867630 0.488210 +outer loop +vertex 21.163698 14.718087 8.937361 +vertex 22.682304 14.658746 8.749882 +vertex 22.698479 14.771414 8.546528 +endloop +endfacet +facet normal 0.074718 0.905827 0.417007 +outer loop +vertex 21.163698 14.718087 8.937361 +vertex 22.698479 14.771414 8.546528 +vertex 21.149673 14.834710 8.686545 +endloop +endfacet +facet normal 0.175628 0.798933 0.575206 +outer loop +vertex 22.682304 14.658746 8.749882 +vertex 24.028313 14.500335 8.558929 +vertex 24.096714 14.599972 8.399652 +endloop +endfacet +facet normal 0.156554 0.858600 0.488157 +outer loop +vertex 22.682304 14.658746 8.749882 +vertex 24.096714 14.599972 8.399652 +vertex 22.698479 14.771414 8.546528 +endloop +endfacet +facet normal -0.008983 0.870139 -0.492724 +outer loop +vertex 21.149673 14.834710 8.686545 +vertex 22.698479 14.771414 8.546528 +vertex 22.672430 14.658748 8.348035 +endloop +endfacet +facet normal 0.010635 0.906013 -0.423115 +outer loop +vertex 21.149673 14.834710 8.686545 +vertex 22.672430 14.658748 8.348035 +vertex 21.130371 14.718086 8.436337 +endloop +endfacet +facet normal 0.038324 0.811775 -0.582711 +outer loop +vertex 22.698479 14.771414 8.546528 +vertex 24.096714 14.599972 8.399652 +vertex 24.027081 14.500335 8.256269 +endloop +endfacet +facet normal 0.067209 0.863893 -0.499172 +outer loop +vertex 22.698479 14.771414 8.546528 +vertex 24.027081 14.500335 8.256269 +vertex 22.672430 14.658748 8.348035 +endloop +endfacet +facet normal 0.097537 0.510328 0.854431 +outer loop +vertex 19.733356 14.375003 9.305555 +vertex 21.174864 14.368311 9.145000 +vertex 21.163698 14.718087 8.937361 +endloop +endfacet +facet normal 0.077329 0.561655 0.823750 +outer loop +vertex 19.733356 14.375003 9.305555 +vertex 21.163698 14.718087 8.937361 +vertex 19.700632 14.726565 9.068924 +endloop +endfacet +facet normal 0.149635 0.432774 0.888997 +outer loop +vertex 21.174864 14.368311 9.145000 +vertex 22.665283 14.321471 8.916937 +vertex 22.682304 14.658746 8.749882 +endloop +endfacet +facet normal 0.125014 0.509412 0.851394 +outer loop +vertex 21.174864 14.368311 9.145000 +vertex 22.682304 14.658746 8.749882 +vertex 21.163698 14.718087 8.937361 +endloop +endfacet +facet normal 0.195237 0.347732 0.917041 +outer loop +vertex 22.665283 14.321471 8.916937 +vertex 23.949944 14.205942 8.687243 +vertex 24.028313 14.500335 8.558929 +endloop +endfacet +facet normal 0.176210 0.429746 0.885590 +outer loop +vertex 22.665283 14.321471 8.916937 +vertex 24.028313 14.500335 8.558929 +vertex 22.682304 14.658746 8.749882 +endloop +endfacet +facet normal 0.237569 0.301556 0.923377 +outer loop +vertex 23.949944 14.205942 8.687243 +vertex 24.762611 14.062502 8.525001 +vertex 25.047661 14.199251 8.407001 +endloop +endfacet +facet normal 0.234975 0.335129 0.912401 +outer loop +vertex 23.949944 14.205942 8.687243 +vertex 25.047661 14.199251 8.407001 +vertex 24.028313 14.500335 8.558929 +endloop +endfacet +facet normal 0.299377 0.671564 0.677772 +outer loop +vertex 24.028313 14.500335 8.558929 +vertex 25.047661 14.199251 8.407001 +vertex 25.190845 14.267941 8.275694 +endloop +endfacet +facet normal 0.294422 0.748252 0.594504 +outer loop +vertex 24.028313 14.500335 8.558929 +vertex 25.190845 14.267941 8.275694 +vertex 24.096714 14.599972 8.399652 +endloop +endfacet +facet normal 0.136319 0.707929 -0.693003 +outer loop +vertex 24.096714 14.599972 8.399652 +vertex 25.190845 14.267941 8.275694 +vertex 25.047661 14.199250 8.177359 +endloop +endfacet +facet normal 0.178012 0.765464 -0.618366 +outer loop +vertex 24.096714 14.599972 8.399652 +vertex 25.047661 14.199250 8.177359 +vertex 24.027081 14.500335 8.256269 +endloop +endfacet +facet normal 0.020907 0.319184 -0.947462 +outer loop +vertex 24.027081 14.500335 8.256269 +vertex 25.047661 14.199250 8.177359 +vertex 24.762611 14.062502 8.125000 +endloop +endfacet +facet normal 0.035814 0.341726 -0.939117 +outer loop +vertex 24.027081 14.500335 8.256269 +vertex 24.762611 14.062502 8.125000 +vertex 23.947687 14.205942 8.146118 +endloop +endfacet +facet normal -0.021727 0.355488 -0.934428 +outer loop +vertex 22.672430 14.658748 8.348035 +vertex 24.027081 14.500335 8.256269 +vertex 23.947687 14.205942 8.146118 +endloop +endfacet +facet normal 0.010905 0.432699 -0.901473 +outer loop +vertex 22.672430 14.658748 8.348035 +vertex 23.947687 14.205942 8.146118 +vertex 22.647228 14.321472 8.185841 +endloop +endfacet +facet normal -0.034768 0.435232 -0.899647 +outer loop +vertex 21.130371 14.718086 8.436337 +vertex 22.672430 14.658748 8.348035 +vertex 22.647228 14.321472 8.185841 +endloop +endfacet +facet normal -0.008821 0.509649 -0.860337 +outer loop +vertex 21.130371 14.718086 8.436337 +vertex 22.647228 14.321472 8.185841 +vertex 21.113926 14.368312 8.229306 +endloop +endfacet +facet normal -0.032112 0.510214 -0.859448 +outer loop +vertex 19.621639 14.726565 8.497743 +vertex 21.130371 14.718086 8.436337 +vertex 21.113926 14.368312 8.229306 +endloop +endfacet +facet normal -0.014833 0.559269 -0.828854 +outer loop +vertex 19.621639 14.726565 8.497743 +vertex 21.113926 14.368312 8.229306 +vertex 19.588913 14.375001 8.261111 +endloop +endfacet +facet normal -0.021865 0.906804 -0.420984 +outer loop +vertex 19.661137 14.843751 8.783334 +vertex 21.149673 14.834710 8.686545 +vertex 21.130371 14.718086 8.436337 +endloop +endfacet +facet normal -0.010198 0.925593 -0.378382 +outer loop +vertex 19.661137 14.843751 8.783334 +vertex 21.130371 14.718086 8.436337 +vertex 19.621639 14.726565 8.497743 +endloop +endfacet +facet normal 0.042957 0.906849 0.419261 +outer loop +vertex 19.700632 14.726565 9.068924 +vertex 21.163698 14.718087 8.937361 +vertex 21.149673 14.834710 8.686545 +endloop +endfacet +facet normal 0.030065 0.926179 0.375884 +outer loop +vertex 19.700632 14.726565 9.068924 +vertex 21.149673 14.834710 8.686545 +vertex 19.661137 14.843751 8.783334 +endloop +endfacet +facet normal -0.595026 0.014885 0.803569 +outer loop +vertex 15.731250 2.553990 8.604167 +vertex 15.695139 1.560369 8.595834 +vertex 16.068663 1.561682 8.872396 +endloop +endfacet +facet normal -0.618699 0.001989 0.785625 +outer loop +vertex 15.731250 2.553990 8.604167 +vertex 16.068663 1.561682 8.872396 +vertex 16.073177 2.555731 8.873438 +endloop +endfacet +facet normal -0.585481 0.007650 0.810650 +outer loop +vertex 15.695139 1.560369 8.595834 +vertex 15.679340 0.737497 8.592188 +vertex 16.066689 0.738199 8.871941 +endloop +endfacet +facet normal -0.595062 0.000980 0.803679 +outer loop +vertex 15.695139 1.560369 8.595834 +vertex 16.066689 0.738199 8.871941 +vertex 16.068663 1.561682 8.872396 +endloop +endfacet +facet normal -0.897700 0.269418 0.348638 +outer loop +vertex 15.767362 3.799347 8.177083 +vertex 15.437763 2.799557 8.101022 +vertex 15.527117 2.781798 8.344819 +endloop +endfacet +facet normal -0.820203 0.100823 -0.563118 +outer loop +vertex 15.767362 3.799347 8.177083 +vertex 15.527117 2.781798 8.344819 +vertex 15.635330 3.801585 8.369792 +endloop +endfacet +facet normal -0.584159 0.001216 0.811638 +outer loop +vertex 15.679340 0.737497 8.592188 +vertex 15.677083 0.000000 8.591667 +vertex 16.066406 0.000000 8.871875 +endloop +endfacet +facet normal -0.585496 0.000154 0.810675 +outer loop +vertex 15.679340 0.737497 8.592188 +vertex 16.066406 0.000000 8.871875 +vertex 16.066689 0.738199 8.871941 +endloop +endfacet +facet normal -0.502158 0.000114 0.864776 +outer loop +vertex 16.066689 0.738199 8.871941 +vertex 16.066406 0.000000 8.871875 +vertex 16.516666 0.000000 9.133333 +endloop +endfacet +facet normal -0.502302 0.000000 0.864693 +outer loop +vertex 16.066689 0.738199 8.871941 +vertex 16.516666 0.000000 9.133333 +vertex 16.516666 0.738753 9.133333 +endloop +endfacet +facet normal -0.502301 0.000725 0.864692 +outer loop +vertex 16.068663 1.561682 8.872396 +vertex 16.066689 0.738199 8.871941 +vertex 16.516666 0.738753 9.133333 +endloop +endfacet +facet normal -0.503297 0.000000 0.864113 +outer loop +vertex 16.068663 1.561682 8.872396 +vertex 16.516666 0.738753 9.133333 +vertex 16.516666 1.562718 9.133333 +endloop +endfacet +facet normal -0.503299 0.001380 0.864111 +outer loop +vertex 16.073177 2.555731 8.873438 +vertex 16.068663 1.561682 8.872396 +vertex 16.516666 1.562718 9.133333 +endloop +endfacet +facet normal -0.505600 0.000000 0.862768 +outer loop +vertex 16.073177 2.555731 8.873438 +vertex 16.516666 1.562718 9.133333 +vertex 16.516666 2.557108 9.133333 +endloop +endfacet +facet normal -0.505605 0.001107 0.862764 +outer loop +vertex 16.077690 3.805630 8.874479 +vertex 16.073177 2.555731 8.873438 +vertex 16.516666 2.557108 9.133333 +endloop +endfacet +facet normal -0.507944 0.000000 0.861390 +outer loop +vertex 16.077690 3.805630 8.874479 +vertex 16.516666 2.557108 9.133333 +vertex 16.516666 3.807135 9.133333 +endloop +endfacet +facet normal -0.618684 0.012638 0.785538 +outer loop +vertex 15.767361 3.803730 8.612500 +vertex 15.731250 2.553990 8.604167 +vertex 16.073177 2.555731 8.873438 +endloop +endfacet +facet normal -0.645074 0.001693 0.764118 +outer loop +vertex 15.767361 3.803730 8.612500 +vertex 16.073177 2.555731 8.873438 +vertex 16.077690 3.805630 8.874479 +endloop +endfacet +facet normal -0.875831 0.081288 0.475723 +outer loop +vertex 15.767361 3.803730 8.612500 +vertex 15.635330 3.801585 8.369792 +vertex 15.527117 2.781798 8.344819 +endloop +endfacet +facet normal -0.755954 0.064263 0.651463 +outer loop +vertex 15.538307 2.783193 8.357668 +vertex 15.518668 2.552154 8.357668 +vertex 15.731250 2.553990 8.604167 +endloop +endfacet +facet normal -0.754405 0.005387 0.656388 +outer loop +vertex 15.767361 3.803730 8.612500 +vertex 15.527117 2.781798 8.344819 +vertex 15.538307 2.783193 8.357668 +endloop +endfacet +facet normal -0.778982 0.018329 0.626778 +outer loop +vertex 15.538307 2.783193 8.357668 +vertex 15.731250 2.553990 8.604167 +vertex 15.767361 3.803730 8.612500 +endloop +endfacet +facet normal -0.931106 0.000000 0.364749 +outer loop +vertex 15.694011 8.850069 8.383333 +vertex 15.694011 7.087864 8.383333 +vertex 15.785417 7.089402 8.616667 +endloop +endfacet +facet normal -0.931105 0.000000 0.364751 +outer loop +vertex 15.694011 8.850069 8.383333 +vertex 15.785417 7.089402 8.616667 +vertex 15.785417 8.851082 8.616667 +endloop +endfacet +facet normal -0.925850 0.003136 0.377877 +outer loop +vertex 15.694011 7.087864 8.383333 +vertex 15.687522 5.352535 8.381836 +vertex 15.783160 5.354500 8.616146 +endloop +endfacet +facet normal -0.931107 0.001101 0.364744 +outer loop +vertex 15.694011 7.087864 8.383333 +vertex 15.783160 5.354500 8.616146 +vertex 15.785417 7.089402 8.616667 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 8.851082 8.616667 +vertex 15.785417 7.089402 8.616667 +vertex 16.079948 7.090765 8.875000 +endloop +endfacet +facet normal -0.659398 0.000000 0.751794 +outer loop +vertex 15.785417 8.851082 8.616667 +vertex 16.079948 7.090765 8.875000 +vertex 16.079948 8.851977 8.875000 +endloop +endfacet +facet normal -0.657566 0.000627 0.753396 +outer loop +vertex 15.785417 7.089402 8.616667 +vertex 15.783160 5.354500 8.616146 +vertex 16.079666 5.356243 8.874935 +endloop +endfacet +facet normal -0.659397 0.000080 0.751795 +outer loop +vertex 15.785417 7.089402 8.616667 +vertex 16.079666 5.356243 8.874935 +vertex 16.079948 7.090765 8.875000 +endloop +endfacet +facet normal -0.727986 0.000000 -0.685592 +outer loop +vertex 15.866667 10.481091 8.200000 +vertex 15.866667 8.849014 8.200000 +vertex 15.694011 8.850069 8.383333 +endloop +endfacet +facet normal -0.727990 0.000000 -0.685587 +outer loop +vertex 15.866667 10.481091 8.200000 +vertex 15.694011 8.850069 8.383333 +vertex 15.694011 10.481650 8.383333 +endloop +endfacet +facet normal -0.727987 0.000000 -0.685591 +outer loop +vertex 15.866667 8.849014 8.200000 +vertex 15.866667 7.086260 8.200000 +vertex 15.694011 7.087864 8.383333 +endloop +endfacet +facet normal -0.727987 0.000000 -0.685591 +outer loop +vertex 15.866667 8.849014 8.200000 +vertex 15.694011 7.087864 8.383333 +vertex 15.694011 8.850069 8.383333 +endloop +endfacet +facet normal -0.734084 0.004633 -0.679043 +outer loop +vertex 15.866667 7.086260 8.200000 +vertex 15.857639 5.350487 8.197917 +vertex 15.687522 5.352535 8.381836 +endloop +endfacet +facet normal -0.727966 0.003314 -0.685605 +outer loop +vertex 15.866667 7.086260 8.200000 +vertex 15.687522 5.352535 8.381836 +vertex 15.694011 7.087864 8.383333 +endloop +endfacet +facet normal -0.823380 0.055509 -0.564769 +outer loop +vertex 15.857639 5.350487 8.197917 +vertex 15.767362 3.799347 8.177083 +vertex 15.635330 3.801585 8.369792 +endloop +endfacet +facet normal -0.733620 0.029959 -0.678900 +outer loop +vertex 15.857639 5.350487 8.197917 +vertex 15.635330 3.801585 8.369792 +vertex 15.687522 5.352535 8.381836 +endloop +endfacet +facet normal -0.878237 0.025846 0.477526 +outer loop +vertex 15.687522 5.352535 8.381836 +vertex 15.635330 3.801585 8.369792 +vertex 15.767361 3.803730 8.612500 +endloop +endfacet +facet normal -0.925837 0.008545 0.377827 +outer loop +vertex 15.687522 5.352535 8.381836 +vertex 15.767361 3.803730 8.612500 +vertex 15.783160 5.354500 8.616146 +endloop +endfacet +facet normal -0.645081 0.004776 0.764099 +outer loop +vertex 15.783160 5.354500 8.616146 +vertex 15.767361 3.803730 8.612500 +vertex 16.077690 3.805630 8.874479 +endloop +endfacet +facet normal -0.657567 0.000617 0.753395 +outer loop +vertex 15.783160 5.354500 8.616146 +vertex 16.077690 3.805630 8.874479 +vertex 16.079666 5.356243 8.874935 +endloop +endfacet +facet normal -0.507942 0.000393 0.861391 +outer loop +vertex 16.079666 5.356243 8.874935 +vertex 16.077690 3.805630 8.874479 +vertex 16.516666 3.807135 9.133333 +endloop +endfacet +facet normal -0.508979 0.000000 0.860779 +outer loop +vertex 16.079666 5.356243 8.874935 +vertex 16.516666 3.807135 9.133333 +vertex 16.516666 5.357624 9.133333 +endloop +endfacet +facet normal -0.508979 0.000051 0.860779 +outer loop +vertex 16.079948 7.090765 8.875000 +vertex 16.079666 5.356243 8.874935 +vertex 16.516666 5.357624 9.133333 +endloop +endfacet +facet normal -0.509126 0.000000 0.860692 +outer loop +vertex 16.079948 7.090765 8.875000 +vertex 16.516666 5.357624 9.133333 +vertex 16.516666 7.091847 9.133333 +endloop +endfacet +facet normal -0.509128 0.000000 0.860691 +outer loop +vertex 16.079948 8.851977 8.875000 +vertex 16.079948 7.090765 8.875000 +vertex 16.516666 7.091847 9.133333 +endloop +endfacet +facet normal -0.509127 0.000000 0.860691 +outer loop +vertex 16.079948 8.851977 8.875000 +vertex 16.516666 7.091847 9.133333 +vertex 16.516666 8.852689 9.133333 +endloop +endfacet +facet normal -0.509127 0.000000 0.860691 +outer loop +vertex 16.079948 10.482659 8.875000 +vertex 16.079948 8.851977 8.875000 +vertex 16.516666 8.852689 9.133333 +endloop +endfacet +facet normal -0.509128 0.000000 0.860691 +outer loop +vertex 16.079948 10.482659 8.875000 +vertex 16.516666 8.852689 9.133333 +vertex 16.516666 10.483034 9.133333 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 10.482183 8.616667 +vertex 15.785417 8.851082 8.616667 +vertex 16.079948 8.851977 8.875000 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 10.482183 8.616667 +vertex 16.079948 8.851977 8.875000 +vertex 16.079948 10.482659 8.875000 +endloop +endfacet +facet normal -0.931105 0.000000 0.364750 +outer loop +vertex 15.694011 10.481650 8.383333 +vertex 15.694011 8.850069 8.383333 +vertex 15.785417 8.851082 8.616667 +endloop +endfacet +facet normal -0.931105 0.000000 0.364750 +outer loop +vertex 15.694011 10.481650 8.383333 +vertex 15.785417 8.851082 8.616667 +vertex 15.785417 10.482183 8.616667 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200653 8.852824 9.371354 +vertex 21.200655 7.092052 9.371354 +vertex 22.754240 7.091278 9.079166 +endloop +endfacet +facet normal 0.184832 -0.000001 0.982770 +outer loop +vertex 21.200653 8.852824 9.371354 +vertex 22.754240 7.091278 9.079166 +vertex 22.754240 8.852315 9.079167 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200655 7.092052 9.371354 +vertex 21.200653 5.357885 9.371354 +vertex 22.754240 5.356898 9.079166 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200655 7.092052 9.371354 +vertex 22.754240 5.356898 9.079166 +vertex 22.754240 7.091278 9.079166 +endloop +endfacet +facet normal 0.214559 -0.000000 0.976711 +outer loop +vertex 22.754240 8.852315 9.079167 +vertex 22.754240 7.091278 9.079166 +vertex 24.226576 7.090393 8.755729 +endloop +endfacet +facet normal 0.214560 -0.000001 0.976711 +outer loop +vertex 22.754240 8.852315 9.079167 +vertex 24.226576 7.090393 8.755729 +vertex 24.226576 8.851733 8.755730 +endloop +endfacet +facet normal 0.214560 0.000000 0.976711 +outer loop +vertex 22.754240 7.091278 9.079166 +vertex 22.754240 5.356898 9.079166 +vertex 24.226576 5.355767 8.755730 +endloop +endfacet +facet normal 0.214560 0.000001 0.976711 +outer loop +vertex 22.754240 7.091278 9.079166 +vertex 24.226576 5.355767 8.755730 +vertex 24.226576 7.090393 8.755729 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 10.483306 9.566667 +vertex 19.769468 8.853203 9.566667 +vertex 21.200653 8.852824 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 10.483306 9.566667 +vertex 21.200653 8.852824 9.371354 +vertex 21.200653 10.483106 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 8.853203 9.566667 +vertex 19.769468 7.092627 9.566667 +vertex 21.200655 7.092052 9.371354 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 8.853203 9.566667 +vertex 21.200655 7.092052 9.371354 +vertex 21.200653 8.852824 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 7.092627 9.566667 +vertex 19.769468 5.358619 9.566667 +vertex 21.200653 5.357885 9.371354 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 7.092627 9.566667 +vertex 21.200653 5.357885 9.371354 +vertex 21.200655 7.092052 9.371354 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 5.358619 9.566667 +vertex 19.769468 3.808219 9.566667 +vertex 21.200653 3.807420 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 5.358619 9.566667 +vertex 21.200653 3.807420 9.371354 +vertex 21.200653 5.357885 9.371354 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200653 5.357885 9.371354 +vertex 21.200653 3.807420 9.371354 +vertex 22.754240 3.806343 9.079166 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200653 5.357885 9.371354 +vertex 22.754240 3.806343 9.079166 +vertex 22.754240 5.356898 9.079166 +endloop +endfacet +facet normal 0.214560 0.000000 0.976711 +outer loop +vertex 22.754240 5.356898 9.079166 +vertex 22.754240 3.806343 9.079166 +vertex 24.226574 3.805111 8.755730 +endloop +endfacet +facet normal 0.214559 -0.000001 0.976711 +outer loop +vertex 22.754240 5.356898 9.079166 +vertex 24.226574 3.805111 8.755730 +vertex 24.226576 5.355767 8.755730 +endloop +endfacet +facet normal 0.236528 0.000000 0.971625 +outer loop +vertex 24.226576 5.355767 8.755730 +vertex 24.226574 3.805111 8.755730 +vertex 25.414011 3.803846 8.466667 +endloop +endfacet +facet normal 0.236527 0.000000 0.971625 +outer loop +vertex 24.226576 5.355767 8.755730 +vertex 25.414011 3.803846 8.466667 +vertex 25.414009 5.354605 8.466666 +endloop +endfacet +facet normal 0.236529 0.000001 0.971625 +outer loop +vertex 24.226576 7.090393 8.755729 +vertex 24.226576 5.355767 8.755730 +vertex 25.414009 5.354605 8.466666 +endloop +endfacet +facet normal 0.236525 -0.000001 0.971625 +outer loop +vertex 24.226576 7.090393 8.755729 +vertex 25.414009 5.354605 8.466666 +vertex 25.414009 7.089484 8.466667 +endloop +endfacet +facet normal 0.236526 -0.000000 0.971625 +outer loop +vertex 24.226576 8.851733 8.755730 +vertex 24.226576 7.090393 8.755729 +vertex 25.414009 7.089484 8.466667 +endloop +endfacet +facet normal 0.236527 0.000000 0.971625 +outer loop +vertex 24.226576 8.851733 8.755730 +vertex 25.414009 7.089484 8.466667 +vertex 25.414009 8.851135 8.466666 +endloop +endfacet +facet normal 0.236527 0.000000 0.971625 +outer loop +vertex 24.226574 10.482530 8.755730 +vertex 24.226576 8.851733 8.755730 +vertex 25.414009 8.851135 8.466666 +endloop +endfacet +facet normal 0.236527 -0.000000 0.971625 +outer loop +vertex 24.226574 10.482530 8.755730 +vertex 25.414009 8.851135 8.466666 +vertex 25.414009 10.482211 8.466667 +endloop +endfacet +facet normal 0.214560 0.000000 0.976711 +outer loop +vertex 22.754240 10.482836 9.079166 +vertex 22.754240 8.852315 9.079167 +vertex 24.226576 8.851733 8.755730 +endloop +endfacet +facet normal 0.214560 0.000001 0.976711 +outer loop +vertex 22.754240 10.482836 9.079166 +vertex 24.226576 8.851733 8.755730 +vertex 24.226574 10.482530 8.755730 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200653 10.483106 9.371354 +vertex 21.200653 8.852824 9.371354 +vertex 22.754240 8.852315 9.079167 +endloop +endfacet +facet normal 0.184833 0.000001 0.982770 +outer loop +vertex 21.200653 10.483106 9.371354 +vertex 22.754240 8.852315 9.079167 +vertex 22.754240 10.482836 9.079166 +endloop +endfacet +facet normal 0.002253 0.000000 -0.999997 +outer loop +vertex 21.109249 8.846521 8.000521 +vertex 22.727156 8.847102 8.004167 +vertex 22.727156 7.083351 8.004167 +endloop +endfacet +facet normal 0.002254 0.000000 -0.999997 +outer loop +vertex 21.109249 8.846521 8.000521 +vertex 22.727156 7.083351 8.004167 +vertex 21.109247 7.082470 8.000521 +endloop +endfacet +facet normal 0.006615 0.000000 -0.999978 +outer loop +vertex 22.727156 8.847102 8.004167 +vertex 24.223190 8.847869 8.014062 +vertex 24.223190 7.084518 8.014062 +endloop +endfacet +facet normal 0.006614 0.000000 -0.999978 +outer loop +vertex 22.727156 8.847102 8.004167 +vertex 24.223190 7.084518 8.014062 +vertex 22.727156 7.083351 8.004167 +endloop +endfacet +facet normal 0.002254 0.000054 -0.999997 +outer loop +vertex 21.109247 7.082470 8.000521 +vertex 22.727156 7.083351 8.004167 +vertex 22.730549 5.346774 8.004080 +endloop +endfacet +facet normal 0.002202 0.000007 -0.999998 +outer loop +vertex 21.109247 7.082470 8.000521 +vertex 22.730549 5.346774 8.004080 +vertex 21.109673 5.345647 8.000510 +endloop +endfacet +facet normal 0.006614 0.000181 -0.999978 +outer loop +vertex 22.727156 7.083351 8.004167 +vertex 24.223190 7.084518 8.014062 +vertex 24.232944 5.348269 8.013813 +endloop +endfacet +facet normal 0.006477 0.000063 -0.999979 +outer loop +vertex 22.727156 7.083351 8.004167 +vertex 24.232944 5.348269 8.013813 +vertex 22.730549 5.346774 8.004080 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 10.479649 8.000000 +vertex 21.109249 10.479773 8.000521 +vertex 21.109249 8.846521 8.000521 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 10.479649 8.000000 +vertex 21.109249 8.846521 8.000521 +vertex 19.552803 8.846289 8.000000 +endloop +endfacet +facet normal 0.002252 -0.000000 -0.999997 +outer loop +vertex 21.109249 10.479773 8.000521 +vertex 22.727158 10.480080 8.004167 +vertex 22.727156 8.847102 8.004167 +endloop +endfacet +facet normal 0.002254 0.000000 -0.999997 +outer loop +vertex 21.109249 10.479773 8.000521 +vertex 22.727156 8.847102 8.004167 +vertex 21.109249 8.846521 8.000521 +endloop +endfacet +facet normal 0.006614 0.000000 -0.999978 +outer loop +vertex 22.727158 10.480080 8.004167 +vertex 24.223190 10.480485 8.014062 +vertex 24.223190 8.847869 8.014062 +endloop +endfacet +facet normal 0.006615 0.000000 -0.999978 +outer loop +vertex 22.727158 10.480080 8.004167 +vertex 24.223190 8.847869 8.014062 +vertex 22.727156 8.847102 8.004167 +endloop +endfacet +facet normal 0.016182 0.000000 -0.999869 +outer loop +vertex 24.223190 10.480485 8.014062 +vertex 25.414011 10.480907 8.033333 +vertex 25.414009 8.848668 8.033333 +endloop +endfacet +facet normal 0.016180 0.000000 -0.999869 +outer loop +vertex 24.223190 10.480485 8.014062 +vertex 25.414009 8.848668 8.033333 +vertex 24.223190 8.847869 8.014062 +endloop +endfacet +facet normal 0.016181 0.000000 -0.999869 +outer loop +vertex 24.223190 8.847869 8.014062 +vertex 25.414009 8.848668 8.033333 +vertex 25.414009 7.085731 8.033333 +endloop +endfacet +facet normal 0.016180 0.000000 -0.999869 +outer loop +vertex 24.223190 8.847869 8.014062 +vertex 25.414009 7.085731 8.033333 +vertex 24.223190 7.084518 8.014062 +endloop +endfacet +facet normal 0.016182 0.000326 -0.999869 +outer loop +vertex 24.223190 7.084518 8.014062 +vertex 25.414009 7.085731 8.033333 +vertex 25.427582 5.349819 8.032986 +endloop +endfacet +facet normal 0.016046 0.000234 -0.999871 +outer loop +vertex 24.223190 7.084518 8.014062 +vertex 25.427582 5.349819 8.032986 +vertex 24.232944 5.348269 8.013813 +endloop +endfacet +facet normal 0.016042 0.003642 -0.999865 +outer loop +vertex 24.232944 5.348269 8.013813 +vertex 25.427582 5.349819 8.032986 +vertex 25.563290 3.798691 8.029513 +endloop +endfacet +facet normal 0.014140 0.002009 -0.999898 +outer loop +vertex 24.232944 5.348269 8.013813 +vertex 25.563290 3.798691 8.029513 +vertex 24.311401 3.796976 8.011806 +endloop +endfacet +facet normal 0.006478 0.001622 -0.999978 +outer loop +vertex 22.730549 5.346774 8.004080 +vertex 24.232944 5.348269 8.013813 +vertex 24.311401 3.796976 8.011806 +endloop +endfacet +facet normal 0.005350 0.000473 -0.999986 +outer loop +vertex 22.730549 5.346774 8.004080 +vertex 24.311401 3.796976 8.011806 +vertex 22.754299 3.795325 8.003472 +endloop +endfacet +facet normal 0.002203 0.000425 -0.999997 +outer loop +vertex 21.109673 5.345647 8.000510 +vertex 22.730549 5.346774 8.004080 +vertex 22.754299 3.795325 8.003472 +endloop +endfacet +facet normal 0.001851 0.000053 -0.999998 +outer loop +vertex 21.109673 5.345647 8.000510 +vertex 22.754299 3.795325 8.003472 +vertex 21.112640 3.794090 8.000434 +endloop +endfacet +facet normal 0.000327 0.000050 -1.000000 +outer loop +vertex 19.552801 5.345193 8.000000 +vertex 21.109673 5.345647 8.000510 +vertex 21.112640 3.794090 8.000434 +endloop +endfacet +facet normal 0.000279 0.000000 -1.000000 +outer loop +vertex 19.552801 5.345193 8.000000 +vertex 21.112640 3.794090 8.000434 +vertex 19.552803 3.793593 8.000000 +endloop +endfacet +facet normal 0.000334 0.000006 -1.000000 +outer loop +vertex 19.552803 7.082114 8.000000 +vertex 21.109247 7.082470 8.000521 +vertex 21.109673 5.345647 8.000510 +endloop +endfacet +facet normal 0.000328 0.000000 -1.000000 +outer loop +vertex 19.552803 7.082114 8.000000 +vertex 21.109673 5.345647 8.000510 +vertex 19.552801 5.345193 8.000000 +endloop +endfacet +facet normal 0.000334 -0.000000 -1.000000 +outer loop +vertex 19.552803 8.846289 8.000000 +vertex 21.109249 8.846521 8.000521 +vertex 21.109247 7.082470 8.000521 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 8.846289 8.000000 +vertex 21.109247 7.082470 8.000521 +vertex 19.552803 7.082114 8.000000 +endloop +endfacet +facet normal -0.008823 0.066099 -0.997774 +outer loop +vertex 21.108089 13.782950 8.097133 +vertex 22.668369 13.756055 8.081554 +vertex 22.706612 12.940510 8.027189 +endloop +endfacet +facet normal -0.000779 0.081272 -0.996692 +outer loop +vertex 21.108089 13.782950 8.097133 +vertex 22.706612 12.940510 8.027189 +vertex 21.108513 12.949333 8.029158 +endloop +endfacet +facet normal -0.004329 0.051922 -0.998642 +outer loop +vertex 22.668369 13.756055 8.081554 +vertex 24.034252 13.696052 8.072515 +vertex 24.159176 12.921573 8.031707 +endloop +endfacet +facet normal 0.003973 0.066698 -0.997765 +outer loop +vertex 22.668369 13.756055 8.081554 +vertex 24.159176 12.921573 8.031707 +vertex 22.706612 12.940510 8.027189 +endloop +endfacet +facet normal -0.001129 0.018480 -0.999829 +outer loop +vertex 21.108513 12.949333 8.029158 +vertex 22.706612 12.940510 8.027189 +vertex 22.724588 11.851659 8.007044 +endloop +endfacet +facet normal 0.001837 0.022844 -0.999737 +outer loop +vertex 21.108513 12.949333 8.029158 +vertex 22.724588 11.851659 8.007044 +vertex 21.109156 11.852637 8.004100 +endloop +endfacet +facet normal 0.003299 0.014571 -0.999888 +outer loop +vertex 22.706612 12.940510 8.027189 +vertex 24.159176 12.921573 8.031707 +vertex 24.215187 11.849457 8.016268 +endloop +endfacet +facet normal 0.006215 0.018601 -0.999808 +outer loop +vertex 22.706612 12.940510 8.027189 +vertex 24.215187 11.849457 8.016268 +vertex 22.724588 11.851659 8.007044 +endloop +endfacet +facet normal -0.019371 0.220396 -0.975218 +outer loop +vertex 19.588913 14.375001 8.261111 +vertex 21.113926 14.368312 8.229306 +vertex 21.108089 13.782950 8.097133 +endloop +endfacet +facet normal -0.007570 0.248826 -0.968519 +outer loop +vertex 19.588913 14.375001 8.261111 +vertex 21.108089 13.782950 8.097133 +vertex 19.568035 13.786793 8.110156 +endloop +endfacet +facet normal -0.022359 0.180528 -0.983316 +outer loop +vertex 21.113926 14.368312 8.229306 +vertex 22.647228 14.321472 8.185841 +vertex 22.668369 13.756055 8.081554 +endloop +endfacet +facet normal -0.005942 0.220306 -0.975413 +outer loop +vertex 21.113926 14.368312 8.229306 +vertex 22.668369 13.756055 8.081554 +vertex 21.108089 13.782950 8.097133 +endloop +endfacet +facet normal -0.017813 0.139884 -0.990008 +outer loop +vertex 22.647228 14.321472 8.185841 +vertex 23.947687 14.205942 8.146118 +vertex 24.034252 13.696052 8.072515 +endloop +endfacet +facet normal 0.001462 0.181435 -0.983402 +outer loop +vertex 22.647228 14.321472 8.185841 +vertex 24.034252 13.696052 8.072515 +vertex 22.668369 13.756055 8.081554 +endloop +endfacet +facet normal -0.007751 0.102426 -0.994711 +outer loop +vertex 23.947687 14.205942 8.146118 +vertex 24.762611 14.062502 8.125000 +vertex 25.061609 13.654374 8.080646 +endloop +endfacet +facet normal 0.013719 0.145136 -0.989317 +outer loop +vertex 23.947687 14.205942 8.146118 +vertex 25.061609 13.654374 8.080646 +vertex 24.034252 13.696052 8.072515 +endloop +endfacet +facet normal 0.009810 0.047003 -0.998847 +outer loop +vertex 24.034252 13.696052 8.072515 +vertex 25.061609 13.654374 8.080646 +vertex 25.302427 12.910231 8.047994 +endloop +endfacet +facet normal 0.014770 0.054988 -0.998378 +outer loop +vertex 24.034252 13.696052 8.072515 +vertex 25.302427 12.910231 8.047994 +vertex 24.159176 12.921573 8.031707 +endloop +endfacet +facet normal 0.014378 0.013398 -0.999807 +outer loop +vertex 24.159176 12.921573 8.031707 +vertex 25.302427 12.910231 8.047994 +vertex 25.400063 11.848212 8.035166 +endloop +endfacet +facet normal 0.015961 0.015231 -0.999757 +outer loop +vertex 24.159176 12.921573 8.031707 +vertex 25.400063 11.848212 8.035166 +vertex 24.215187 11.849457 8.016268 +endloop +endfacet +facet normal 0.015950 0.001503 -0.999872 +outer loop +vertex 24.215187 11.849457 8.016268 +vertex 25.400063 11.848212 8.035166 +vertex 25.414011 10.480907 8.033333 +endloop +endfacet +facet normal 0.016180 0.001706 -0.999868 +outer loop +vertex 24.215187 11.849457 8.016268 +vertex 25.414011 10.480907 8.033333 +vertex 24.223190 10.480485 8.014062 +endloop +endfacet +facet normal 0.006190 0.001647 -0.999980 +outer loop +vertex 22.724588 11.851659 8.007044 +vertex 24.215187 11.849457 8.016268 +vertex 24.223190 10.480485 8.014062 +endloop +endfacet +facet normal 0.006613 0.002110 -0.999976 +outer loop +vertex 22.724588 11.851659 8.007044 +vertex 24.223190 10.480485 8.014062 +vertex 22.727158 10.480080 8.004167 +endloop +endfacet +facet normal 0.001824 0.002101 -0.999996 +outer loop +vertex 21.109156 11.852637 8.004100 +vertex 22.724588 11.851659 8.007044 +vertex 22.727158 10.480080 8.004167 +endloop +endfacet +facet normal 0.002254 0.002607 -0.999994 +outer loop +vertex 21.109156 11.852637 8.004100 +vertex 22.727158 10.480080 8.004167 +vertex 21.109249 10.479773 8.000521 +endloop +endfacet +facet normal 0.000012 0.002607 -0.999997 +outer loop +vertex 19.553366 11.852744 8.004080 +vertex 21.109156 11.852637 8.004100 +vertex 21.109249 10.479773 8.000521 +endloop +endfacet +facet normal 0.000335 0.002971 -0.999996 +outer loop +vertex 19.553366 11.852744 8.004080 +vertex 21.109249 10.479773 8.000521 +vertex 19.552803 10.479649 8.000000 +endloop +endfacet +facet normal -0.002227 0.022841 -0.999737 +outer loop +vertex 19.557316 12.950583 8.032639 +vertex 21.108513 12.949333 8.029158 +vertex 21.109156 11.852637 8.004100 +endloop +endfacet +facet normal 0.000016 0.026005 -0.999662 +outer loop +vertex 19.557316 12.950583 8.032639 +vertex 21.109156 11.852637 8.004100 +vertex 19.553366 11.852744 8.004080 +endloop +endfacet +facet normal -0.008225 0.081266 -0.996659 +outer loop +vertex 19.568035 13.786793 8.110156 +vertex 21.108089 13.782950 8.097133 +vertex 21.108513 12.949333 8.029158 +endloop +endfacet +facet normal -0.002161 0.092332 -0.995726 +outer loop +vertex 19.568035 13.786793 8.110156 +vertex 21.108513 12.949333 8.029158 +vertex 19.557316 12.950583 8.032639 +endloop +endfacet +facet normal 0.180586 0.080679 0.980245 +outer loop +vertex 21.186642 13.783004 9.276489 +vertex 21.196112 12.949749 9.343325 +vertex 22.732565 12.940852 9.061005 +endloop +endfacet +facet normal 0.171458 0.063177 0.983164 +outer loop +vertex 21.186642 13.783004 9.276489 +vertex 22.732565 12.940852 9.061005 +vertex 22.691643 13.756097 9.015754 +endloop +endfacet +facet normal 0.184294 0.022663 0.982610 +outer loop +vertex 21.196112 12.949749 9.343325 +vertex 21.200087 11.854041 9.367850 +vertex 22.751530 11.852822 9.076897 +endloop +endfacet +facet normal 0.180792 0.017514 0.983365 +outer loop +vertex 21.196112 12.949749 9.343325 +vertex 22.751530 11.852822 9.076897 +vertex 22.732565 12.940852 9.061005 +endloop +endfacet +facet normal 0.211063 0.064731 0.975327 +outer loop +vertex 22.691643 13.756097 9.015754 +vertex 22.732565 12.940852 9.061005 +vertex 24.162418 12.921827 8.752842 +endloop +endfacet +facet normal 0.202004 0.047845 0.978215 +outer loop +vertex 22.691643 13.756097 9.015754 +vertex 24.162418 12.921827 8.752842 +vertex 24.037163 13.696084 8.740838 +endloop +endfacet +facet normal 0.214084 0.017997 0.976649 +outer loop +vertex 22.732565 12.940852 9.061005 +vertex 22.751530 11.852822 9.076897 +vertex 24.218555 11.850319 8.755368 +endloop +endfacet +facet normal 0.210835 0.013350 0.977430 +outer loop +vertex 22.732565 12.940852 9.061005 +vertex 24.218555 11.850319 8.755368 +vertex 24.162418 12.921827 8.752842 +endloop +endfacet +facet normal 0.121374 0.250804 0.960399 +outer loop +vertex 19.733356 14.375003 9.305555 +vertex 19.754234 13.786851 9.456511 +vertex 21.186642 13.783004 9.276489 +endloop +endfacet +facet normal 0.108992 0.219970 0.969399 +outer loop +vertex 19.733356 14.375003 9.305555 +vertex 21.186642 13.783004 9.276489 +vertex 21.174864 14.368311 9.145000 +endloop +endfacet +facet normal 0.131593 0.093219 0.986911 +outer loop +vertex 19.754234 13.786851 9.456511 +vertex 19.764956 12.951040 9.534028 +vertex 21.196112 12.949749 9.343325 +endloop +endfacet +facet normal 0.124502 0.080737 0.988929 +outer loop +vertex 19.754234 13.786851 9.456511 +vertex 21.196112 12.949749 9.343325 +vertex 21.186642 13.783004 9.276489 +endloop +endfacet +facet normal 0.134783 0.026278 0.990527 +outer loop +vertex 19.764956 12.951040 9.534028 +vertex 19.768904 11.854286 9.562587 +vertex 21.200087 11.854041 9.367850 +endloop +endfacet +facet normal 0.132070 0.022661 0.990981 +outer loop +vertex 19.764956 12.951040 9.534028 +vertex 21.200087 11.854041 9.367850 +vertex 21.196112 12.949749 9.343325 +endloop +endfacet +facet normal 0.135215 0.003004 0.990812 +outer loop +vertex 19.768904 11.854286 9.562587 +vertex 19.769468 10.483306 9.566667 +vertex 21.200653 10.483106 9.371354 +endloop +endfacet +facet normal 0.134824 0.002588 0.990866 +outer loop +vertex 19.768904 11.854286 9.562587 +vertex 21.200653 10.483106 9.371354 +vertex 21.200087 11.854041 9.367850 +endloop +endfacet +facet normal 0.184832 0.002589 0.982767 +outer loop +vertex 21.200087 11.854041 9.367850 +vertex 21.200653 10.483106 9.371354 +vertex 22.754240 10.482836 9.079166 +endloop +endfacet +facet normal 0.184326 0.001993 0.982863 +outer loop +vertex 21.200087 11.854041 9.367850 +vertex 22.754240 10.482836 9.079166 +vertex 22.751530 11.852822 9.076897 +endloop +endfacet +facet normal 0.214560 0.002042 0.976709 +outer loop +vertex 22.751530 11.852822 9.076897 +vertex 22.754240 10.482836 9.079166 +vertex 24.226574 10.482530 8.755730 +endloop +endfacet +facet normal 0.214092 0.001513 0.976812 +outer loop +vertex 22.751530 11.852822 9.076897 +vertex 24.226574 10.482530 8.755730 +vertex 24.218555 11.850319 8.755368 +endloop +endfacet +facet normal 0.236527 0.001643 0.971624 +outer loop +vertex 24.218555 11.850319 8.755368 +vertex 24.226574 10.482530 8.755730 +vertex 25.414009 10.482211 8.466667 +endloop +endfacet +facet normal 0.236297 0.001430 0.971680 +outer loop +vertex 24.218555 11.850319 8.755368 +vertex 25.414009 10.482211 8.466667 +vertex 25.400064 11.848762 8.468046 +endloop +endfacet +facet normal 0.236288 0.014670 0.971572 +outer loop +vertex 24.162418 12.921827 8.752842 +vertex 24.218555 11.850319 8.755368 +vertex 25.400064 11.848762 8.468046 +endloop +endfacet +facet normal 0.234715 0.012748 0.971981 +outer loop +vertex 24.162418 12.921827 8.752842 +vertex 25.400064 11.848762 8.468046 +vertex 25.302429 12.910394 8.477700 +endloop +endfacet +facet normal 0.234786 0.053031 0.970600 +outer loop +vertex 24.037163 13.696084 8.740838 +vertex 24.162418 12.921827 8.752842 +vertex 25.302429 12.910394 8.477700 +endloop +endfacet +facet normal 0.229893 0.044615 0.972193 +outer loop +vertex 24.037163 13.696084 8.740838 +vertex 25.302429 12.910394 8.477700 +vertex 25.061607 13.654394 8.500504 +endloop +endfacet +facet normal 0.231550 0.140794 0.962581 +outer loop +vertex 23.949944 14.205942 8.687243 +vertex 24.037163 13.696084 8.740838 +vertex 25.061607 13.654394 8.500504 +endloop +endfacet +facet normal 0.211196 0.096345 0.972684 +outer loop +vertex 23.949944 14.205942 8.687243 +vertex 25.061607 13.654394 8.500504 +vertex 24.762611 14.062502 8.525001 +endloop +endfacet +facet normal 0.204599 0.177777 0.962567 +outer loop +vertex 22.665283 14.321471 8.916937 +vertex 22.691643 13.756097 9.015754 +vertex 24.037163 13.696084 8.740838 +endloop +endfacet +facet normal 0.186093 0.134147 0.973331 +outer loop +vertex 22.665283 14.321471 8.916937 +vertex 24.037163 13.696084 8.740838 +vertex 23.949944 14.205942 8.687243 +endloop +endfacet +facet normal 0.170355 0.219245 0.960682 +outer loop +vertex 21.174864 14.368311 9.145000 +vertex 21.186642 13.783004 9.276489 +vertex 22.691643 13.756097 9.015754 +endloop +endfacet +facet normal 0.154304 0.177086 0.972024 +outer loop +vertex 21.174864 14.368311 9.145000 +vertex 22.691643 13.756097 9.015754 +vertex 22.665283 14.321471 8.916937 +endloop +endfacet +facet normal -0.925336 0.179809 0.333800 +outer loop +vertex 15.926517 13.692226 8.431500 +vertex 15.764791 12.920452 8.398901 +vertex 15.848109 12.930644 8.624383 +endloop +endfacet +facet normal -0.942867 0.169688 0.286718 +outer loop +vertex 15.926517 13.692226 8.431500 +vertex 15.848109 12.930644 8.624383 +vertex 15.996692 13.725345 8.642670 +endloop +endfacet +facet normal -0.930933 0.049248 0.361855 +outer loop +vertex 15.764791 12.920452 8.398901 +vertex 15.702858 11.849790 8.385280 +vertex 15.793253 11.851281 8.617631 +endloop +endfacet +facet normal -0.937705 0.045501 0.344440 +outer loop +vertex 15.764791 12.920452 8.398901 +vertex 15.793253 11.851281 8.617631 +vertex 15.848109 12.930644 8.624383 +endloop +endfacet +facet normal -0.661215 0.106536 0.742593 +outer loop +vertex 15.996692 13.725345 8.642670 +vertex 15.848109 12.930644 8.624383 +vertex 16.126717 12.920577 8.873900 +endloop +endfacet +facet normal -0.680988 0.098469 0.725644 +outer loop +vertex 15.996692 13.725345 8.642670 +vertex 16.126717 12.920577 8.873900 +vertex 16.239573 13.692245 8.875096 +endloop +endfacet +facet normal -0.659997 0.028847 0.750714 +outer loop +vertex 15.848109 12.930644 8.624383 +vertex 15.793253 11.851281 8.617631 +vertex 16.085794 11.850214 8.874863 +endloop +endfacet +facet normal -0.666398 0.026147 0.745138 +outer loop +vertex 15.848109 12.930644 8.624383 +vertex 16.085794 11.850214 8.874863 +vertex 16.126717 12.920577 8.873900 +endloop +endfacet +facet normal -0.638856 0.372476 -0.673145 +outer loop +vertex 16.245834 14.062501 8.333333 +vertex 16.073439 13.654376 8.271119 +vertex 15.926517 13.692226 8.431500 +endloop +endfacet +facet normal -0.679728 0.428326 -0.595404 +outer loop +vertex 16.245834 14.062501 8.333333 +vertex 15.926517 13.692226 8.431500 +vertex 16.212664 14.199250 8.469577 +endloop +endfacet +facet normal -0.708344 0.179013 -0.682791 +outer loop +vertex 16.073439 13.654376 8.271119 +vertex 15.932369 12.910254 8.222376 +vertex 15.764791 12.920452 8.398901 +endloop +endfacet +facet normal -0.704649 0.176686 -0.687205 +outer loop +vertex 16.073439 13.654376 8.271119 +vertex 15.764791 12.920452 8.398901 +vertex 15.926517 13.692226 8.431500 +endloop +endfacet +facet normal -0.726457 0.051960 -0.685245 +outer loop +vertex 15.932369 12.910254 8.222376 +vertex 15.874880 11.848289 8.202797 +vertex 15.702858 11.849790 8.385280 +endloop +endfacet +facet normal -0.722856 0.050581 -0.689144 +outer loop +vertex 15.932369 12.910254 8.222376 +vertex 15.702858 11.849790 8.385280 +vertex 15.764791 12.920452 8.398901 +endloop +endfacet +facet normal -0.727964 0.005775 -0.685591 +outer loop +vertex 15.874880 11.848289 8.202797 +vertex 15.866667 10.481091 8.200000 +vertex 15.694011 10.481650 8.383333 +endloop +endfacet +facet normal -0.727620 0.005683 -0.685957 +outer loop +vertex 15.874880 11.848289 8.202797 +vertex 15.694011 10.481650 8.383333 +vertex 15.702858 11.849790 8.385280 +endloop +endfacet +facet normal -0.931095 0.005502 0.364735 +outer loop +vertex 15.702858 11.849790 8.385280 +vertex 15.694011 10.481650 8.383333 +vertex 15.785417 10.482183 8.616667 +endloop +endfacet +facet normal -0.931956 0.005079 0.362536 +outer loop +vertex 15.702858 11.849790 8.385280 +vertex 15.785417 10.482183 8.616667 +vertex 15.793253 11.851281 8.617631 +endloop +endfacet +facet normal -0.659395 0.003245 0.751790 +outer loop +vertex 15.793253 11.851281 8.617631 +vertex 15.785417 10.482183 8.616667 +vertex 16.079948 10.482659 8.875000 +endloop +endfacet +facet normal -0.660322 0.002898 0.750977 +outer loop +vertex 15.793253 11.851281 8.617631 +vertex 16.079948 10.482659 8.875000 +vertex 16.085794 11.850214 8.874863 +endloop +endfacet +facet normal -0.509129 0.002264 0.860687 +outer loop +vertex 16.085794 11.850214 8.874863 +vertex 16.079948 10.482659 8.875000 +vertex 16.516666 10.483034 9.133333 +endloop +endfacet +facet normal -0.509449 0.002124 0.860498 +outer loop +vertex 16.085794 11.850214 8.874863 +vertex 16.516666 10.483034 9.133333 +vertex 16.520052 11.849110 9.131964 +endloop +endfacet +facet normal -0.509309 0.020245 0.860346 +outer loop +vertex 16.126717 12.920577 8.873900 +vertex 16.085794 11.850214 8.874863 +vertex 16.520052 11.849110 9.131964 +endloop +endfacet +facet normal -0.511418 0.019182 0.859118 +outer loop +vertex 16.126717 12.920577 8.873900 +vertex 16.520052 11.849110 9.131964 +vertex 16.543751 12.910495 9.122376 +endloop +endfacet +facet normal -0.509175 0.073137 0.857550 +outer loop +vertex 16.239573 13.692245 8.875096 +vertex 16.126717 12.920577 8.873900 +vertex 16.543751 12.910495 9.122376 +endloop +endfacet +facet normal -0.516933 0.068762 0.853260 +outer loop +vertex 16.239573 13.692245 8.875096 +vertex 16.543751 12.910495 9.122376 +vertex 16.602932 13.654407 9.098280 +endloop +endfacet +facet normal -0.499636 0.188695 0.845433 +outer loop +vertex 16.475306 14.199250 8.901251 +vertex 16.239573 13.692245 8.875096 +vertex 16.602932 13.654407 9.098280 +endloop +endfacet +facet normal -0.551613 0.166355 0.817343 +outer loop +vertex 16.475306 14.199250 8.901251 +vertex 16.602932 13.654407 9.098280 +vertex 16.679167 14.062502 9.066667 +endloop +endfacet +facet normal -0.639902 0.292996 0.710408 +outer loop +vertex 16.284452 14.267941 8.678086 +vertex 15.996692 13.725345 8.642670 +vertex 16.239573 13.692245 8.875096 +endloop +endfacet +facet normal -0.684012 0.283353 0.672190 +outer loop +vertex 16.284452 14.267941 8.678086 +vertex 16.239573 13.692245 8.875096 +vertex 16.475306 14.199250 8.901251 +endloop +endfacet +facet normal -0.857875 0.468253 0.211637 +outer loop +vertex 16.212664 14.199250 8.469577 +vertex 15.926517 13.692226 8.431500 +vertex 15.996692 13.725345 8.642670 +endloop +endfacet +facet normal -0.877249 0.455323 0.152037 +outer loop +vertex 16.212664 14.199250 8.469577 +vertex 15.996692 13.725345 8.642670 +vertex 16.284452 14.267941 8.678086 +endloop +endfacet +facet normal 0.272812 0.001670 -0.962066 +outer loop +vertex 26.156590 5.351182 8.067980 +vertex 26.146835 7.086800 8.068229 +vertex 26.391111 7.087739 8.137500 +endloop +endfacet +facet normal 0.280158 0.000596 -0.959954 +outer loop +vertex 26.156590 5.351182 8.067980 +vertex 26.391111 7.087739 8.137500 +vertex 26.394505 5.352378 8.137413 +endloop +endfacet +facet normal 0.272823 0.000000 -0.962064 +outer loop +vertex 26.146835 7.086800 8.068229 +vertex 26.146837 8.849369 8.068229 +vertex 26.391111 8.849988 8.137500 +endloop +endfacet +facet normal 0.272818 0.000000 -0.962066 +outer loop +vertex 26.146835 7.086800 8.068229 +vertex 26.391111 8.849988 8.137500 +vertex 26.391111 7.087739 8.137500 +endloop +endfacet +facet normal 0.457040 0.000850 0.889446 +outer loop +vertex 26.394505 5.352378 8.137413 +vertex 26.391111 7.087739 8.137500 +vertex 26.146837 7.088612 8.263021 +endloop +endfacet +facet normal 0.452912 0.000105 0.891555 +outer loop +vertex 26.394505 5.352378 8.137413 +vertex 26.146837 7.088612 8.263021 +vertex 26.147263 5.353491 8.263010 +endloop +endfacet +facet normal 0.457043 0.000000 0.889445 +outer loop +vertex 26.391111 7.087739 8.137500 +vertex 26.391111 8.849988 8.137500 +vertex 26.146837 8.850560 8.263021 +endloop +endfacet +facet normal 0.457042 0.000000 0.889445 +outer loop +vertex 26.391111 7.087739 8.137500 +vertex 26.146837 8.850560 8.263021 +vertex 26.146837 7.088612 8.263021 +endloop +endfacet +facet normal 0.047932 0.006430 -0.998830 +outer loop +vertex 25.563290 3.798691 8.029513 +vertex 25.427582 5.349819 8.032986 +vertex 26.156590 5.351182 8.067980 +endloop +endfacet +facet normal 0.054188 0.004033 -0.998523 +outer loop +vertex 25.563290 3.798691 8.029513 +vertex 26.156590 5.351182 8.067980 +vertex 26.235046 3.800150 8.065972 +endloop +endfacet +facet normal 0.047565 0.000572 -0.998868 +outer loop +vertex 25.427582 5.349819 8.032986 +vertex 25.414009 7.085731 8.033333 +vertex 26.146835 7.086800 8.068229 +endloop +endfacet +facet normal 0.047943 0.000413 -0.998850 +outer loop +vertex 25.427582 5.349819 8.032986 +vertex 26.146835 7.086800 8.068229 +vertex 26.156590 5.351182 8.067980 +endloop +endfacet +facet normal 0.047566 0.000000 -0.998868 +outer loop +vertex 25.414009 7.085731 8.033333 +vertex 25.414009 8.848668 8.033333 +vertex 26.146837 8.849369 8.068229 +endloop +endfacet +facet normal 0.047563 0.000000 -0.998868 +outer loop +vertex 25.414009 7.085731 8.033333 +vertex 26.146837 8.849369 8.068229 +vertex 26.146835 7.086800 8.068229 +endloop +endfacet +facet normal 0.047565 0.000000 -0.998868 +outer loop +vertex 25.414009 8.848668 8.033333 +vertex 25.414011 10.480907 8.033333 +vertex 26.146835 10.481279 8.068229 +endloop +endfacet +facet normal 0.047563 0.000000 -0.998868 +outer loop +vertex 25.414009 8.848668 8.033333 +vertex 26.146835 10.481279 8.068229 +vertex 26.146837 8.849369 8.068229 +endloop +endfacet +facet normal 0.272819 0.000000 -0.962065 +outer loop +vertex 26.146837 8.849369 8.068229 +vertex 26.146835 10.481279 8.068229 +vertex 26.391113 10.481605 8.137500 +endloop +endfacet +facet normal 0.272821 0.000000 -0.962065 +outer loop +vertex 26.146837 8.849369 8.068229 +vertex 26.391113 10.481605 8.137500 +vertex 26.391111 8.849988 8.137500 +endloop +endfacet +facet normal 0.457041 0.000000 0.889446 +outer loop +vertex 26.391111 8.849988 8.137500 +vertex 26.391113 10.481605 8.137500 +vertex 26.146837 10.481909 8.263021 +endloop +endfacet +facet normal 0.457041 0.000000 0.889446 +outer loop +vertex 26.391111 8.849988 8.137500 +vertex 26.146837 10.481909 8.263021 +vertex 26.146837 8.850560 8.263021 +endloop +endfacet +facet normal 0.267744 0.000000 0.963490 +outer loop +vertex 26.146837 8.850560 8.263021 +vertex 26.146837 10.481909 8.263021 +vertex 25.414009 10.482211 8.466667 +endloop +endfacet +facet normal 0.267747 -0.000000 0.963489 +outer loop +vertex 26.146837 8.850560 8.263021 +vertex 25.414009 10.482211 8.466667 +vertex 25.414009 8.851135 8.466666 +endloop +endfacet +facet normal 0.267745 0.000000 0.963490 +outer loop +vertex 26.146837 7.088612 8.263021 +vertex 26.146837 8.850560 8.263021 +vertex 25.414009 8.851135 8.466666 +endloop +endfacet +facet normal 0.267747 0.000000 0.963489 +outer loop +vertex 26.146837 7.088612 8.263021 +vertex 25.414009 8.851135 8.466666 +vertex 25.414009 7.089484 8.466667 +endloop +endfacet +facet normal 0.267745 0.000061 0.963490 +outer loop +vertex 26.147263 5.353491 8.263010 +vertex 26.146837 7.088612 8.263021 +vertex 25.414009 7.089484 8.466667 +endloop +endfacet +facet normal 0.267615 -0.000001 0.963526 +outer loop +vertex 26.147263 5.353491 8.263010 +vertex 25.414009 7.089484 8.466667 +vertex 25.414009 5.354605 8.466666 +endloop +endfacet +facet normal 0.267612 0.000465 0.963526 +outer loop +vertex 26.150230 3.802635 8.262934 +vertex 26.147263 5.353491 8.263010 +vertex 25.414009 5.354605 8.466666 +endloop +endfacet +facet normal 0.266708 0.000000 0.963777 +outer loop +vertex 26.150230 3.802635 8.262934 +vertex 25.414009 5.354605 8.466666 +vertex 25.414011 3.803846 8.466667 +endloop +endfacet +facet normal 0.452912 0.006587 0.891531 +outer loop +vertex 26.418255 3.801430 8.136806 +vertex 26.394505 5.352378 8.137413 +vertex 26.147263 5.353491 8.263010 +endloop +endfacet +facet normal 0.425800 0.000771 0.904817 +outer loop +vertex 26.418255 3.801430 8.136806 +vertex 26.147263 5.353491 8.263010 +vertex 26.150230 3.802635 8.262934 +endloop +endfacet +facet normal 0.280041 0.015408 -0.959864 +outer loop +vertex 26.235046 3.800150 8.065972 +vertex 26.156590 5.351182 8.067980 +vertex 26.394505 5.352378 8.137413 +endloop +endfacet +facet normal 0.360580 0.005887 -0.932710 +outer loop +vertex 26.235046 3.800150 8.065972 +vertex 26.394505 5.352378 8.137413 +vertex 26.418255 3.801430 8.136806 +endloop +endfacet +facet normal 0.000279 -0.000002 -1.000000 +outer loop +vertex 19.549603 3.206118 8.000000 +vertex 19.552803 3.793593 8.000000 +vertex 21.112640 3.794090 8.000434 +endloop +endfacet +facet normal 0.000278 0.000141 -1.000000 +outer loop +vertex 21.112640 3.794090 8.000434 +vertex 21.113426 3.276875 8.000361 +vertex 20.253862 3.238130 8.000117 +endloop +endfacet +facet normal 0.000149 0.000336 -1.000000 +outer loop +vertex 21.112640 3.794090 8.000434 +vertex 20.253862 3.238130 8.000117 +vertex 19.549603 3.206118 8.000000 +endloop +endfacet +facet normal 0.001849 0.001191 -0.999998 +outer loop +vertex 21.112640 3.794090 8.000434 +vertex 22.754299 3.795325 8.003472 +vertex 22.772949 3.352315 8.002979 +endloop +endfacet +facet normal 0.001572 0.000143 -0.999999 +outer loop +vertex 21.112640 3.794090 8.000434 +vertex 22.772949 3.352315 8.002979 +vertex 21.113426 3.276875 8.000361 +endloop +endfacet +facet normal 0.005351 0.001338 -0.999985 +outer loop +vertex 22.772949 3.352315 8.002979 +vertex 22.754299 3.795325 8.003472 +vertex 24.311401 3.796976 8.011806 +endloop +endfacet +facet normal 0.004311 0.004769 -0.999979 +outer loop +vertex 24.311401 3.796976 8.011806 +vertex 24.394966 3.272750 8.009665 +vertex 22.854446 3.356021 8.003421 +endloop +endfacet +facet normal 0.005352 0.001310 -0.999985 +outer loop +vertex 24.311401 3.796976 8.011806 +vertex 22.854446 3.356021 8.003421 +vertex 22.772949 3.352315 8.002979 +endloop +endfacet +facet normal 0.014136 0.006335 -0.999880 +outer loop +vertex 24.394966 3.272750 8.009665 +vertex 24.311401 3.796976 8.011806 +vertex 25.563290 3.798691 8.029513 +endloop +endfacet +facet normal 0.004153 0.014740 -0.999883 +outer loop +vertex 25.563290 3.798691 8.029513 +vertex 25.864864 3.190336 8.021798 +vertex 25.666662 3.218757 8.021394 +endloop +endfacet +facet normal 0.005494 -0.086999 -0.996193 +outer loop +vertex 25.666662 3.218757 8.021394 +vertex 25.143169 3.211766 8.019117 +vertex 24.394966 3.272750 8.009665 +endloop +endfacet +facet normal 0.009890 0.015761 -0.999827 +outer loop +vertex 25.563290 3.798691 8.029513 +vertex 25.666662 3.218757 8.021394 +vertex 24.394966 3.272750 8.009665 +endloop +endfacet +facet normal -0.239839 0.068515 0.968392 +outer loop +vertex 17.072617 13.696096 9.291351 +vertex 17.062820 12.921923 9.343699 +vertex 17.730688 12.940925 9.507764 +endloop +endfacet +facet normal -0.226513 0.080775 0.970653 +outer loop +vertex 17.072617 13.696096 9.291351 +vertex 17.730688 12.940925 9.507764 +vertex 17.714960 13.756108 9.436255 +endloop +endfacet +facet normal -0.244656 0.018514 0.969433 +outer loop +vertex 17.062820 12.921923 9.343699 +vertex 17.055969 11.850637 9.362429 +vertex 17.735132 11.853064 9.533783 +endloop +endfacet +facet normal -0.239097 0.022242 0.970741 +outer loop +vertex 17.062820 12.921923 9.343699 +vertex 17.735132 11.853064 9.533783 +vertex 17.730688 12.940925 9.507764 +endloop +endfacet +facet normal -0.085274 0.085436 0.992688 +outer loop +vertex 17.714960 13.756108 9.436255 +vertex 17.730688 12.940925 9.507764 +vertex 18.612646 12.949788 9.582764 +endloop +endfacet +facet normal -0.078599 0.092845 0.992573 +outer loop +vertex 17.714960 13.756108 9.436255 +vertex 18.612646 12.949788 9.582764 +vertex 18.594265 13.783009 9.503368 +endloop +endfacet +facet normal -0.088100 0.023458 0.995835 +outer loop +vertex 17.730688 12.940925 9.507764 +vertex 17.735132 11.853064 9.533783 +vertex 18.619249 11.854171 9.611973 +endloop +endfacet +facet normal -0.084963 0.026042 0.996044 +outer loop +vertex 17.730688 12.940925 9.507764 +vertex 18.619249 11.854171 9.611973 +vertex 18.612646 12.949788 9.582764 +endloop +endfacet +facet normal -0.387115 0.142882 0.910893 +outer loop +vertex 16.679167 14.062502 9.066667 +vertex 16.602932 13.654407 9.098280 +vertex 17.072617 13.696096 9.291351 +endloop +endfacet +facet normal -0.356462 0.179492 0.916906 +outer loop +vertex 16.679167 14.062502 9.066667 +vertex 17.072617 13.696096 9.291351 +vertex 17.038143 14.205942 9.178142 +endloop +endfacet +facet normal -0.392621 0.060960 0.917678 +outer loop +vertex 16.602932 13.654407 9.098280 +vertex 16.543751 12.910495 9.122376 +vertex 17.062820 12.921923 9.343699 +endloop +endfacet +facet normal -0.384431 0.067120 0.920710 +outer loop +vertex 16.602932 13.654407 9.098280 +vertex 17.062820 12.921923 9.343699 +vertex 17.072617 13.696096 9.291351 +endloop +endfacet +facet normal -0.395039 0.017117 0.918505 +outer loop +vertex 16.543751 12.910495 9.122376 +vertex 16.520052 11.849110 9.131964 +vertex 17.055969 11.850637 9.362429 +endloop +endfacet +facet normal -0.392498 0.018588 0.919565 +outer loop +vertex 16.543751 12.910495 9.122376 +vertex 17.055969 11.850637 9.362429 +vertex 17.062820 12.921923 9.343699 +endloop +endfacet +facet normal -0.395447 0.001900 0.918487 +outer loop +vertex 16.520052 11.849110 9.131964 +vertex 16.516666 10.483034 9.133333 +vertex 17.054993 10.483283 9.365105 +endloop +endfacet +facet normal -0.395063 0.002080 0.918652 +outer loop +vertex 16.520052 11.849110 9.131964 +vertex 17.054993 10.483283 9.365105 +vertex 17.055969 11.850637 9.362429 +endloop +endfacet +facet normal -0.245485 0.002073 0.969398 +outer loop +vertex 17.055969 11.850637 9.362429 +vertex 17.054993 10.483283 9.365105 +vertex 17.735767 10.483404 9.537500 +endloop +endfacet +facet normal -0.244643 0.002518 0.969610 +outer loop +vertex 17.055969 11.850637 9.362429 +vertex 17.735767 10.483404 9.537500 +vertex 17.735132 11.853064 9.533783 +endloop +endfacet +facet normal -0.088573 0.002662 0.996066 +outer loop +vertex 17.735132 11.853064 9.533783 +vertex 17.735767 10.483404 9.537500 +vertex 18.620193 10.483410 9.616146 +endloop +endfacet +facet normal -0.088099 0.002972 0.996107 +outer loop +vertex 17.735132 11.853064 9.533783 +vertex 18.620193 10.483410 9.616146 +vertex 18.619249 11.854171 9.611973 +endloop +endfacet +facet normal 0.043015 0.003071 0.999070 +outer loop +vertex 18.619249 11.854171 9.611973 +vertex 18.620193 10.483410 9.616146 +vertex 19.769468 10.483306 9.566667 +endloop +endfacet +facet normal 0.042916 0.002991 0.999074 +outer loop +vertex 18.619249 11.854171 9.611973 +vertex 19.769468 10.483306 9.566667 +vertex 19.768904 11.854286 9.562587 +endloop +endfacet +facet normal 0.042899 0.026884 0.998718 +outer loop +vertex 18.612646 12.949788 9.582764 +vertex 18.619249 11.854171 9.611973 +vertex 19.768904 11.854286 9.562587 +endloop +endfacet +facet normal 0.042213 0.026159 0.998766 +outer loop +vertex 18.612646 12.949788 9.582764 +vertex 19.768904 11.854286 9.562587 +vertex 19.764956 12.951040 9.534028 +endloop +endfacet +facet normal 0.041959 0.095692 0.994526 +outer loop +vertex 18.594265 13.783009 9.503368 +vertex 18.612646 12.949788 9.582764 +vertex 19.764956 12.951040 9.534028 +endloop +endfacet +facet normal 0.039880 0.092783 0.994887 +outer loop +vertex 18.594265 13.783009 9.503368 +vertex 19.764956 12.951040 9.534028 +vertex 19.754234 13.786851 9.456511 +endloop +endfacet +facet normal 0.038138 0.258394 0.965287 +outer loop +vertex 18.556152 14.368310 9.348197 +vertex 18.594265 13.783009 9.503368 +vertex 19.754234 13.786851 9.456511 +endloop +endfacet +facet normal 0.033635 0.249582 0.967769 +outer loop +vertex 18.556152 14.368310 9.348197 +vertex 19.754234 13.786851 9.456511 +vertex 19.733356 14.375003 9.305555 +endloop +endfacet +facet normal -0.081163 0.238954 0.967633 +outer loop +vertex 17.665802 14.321471 9.292516 +vertex 17.714960 13.756108 9.436255 +vertex 18.594265 13.783009 9.503368 +endloop +endfacet +facet normal -0.073570 0.251088 0.965164 +outer loop +vertex 17.665802 14.321471 9.292516 +vertex 18.594265 13.783009 9.503368 +vertex 18.556152 14.368310 9.348197 +endloop +endfacet +facet normal -0.233162 0.195746 0.952533 +outer loop +vertex 17.038143 14.205942 9.178142 +vertex 17.072617 13.696096 9.291351 +vertex 17.714960 13.756108 9.436255 +endloop +endfacet +facet normal -0.214355 0.223125 0.950930 +outer loop +vertex 17.038143 14.205942 9.178142 +vertex 17.714960 13.756108 9.436255 +vertex 17.665802 14.321471 9.292516 +endloop +endfacet +facet normal -0.070047 0.096169 -0.992897 +outer loop +vertex 16.490507 13.696053 8.177488 +vertex 17.211208 13.756058 8.132457 +vertex 17.162565 12.940511 8.056896 +endloop +endfacet +facet normal -0.074129 0.092546 -0.992945 +outer loop +vertex 16.490507 13.696053 8.177488 +vertex 17.162565 12.940511 8.056896 +vertex 16.396286 12.921580 8.112339 +endloop +endfacet +facet normal -0.017663 0.095869 -0.995237 +outer loop +vertex 17.211208 13.756058 8.132457 +vertex 18.246180 13.782951 8.116679 +vertex 18.223684 12.949332 8.036777 +endloop +endfacet +facet normal -0.019650 0.093399 -0.995435 +outer loop +vertex 17.211208 13.756058 8.132457 +vertex 18.223684 12.949332 8.036777 +vertex 17.162565 12.940511 8.056896 +endloop +endfacet +facet normal -0.072800 0.026879 -0.996984 +outer loop +vertex 16.396286 12.921580 8.112339 +vertex 17.162565 12.940511 8.056896 +vertex 17.142763 11.851664 8.028987 +endloop +endfacet +facet normal -0.074728 0.025524 -0.996877 +outer loop +vertex 16.396286 12.921580 8.112339 +vertex 17.142763 11.851664 8.028987 +vertex 16.356508 11.849483 8.087871 +endloop +endfacet +facet normal -0.019174 0.026985 -0.999452 +outer loop +vertex 17.162565 12.940511 8.056896 +vertex 18.223684 12.949332 8.036777 +vertex 18.215160 11.852634 8.007331 +endloop +endfacet +facet normal -0.020208 0.025985 -0.999458 +outer loop +vertex 17.162565 12.940511 8.056896 +vertex 18.215160 11.852634 8.007331 +vertex 17.142763 11.851664 8.028987 +endloop +endfacet +facet normal -0.199719 0.267532 -0.942623 +outer loop +vertex 16.245834 14.062501 8.333333 +vertex 16.597235 14.205941 8.299591 +vertex 16.490507 13.696053 8.177488 +endloop +endfacet +facet normal -0.235532 0.242939 -0.941013 +outer loop +vertex 16.245834 14.062501 8.333333 +vertex 16.490507 13.696053 8.177488 +vertex 16.073439 13.654376 8.271119 +endloop +endfacet +facet normal -0.073220 0.257975 -0.963373 +outer loop +vertex 16.597235 14.205941 8.299591 +vertex 17.280115 14.321469 8.278626 +vertex 17.211208 13.756058 8.132457 +endloop +endfacet +facet normal -0.080975 0.248118 -0.965340 +outer loop +vertex 16.597235 14.205941 8.299591 +vertex 17.211208 13.756058 8.132457 +vertex 16.490507 13.696053 8.177488 +endloop +endfacet +facet normal -0.018209 0.257842 -0.966015 +outer loop +vertex 17.280115 14.321469 8.278626 +vertex 18.286760 14.368312 8.272155 +vertex 18.246180 13.782951 8.116679 +endloop +endfacet +facet normal -0.021312 0.252667 -0.967319 +outer loop +vertex 17.280115 14.321469 8.278626 +vertex 18.246180 13.782951 8.116679 +vertex 17.211208 13.756058 8.132457 +endloop +endfacet +facet normal -0.009492 0.248886 -0.968486 +outer loop +vertex 18.286760 14.368312 8.272155 +vertex 19.588913 14.375001 8.261111 +vertex 19.568035 13.786793 8.110156 +endloop +endfacet +facet normal -0.005515 0.257059 -0.966380 +outer loop +vertex 18.286760 14.368312 8.272155 +vertex 19.568035 13.786793 8.110156 +vertex 18.246180 13.782951 8.116679 +endloop +endfacet +facet normal -0.005183 0.092370 -0.995711 +outer loop +vertex 18.246180 13.782951 8.116679 +vertex 19.568035 13.786793 8.110156 +vertex 19.557316 12.950583 8.032639 +endloop +endfacet +facet normal -0.003178 0.095497 -0.995425 +outer loop +vertex 18.246180 13.782951 8.116679 +vertex 19.557316 12.950583 8.032639 +vertex 18.223684 12.949332 8.036777 +endloop +endfacet +facet normal -0.003128 0.026016 -0.999657 +outer loop +vertex 18.223684 12.949332 8.036777 +vertex 19.557316 12.950583 8.032639 +vertex 19.553366 11.852744 8.004080 +endloop +endfacet +facet normal -0.002431 0.026859 -0.999636 +outer loop +vertex 18.223684 12.949332 8.036777 +vertex 19.553366 11.852744 8.004080 +vertex 18.215160 11.852634 8.007331 +endloop +endfacet +facet normal -0.002431 0.002972 -0.999993 +outer loop +vertex 18.215160 11.852634 8.007331 +vertex 19.553366 11.852744 8.004080 +vertex 19.552803 10.479649 8.000000 +endloop +endfacet +facet normal -0.002333 0.003066 -0.999993 +outer loop +vertex 18.215160 11.852634 8.007331 +vertex 19.552803 10.479649 8.000000 +vertex 18.213943 10.479768 8.003125 +endloop +endfacet +facet normal -0.020192 0.003081 -0.999791 +outer loop +vertex 17.142763 11.851664 8.028987 +vertex 18.215160 11.852634 8.007331 +vertex 18.213943 10.479768 8.003125 +endloop +endfacet +facet normal -0.020361 0.002948 -0.999788 +outer loop +vertex 17.142763 11.851664 8.028987 +vertex 18.213943 10.479768 8.003125 +vertex 17.139935 10.480088 8.025000 +endloop +endfacet +facet normal -0.074690 0.003053 -0.997202 +outer loop +vertex 16.356508 11.849483 8.087871 +vertex 17.142763 11.851664 8.028987 +vertex 17.139935 10.480088 8.025000 +endloop +endfacet +facet normal -0.075031 0.002858 -0.997177 +outer loop +vertex 16.356508 11.849483 8.087871 +vertex 17.139935 10.480088 8.025000 +vertex 16.350824 10.480552 8.084374 +endloop +endfacet +facet normal -0.232106 0.003448 -0.972684 +outer loop +vertex 15.874880 11.848289 8.202797 +vertex 16.356508 11.849483 8.087871 +vertex 16.350824 10.480552 8.084374 +endloop +endfacet +facet normal -0.232286 0.003385 -0.972641 +outer loop +vertex 15.874880 11.848289 8.202797 +vertex 16.350824 10.480552 8.084374 +vertex 15.866667 10.481091 8.200000 +endloop +endfacet +facet normal -0.231388 0.030777 -0.972375 +outer loop +vertex 15.932369 12.910254 8.222376 +vertex 16.396286 12.921580 8.112339 +vertex 16.356508 11.849483 8.087871 +endloop +endfacet +facet normal -0.232069 0.030489 -0.972221 +outer loop +vertex 15.932369 12.910254 8.222376 +vertex 16.356508 11.849483 8.087871 +vertex 15.874880 11.848289 8.202797 +endloop +endfacet +facet normal -0.228104 0.109138 -0.967501 +outer loop +vertex 16.073439 13.654376 8.271119 +vertex 16.490507 13.696053 8.177488 +vertex 16.396286 12.921580 8.112339 +endloop +endfacet +facet normal -0.231940 0.107299 -0.966794 +outer loop +vertex 16.073439 13.654376 8.271119 +vertex 16.396286 12.921580 8.112339 +vertex 15.932369 12.910254 8.222376 +endloop +endfacet +facet normal -0.075032 0.000000 -0.997181 +outer loop +vertex 16.350824 8.847996 8.084374 +vertex 17.139935 8.847120 8.025000 +vertex 17.139935 7.083379 8.025000 +endloop +endfacet +facet normal -0.075029 0.000000 -0.997181 +outer loop +vertex 16.350824 8.847996 8.084374 +vertex 17.139935 7.083379 8.025000 +vertex 16.350824 7.084708 8.084374 +endloop +endfacet +facet normal -0.020364 0.000000 -0.999793 +outer loop +vertex 17.139935 8.847120 8.025000 +vertex 18.213943 8.846513 8.003125 +vertex 18.213943 7.082455 8.003125 +endloop +endfacet +facet normal -0.020362 0.000000 -0.999793 +outer loop +vertex 17.139935 8.847120 8.025000 +vertex 18.213943 7.082455 8.003125 +vertex 17.139935 7.083379 8.025000 +endloop +endfacet +facet normal -0.075030 0.000396 -0.997181 +outer loop +vertex 16.350824 7.084708 8.084374 +vertex 17.139935 7.083379 8.025000 +vertex 17.137676 5.346809 8.024479 +endloop +endfacet +facet normal -0.073410 0.001135 -0.997301 +outer loop +vertex 16.350824 7.084708 8.084374 +vertex 17.137676 5.346809 8.024479 +vertex 16.344336 5.348505 8.082877 +endloop +endfacet +facet normal -0.020363 0.000041 -0.999793 +outer loop +vertex 17.139935 7.083379 8.025000 +vertex 18.213943 7.082455 8.003125 +vertex 18.213661 5.345628 8.003059 +endloop +endfacet +facet normal -0.019902 0.000326 -0.999802 +outer loop +vertex 17.139935 7.083379 8.025000 +vertex 18.213661 5.345628 8.003059 +vertex 17.137676 5.346809 8.024479 +endloop +endfacet +facet normal -0.232287 0.000000 -0.972647 +outer loop +vertex 15.866667 10.481091 8.200000 +vertex 16.350824 10.480552 8.084374 +vertex 16.350824 8.847996 8.084374 +endloop +endfacet +facet normal -0.232285 0.000000 -0.972648 +outer loop +vertex 15.866667 10.481091 8.200000 +vertex 16.350824 8.847996 8.084374 +vertex 15.866667 8.849014 8.200000 +endloop +endfacet +facet normal -0.075030 0.000000 -0.997181 +outer loop +vertex 16.350824 10.480552 8.084374 +vertex 17.139935 10.480088 8.025000 +vertex 17.139935 8.847120 8.025000 +endloop +endfacet +facet normal -0.075030 0.000000 -0.997181 +outer loop +vertex 16.350824 10.480552 8.084374 +vertex 17.139935 8.847120 8.025000 +vertex 16.350824 8.847996 8.084374 +endloop +endfacet +facet normal -0.020364 0.000000 -0.999793 +outer loop +vertex 17.139935 10.480088 8.025000 +vertex 18.213943 10.479768 8.003125 +vertex 18.213943 8.846513 8.003125 +endloop +endfacet +facet normal -0.020362 0.000000 -0.999793 +outer loop +vertex 17.139935 10.480088 8.025000 +vertex 18.213943 8.846513 8.003125 +vertex 17.139935 8.847120 8.025000 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 10.479768 8.003125 +vertex 19.552803 10.479649 8.000000 +vertex 19.552803 8.846289 8.000000 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 10.479768 8.003125 +vertex 19.552803 8.846289 8.000000 +vertex 18.213943 8.846513 8.003125 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 8.846513 8.003125 +vertex 19.552803 8.846289 8.000000 +vertex 19.552803 7.082114 8.000000 +endloop +endfacet +facet normal -0.002335 0.000000 -0.999997 +outer loop +vertex 18.213943 8.846513 8.003125 +vertex 19.552803 7.082114 8.000000 +vertex 18.213943 7.082455 8.003125 +endloop +endfacet +facet normal -0.002333 0.000000 -0.999997 +outer loop +vertex 18.213943 7.082455 8.003125 +vertex 19.552803 7.082114 8.000000 +vertex 19.552801 5.345193 8.000000 +endloop +endfacet +facet normal -0.002284 0.000038 -0.999997 +outer loop +vertex 18.213943 7.082455 8.003125 +vertex 19.552801 5.345193 8.000000 +vertex 18.213661 5.345628 8.003059 +endloop +endfacet +facet normal -0.002284 0.000000 -0.999997 +outer loop +vertex 18.213661 5.345628 8.003059 +vertex 19.552801 5.345193 8.000000 +vertex 19.552803 3.793593 8.000000 +endloop +endfacet +facet normal -0.001942 0.000296 -0.999998 +outer loop +vertex 18.213661 5.345628 8.003059 +vertex 19.552803 3.793593 8.000000 +vertex 18.211685 3.794066 8.002604 +endloop +endfacet +facet normal -0.019902 0.000319 -0.999802 +outer loop +vertex 17.137676 5.346809 8.024479 +vertex 18.213661 5.345628 8.003059 +vertex 18.211685 3.794066 8.002604 +endloop +endfacet +facet normal -0.016721 0.002520 -0.999857 +outer loop +vertex 17.137676 5.346809 8.024479 +vertex 18.211685 3.794066 8.002604 +vertex 17.121878 3.795351 8.020833 +endloop +endfacet +facet normal -0.073407 0.003091 -0.997297 +outer loop +vertex 16.344336 5.348505 8.082877 +vertex 17.137676 5.346809 8.024479 +vertex 17.121878 3.795351 8.020833 +endloop +endfacet +facet normal -0.060126 0.009772 -0.998143 +outer loop +vertex 16.344336 5.348505 8.082877 +vertex 17.121878 3.795351 8.020833 +vertex 16.292145 3.797194 8.070833 +endloop +endfacet +facet normal -0.229944 0.015290 -0.973084 +outer loop +vertex 15.857639 5.350487 8.197917 +vertex 16.344336 5.348505 8.082877 +vertex 16.292145 3.797194 8.070833 +endloop +endfacet +facet normal -0.198281 0.024701 -0.979834 +outer loop +vertex 15.857639 5.350487 8.197917 +vertex 16.292145 3.797194 8.070833 +vertex 15.767362 3.799347 8.177083 +endloop +endfacet +facet normal -0.232282 0.001707 -0.972647 +outer loop +vertex 15.866667 7.086260 8.200000 +vertex 16.350824 7.084708 8.084374 +vertex 16.344336 5.348505 8.082877 +endloop +endfacet +facet normal -0.230020 0.002364 -0.973183 +outer loop +vertex 15.866667 7.086260 8.200000 +vertex 16.344336 5.348505 8.082877 +vertex 15.857639 5.350487 8.197917 +endloop +endfacet +facet normal -0.232285 0.000000 -0.972648 +outer loop +vertex 15.866667 8.849014 8.200000 +vertex 16.350824 8.847996 8.084374 +vertex 16.350824 7.084708 8.084374 +endloop +endfacet +facet normal -0.232286 0.000000 -0.972647 +outer loop +vertex 15.866667 8.849014 8.200000 +vertex 16.350824 7.084708 8.084374 +vertex 15.866667 7.086260 8.200000 +endloop +endfacet +facet normal -0.245485 0.000000 0.969400 +outer loop +vertex 17.054993 8.853159 9.365105 +vertex 17.054991 7.092562 9.365105 +vertex 17.735767 7.092913 9.537500 +endloop +endfacet +facet normal -0.245487 0.000000 0.969400 +outer loop +vertex 17.054993 8.853159 9.365105 +vertex 17.735767 7.092913 9.537500 +vertex 17.735767 8.853391 9.537500 +endloop +endfacet +facet normal -0.245487 -0.000000 0.969400 +outer loop +vertex 17.054991 7.092562 9.365105 +vertex 17.054993 5.358536 9.365105 +vertex 17.735767 5.358985 9.537500 +endloop +endfacet +facet normal -0.245484 0.000000 0.969401 +outer loop +vertex 17.054991 7.092562 9.365105 +vertex 17.735767 5.358985 9.537500 +vertex 17.735767 7.092913 9.537500 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 8.853391 9.537500 +vertex 17.735767 7.092913 9.537500 +vertex 18.620193 7.092926 9.616146 +endloop +endfacet +facet normal -0.088574 0.000000 0.996070 +outer loop +vertex 17.735767 8.853391 9.537500 +vertex 18.620193 7.092926 9.616146 +vertex 18.620193 8.853397 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 7.092913 9.537500 +vertex 17.735767 5.358985 9.537500 +vertex 18.620193 5.359001 9.616146 +endloop +endfacet +facet normal -0.088572 0.000000 0.996070 +outer loop +vertex 17.735767 7.092913 9.537500 +vertex 18.620193 5.359001 9.616146 +vertex 18.620193 7.092926 9.616146 +endloop +endfacet +facet normal -0.395446 0.000000 0.918489 +outer loop +vertex 16.516666 10.483034 9.133333 +vertex 16.516666 8.852689 9.133333 +vertex 17.054993 8.853159 9.365105 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 10.483034 9.133333 +vertex 17.054993 8.853159 9.365105 +vertex 17.054993 10.483283 9.365105 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 8.852689 9.133333 +vertex 16.516666 7.091847 9.133333 +vertex 17.054991 7.092562 9.365105 +endloop +endfacet +facet normal -0.395445 0.000001 0.918489 +outer loop +vertex 16.516666 8.852689 9.133333 +vertex 17.054991 7.092562 9.365105 +vertex 17.054993 8.853159 9.365105 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 7.091847 9.133333 +vertex 16.516666 5.357624 9.133333 +vertex 17.054993 5.358536 9.365105 +endloop +endfacet +facet normal -0.395448 -0.000001 0.918488 +outer loop +vertex 16.516666 7.091847 9.133333 +vertex 17.054993 5.358536 9.365105 +vertex 17.054991 7.092562 9.365105 +endloop +endfacet +facet normal -0.395449 0.000000 0.918488 +outer loop +vertex 16.516666 5.357624 9.133333 +vertex 16.516666 3.807135 9.133333 +vertex 17.054991 3.808129 9.365105 +endloop +endfacet +facet normal -0.395446 0.000001 0.918489 +outer loop +vertex 16.516666 5.357624 9.133333 +vertex 17.054991 3.808129 9.365105 +vertex 17.054993 5.358536 9.365105 +endloop +endfacet +facet normal -0.245485 0.000000 0.969400 +outer loop +vertex 17.054993 5.358536 9.365105 +vertex 17.054991 3.808129 9.365105 +vertex 17.735767 3.808617 9.537500 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 5.358536 9.365105 +vertex 17.735767 3.808617 9.537500 +vertex 17.735767 5.358985 9.537500 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 5.358985 9.537500 +vertex 17.735767 3.808617 9.537500 +vertex 18.620193 3.808635 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 5.358985 9.537500 +vertex 18.620193 3.808635 9.616146 +vertex 18.620193 5.359001 9.616146 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 5.359001 9.616146 +vertex 18.620193 3.808635 9.616146 +vertex 19.769468 3.808219 9.566667 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 5.359001 9.616146 +vertex 19.769468 3.808219 9.566667 +vertex 19.769468 5.358619 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999075 +outer loop +vertex 18.620193 7.092926 9.616146 +vertex 18.620193 5.359001 9.616146 +vertex 19.769468 5.358619 9.566667 +endloop +endfacet +facet normal 0.043012 0.000000 0.999075 +outer loop +vertex 18.620193 7.092926 9.616146 +vertex 19.769468 5.358619 9.566667 +vertex 19.769468 7.092627 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999074 +outer loop +vertex 18.620193 8.853397 9.616146 +vertex 18.620193 7.092926 9.616146 +vertex 19.769468 7.092627 9.566667 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 8.853397 9.616146 +vertex 19.769468 7.092627 9.566667 +vertex 19.769468 8.853203 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999074 +outer loop +vertex 18.620193 10.483410 9.616146 +vertex 18.620193 8.853397 9.616146 +vertex 19.769468 8.853203 9.566667 +endloop +endfacet +facet normal 0.043013 0.000000 0.999074 +outer loop +vertex 18.620193 10.483410 9.616146 +vertex 19.769468 8.853203 9.566667 +vertex 19.769468 10.483306 9.566667 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 10.483404 9.537500 +vertex 17.735767 8.853391 9.537500 +vertex 18.620193 8.853397 9.616146 +endloop +endfacet +facet normal -0.088574 0.000000 0.996070 +outer loop +vertex 17.735767 10.483404 9.537500 +vertex 18.620193 8.853397 9.616146 +vertex 18.620193 10.483410 9.616146 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 10.483283 9.365105 +vertex 17.054993 8.853159 9.365105 +vertex 17.735767 8.853391 9.537500 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 10.483283 9.365105 +vertex 17.735767 8.853391 9.537500 +vertex 17.735767 10.483404 9.537500 +endloop +endfacet +facet normal -0.260777 0.853498 0.451151 +outer loop +vertex 16.905079 14.500335 8.978137 +vertex 17.568832 14.658748 9.062117 +vertex 17.455404 14.771413 8.783410 +endloop +endfacet +facet normal -0.242171 0.840189 0.485218 +outer loop +vertex 16.905079 14.500335 8.978137 +vertex 17.455404 14.771413 8.783410 +vertex 16.756197 14.599973 8.731298 +endloop +endfacet +facet normal -0.076539 0.920191 0.383914 +outer loop +vertex 17.568832 14.658748 9.062117 +vertex 18.493919 14.718086 9.104320 +vertex 18.419262 14.834710 8.809905 +endloop +endfacet +facet normal -0.071003 0.914397 0.398542 +outer loop +vertex 17.568832 14.658748 9.062117 +vertex 18.419262 14.834710 8.809905 +vertex 17.455404 14.771413 8.783410 +endloop +endfacet +facet normal -0.204917 0.929214 -0.307524 +outer loop +vertex 16.756197 14.599973 8.731298 +vertex 17.455404 14.771413 8.783410 +vertex 17.359531 14.658748 8.506865 +endloop +endfacet +facet normal -0.205023 0.929081 -0.307853 +outer loop +vertex 16.756197 14.599973 8.731298 +vertex 17.359531 14.658748 8.506865 +vertex 16.664705 14.500335 8.491524 +endloop +endfacet +facet normal -0.051432 0.932644 -0.357113 +outer loop +vertex 17.455404 14.771413 8.783410 +vertex 18.419262 14.834710 8.809905 +vertex 18.346798 14.718087 8.515761 +endloop +endfacet +facet normal -0.052708 0.931059 -0.361041 +outer loop +vertex 17.455404 14.771413 8.783410 +vertex 18.346798 14.718087 8.515761 +vertex 17.359531 14.658748 8.506865 +endloop +endfacet +facet normal -0.409180 0.378964 0.830035 +outer loop +vertex 16.679167 14.062502 9.066667 +vertex 17.038143 14.205942 9.178142 +vertex 16.905079 14.500335 8.978137 +endloop +endfacet +facet normal -0.414807 0.381097 0.826257 +outer loop +vertex 16.679167 14.062502 9.066667 +vertex 16.905079 14.500335 8.978137 +vertex 16.475306 14.199250 8.901251 +endloop +endfacet +facet normal -0.243370 0.498430 0.832069 +outer loop +vertex 17.038143 14.205942 9.178142 +vertex 17.665802 14.321471 9.292516 +vertex 17.568832 14.658748 9.062117 +endloop +endfacet +facet normal -0.221536 0.477507 0.850241 +outer loop +vertex 17.038143 14.205942 9.178142 +vertex 17.568832 14.658748 9.062117 +vertex 16.905079 14.500335 8.978137 +endloop +endfacet +facet normal -0.081027 0.560320 0.824303 +outer loop +vertex 17.665802 14.321471 9.292516 +vertex 18.556152 14.368310 9.348197 +vertex 18.493919 14.718086 9.104320 +endloop +endfacet +facet normal -0.073170 0.548127 0.833189 +outer loop +vertex 17.665802 14.321471 9.292516 +vertex 18.493919 14.718086 9.104320 +vertex 17.568832 14.658748 9.062117 +endloop +endfacet +facet normal 0.026814 0.559897 0.828128 +outer loop +vertex 18.556152 14.368310 9.348197 +vertex 19.733356 14.375003 9.305555 +vertex 19.700632 14.726565 9.068924 +endloop +endfacet +facet normal 0.019972 0.574211 0.818464 +outer loop +vertex 18.556152 14.368310 9.348197 +vertex 19.700632 14.726565 9.068924 +vertex 18.493919 14.718086 9.104320 +endloop +endfacet +facet normal 0.004617 0.925364 0.379051 +outer loop +vertex 18.493919 14.718086 9.104320 +vertex 19.700632 14.726565 9.068924 +vertex 19.661137 14.843751 8.783334 +endloop +endfacet +facet normal 0.001105 0.929810 0.368039 +outer loop +vertex 18.493919 14.718086 9.104320 +vertex 19.661137 14.843751 8.783334 +vertex 18.419262 14.834710 8.809905 +endloop +endfacet +facet normal -0.014824 0.925763 -0.377813 +outer loop +vertex 18.419262 14.834710 8.809905 +vertex 19.661137 14.843751 8.783334 +vertex 19.621639 14.726565 8.497743 +endloop +endfacet +facet normal -0.011363 0.930494 -0.366131 +outer loop +vertex 18.419262 14.834710 8.809905 +vertex 19.621639 14.726565 8.497743 +vertex 18.346798 14.718087 8.515761 +endloop +endfacet +facet normal -0.015434 0.559302 -0.828820 +outer loop +vertex 18.346798 14.718087 8.515761 +vertex 19.621639 14.726565 8.497743 +vertex 19.588913 14.375001 8.261111 +endloop +endfacet +facet normal -0.009894 0.572629 -0.819755 +outer loop +vertex 18.346798 14.718087 8.515761 +vertex 19.588913 14.375001 8.261111 +vertex 18.286760 14.368312 8.272155 +endloop +endfacet +facet normal -0.027153 0.574440 -0.818096 +outer loop +vertex 17.359531 14.658748 8.506865 +vertex 18.346798 14.718087 8.515761 +vertex 18.286760 14.368312 8.272155 +endloop +endfacet +facet normal -0.031604 0.565255 -0.824310 +outer loop +vertex 17.359531 14.658748 8.506865 +vertex 18.286760 14.368312 8.272155 +vertex 17.280115 14.321469 8.278626 +endloop +endfacet +facet normal -0.113200 0.574987 -0.810294 +outer loop +vertex 16.664705 14.500335 8.491524 +vertex 17.359531 14.658748 8.506865 +vertex 17.280115 14.321469 8.278626 +endloop +endfacet +facet normal -0.120109 0.561358 -0.818811 +outer loop +vertex 16.664705 14.500335 8.491524 +vertex 17.280115 14.321469 8.278626 +vertex 16.597235 14.205941 8.299591 +endloop +endfacet +facet normal -0.341250 0.566992 -0.749712 +outer loop +vertex 16.212664 14.199250 8.469577 +vertex 16.664705 14.500335 8.491524 +vertex 16.597235 14.205941 8.299591 +endloop +endfacet +facet normal -0.324005 0.627125 -0.708333 +outer loop +vertex 16.212664 14.199250 8.469577 +vertex 16.597235 14.205941 8.299591 +vertex 16.245834 14.062501 8.333333 +endloop +endfacet +facet normal -0.561493 0.817890 -0.125621 +outer loop +vertex 16.284452 14.267941 8.678086 +vertex 16.756197 14.599973 8.731298 +vertex 16.664705 14.500335 8.491524 +endloop +endfacet +facet normal -0.549505 0.831193 -0.084632 +outer loop +vertex 16.284452 14.267941 8.678086 +vertex 16.664705 14.500335 8.491524 +vertex 16.212664 14.199250 8.469577 +endloop +endfacet +facet normal -0.536386 0.619208 0.573473 +outer loop +vertex 16.475306 14.199250 8.901251 +vertex 16.905079 14.500335 8.978137 +vertex 16.756197 14.599973 8.731298 +endloop +endfacet +facet normal -0.499132 0.610628 0.614818 +outer loop +vertex 16.475306 14.199250 8.901251 +vertex 16.756197 14.599973 8.731298 +vertex 16.284452 14.267941 8.678086 +endloop +endfacet +facet normal 0.266707 0.001315 0.963777 +outer loop +vertex 26.157015 2.552997 8.262760 +vertex 26.150230 3.802635 8.262934 +vertex 25.414011 3.803846 8.466667 +endloop +endfacet +facet normal 0.264650 -0.000001 0.964345 +outer loop +vertex 26.157015 2.552997 8.262760 +vertex 25.414011 3.803846 8.466667 +vertex 25.414009 2.554101 8.466667 +endloop +endfacet +facet normal 0.185636 -0.004848 0.982607 +outer loop +vertex 21.194145 -2.557368 9.374599 +vertex 22.751976 -2.556384 9.080295 +vertex 22.736126 -1.562173 9.088195 +endloop +endfacet +facet normal 0.191110 -0.013657 0.981474 +outer loop +vertex 21.194145 -2.557368 9.374599 +vertex 22.736126 -1.562173 9.088195 +vertex 21.148577 -1.562913 9.397309 +endloop +endfacet +facet normal 0.191110 -0.013928 0.981470 +outer loop +vertex 21.148577 -1.562913 9.397309 +vertex 22.736126 -1.562173 9.088195 +vertex 22.697634 -0.738462 9.107379 +endloop +endfacet +facet normal 0.204879 -0.041043 0.977926 +outer loop +vertex 21.148577 -1.562913 9.397309 +vertex 22.697634 -0.738462 9.107379 +vertex 21.028854 -0.738857 9.456977 +endloop +endfacet +facet normal 0.214909 -0.000543 0.976634 +outer loop +vertex 22.751976 -2.556384 9.080295 +vertex 24.226292 -2.555258 8.755870 +vertex 24.224312 -1.561327 8.756858 +endloop +endfacet +facet normal 0.217324 -0.004292 0.976090 +outer loop +vertex 22.751976 -2.556384 9.080295 +vertex 24.224312 -1.561327 8.756858 +vertex 22.736126 -1.562173 9.088195 +endloop +endfacet +facet normal 0.217324 -0.001572 0.976098 +outer loop +vertex 22.736126 -1.562173 9.088195 +vertex 24.224312 -1.561327 8.756858 +vertex 24.219498 -0.738010 8.759255 +endloop +endfacet +facet normal 0.222975 -0.012281 0.974747 +outer loop +vertex 22.736126 -1.562173 9.088195 +vertex 24.219498 -0.738010 8.759255 +vertex 22.697634 -0.738462 9.107379 +endloop +endfacet +facet normal 0.135217 -0.001868 0.990814 +outer loop +vertex 19.769468 -3.808219 9.566667 +vertex 21.200653 -3.807420 9.371354 +vertex 21.194145 -2.557368 9.374599 +endloop +endfacet +facet normal 0.135843 -0.002594 0.990727 +outer loop +vertex 19.769468 -3.808219 9.566667 +vertex 21.194145 -2.557368 9.374599 +vertex 19.760412 -2.558099 9.571181 +endloop +endfacet +facet normal 0.135833 -0.016398 0.990596 +outer loop +vertex 19.760412 -2.558099 9.571181 +vertex 21.194145 -2.557368 9.374599 +vertex 21.148577 -1.562913 9.397309 +endloop +endfacet +facet normal 0.140126 -0.022514 0.989878 +outer loop +vertex 19.760412 -2.558099 9.571181 +vertex 21.148577 -1.562913 9.397309 +vertex 19.697014 -1.563463 9.602778 +endloop +endfacet +facet normal 0.139988 -0.051260 0.988825 +outer loop +vertex 19.697014 -1.563463 9.602778 +vertex 21.148577 -1.562913 9.397309 +vertex 21.028854 -0.738857 9.456977 +endloop +endfacet +facet normal 0.155681 -0.077323 0.984776 +outer loop +vertex 19.697014 -1.563463 9.602778 +vertex 21.028854 -0.738857 9.456977 +vertex 19.506817 -0.739151 9.697570 +endloop +endfacet +facet normal 0.155540 -0.088368 0.983869 +outer loop +vertex 19.506817 -0.739151 9.697570 +vertex 21.028854 -0.738857 9.456977 +vertex 20.833847 0.000000 9.554167 +endloop +endfacet +facet normal 0.219664 -0.209516 0.952812 +outer loop +vertex 19.506817 -0.739151 9.697570 +vertex 20.833847 0.000000 9.554167 +vertex 18.900000 0.000000 10.000000 +endloop +endfacet +facet normal 0.205006 -0.019935 0.978558 +outer loop +vertex 21.028854 -0.738857 9.456977 +vertex 22.697634 -0.738462 9.107379 +vertex 22.645555 0.000000 9.133333 +endloop +endfacet +facet normal 0.225734 -0.068252 0.971795 +outer loop +vertex 21.028854 -0.738857 9.456977 +vertex 22.645555 0.000000 9.133333 +vertex 20.833847 0.000000 9.554167 +endloop +endfacet +facet normal 0.222988 -0.002319 0.974818 +outer loop +vertex 22.697634 -0.738462 9.107379 +vertex 24.219498 -0.738010 8.759255 +vertex 24.212990 0.000000 8.762500 +endloop +endfacet +facet normal 0.230193 -0.017963 0.972979 +outer loop +vertex 22.697634 -0.738462 9.107379 +vertex 24.212990 0.000000 8.762500 +vertex 22.645555 0.000000 9.133333 +endloop +endfacet +facet normal 0.237910 0.000000 0.971287 +outer loop +vertex 24.219498 -0.738010 8.759255 +vertex 25.414009 -0.737545 8.466667 +vertex 25.414011 0.000000 8.466667 +endloop +endfacet +facet normal 0.239169 -0.002159 0.970976 +outer loop +vertex 24.219498 -0.738010 8.759255 +vertex 25.414011 0.000000 8.466667 +vertex 24.212990 0.000000 8.762500 +endloop +endfacet +facet normal 0.236973 -0.000002 0.971516 +outer loop +vertex 24.224312 -1.561327 8.756858 +vertex 25.414009 -1.560457 8.466666 +vertex 25.414009 -0.737545 8.466667 +endloop +endfacet +facet normal 0.237910 -0.001437 0.971286 +outer loop +vertex 24.224312 -1.561327 8.756858 +vertex 25.414009 -0.737545 8.466667 +vertex 24.219498 -0.738010 8.759255 +endloop +endfacet +facet normal 0.236582 0.000001 0.971611 +outer loop +vertex 24.226292 -2.555258 8.755870 +vertex 25.414009 -2.554101 8.466667 +vertex 25.414009 -1.560457 8.466666 +endloop +endfacet +facet normal 0.236973 -0.000494 0.971516 +outer loop +vertex 24.226292 -2.555258 8.755870 +vertex 25.414009 -1.560457 8.466666 +vertex 24.224312 -1.561327 8.756858 +endloop +endfacet +facet normal 0.236526 0.000001 0.971625 +outer loop +vertex 24.226574 -3.805111 8.755730 +vertex 25.414011 -3.803846 8.466667 +vertex 25.414009 -2.554101 8.466667 +endloop +endfacet +facet normal 0.236583 -0.000056 0.971611 +outer loop +vertex 24.226574 -3.805111 8.755730 +vertex 25.414009 -2.554101 8.466667 +vertex 24.226292 -2.555258 8.755870 +endloop +endfacet +facet normal 0.214560 -0.000062 0.976711 +outer loop +vertex 22.754240 -3.806343 9.079166 +vertex 24.226574 -3.805111 8.755730 +vertex 24.226292 -2.555258 8.755870 +endloop +endfacet +facet normal 0.214909 -0.000492 0.976634 +outer loop +vertex 22.754240 -3.806343 9.079166 +vertex 24.226292 -2.555258 8.755870 +vertex 22.751976 -2.556384 9.080295 +endloop +endfacet +facet normal 0.184832 -0.000552 0.982770 +outer loop +vertex 21.200653 -3.807420 9.371354 +vertex 22.754240 -3.806343 9.079166 +vertex 22.751976 -2.556384 9.080295 +endloop +endfacet +facet normal 0.185636 -0.001584 0.982617 +outer loop +vertex 21.200653 -3.807420 9.371354 +vertex 22.751976 -2.556384 9.080295 +vertex 21.194145 -2.557368 9.374599 +endloop +endfacet +facet normal -0.111632 -0.027793 0.993361 +outer loop +vertex 18.613682 -2.558479 9.619390 +vertex 18.568115 -1.563749 9.642101 +vertex 17.717653 -1.563737 9.546528 +endloop +endfacet +facet normal -0.091368 -0.009363 0.995773 +outer loop +vertex 18.613682 -2.558479 9.619390 +vertex 17.717653 -1.563737 9.546528 +vertex 17.733503 -2.558462 9.538629 +endloop +endfacet +facet normal -0.261688 -0.011834 0.965080 +outer loop +vertex 17.733503 -2.558462 9.538629 +vertex 17.717653 -1.563737 9.546528 +vertex 17.052727 -1.563401 9.366233 +endloop +endfacet +facet normal -0.247479 -0.001455 0.968892 +outer loop +vertex 17.733503 -2.558462 9.538629 +vertex 17.052727 -1.563401 9.366233 +vertex 17.054708 -2.558016 9.365246 +endloop +endfacet +facet normal -0.173365 -0.096112 0.980157 +outer loop +vertex 18.568115 -1.563749 9.642101 +vertex 18.448393 -0.739303 9.701769 +vertex 17.679161 -0.739297 9.565712 +endloop +endfacet +facet normal -0.111632 -0.028326 0.993346 +outer loop +vertex 18.568115 -1.563749 9.642101 +vertex 17.679161 -0.739297 9.565712 +vertex 17.717653 -1.563737 9.546528 +endloop +endfacet +facet normal -0.297839 -0.036104 0.953933 +outer loop +vertex 17.717653 -1.563737 9.546528 +vertex 17.679161 -0.739297 9.565712 +vertex 17.047916 -0.739118 9.368630 +endloop +endfacet +facet normal -0.261698 -0.004334 0.965140 +outer loop +vertex 17.717653 -1.563737 9.546528 +vertex 17.047916 -0.739118 9.368630 +vertex 17.052727 -1.563401 9.366233 +endloop +endfacet +facet normal 0.042005 -0.003304 0.999112 +outer loop +vertex 19.769468 -3.808219 9.566667 +vertex 19.760412 -2.558099 9.571181 +vertex 18.613682 -2.558479 9.619390 +endloop +endfacet +facet normal 0.043013 -0.002369 0.999072 +outer loop +vertex 19.769468 -3.808219 9.566667 +vertex 18.613682 -2.558479 9.619390 +vertex 18.620193 -3.808635 9.616146 +endloop +endfacet +facet normal -0.091374 -0.003061 0.995812 +outer loop +vertex 18.620193 -3.808635 9.616146 +vertex 18.613682 -2.558479 9.619390 +vertex 17.733503 -2.558462 9.538629 +endloop +endfacet +facet normal -0.088572 -0.001059 0.996069 +outer loop +vertex 18.620193 -3.808635 9.616146 +vertex 17.733503 -2.558462 9.538629 +vertex 17.735767 -3.808617 9.537500 +endloop +endfacet +facet normal -0.247484 -0.001322 0.968891 +outer loop +vertex 17.735767 -3.808617 9.537500 +vertex 17.733503 -2.558462 9.538629 +vertex 17.054708 -2.558016 9.365246 +endloop +endfacet +facet normal -0.245484 -0.000165 0.969401 +outer loop +vertex 17.735767 -3.808617 9.537500 +vertex 17.054708 -2.558016 9.365246 +vertex 17.054991 -3.808129 9.365105 +endloop +endfacet +facet normal -0.395828 -0.000193 0.918324 +outer loop +vertex 17.054991 -3.808129 9.365105 +vertex 17.054708 -2.558016 9.365246 +vertex 16.516666 -2.557108 9.133333 +endloop +endfacet +facet normal -0.395446 0.000000 0.918489 +outer loop +vertex 17.054991 -3.808129 9.365105 +vertex 16.516666 -2.557108 9.133333 +vertex 16.516666 -3.807135 9.133333 +endloop +endfacet +facet normal -0.398483 -0.001704 0.917174 +outer loop +vertex 17.054708 -2.558016 9.365246 +vertex 17.052727 -1.563401 9.366233 +vertex 16.516666 -1.562718 9.133333 +endloop +endfacet +facet normal -0.395827 0.000000 0.918325 +outer loop +vertex 17.054708 -2.558016 9.365246 +vertex 16.516666 -1.562718 9.133333 +vertex 16.516666 -2.557108 9.133333 +endloop +endfacet +facet normal -0.404967 -0.005022 0.914317 +outer loop +vertex 17.052727 -1.563401 9.366233 +vertex 17.047916 -0.739118 9.368630 +vertex 16.516666 -0.738753 9.133333 +endloop +endfacet +facet normal -0.398481 0.000000 0.917176 +outer loop +vertex 17.052727 -1.563401 9.366233 +vertex 16.516666 -0.738753 9.133333 +vertex 16.516666 -1.562718 9.133333 +endloop +endfacet +facet normal -0.413823 -0.007640 0.910325 +outer loop +vertex 17.047916 -0.739118 9.368630 +vertex 17.041407 0.000000 9.371875 +vertex 16.516666 0.000000 9.133333 +endloop +endfacet +facet normal -0.404970 0.000000 0.914330 +outer loop +vertex 17.047916 -0.739118 9.368630 +vertex 16.516666 0.000000 9.133333 +vertex 16.516666 -0.738753 9.133333 +endloop +endfacet +facet normal -0.350771 -0.057525 0.934693 +outer loop +vertex 17.679161 -0.739297 9.565712 +vertex 17.627083 0.000000 9.591667 +vertex 17.041407 0.000000 9.371875 +endloop +endfacet +facet normal -0.298019 -0.006815 0.954536 +outer loop +vertex 17.679161 -0.739297 9.565712 +vertex 17.041407 0.000000 9.371875 +vertex 17.047916 -0.739118 9.368630 +endloop +endfacet +facet normal -0.307649 -0.203345 0.929518 +outer loop +vertex 18.448393 -0.739303 9.701769 +vertex 18.253386 0.000000 9.798959 +vertex 17.627083 0.000000 9.591667 +endloop +endfacet +facet normal -0.173979 -0.046789 0.983637 +outer loop +vertex 18.448393 -0.739303 9.701769 +vertex 17.627083 0.000000 9.591667 +vertex 17.679161 -0.739297 9.565712 +endloop +endfacet +facet normal -0.250697 -0.535724 0.806319 +outer loop +vertex 19.506817 -0.739151 9.697570 +vertex 18.900000 0.000000 10.000000 +vertex 18.253386 0.000000 9.798959 +endloop +endfacet +facet normal 0.003953 -0.129314 0.991596 +outer loop +vertex 19.506817 -0.739151 9.697570 +vertex 18.253386 0.000000 9.798959 +vertex 18.448393 -0.739303 9.701769 +endloop +endfacet +facet normal 0.003957 -0.113339 0.993548 +outer loop +vertex 19.697014 -1.563463 9.602778 +vertex 19.506817 -0.739151 9.697570 +vertex 18.448393 -0.739303 9.701769 +endloop +endfacet +facet normal 0.034751 -0.067120 0.997140 +outer loop +vertex 19.697014 -1.563463 9.602778 +vertex 18.448393 -0.739303 9.701769 +vertex 18.568115 -1.563749 9.642101 +endloop +endfacet +facet normal 0.034805 -0.029516 0.998958 +outer loop +vertex 19.760412 -2.558099 9.571181 +vertex 19.697014 -1.563463 9.602778 +vertex 18.568115 -1.563749 9.642101 +endloop +endfacet +facet normal 0.042000 -0.020882 0.998899 +outer loop +vertex 19.760412 -2.558099 9.571181 +vertex 18.568115 -1.563749 9.642101 +vertex 18.613682 -2.558479 9.619390 +endloop +endfacet +facet normal 0.274405 -0.047052 -0.960462 +outer loop +vertex 26.129168 -11.849633 8.070150 +vertex 26.372263 -11.851037 8.139670 +vertex 26.240326 -12.930573 8.154860 +endloop +endfacet +facet normal 0.288243 -0.045314 -0.956485 +outer loop +vertex 26.129168 -11.849633 8.070150 +vertex 26.240326 -12.930573 8.154860 +vertex 26.005474 -12.920404 8.083603 +endloop +endfacet +facet normal 0.278988 -0.174332 -0.944338 +outer loop +vertex 26.005474 -12.920404 8.083603 +vertex 26.240326 -12.930573 8.154860 +vertex 25.882959 -13.725335 8.196007 +endloop +endfacet +facet normal 0.318796 -0.178510 -0.930862 +outer loop +vertex 26.005474 -12.920404 8.083603 +vertex 25.882959 -13.725335 8.196007 +vertex 25.675266 -13.692222 8.118527 +endloop +endfacet +facet normal 0.458197 -0.040470 0.887929 +outer loop +vertex 26.372263 -11.851037 8.139670 +vertex 26.129166 -11.849897 8.265168 +vertex 26.005474 -12.920482 8.280198 +endloop +endfacet +facet normal 0.468854 -0.044890 0.882134 +outer loop +vertex 26.372263 -11.851037 8.139670 +vertex 26.005474 -12.920482 8.280198 +vertex 26.240326 -12.930573 8.154860 +endloop +endfacet +facet normal 0.460435 -0.150400 0.874859 +outer loop +vertex 26.240326 -12.930573 8.154860 +vertex 26.005474 -12.920482 8.280198 +vertex 25.675266 -13.692232 8.321312 +endloop +endfacet +facet normal 0.487982 -0.175154 0.855099 +outer loop +vertex 26.240326 -12.930573 8.154860 +vertex 25.675266 -13.692232 8.321312 +vertex 25.882959 -13.725335 8.196007 +endloop +endfacet +facet normal 0.047563 -0.002017 -0.998866 +outer loop +vertex 25.414011 -10.480907 8.033333 +vertex 26.146835 -10.481279 8.068229 +vertex 26.129168 -11.849633 8.070150 +endloop +endfacet +facet normal 0.047924 -0.001828 -0.998849 +outer loop +vertex 25.414011 -10.480907 8.033333 +vertex 26.129168 -11.849633 8.070150 +vertex 25.400063 -11.848212 8.035166 +endloop +endfacet +facet normal 0.047885 -0.018078 -0.998689 +outer loop +vertex 25.400063 -11.848212 8.035166 +vertex 26.129168 -11.849633 8.070150 +vertex 26.005474 -12.920404 8.083603 +endloop +endfacet +facet normal 0.050335 -0.016690 -0.998593 +outer loop +vertex 25.400063 -11.848212 8.035166 +vertex 26.005474 -12.920404 8.083603 +vertex 25.302427 -12.910231 8.047994 +endloop +endfacet +facet normal 0.049517 -0.066280 -0.996572 +outer loop +vertex 25.302427 -12.910231 8.047994 +vertex 26.005474 -12.920404 8.083603 +vertex 25.675266 -13.692222 8.118527 +endloop +endfacet +facet normal 0.057660 -0.062379 -0.996386 +outer loop +vertex 25.302427 -12.910231 8.047994 +vertex 25.675266 -13.692222 8.118527 +vertex 25.061609 -13.654374 8.080646 +endloop +endfacet +facet normal 0.049852 -0.175785 -0.983165 +outer loop +vertex 25.061609 -13.654374 8.080646 +vertex 25.675266 -13.692222 8.118527 +vertex 25.047661 -14.199250 8.177359 +endloop +endfacet +facet normal 0.095358 -0.176334 -0.979701 +outer loop +vertex 25.061609 -13.654374 8.080646 +vertex 25.047661 -14.199250 8.177359 +vertex 24.762611 -14.062502 8.125000 +endloop +endfacet +facet normal 0.249862 -0.445005 -0.859965 +outer loop +vertex 25.675266 -13.692222 8.118527 +vertex 25.882959 -13.725335 8.196007 +vertex 25.190845 -14.267941 8.275694 +endloop +endfacet +facet normal 0.320723 -0.490980 -0.809985 +outer loop +vertex 25.675266 -13.692222 8.118527 +vertex 25.190845 -14.267941 8.275694 +vertex 25.047661 -14.199250 8.177359 +endloop +endfacet +facet normal 0.428374 -0.392727 0.813794 +outer loop +vertex 25.882959 -13.725335 8.196007 +vertex 25.675266 -13.692232 8.321312 +vertex 25.047661 -14.199251 8.407001 +endloop +endfacet +facet normal 0.459444 -0.475901 0.749953 +outer loop +vertex 25.882959 -13.725335 8.196007 +vertex 25.047661 -14.199251 8.407001 +vertex 25.190845 -14.267941 8.275694 +endloop +endfacet +facet normal 0.269544 -0.140295 0.952714 +outer loop +vertex 25.675266 -13.692232 8.321312 +vertex 25.061607 -13.654394 8.500504 +vertex 24.762611 -14.062502 8.525001 +endloop +endfacet +facet normal 0.290536 -0.201539 0.935399 +outer loop +vertex 25.675266 -13.692232 8.321312 +vertex 24.762611 -14.062502 8.525001 +vertex 25.047661 -14.199251 8.407001 +endloop +endfacet +facet normal 0.269238 -0.057685 0.961344 +outer loop +vertex 26.005474 -12.920482 8.280198 +vertex 25.302429 -12.910394 8.477700 +vertex 25.061607 -13.654394 8.500504 +endloop +endfacet +facet normal 0.275863 -0.066949 0.958863 +outer loop +vertex 26.005474 -12.920482 8.280198 +vertex 25.061607 -13.654394 8.500504 +vertex 25.675266 -13.692232 8.321312 +endloop +endfacet +facet normal 0.268016 -0.015887 0.963283 +outer loop +vertex 26.129166 -11.849897 8.265168 +vertex 25.400064 -11.848762 8.468046 +vertex 25.302429 -12.910394 8.477700 +endloop +endfacet +facet normal 0.270178 -0.017701 0.962648 +outer loop +vertex 26.129166 -11.849897 8.265168 +vertex 25.302429 -12.910394 8.477700 +vertex 26.005474 -12.920482 8.280198 +endloop +endfacet +facet normal 0.267745 -0.001759 0.963488 +outer loop +vertex 26.146837 -10.481909 8.263021 +vertex 25.414009 -10.482211 8.466667 +vertex 25.400064 -11.848762 8.468046 +endloop +endfacet +facet normal 0.268070 -0.001951 0.963397 +outer loop +vertex 26.146837 -10.481909 8.263021 +vertex 25.400064 -11.848762 8.468046 +vertex 26.129166 -11.849897 8.265168 +endloop +endfacet +facet normal 0.457041 -0.004507 0.889434 +outer loop +vertex 26.391113 -10.481605 8.137500 +vertex 26.146837 -10.481909 8.263021 +vertex 26.129166 -11.849897 8.265168 +endloop +endfacet +facet normal 0.458701 -0.004906 0.888577 +outer loop +vertex 26.391113 -10.481605 8.137500 +vertex 26.129166 -11.849897 8.265168 +vertex 26.372263 -11.851037 8.139670 +endloop +endfacet +facet normal 0.272809 -0.005281 -0.962054 +outer loop +vertex 26.146835 -10.481279 8.068229 +vertex 26.391113 -10.481605 8.137500 +vertex 26.372263 -11.851037 8.139670 +endloop +endfacet +facet normal 0.274929 -0.004899 -0.961452 +outer loop +vertex 26.146835 -10.481279 8.068229 +vertex 26.372263 -11.851037 8.139670 +vertex 26.129168 -11.849633 8.070150 +endloop +endfacet +facet normal 0.074718 -0.905827 0.417007 +outer loop +vertex 21.163698 -14.718087 8.937361 +vertex 21.149673 -14.834710 8.686545 +vertex 22.698479 -14.771414 8.546528 +endloop +endfacet +facet normal 0.094174 -0.867630 0.488210 +outer loop +vertex 21.163698 -14.718087 8.937361 +vertex 22.698479 -14.771414 8.546528 +vertex 22.682304 -14.658746 8.749882 +endloop +endfacet +facet normal 0.156554 -0.858600 0.488157 +outer loop +vertex 22.682304 -14.658746 8.749882 +vertex 22.698479 -14.771414 8.546528 +vertex 24.096714 -14.599972 8.399652 +endloop +endfacet +facet normal 0.175628 -0.798933 0.575206 +outer loop +vertex 22.682304 -14.658746 8.749882 +vertex 24.096714 -14.599972 8.399652 +vertex 24.028313 -14.500335 8.558929 +endloop +endfacet +facet normal 0.010635 -0.906013 -0.423115 +outer loop +vertex 21.149673 -14.834710 8.686545 +vertex 21.130371 -14.718086 8.436337 +vertex 22.672430 -14.658748 8.348035 +endloop +endfacet +facet normal -0.008983 -0.870139 -0.492724 +outer loop +vertex 21.149673 -14.834710 8.686545 +vertex 22.672430 -14.658748 8.348035 +vertex 22.698479 -14.771414 8.546528 +endloop +endfacet +facet normal 0.067209 -0.863893 -0.499172 +outer loop +vertex 22.698479 -14.771414 8.546528 +vertex 22.672430 -14.658748 8.348035 +vertex 24.027081 -14.500335 8.256269 +endloop +endfacet +facet normal 0.038324 -0.811775 -0.582711 +outer loop +vertex 22.698479 -14.771414 8.546528 +vertex 24.027081 -14.500335 8.256269 +vertex 24.096714 -14.599972 8.399652 +endloop +endfacet +facet normal 0.077329 -0.561655 0.823750 +outer loop +vertex 19.733356 -14.375003 9.305555 +vertex 19.700632 -14.726565 9.068924 +vertex 21.163698 -14.718087 8.937361 +endloop +endfacet +facet normal 0.097537 -0.510328 0.854431 +outer loop +vertex 19.733356 -14.375003 9.305555 +vertex 21.163698 -14.718087 8.937361 +vertex 21.174864 -14.368311 9.145000 +endloop +endfacet +facet normal 0.125014 -0.509412 0.851394 +outer loop +vertex 21.174864 -14.368311 9.145000 +vertex 21.163698 -14.718087 8.937361 +vertex 22.682304 -14.658746 8.749882 +endloop +endfacet +facet normal 0.149635 -0.432774 0.888997 +outer loop +vertex 21.174864 -14.368311 9.145000 +vertex 22.682304 -14.658746 8.749882 +vertex 22.665283 -14.321471 8.916937 +endloop +endfacet +facet normal 0.176210 -0.429746 0.885590 +outer loop +vertex 22.665283 -14.321471 8.916937 +vertex 22.682304 -14.658746 8.749882 +vertex 24.028313 -14.500335 8.558929 +endloop +endfacet +facet normal 0.195237 -0.347732 0.917041 +outer loop +vertex 22.665283 -14.321471 8.916937 +vertex 24.028313 -14.500335 8.558929 +vertex 23.949944 -14.205942 8.687243 +endloop +endfacet +facet normal 0.234975 -0.335129 0.912401 +outer loop +vertex 23.949944 -14.205942 8.687243 +vertex 24.028313 -14.500335 8.558929 +vertex 25.047661 -14.199251 8.407001 +endloop +endfacet +facet normal 0.237569 -0.301556 0.923377 +outer loop +vertex 23.949944 -14.205942 8.687243 +vertex 25.047661 -14.199251 8.407001 +vertex 24.762611 -14.062502 8.525001 +endloop +endfacet +facet normal 0.294422 -0.748252 0.594504 +outer loop +vertex 24.028313 -14.500335 8.558929 +vertex 24.096714 -14.599972 8.399652 +vertex 25.190845 -14.267941 8.275694 +endloop +endfacet +facet normal 0.299377 -0.671564 0.677772 +outer loop +vertex 24.028313 -14.500335 8.558929 +vertex 25.190845 -14.267941 8.275694 +vertex 25.047661 -14.199251 8.407001 +endloop +endfacet +facet normal 0.178012 -0.765464 -0.618366 +outer loop +vertex 24.096714 -14.599972 8.399652 +vertex 24.027081 -14.500335 8.256269 +vertex 25.047661 -14.199250 8.177359 +endloop +endfacet +facet normal 0.136319 -0.707929 -0.693003 +outer loop +vertex 24.096714 -14.599972 8.399652 +vertex 25.047661 -14.199250 8.177359 +vertex 25.190845 -14.267941 8.275694 +endloop +endfacet +facet normal 0.035814 -0.341726 -0.939117 +outer loop +vertex 24.027081 -14.500335 8.256269 +vertex 23.947687 -14.205942 8.146118 +vertex 24.762611 -14.062502 8.125000 +endloop +endfacet +facet normal 0.020907 -0.319184 -0.947462 +outer loop +vertex 24.027081 -14.500335 8.256269 +vertex 24.762611 -14.062502 8.125000 +vertex 25.047661 -14.199250 8.177359 +endloop +endfacet +facet normal 0.010905 -0.432699 -0.901473 +outer loop +vertex 22.672430 -14.658748 8.348035 +vertex 22.647228 -14.321472 8.185841 +vertex 23.947687 -14.205942 8.146118 +endloop +endfacet +facet normal -0.021727 -0.355488 -0.934428 +outer loop +vertex 22.672430 -14.658748 8.348035 +vertex 23.947687 -14.205942 8.146118 +vertex 24.027081 -14.500335 8.256269 +endloop +endfacet +facet normal -0.008821 -0.509649 -0.860337 +outer loop +vertex 21.130371 -14.718086 8.436337 +vertex 21.113926 -14.368312 8.229306 +vertex 22.647228 -14.321472 8.185841 +endloop +endfacet +facet normal -0.034768 -0.435232 -0.899647 +outer loop +vertex 21.130371 -14.718086 8.436337 +vertex 22.647228 -14.321472 8.185841 +vertex 22.672430 -14.658748 8.348035 +endloop +endfacet +facet normal -0.014833 -0.559269 -0.828854 +outer loop +vertex 19.621639 -14.726565 8.497743 +vertex 19.588913 -14.375001 8.261111 +vertex 21.113926 -14.368312 8.229306 +endloop +endfacet +facet normal -0.032112 -0.510214 -0.859448 +outer loop +vertex 19.621639 -14.726565 8.497743 +vertex 21.113926 -14.368312 8.229306 +vertex 21.130371 -14.718086 8.436337 +endloop +endfacet +facet normal -0.010198 -0.925593 -0.378382 +outer loop +vertex 19.661137 -14.843751 8.783334 +vertex 19.621639 -14.726565 8.497743 +vertex 21.130371 -14.718086 8.436337 +endloop +endfacet +facet normal -0.021865 -0.906804 -0.420984 +outer loop +vertex 19.661137 -14.843751 8.783334 +vertex 21.130371 -14.718086 8.436337 +vertex 21.149673 -14.834710 8.686545 +endloop +endfacet +facet normal 0.030065 -0.926179 0.375884 +outer loop +vertex 19.700632 -14.726565 9.068924 +vertex 19.661137 -14.843751 8.783334 +vertex 21.149673 -14.834710 8.686545 +endloop +endfacet +facet normal 0.042957 -0.906849 0.419261 +outer loop +vertex 19.700632 -14.726565 9.068924 +vertex 21.149673 -14.834710 8.686545 +vertex 21.163698 -14.718087 8.937361 +endloop +endfacet +facet normal -0.618699 -0.001989 0.785625 +outer loop +vertex 15.731250 -2.553990 8.604167 +vertex 16.073177 -2.555731 8.873438 +vertex 16.068663 -1.561682 8.872396 +endloop +endfacet +facet normal -0.595026 -0.014885 0.803569 +outer loop +vertex 15.731250 -2.553990 8.604167 +vertex 16.068663 -1.561682 8.872396 +vertex 15.695139 -1.560369 8.595834 +endloop +endfacet +facet normal -0.595062 -0.000980 0.803679 +outer loop +vertex 15.695139 -1.560369 8.595834 +vertex 16.068663 -1.561682 8.872396 +vertex 16.066689 -0.738199 8.871941 +endloop +endfacet +facet normal -0.585481 -0.007650 0.810650 +outer loop +vertex 15.695139 -1.560369 8.595834 +vertex 16.066689 -0.738199 8.871941 +vertex 15.679340 -0.737497 8.592188 +endloop +endfacet +facet normal -0.785302 -0.300087 -0.541525 +outer loop +vertex 15.436029 -2.794299 8.100622 +vertex 15.767362 -3.799347 8.177083 +vertex 15.635330 -3.801585 8.369792 +endloop +endfacet +facet normal -0.969785 -0.108257 -0.218626 +outer loop +vertex 15.635330 -3.801585 8.369792 +vertex 15.527050 -2.781158 8.344804 +vertex 15.539762 -2.781838 8.288755 +endloop +endfacet +facet normal -0.873666 -0.043369 0.484590 +outer loop +vertex 15.635330 -3.801585 8.369792 +vertex 15.539762 -2.781838 8.288755 +vertex 15.436029 -2.794299 8.100622 +endloop +endfacet +facet normal -0.585496 -0.000154 0.810675 +outer loop +vertex 15.679340 -0.737497 8.592188 +vertex 16.066689 -0.738199 8.871941 +vertex 16.066406 0.000000 8.871875 +endloop +endfacet +facet normal -0.584159 -0.001216 0.811638 +outer loop +vertex 15.679340 -0.737497 8.592188 +vertex 16.066406 0.000000 8.871875 +vertex 15.677083 0.000000 8.591667 +endloop +endfacet +facet normal -0.502302 0.000000 0.864693 +outer loop +vertex 16.066689 -0.738199 8.871941 +vertex 16.516666 -0.738753 9.133333 +vertex 16.516666 0.000000 9.133333 +endloop +endfacet +facet normal -0.502158 -0.000114 0.864776 +outer loop +vertex 16.066689 -0.738199 8.871941 +vertex 16.516666 0.000000 9.133333 +vertex 16.066406 0.000000 8.871875 +endloop +endfacet +facet normal -0.503297 0.000000 0.864113 +outer loop +vertex 16.068663 -1.561682 8.872396 +vertex 16.516666 -1.562718 9.133333 +vertex 16.516666 -0.738753 9.133333 +endloop +endfacet +facet normal -0.502301 -0.000725 0.864692 +outer loop +vertex 16.068663 -1.561682 8.872396 +vertex 16.516666 -0.738753 9.133333 +vertex 16.066689 -0.738199 8.871941 +endloop +endfacet +facet normal -0.505600 0.000000 0.862768 +outer loop +vertex 16.073177 -2.555731 8.873438 +vertex 16.516666 -2.557108 9.133333 +vertex 16.516666 -1.562718 9.133333 +endloop +endfacet +facet normal -0.503299 -0.001380 0.864111 +outer loop +vertex 16.073177 -2.555731 8.873438 +vertex 16.516666 -1.562718 9.133333 +vertex 16.068663 -1.561682 8.872396 +endloop +endfacet +facet normal -0.507944 0.000000 0.861390 +outer loop +vertex 16.077690 -3.805630 8.874479 +vertex 16.516666 -3.807135 9.133333 +vertex 16.516666 -2.557108 9.133333 +endloop +endfacet +facet normal -0.505605 -0.001107 0.862764 +outer loop +vertex 16.077690 -3.805630 8.874479 +vertex 16.516666 -2.557108 9.133333 +vertex 16.073177 -2.555731 8.873438 +endloop +endfacet +facet normal -0.645074 -0.001693 0.764118 +outer loop +vertex 15.767361 -3.803730 8.612500 +vertex 16.077690 -3.805630 8.874479 +vertex 16.073177 -2.555731 8.873438 +endloop +endfacet +facet normal -0.618684 -0.012638 0.785538 +outer loop +vertex 15.767361 -3.803730 8.612500 +vertex 16.073177 -2.555731 8.873438 +vertex 15.731250 -2.553990 8.604167 +endloop +endfacet +facet normal -0.875831 -0.081287 0.475723 +outer loop +vertex 15.527050 -2.781158 8.344804 +vertex 15.635330 -3.801585 8.369792 +vertex 15.767361 -3.803730 8.612500 +endloop +endfacet +facet normal -0.757231 -0.017527 0.652912 +outer loop +vertex 15.767361 -3.803730 8.612500 +vertex 15.731250 -2.553990 8.604167 +vertex 15.518668 -2.552154 8.357668 +endloop +endfacet +facet normal -0.820034 0.000000 0.572315 +outer loop +vertex 15.518668 -2.552154 8.357668 +vertex 15.533590 -2.727700 8.357668 +vertex 15.538308 -2.783193 8.357668 +endloop +endfacet +facet normal -0.752865 -0.004612 0.658159 +outer loop +vertex 15.538308 -2.783193 8.357668 +vertex 15.527050 -2.781158 8.344804 +vertex 15.767361 -3.803730 8.612500 +endloop +endfacet +facet normal -0.870708 -0.074008 0.486199 +outer loop +vertex 15.767361 -3.803730 8.612500 +vertex 15.518668 -2.552154 8.357668 +vertex 15.538308 -2.783193 8.357668 +endloop +endfacet +facet normal -0.931105 0.000000 0.364751 +outer loop +vertex 15.694011 -8.850069 8.383333 +vertex 15.785417 -8.851082 8.616667 +vertex 15.785417 -7.089402 8.616667 +endloop +endfacet +facet normal -0.931106 0.000000 0.364749 +outer loop +vertex 15.694011 -8.850069 8.383333 +vertex 15.785417 -7.089402 8.616667 +vertex 15.694011 -7.087864 8.383333 +endloop +endfacet +facet normal -0.931107 -0.001101 0.364744 +outer loop +vertex 15.694011 -7.087864 8.383333 +vertex 15.785417 -7.089402 8.616667 +vertex 15.783160 -5.354500 8.616146 +endloop +endfacet +facet normal -0.925850 -0.003136 0.377877 +outer loop +vertex 15.694011 -7.087864 8.383333 +vertex 15.783160 -5.354500 8.616146 +vertex 15.687522 -5.352535 8.381836 +endloop +endfacet +facet normal -0.659398 0.000000 0.751794 +outer loop +vertex 15.785417 -8.851082 8.616667 +vertex 16.079948 -8.851977 8.875000 +vertex 16.079948 -7.090765 8.875000 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 -8.851082 8.616667 +vertex 16.079948 -7.090765 8.875000 +vertex 15.785417 -7.089402 8.616667 +endloop +endfacet +facet normal -0.659397 -0.000080 0.751795 +outer loop +vertex 15.785417 -7.089402 8.616667 +vertex 16.079948 -7.090765 8.875000 +vertex 16.079666 -5.356243 8.874935 +endloop +endfacet +facet normal -0.657566 -0.000627 0.753396 +outer loop +vertex 15.785417 -7.089402 8.616667 +vertex 16.079666 -5.356243 8.874935 +vertex 15.783160 -5.354500 8.616146 +endloop +endfacet +facet normal -0.727990 0.000000 -0.685587 +outer loop +vertex 15.866667 -10.481091 8.200000 +vertex 15.694011 -10.481650 8.383333 +vertex 15.694011 -8.850069 8.383333 +endloop +endfacet +facet normal -0.727986 0.000000 -0.685592 +outer loop +vertex 15.866667 -10.481091 8.200000 +vertex 15.694011 -8.850069 8.383333 +vertex 15.866667 -8.849014 8.200000 +endloop +endfacet +facet normal -0.727987 0.000000 -0.685591 +outer loop +vertex 15.866667 -8.849014 8.200000 +vertex 15.694011 -8.850069 8.383333 +vertex 15.694011 -7.087864 8.383333 +endloop +endfacet +facet normal -0.727987 0.000000 -0.685591 +outer loop +vertex 15.866667 -8.849014 8.200000 +vertex 15.694011 -7.087864 8.383333 +vertex 15.866667 -7.086260 8.200000 +endloop +endfacet +facet normal -0.727966 -0.003314 -0.685605 +outer loop +vertex 15.866667 -7.086260 8.200000 +vertex 15.694011 -7.087864 8.383333 +vertex 15.687522 -5.352535 8.381836 +endloop +endfacet +facet normal -0.734084 -0.004633 -0.679043 +outer loop +vertex 15.866667 -7.086260 8.200000 +vertex 15.687522 -5.352535 8.381836 +vertex 15.857639 -5.350487 8.197917 +endloop +endfacet +facet normal -0.733620 -0.029959 -0.678900 +outer loop +vertex 15.857639 -5.350487 8.197917 +vertex 15.687522 -5.352535 8.381836 +vertex 15.635330 -3.801585 8.369792 +endloop +endfacet +facet normal -0.823380 -0.055509 -0.564769 +outer loop +vertex 15.857639 -5.350487 8.197917 +vertex 15.635330 -3.801585 8.369792 +vertex 15.767362 -3.799347 8.177083 +endloop +endfacet +facet normal -0.925837 -0.008545 0.377827 +outer loop +vertex 15.687522 -5.352535 8.381836 +vertex 15.783160 -5.354500 8.616146 +vertex 15.767361 -3.803730 8.612500 +endloop +endfacet +facet normal -0.878237 -0.025846 0.477526 +outer loop +vertex 15.687522 -5.352535 8.381836 +vertex 15.767361 -3.803730 8.612500 +vertex 15.635330 -3.801585 8.369792 +endloop +endfacet +facet normal -0.657567 -0.000617 0.753395 +outer loop +vertex 15.783160 -5.354500 8.616146 +vertex 16.079666 -5.356243 8.874935 +vertex 16.077690 -3.805630 8.874479 +endloop +endfacet +facet normal -0.645081 -0.004776 0.764099 +outer loop +vertex 15.783160 -5.354500 8.616146 +vertex 16.077690 -3.805630 8.874479 +vertex 15.767361 -3.803730 8.612500 +endloop +endfacet +facet normal -0.508979 0.000000 0.860779 +outer loop +vertex 16.079666 -5.356243 8.874935 +vertex 16.516666 -5.357624 9.133333 +vertex 16.516666 -3.807135 9.133333 +endloop +endfacet +facet normal -0.507942 -0.000393 0.861391 +outer loop +vertex 16.079666 -5.356243 8.874935 +vertex 16.516666 -3.807135 9.133333 +vertex 16.077690 -3.805630 8.874479 +endloop +endfacet +facet normal -0.509126 0.000000 0.860692 +outer loop +vertex 16.079948 -7.090765 8.875000 +vertex 16.516666 -7.091847 9.133333 +vertex 16.516666 -5.357624 9.133333 +endloop +endfacet +facet normal -0.508979 -0.000051 0.860779 +outer loop +vertex 16.079948 -7.090765 8.875000 +vertex 16.516666 -5.357624 9.133333 +vertex 16.079666 -5.356243 8.874935 +endloop +endfacet +facet normal -0.509127 0.000000 0.860691 +outer loop +vertex 16.079948 -8.851977 8.875000 +vertex 16.516666 -8.852689 9.133333 +vertex 16.516666 -7.091847 9.133333 +endloop +endfacet +facet normal -0.509128 0.000000 0.860691 +outer loop +vertex 16.079948 -8.851977 8.875000 +vertex 16.516666 -7.091847 9.133333 +vertex 16.079948 -7.090765 8.875000 +endloop +endfacet +facet normal -0.509128 0.000000 0.860691 +outer loop +vertex 16.079948 -10.482659 8.875000 +vertex 16.516666 -10.483034 9.133333 +vertex 16.516666 -8.852689 9.133333 +endloop +endfacet +facet normal -0.509127 0.000000 0.860691 +outer loop +vertex 16.079948 -10.482659 8.875000 +vertex 16.516666 -8.852689 9.133333 +vertex 16.079948 -8.851977 8.875000 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 -10.482183 8.616667 +vertex 16.079948 -10.482659 8.875000 +vertex 16.079948 -8.851977 8.875000 +endloop +endfacet +facet normal -0.659397 0.000000 0.751795 +outer loop +vertex 15.785417 -10.482183 8.616667 +vertex 16.079948 -8.851977 8.875000 +vertex 15.785417 -8.851082 8.616667 +endloop +endfacet +facet normal -0.931105 0.000000 0.364750 +outer loop +vertex 15.694011 -10.481650 8.383333 +vertex 15.785417 -10.482183 8.616667 +vertex 15.785417 -8.851082 8.616667 +endloop +endfacet +facet normal -0.931105 0.000000 0.364750 +outer loop +vertex 15.694011 -10.481650 8.383333 +vertex 15.785417 -8.851082 8.616667 +vertex 15.694011 -8.850069 8.383333 +endloop +endfacet +facet normal 0.184832 0.000001 0.982770 +outer loop +vertex 21.200653 -8.852824 9.371354 +vertex 22.754240 -8.852315 9.079167 +vertex 22.754240 -7.091278 9.079166 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200653 -8.852824 9.371354 +vertex 22.754240 -7.091278 9.079166 +vertex 21.200655 -7.092052 9.371354 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200655 -7.092052 9.371354 +vertex 22.754240 -7.091278 9.079166 +vertex 22.754240 -5.356898 9.079166 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200655 -7.092052 9.371354 +vertex 22.754240 -5.356898 9.079166 +vertex 21.200653 -5.357885 9.371354 +endloop +endfacet +facet normal 0.214560 0.000001 0.976711 +outer loop +vertex 22.754240 -8.852315 9.079167 +vertex 24.226576 -8.851733 8.755730 +vertex 24.226576 -7.090393 8.755729 +endloop +endfacet +facet normal 0.214559 0.000000 0.976711 +outer loop +vertex 22.754240 -8.852315 9.079167 +vertex 24.226576 -7.090393 8.755729 +vertex 22.754240 -7.091278 9.079166 +endloop +endfacet +facet normal 0.214560 -0.000001 0.976711 +outer loop +vertex 22.754240 -7.091278 9.079166 +vertex 24.226576 -7.090393 8.755729 +vertex 24.226576 -5.355767 8.755730 +endloop +endfacet +facet normal 0.214560 0.000000 0.976711 +outer loop +vertex 22.754240 -7.091278 9.079166 +vertex 24.226576 -5.355767 8.755730 +vertex 22.754240 -5.356898 9.079166 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 -10.483306 9.566667 +vertex 21.200653 -10.483106 9.371354 +vertex 21.200653 -8.852824 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 -10.483306 9.566667 +vertex 21.200653 -8.852824 9.371354 +vertex 19.769468 -8.853203 9.566667 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 -8.853203 9.566667 +vertex 21.200653 -8.852824 9.371354 +vertex 21.200655 -7.092052 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 -8.853203 9.566667 +vertex 21.200655 -7.092052 9.371354 +vertex 19.769468 -7.092627 9.566667 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 -7.092627 9.566667 +vertex 21.200655 -7.092052 9.371354 +vertex 21.200653 -5.357885 9.371354 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 -7.092627 9.566667 +vertex 21.200653 -5.357885 9.371354 +vertex 19.769468 -5.358619 9.566667 +endloop +endfacet +facet normal 0.135216 0.000000 0.990816 +outer loop +vertex 19.769468 -5.358619 9.566667 +vertex 21.200653 -5.357885 9.371354 +vertex 21.200653 -3.807420 9.371354 +endloop +endfacet +facet normal 0.135215 0.000000 0.990816 +outer loop +vertex 19.769468 -5.358619 9.566667 +vertex 21.200653 -3.807420 9.371354 +vertex 19.769468 -3.808219 9.566667 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200653 -5.357885 9.371354 +vertex 22.754240 -5.356898 9.079166 +vertex 22.754240 -3.806343 9.079166 +endloop +endfacet +facet normal 0.184833 0.000000 0.982770 +outer loop +vertex 21.200653 -5.357885 9.371354 +vertex 22.754240 -3.806343 9.079166 +vertex 21.200653 -3.807420 9.371354 +endloop +endfacet +facet normal 0.214559 0.000001 0.976711 +outer loop +vertex 22.754240 -5.356898 9.079166 +vertex 24.226576 -5.355767 8.755730 +vertex 24.226574 -3.805111 8.755730 +endloop +endfacet +facet normal 0.214560 0.000000 0.976711 +outer loop +vertex 22.754240 -5.356898 9.079166 +vertex 24.226574 -3.805111 8.755730 +vertex 22.754240 -3.806343 9.079166 +endloop +endfacet +facet normal 0.236527 -0.000000 0.971625 +outer loop +vertex 24.226576 -5.355767 8.755730 +vertex 25.414009 -5.354605 8.466666 +vertex 25.414011 -3.803846 8.466667 +endloop +endfacet +facet normal 0.236528 0.000000 0.971625 +outer loop +vertex 24.226576 -5.355767 8.755730 +vertex 25.414011 -3.803846 8.466667 +vertex 24.226574 -3.805111 8.755730 +endloop +endfacet +facet normal 0.236525 0.000001 0.971625 +outer loop +vertex 24.226576 -7.090393 8.755729 +vertex 25.414009 -7.089484 8.466667 +vertex 25.414009 -5.354605 8.466666 +endloop +endfacet +facet normal 0.236529 -0.000001 0.971625 +outer loop +vertex 24.226576 -7.090393 8.755729 +vertex 25.414009 -5.354605 8.466666 +vertex 24.226576 -5.355767 8.755730 +endloop +endfacet +facet normal 0.236527 -0.000000 0.971625 +outer loop +vertex 24.226576 -8.851733 8.755730 +vertex 25.414009 -8.851135 8.466666 +vertex 25.414009 -7.089484 8.466667 +endloop +endfacet +facet normal 0.236526 0.000000 0.971625 +outer loop +vertex 24.226576 -8.851733 8.755730 +vertex 25.414009 -7.089484 8.466667 +vertex 24.226576 -7.090393 8.755729 +endloop +endfacet +facet normal 0.236527 0.000000 0.971625 +outer loop +vertex 24.226574 -10.482530 8.755730 +vertex 25.414009 -10.482211 8.466667 +vertex 25.414009 -8.851135 8.466666 +endloop +endfacet +facet normal 0.236527 -0.000000 0.971625 +outer loop +vertex 24.226574 -10.482530 8.755730 +vertex 25.414009 -8.851135 8.466666 +vertex 24.226576 -8.851733 8.755730 +endloop +endfacet +facet normal 0.214560 -0.000001 0.976711 +outer loop +vertex 22.754240 -10.482836 9.079166 +vertex 24.226574 -10.482530 8.755730 +vertex 24.226576 -8.851733 8.755730 +endloop +endfacet +facet normal 0.214560 -0.000000 0.976711 +outer loop +vertex 22.754240 -10.482836 9.079166 +vertex 24.226576 -8.851733 8.755730 +vertex 22.754240 -8.852315 9.079167 +endloop +endfacet +facet normal 0.184833 -0.000001 0.982770 +outer loop +vertex 21.200653 -10.483106 9.371354 +vertex 22.754240 -10.482836 9.079166 +vertex 22.754240 -8.852315 9.079167 +endloop +endfacet +facet normal 0.184832 0.000000 0.982770 +outer loop +vertex 21.200653 -10.483106 9.371354 +vertex 22.754240 -8.852315 9.079167 +vertex 21.200653 -8.852824 9.371354 +endloop +endfacet +facet normal 0.002254 0.000000 -0.999997 +outer loop +vertex 21.109249 -8.846521 8.000521 +vertex 21.109247 -7.082470 8.000521 +vertex 22.727156 -7.083351 8.004167 +endloop +endfacet +facet normal 0.002253 0.000000 -0.999997 +outer loop +vertex 21.109249 -8.846521 8.000521 +vertex 22.727156 -7.083351 8.004167 +vertex 22.727156 -8.847102 8.004167 +endloop +endfacet +facet normal 0.006614 0.000000 -0.999978 +outer loop +vertex 22.727156 -8.847102 8.004167 +vertex 22.727156 -7.083351 8.004167 +vertex 24.223190 -7.084518 8.014062 +endloop +endfacet +facet normal 0.006615 0.000000 -0.999978 +outer loop +vertex 22.727156 -8.847102 8.004167 +vertex 24.223190 -7.084518 8.014062 +vertex 24.223190 -8.847869 8.014062 +endloop +endfacet +facet normal 0.002202 -0.000007 -0.999998 +outer loop +vertex 21.109247 -7.082470 8.000521 +vertex 21.109673 -5.345647 8.000510 +vertex 22.730549 -5.346774 8.004080 +endloop +endfacet +facet normal 0.002254 -0.000054 -0.999997 +outer loop +vertex 21.109247 -7.082470 8.000521 +vertex 22.730549 -5.346774 8.004080 +vertex 22.727156 -7.083351 8.004167 +endloop +endfacet +facet normal 0.006477 -0.000063 -0.999979 +outer loop +vertex 22.727156 -7.083351 8.004167 +vertex 22.730549 -5.346774 8.004080 +vertex 24.232944 -5.348269 8.013813 +endloop +endfacet +facet normal 0.006614 -0.000181 -0.999978 +outer loop +vertex 22.727156 -7.083351 8.004167 +vertex 24.232944 -5.348269 8.013813 +vertex 24.223190 -7.084518 8.014062 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 -10.479649 8.000000 +vertex 19.552803 -8.846289 8.000000 +vertex 21.109249 -8.846521 8.000521 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 -10.479649 8.000000 +vertex 21.109249 -8.846521 8.000521 +vertex 21.109249 -10.479773 8.000521 +endloop +endfacet +facet normal 0.002254 0.000000 -0.999997 +outer loop +vertex 21.109249 -10.479773 8.000521 +vertex 21.109249 -8.846521 8.000521 +vertex 22.727156 -8.847102 8.004167 +endloop +endfacet +facet normal 0.002252 0.000000 -0.999997 +outer loop +vertex 21.109249 -10.479773 8.000521 +vertex 22.727156 -8.847102 8.004167 +vertex 22.727158 -10.480080 8.004167 +endloop +endfacet +facet normal 0.006615 0.000000 -0.999978 +outer loop +vertex 22.727158 -10.480080 8.004167 +vertex 22.727156 -8.847102 8.004167 +vertex 24.223190 -8.847869 8.014062 +endloop +endfacet +facet normal 0.006614 0.000000 -0.999978 +outer loop +vertex 22.727158 -10.480080 8.004167 +vertex 24.223190 -8.847869 8.014062 +vertex 24.223190 -10.480485 8.014062 +endloop +endfacet +facet normal 0.016180 0.000000 -0.999869 +outer loop +vertex 24.223190 -10.480485 8.014062 +vertex 24.223190 -8.847869 8.014062 +vertex 25.414009 -8.848668 8.033333 +endloop +endfacet +facet normal 0.016182 0.000000 -0.999869 +outer loop +vertex 24.223190 -10.480485 8.014062 +vertex 25.414009 -8.848668 8.033333 +vertex 25.414011 -10.480907 8.033333 +endloop +endfacet +facet normal 0.016180 0.000000 -0.999869 +outer loop +vertex 24.223190 -8.847869 8.014062 +vertex 24.223190 -7.084518 8.014062 +vertex 25.414009 -7.085731 8.033333 +endloop +endfacet +facet normal 0.016181 0.000000 -0.999869 +outer loop +vertex 24.223190 -8.847869 8.014062 +vertex 25.414009 -7.085731 8.033333 +vertex 25.414009 -8.848668 8.033333 +endloop +endfacet +facet normal 0.016046 -0.000234 -0.999871 +outer loop +vertex 24.223190 -7.084518 8.014062 +vertex 24.232944 -5.348269 8.013813 +vertex 25.427582 -5.349819 8.032986 +endloop +endfacet +facet normal 0.016182 -0.000326 -0.999869 +outer loop +vertex 24.223190 -7.084518 8.014062 +vertex 25.427582 -5.349819 8.032986 +vertex 25.414009 -7.085731 8.033333 +endloop +endfacet +facet normal 0.014140 -0.002009 -0.999898 +outer loop +vertex 24.232944 -5.348269 8.013813 +vertex 24.311401 -3.796976 8.011806 +vertex 25.563290 -3.798691 8.029513 +endloop +endfacet +facet normal 0.016042 -0.003642 -0.999865 +outer loop +vertex 24.232944 -5.348269 8.013813 +vertex 25.563290 -3.798691 8.029513 +vertex 25.427582 -5.349819 8.032986 +endloop +endfacet +facet normal 0.005350 -0.000473 -0.999986 +outer loop +vertex 22.730549 -5.346774 8.004080 +vertex 22.754299 -3.795325 8.003472 +vertex 24.311401 -3.796976 8.011806 +endloop +endfacet +facet normal 0.006478 -0.001622 -0.999978 +outer loop +vertex 22.730549 -5.346774 8.004080 +vertex 24.311401 -3.796976 8.011806 +vertex 24.232944 -5.348269 8.013813 +endloop +endfacet +facet normal 0.001851 -0.000053 -0.999998 +outer loop +vertex 21.109673 -5.345647 8.000510 +vertex 21.112640 -3.794090 8.000434 +vertex 22.754299 -3.795325 8.003472 +endloop +endfacet +facet normal 0.002203 -0.000425 -0.999997 +outer loop +vertex 21.109673 -5.345647 8.000510 +vertex 22.754299 -3.795325 8.003472 +vertex 22.730549 -5.346774 8.004080 +endloop +endfacet +facet normal 0.000279 0.000000 -1.000000 +outer loop +vertex 19.552801 -5.345193 8.000000 +vertex 19.552803 -3.793593 8.000000 +vertex 21.112640 -3.794090 8.000434 +endloop +endfacet +facet normal 0.000327 -0.000050 -1.000000 +outer loop +vertex 19.552801 -5.345193 8.000000 +vertex 21.112640 -3.794090 8.000434 +vertex 21.109673 -5.345647 8.000510 +endloop +endfacet +facet normal 0.000328 0.000000 -1.000000 +outer loop +vertex 19.552803 -7.082114 8.000000 +vertex 19.552801 -5.345193 8.000000 +vertex 21.109673 -5.345647 8.000510 +endloop +endfacet +facet normal 0.000334 -0.000006 -1.000000 +outer loop +vertex 19.552803 -7.082114 8.000000 +vertex 21.109673 -5.345647 8.000510 +vertex 21.109247 -7.082470 8.000521 +endloop +endfacet +facet normal 0.000335 0.000000 -1.000000 +outer loop +vertex 19.552803 -8.846289 8.000000 +vertex 19.552803 -7.082114 8.000000 +vertex 21.109247 -7.082470 8.000521 +endloop +endfacet +facet normal 0.000334 0.000000 -1.000000 +outer loop +vertex 19.552803 -8.846289 8.000000 +vertex 21.109247 -7.082470 8.000521 +vertex 21.109249 -8.846521 8.000521 +endloop +endfacet +facet normal -0.000779 -0.081272 -0.996692 +outer loop +vertex 21.108089 -13.782950 8.097133 +vertex 21.108513 -12.949333 8.029158 +vertex 22.706612 -12.940510 8.027189 +endloop +endfacet +facet normal -0.008823 -0.066099 -0.997774 +outer loop +vertex 21.108089 -13.782950 8.097133 +vertex 22.706612 -12.940510 8.027189 +vertex 22.668369 -13.756055 8.081554 +endloop +endfacet +facet normal 0.003973 -0.066698 -0.997765 +outer loop +vertex 22.668369 -13.756055 8.081554 +vertex 22.706612 -12.940510 8.027189 +vertex 24.159176 -12.921573 8.031707 +endloop +endfacet +facet normal -0.004329 -0.051922 -0.998642 +outer loop +vertex 22.668369 -13.756055 8.081554 +vertex 24.159176 -12.921573 8.031707 +vertex 24.034252 -13.696052 8.072515 +endloop +endfacet +facet normal 0.001837 -0.022844 -0.999737 +outer loop +vertex 21.108513 -12.949333 8.029158 +vertex 21.109156 -11.852637 8.004100 +vertex 22.724588 -11.851659 8.007044 +endloop +endfacet +facet normal -0.001129 -0.018480 -0.999829 +outer loop +vertex 21.108513 -12.949333 8.029158 +vertex 22.724588 -11.851659 8.007044 +vertex 22.706612 -12.940510 8.027189 +endloop +endfacet +facet normal 0.006215 -0.018601 -0.999808 +outer loop +vertex 22.706612 -12.940510 8.027189 +vertex 22.724588 -11.851659 8.007044 +vertex 24.215187 -11.849457 8.016268 +endloop +endfacet +facet normal 0.003299 -0.014571 -0.999888 +outer loop +vertex 22.706612 -12.940510 8.027189 +vertex 24.215187 -11.849457 8.016268 +vertex 24.159176 -12.921573 8.031707 +endloop +endfacet +facet normal -0.007570 -0.248826 -0.968519 +outer loop +vertex 19.588913 -14.375001 8.261111 +vertex 19.568035 -13.786793 8.110156 +vertex 21.108089 -13.782950 8.097133 +endloop +endfacet +facet normal -0.019371 -0.220396 -0.975218 +outer loop +vertex 19.588913 -14.375001 8.261111 +vertex 21.108089 -13.782950 8.097133 +vertex 21.113926 -14.368312 8.229306 +endloop +endfacet +facet normal -0.005942 -0.220306 -0.975413 +outer loop +vertex 21.113926 -14.368312 8.229306 +vertex 21.108089 -13.782950 8.097133 +vertex 22.668369 -13.756055 8.081554 +endloop +endfacet +facet normal -0.022359 -0.180528 -0.983316 +outer loop +vertex 21.113926 -14.368312 8.229306 +vertex 22.668369 -13.756055 8.081554 +vertex 22.647228 -14.321472 8.185841 +endloop +endfacet +facet normal 0.001462 -0.181435 -0.983402 +outer loop +vertex 22.647228 -14.321472 8.185841 +vertex 22.668369 -13.756055 8.081554 +vertex 24.034252 -13.696052 8.072515 +endloop +endfacet +facet normal -0.017813 -0.139884 -0.990008 +outer loop +vertex 22.647228 -14.321472 8.185841 +vertex 24.034252 -13.696052 8.072515 +vertex 23.947687 -14.205942 8.146118 +endloop +endfacet +facet normal 0.013719 -0.145136 -0.989317 +outer loop +vertex 23.947687 -14.205942 8.146118 +vertex 24.034252 -13.696052 8.072515 +vertex 25.061609 -13.654374 8.080646 +endloop +endfacet +facet normal -0.007751 -0.102426 -0.994711 +outer loop +vertex 23.947687 -14.205942 8.146118 +vertex 25.061609 -13.654374 8.080646 +vertex 24.762611 -14.062502 8.125000 +endloop +endfacet +facet normal 0.014770 -0.054988 -0.998378 +outer loop +vertex 24.034252 -13.696052 8.072515 +vertex 24.159176 -12.921573 8.031707 +vertex 25.302427 -12.910231 8.047994 +endloop +endfacet +facet normal 0.009810 -0.047003 -0.998847 +outer loop +vertex 24.034252 -13.696052 8.072515 +vertex 25.302427 -12.910231 8.047994 +vertex 25.061609 -13.654374 8.080646 +endloop +endfacet +facet normal 0.015961 -0.015231 -0.999757 +outer loop +vertex 24.159176 -12.921573 8.031707 +vertex 24.215187 -11.849457 8.016268 +vertex 25.400063 -11.848212 8.035166 +endloop +endfacet +facet normal 0.014378 -0.013398 -0.999807 +outer loop +vertex 24.159176 -12.921573 8.031707 +vertex 25.400063 -11.848212 8.035166 +vertex 25.302427 -12.910231 8.047994 +endloop +endfacet +facet normal 0.016180 -0.001706 -0.999868 +outer loop +vertex 24.215187 -11.849457 8.016268 +vertex 24.223190 -10.480485 8.014062 +vertex 25.414011 -10.480907 8.033333 +endloop +endfacet +facet normal 0.015950 -0.001503 -0.999872 +outer loop +vertex 24.215187 -11.849457 8.016268 +vertex 25.414011 -10.480907 8.033333 +vertex 25.400063 -11.848212 8.035166 +endloop +endfacet +facet normal 0.006613 -0.002110 -0.999976 +outer loop +vertex 22.724588 -11.851659 8.007044 +vertex 22.727158 -10.480080 8.004167 +vertex 24.223190 -10.480485 8.014062 +endloop +endfacet +facet normal 0.006190 -0.001647 -0.999980 +outer loop +vertex 22.724588 -11.851659 8.007044 +vertex 24.223190 -10.480485 8.014062 +vertex 24.215187 -11.849457 8.016268 +endloop +endfacet +facet normal 0.002254 -0.002607 -0.999994 +outer loop +vertex 21.109156 -11.852637 8.004100 +vertex 21.109249 -10.479773 8.000521 +vertex 22.727158 -10.480080 8.004167 +endloop +endfacet +facet normal 0.001824 -0.002101 -0.999996 +outer loop +vertex 21.109156 -11.852637 8.004100 +vertex 22.727158 -10.480080 8.004167 +vertex 22.724588 -11.851659 8.007044 +endloop +endfacet +facet normal 0.000335 -0.002971 -0.999996 +outer loop +vertex 19.553366 -11.852744 8.004080 +vertex 19.552803 -10.479649 8.000000 +vertex 21.109249 -10.479773 8.000521 +endloop +endfacet +facet normal 0.000012 -0.002607 -0.999997 +outer loop +vertex 19.553366 -11.852744 8.004080 +vertex 21.109249 -10.479773 8.000521 +vertex 21.109156 -11.852637 8.004100 +endloop +endfacet +facet normal 0.000016 -0.026005 -0.999662 +outer loop +vertex 19.557316 -12.950583 8.032639 +vertex 19.553366 -11.852744 8.004080 +vertex 21.109156 -11.852637 8.004100 +endloop +endfacet +facet normal -0.002227 -0.022841 -0.999737 +outer loop +vertex 19.557316 -12.950583 8.032639 +vertex 21.109156 -11.852637 8.004100 +vertex 21.108513 -12.949333 8.029158 +endloop +endfacet +facet normal -0.002161 -0.092332 -0.995726 +outer loop +vertex 19.568035 -13.786793 8.110156 +vertex 19.557316 -12.950583 8.032639 +vertex 21.108513 -12.949333 8.029158 +endloop +endfacet +facet normal -0.008225 -0.081266 -0.996659 +outer loop +vertex 19.568035 -13.786793 8.110156 +vertex 21.108513 -12.949333 8.029158 +vertex 21.108089 -13.782950 8.097133 +endloop +endfacet +facet normal 0.171458 -0.063177 0.983164 +outer loop +vertex 21.186642 -13.783004 9.276489 +vertex 22.691643 -13.756097 9.015754 +vertex 22.732565 -12.940852 9.061005 +endloop +endfacet +facet normal 0.180586 -0.080679 0.980245 +outer loop +vertex 21.186642 -13.783004 9.276489 +vertex 22.732565 -12.940852 9.061005 +vertex 21.196112 -12.949749 9.343325 +endloop +endfacet +facet normal 0.180792 -0.017514 0.983365 +outer loop +vertex 21.196112 -12.949749 9.343325 +vertex 22.732565 -12.940852 9.061005 +vertex 22.751530 -11.852822 9.076897 +endloop +endfacet +facet normal 0.184294 -0.022663 0.982610 +outer loop +vertex 21.196112 -12.949749 9.343325 +vertex 22.751530 -11.852822 9.076897 +vertex 21.200087 -11.854041 9.367850 +endloop +endfacet +facet normal 0.202004 -0.047845 0.978215 +outer loop +vertex 22.691643 -13.756097 9.015754 +vertex 24.037163 -13.696084 8.740838 +vertex 24.162418 -12.921827 8.752842 +endloop +endfacet +facet normal 0.211063 -0.064731 0.975327 +outer loop +vertex 22.691643 -13.756097 9.015754 +vertex 24.162418 -12.921827 8.752842 +vertex 22.732565 -12.940852 9.061005 +endloop +endfacet +facet normal 0.210835 -0.013350 0.977430 +outer loop +vertex 22.732565 -12.940852 9.061005 +vertex 24.162418 -12.921827 8.752842 +vertex 24.218555 -11.850319 8.755368 +endloop +endfacet +facet normal 0.214084 -0.017997 0.976649 +outer loop +vertex 22.732565 -12.940852 9.061005 +vertex 24.218555 -11.850319 8.755368 +vertex 22.751530 -11.852822 9.076897 +endloop +endfacet +facet normal 0.108992 -0.219970 0.969399 +outer loop +vertex 19.733356 -14.375003 9.305555 +vertex 21.174864 -14.368311 9.145000 +vertex 21.186642 -13.783004 9.276489 +endloop +endfacet +facet normal 0.121374 -0.250804 0.960399 +outer loop +vertex 19.733356 -14.375003 9.305555 +vertex 21.186642 -13.783004 9.276489 +vertex 19.754234 -13.786851 9.456511 +endloop +endfacet +facet normal 0.124502 -0.080737 0.988929 +outer loop +vertex 19.754234 -13.786851 9.456511 +vertex 21.186642 -13.783004 9.276489 +vertex 21.196112 -12.949749 9.343325 +endloop +endfacet +facet normal 0.131593 -0.093219 0.986911 +outer loop +vertex 19.754234 -13.786851 9.456511 +vertex 21.196112 -12.949749 9.343325 +vertex 19.764956 -12.951040 9.534028 +endloop +endfacet +facet normal 0.132070 -0.022661 0.990981 +outer loop +vertex 19.764956 -12.951040 9.534028 +vertex 21.196112 -12.949749 9.343325 +vertex 21.200087 -11.854041 9.367850 +endloop +endfacet +facet normal 0.134783 -0.026278 0.990527 +outer loop +vertex 19.764956 -12.951040 9.534028 +vertex 21.200087 -11.854041 9.367850 +vertex 19.768904 -11.854286 9.562587 +endloop +endfacet +facet normal 0.134824 -0.002588 0.990866 +outer loop +vertex 19.768904 -11.854286 9.562587 +vertex 21.200087 -11.854041 9.367850 +vertex 21.200653 -10.483106 9.371354 +endloop +endfacet +facet normal 0.135215 -0.003004 0.990812 +outer loop +vertex 19.768904 -11.854286 9.562587 +vertex 21.200653 -10.483106 9.371354 +vertex 19.769468 -10.483306 9.566667 +endloop +endfacet +facet normal 0.184326 -0.001993 0.982863 +outer loop +vertex 21.200087 -11.854041 9.367850 +vertex 22.751530 -11.852822 9.076897 +vertex 22.754240 -10.482836 9.079166 +endloop +endfacet +facet normal 0.184832 -0.002589 0.982767 +outer loop +vertex 21.200087 -11.854041 9.367850 +vertex 22.754240 -10.482836 9.079166 +vertex 21.200653 -10.483106 9.371354 +endloop +endfacet +facet normal 0.214092 -0.001513 0.976812 +outer loop +vertex 22.751530 -11.852822 9.076897 +vertex 24.218555 -11.850319 8.755368 +vertex 24.226574 -10.482530 8.755730 +endloop +endfacet +facet normal 0.214560 -0.002042 0.976709 +outer loop +vertex 22.751530 -11.852822 9.076897 +vertex 24.226574 -10.482530 8.755730 +vertex 22.754240 -10.482836 9.079166 +endloop +endfacet +facet normal 0.236297 -0.001430 0.971680 +outer loop +vertex 24.218555 -11.850319 8.755368 +vertex 25.400064 -11.848762 8.468046 +vertex 25.414009 -10.482211 8.466667 +endloop +endfacet +facet normal 0.236527 -0.001643 0.971624 +outer loop +vertex 24.218555 -11.850319 8.755368 +vertex 25.414009 -10.482211 8.466667 +vertex 24.226574 -10.482530 8.755730 +endloop +endfacet +facet normal 0.234715 -0.012748 0.971981 +outer loop +vertex 24.162418 -12.921827 8.752842 +vertex 25.302429 -12.910394 8.477700 +vertex 25.400064 -11.848762 8.468046 +endloop +endfacet +facet normal 0.236288 -0.014670 0.971572 +outer loop +vertex 24.162418 -12.921827 8.752842 +vertex 25.400064 -11.848762 8.468046 +vertex 24.218555 -11.850319 8.755368 +endloop +endfacet +facet normal 0.229893 -0.044615 0.972193 +outer loop +vertex 24.037163 -13.696084 8.740838 +vertex 25.061607 -13.654394 8.500504 +vertex 25.302429 -12.910394 8.477700 +endloop +endfacet +facet normal 0.234786 -0.053031 0.970600 +outer loop +vertex 24.037163 -13.696084 8.740838 +vertex 25.302429 -12.910394 8.477700 +vertex 24.162418 -12.921827 8.752842 +endloop +endfacet +facet normal 0.211196 -0.096345 0.972684 +outer loop +vertex 23.949944 -14.205942 8.687243 +vertex 24.762611 -14.062502 8.525001 +vertex 25.061607 -13.654394 8.500504 +endloop +endfacet +facet normal 0.231550 -0.140794 0.962581 +outer loop +vertex 23.949944 -14.205942 8.687243 +vertex 25.061607 -13.654394 8.500504 +vertex 24.037163 -13.696084 8.740838 +endloop +endfacet +facet normal 0.186093 -0.134147 0.973331 +outer loop +vertex 22.665283 -14.321471 8.916937 +vertex 23.949944 -14.205942 8.687243 +vertex 24.037163 -13.696084 8.740838 +endloop +endfacet +facet normal 0.204599 -0.177777 0.962567 +outer loop +vertex 22.665283 -14.321471 8.916937 +vertex 24.037163 -13.696084 8.740838 +vertex 22.691643 -13.756097 9.015754 +endloop +endfacet +facet normal 0.154304 -0.177086 0.972024 +outer loop +vertex 21.174864 -14.368311 9.145000 +vertex 22.665283 -14.321471 8.916937 +vertex 22.691643 -13.756097 9.015754 +endloop +endfacet +facet normal 0.170355 -0.219245 0.960682 +outer loop +vertex 21.174864 -14.368311 9.145000 +vertex 22.691643 -13.756097 9.015754 +vertex 21.186642 -13.783004 9.276489 +endloop +endfacet +facet normal -0.942867 -0.169688 0.286718 +outer loop +vertex 15.926517 -13.692226 8.431500 +vertex 15.996692 -13.725345 8.642670 +vertex 15.848109 -12.930644 8.624383 +endloop +endfacet +facet normal -0.925336 -0.179809 0.333800 +outer loop +vertex 15.926517 -13.692226 8.431500 +vertex 15.848109 -12.930644 8.624383 +vertex 15.764791 -12.920452 8.398901 +endloop +endfacet +facet normal -0.937705 -0.045501 0.344440 +outer loop +vertex 15.764791 -12.920452 8.398901 +vertex 15.848109 -12.930644 8.624383 +vertex 15.793253 -11.851281 8.617631 +endloop +endfacet +facet normal -0.930933 -0.049248 0.361855 +outer loop +vertex 15.764791 -12.920452 8.398901 +vertex 15.793253 -11.851281 8.617631 +vertex 15.702858 -11.849790 8.385280 +endloop +endfacet +facet normal -0.680988 -0.098469 0.725644 +outer loop +vertex 15.996692 -13.725345 8.642670 +vertex 16.239573 -13.692245 8.875096 +vertex 16.126717 -12.920577 8.873900 +endloop +endfacet +facet normal -0.661215 -0.106536 0.742593 +outer loop +vertex 15.996692 -13.725345 8.642670 +vertex 16.126717 -12.920577 8.873900 +vertex 15.848109 -12.930644 8.624383 +endloop +endfacet +facet normal -0.666398 -0.026147 0.745138 +outer loop +vertex 15.848109 -12.930644 8.624383 +vertex 16.126717 -12.920577 8.873900 +vertex 16.085794 -11.850214 8.874863 +endloop +endfacet +facet normal -0.659997 -0.028847 0.750714 +outer loop +vertex 15.848109 -12.930644 8.624383 +vertex 16.085794 -11.850214 8.874863 +vertex 15.793253 -11.851281 8.617631 +endloop +endfacet +facet normal -0.679728 -0.428326 -0.595404 +outer loop +vertex 16.245834 -14.062501 8.333333 +vertex 16.212664 -14.199250 8.469577 +vertex 15.926517 -13.692226 8.431500 +endloop +endfacet +facet normal -0.638856 -0.372476 -0.673145 +outer loop +vertex 16.245834 -14.062501 8.333333 +vertex 15.926517 -13.692226 8.431500 +vertex 16.073439 -13.654376 8.271119 +endloop +endfacet +facet normal -0.704649 -0.176686 -0.687205 +outer loop +vertex 16.073439 -13.654376 8.271119 +vertex 15.926517 -13.692226 8.431500 +vertex 15.764791 -12.920452 8.398901 +endloop +endfacet +facet normal -0.708344 -0.179013 -0.682791 +outer loop +vertex 16.073439 -13.654376 8.271119 +vertex 15.764791 -12.920452 8.398901 +vertex 15.932369 -12.910254 8.222376 +endloop +endfacet +facet normal -0.722856 -0.050581 -0.689144 +outer loop +vertex 15.932369 -12.910254 8.222376 +vertex 15.764791 -12.920452 8.398901 +vertex 15.702858 -11.849790 8.385280 +endloop +endfacet +facet normal -0.726457 -0.051960 -0.685245 +outer loop +vertex 15.932369 -12.910254 8.222376 +vertex 15.702858 -11.849790 8.385280 +vertex 15.874880 -11.848289 8.202797 +endloop +endfacet +facet normal -0.727620 -0.005683 -0.685957 +outer loop +vertex 15.874880 -11.848289 8.202797 +vertex 15.702858 -11.849790 8.385280 +vertex 15.694011 -10.481650 8.383333 +endloop +endfacet +facet normal -0.727964 -0.005775 -0.685591 +outer loop +vertex 15.874880 -11.848289 8.202797 +vertex 15.694011 -10.481650 8.383333 +vertex 15.866667 -10.481091 8.200000 +endloop +endfacet +facet normal -0.931956 -0.005079 0.362536 +outer loop +vertex 15.702858 -11.849790 8.385280 +vertex 15.793253 -11.851281 8.617631 +vertex 15.785417 -10.482183 8.616667 +endloop +endfacet +facet normal -0.931095 -0.005502 0.364735 +outer loop +vertex 15.702858 -11.849790 8.385280 +vertex 15.785417 -10.482183 8.616667 +vertex 15.694011 -10.481650 8.383333 +endloop +endfacet +facet normal -0.660322 -0.002898 0.750977 +outer loop +vertex 15.793253 -11.851281 8.617631 +vertex 16.085794 -11.850214 8.874863 +vertex 16.079948 -10.482659 8.875000 +endloop +endfacet +facet normal -0.659395 -0.003245 0.751790 +outer loop +vertex 15.793253 -11.851281 8.617631 +vertex 16.079948 -10.482659 8.875000 +vertex 15.785417 -10.482183 8.616667 +endloop +endfacet +facet normal -0.509449 -0.002124 0.860498 +outer loop +vertex 16.085794 -11.850214 8.874863 +vertex 16.520052 -11.849110 9.131964 +vertex 16.516666 -10.483034 9.133333 +endloop +endfacet +facet normal -0.509129 -0.002264 0.860687 +outer loop +vertex 16.085794 -11.850214 8.874863 +vertex 16.516666 -10.483034 9.133333 +vertex 16.079948 -10.482659 8.875000 +endloop +endfacet +facet normal -0.511418 -0.019182 0.859118 +outer loop +vertex 16.126717 -12.920577 8.873900 +vertex 16.543751 -12.910495 9.122376 +vertex 16.520052 -11.849110 9.131964 +endloop +endfacet +facet normal -0.509309 -0.020245 0.860346 +outer loop +vertex 16.126717 -12.920577 8.873900 +vertex 16.520052 -11.849110 9.131964 +vertex 16.085794 -11.850214 8.874863 +endloop +endfacet +facet normal -0.516933 -0.068762 0.853260 +outer loop +vertex 16.239573 -13.692245 8.875096 +vertex 16.602932 -13.654407 9.098280 +vertex 16.543751 -12.910495 9.122376 +endloop +endfacet +facet normal -0.509175 -0.073137 0.857550 +outer loop +vertex 16.239573 -13.692245 8.875096 +vertex 16.543751 -12.910495 9.122376 +vertex 16.126717 -12.920577 8.873900 +endloop +endfacet +facet normal -0.551613 -0.166355 0.817343 +outer loop +vertex 16.475306 -14.199250 8.901251 +vertex 16.679167 -14.062502 9.066667 +vertex 16.602932 -13.654407 9.098280 +endloop +endfacet +facet normal -0.499636 -0.188695 0.845433 +outer loop +vertex 16.475306 -14.199250 8.901251 +vertex 16.602932 -13.654407 9.098280 +vertex 16.239573 -13.692245 8.875096 +endloop +endfacet +facet normal -0.684012 -0.283353 0.672190 +outer loop +vertex 16.284452 -14.267941 8.678086 +vertex 16.475306 -14.199250 8.901251 +vertex 16.239573 -13.692245 8.875096 +endloop +endfacet +facet normal -0.639902 -0.292996 0.710408 +outer loop +vertex 16.284452 -14.267941 8.678086 +vertex 16.239573 -13.692245 8.875096 +vertex 15.996692 -13.725345 8.642670 +endloop +endfacet +facet normal -0.877249 -0.455323 0.152037 +outer loop +vertex 16.212664 -14.199250 8.469577 +vertex 16.284452 -14.267941 8.678086 +vertex 15.996692 -13.725345 8.642670 +endloop +endfacet +facet normal -0.857875 -0.468253 0.211637 +outer loop +vertex 16.212664 -14.199250 8.469577 +vertex 15.996692 -13.725345 8.642670 +vertex 15.926517 -13.692226 8.431500 +endloop +endfacet +facet normal 0.280158 -0.000596 -0.959954 +outer loop +vertex 26.156590 -5.351182 8.067980 +vertex 26.394505 -5.352378 8.137413 +vertex 26.391111 -7.087739 8.137500 +endloop +endfacet +facet normal 0.272812 -0.001670 -0.962066 +outer loop +vertex 26.156590 -5.351182 8.067980 +vertex 26.391111 -7.087739 8.137500 +vertex 26.146835 -7.086800 8.068229 +endloop +endfacet +facet normal 0.272818 0.000000 -0.962066 +outer loop +vertex 26.146835 -7.086800 8.068229 +vertex 26.391111 -7.087739 8.137500 +vertex 26.391111 -8.849988 8.137500 +endloop +endfacet +facet normal 0.272823 0.000000 -0.962064 +outer loop +vertex 26.146835 -7.086800 8.068229 +vertex 26.391111 -8.849988 8.137500 +vertex 26.146837 -8.849369 8.068229 +endloop +endfacet +facet normal 0.452912 -0.000105 0.891555 +outer loop +vertex 26.394505 -5.352378 8.137413 +vertex 26.147263 -5.353491 8.263010 +vertex 26.146837 -7.088612 8.263021 +endloop +endfacet +facet normal 0.457040 -0.000850 0.889446 +outer loop +vertex 26.394505 -5.352378 8.137413 +vertex 26.146837 -7.088612 8.263021 +vertex 26.391111 -7.087739 8.137500 +endloop +endfacet +facet normal 0.457042 0.000000 0.889445 +outer loop +vertex 26.391111 -7.087739 8.137500 +vertex 26.146837 -7.088612 8.263021 +vertex 26.146837 -8.850560 8.263021 +endloop +endfacet +facet normal 0.457043 0.000000 0.889445 +outer loop +vertex 26.391111 -7.087739 8.137500 +vertex 26.146837 -8.850560 8.263021 +vertex 26.391111 -8.849988 8.137500 +endloop +endfacet +facet normal 0.054188 -0.004033 -0.998523 +outer loop +vertex 25.563290 -3.798691 8.029513 +vertex 26.235046 -3.800150 8.065972 +vertex 26.156590 -5.351182 8.067980 +endloop +endfacet +facet normal 0.047932 -0.006430 -0.998830 +outer loop +vertex 25.563290 -3.798691 8.029513 +vertex 26.156590 -5.351182 8.067980 +vertex 25.427582 -5.349819 8.032986 +endloop +endfacet +facet normal 0.047943 -0.000413 -0.998850 +outer loop +vertex 25.427582 -5.349819 8.032986 +vertex 26.156590 -5.351182 8.067980 +vertex 26.146835 -7.086800 8.068229 +endloop +endfacet +facet normal 0.047565 -0.000572 -0.998868 +outer loop +vertex 25.427582 -5.349819 8.032986 +vertex 26.146835 -7.086800 8.068229 +vertex 25.414009 -7.085731 8.033333 +endloop +endfacet +facet normal 0.047563 0.000000 -0.998868 +outer loop +vertex 25.414009 -7.085731 8.033333 +vertex 26.146835 -7.086800 8.068229 +vertex 26.146837 -8.849369 8.068229 +endloop +endfacet +facet normal 0.047566 0.000000 -0.998868 +outer loop +vertex 25.414009 -7.085731 8.033333 +vertex 26.146837 -8.849369 8.068229 +vertex 25.414009 -8.848668 8.033333 +endloop +endfacet +facet normal 0.047563 0.000000 -0.998868 +outer loop +vertex 25.414009 -8.848668 8.033333 +vertex 26.146837 -8.849369 8.068229 +vertex 26.146835 -10.481279 8.068229 +endloop +endfacet +facet normal 0.047565 0.000000 -0.998868 +outer loop +vertex 25.414009 -8.848668 8.033333 +vertex 26.146835 -10.481279 8.068229 +vertex 25.414011 -10.480907 8.033333 +endloop +endfacet +facet normal 0.272821 0.000000 -0.962065 +outer loop +vertex 26.146837 -8.849369 8.068229 +vertex 26.391111 -8.849988 8.137500 +vertex 26.391113 -10.481605 8.137500 +endloop +endfacet +facet normal 0.272819 0.000000 -0.962065 +outer loop +vertex 26.146837 -8.849369 8.068229 +vertex 26.391113 -10.481605 8.137500 +vertex 26.146835 -10.481279 8.068229 +endloop +endfacet +facet normal 0.457041 0.000000 0.889446 +outer loop +vertex 26.391111 -8.849988 8.137500 +vertex 26.146837 -8.850560 8.263021 +vertex 26.146837 -10.481909 8.263021 +endloop +endfacet +facet normal 0.457041 0.000000 0.889446 +outer loop +vertex 26.391111 -8.849988 8.137500 +vertex 26.146837 -10.481909 8.263021 +vertex 26.391113 -10.481605 8.137500 +endloop +endfacet +facet normal 0.267747 0.000000 0.963489 +outer loop +vertex 26.146837 -8.850560 8.263021 +vertex 25.414009 -8.851135 8.466666 +vertex 25.414009 -10.482211 8.466667 +endloop +endfacet +facet normal 0.267744 0.000000 0.963490 +outer loop +vertex 26.146837 -8.850560 8.263021 +vertex 25.414009 -10.482211 8.466667 +vertex 26.146837 -10.481909 8.263021 +endloop +endfacet +facet normal 0.267747 -0.000000 0.963489 +outer loop +vertex 26.146837 -7.088612 8.263021 +vertex 25.414009 -7.089484 8.466667 +vertex 25.414009 -8.851135 8.466666 +endloop +endfacet +facet normal 0.267745 0.000000 0.963490 +outer loop +vertex 26.146837 -7.088612 8.263021 +vertex 25.414009 -8.851135 8.466666 +vertex 26.146837 -8.850560 8.263021 +endloop +endfacet +facet normal 0.267615 0.000001 0.963526 +outer loop +vertex 26.147263 -5.353491 8.263010 +vertex 25.414009 -5.354605 8.466666 +vertex 25.414009 -7.089484 8.466667 +endloop +endfacet +facet normal 0.267745 -0.000061 0.963490 +outer loop +vertex 26.147263 -5.353491 8.263010 +vertex 25.414009 -7.089484 8.466667 +vertex 26.146837 -7.088612 8.263021 +endloop +endfacet +facet normal 0.266708 -0.000000 0.963777 +outer loop +vertex 26.150230 -3.802635 8.262934 +vertex 25.414011 -3.803846 8.466667 +vertex 25.414009 -5.354605 8.466666 +endloop +endfacet +facet normal 0.267612 -0.000465 0.963526 +outer loop +vertex 26.150230 -3.802635 8.262934 +vertex 25.414009 -5.354605 8.466666 +vertex 26.147263 -5.353491 8.263010 +endloop +endfacet +facet normal 0.425800 -0.000771 0.904817 +outer loop +vertex 26.418255 -3.801430 8.136806 +vertex 26.150230 -3.802635 8.262934 +vertex 26.147263 -5.353491 8.263010 +endloop +endfacet +facet normal 0.452912 -0.006587 0.891531 +outer loop +vertex 26.418255 -3.801430 8.136806 +vertex 26.147263 -5.353491 8.263010 +vertex 26.394505 -5.352378 8.137413 +endloop +endfacet +facet normal 0.360580 -0.005887 -0.932710 +outer loop +vertex 26.235046 -3.800150 8.065972 +vertex 26.418255 -3.801430 8.136806 +vertex 26.394505 -5.352378 8.137413 +endloop +endfacet +facet normal 0.280041 -0.015408 -0.959864 +outer loop +vertex 26.235046 -3.800150 8.065972 +vertex 26.394505 -5.352378 8.137413 +vertex 26.156590 -5.351182 8.067980 +endloop +endfacet +facet normal 0.000279 0.000002 -1.000000 +outer loop +vertex 21.112640 -3.794090 8.000434 +vertex 19.552803 -3.793593 8.000000 +vertex 19.549599 -3.205483 8.000000 +endloop +endfacet +facet normal 0.000277 -0.000139 -1.000000 +outer loop +vertex 20.747240 -3.260331 8.000258 +vertex 21.113426 -3.276976 8.000362 +vertex 21.112640 -3.794090 8.000434 +endloop +endfacet +facet normal 0.000208 -0.000187 -1.000000 +outer loop +vertex 19.549599 -3.205483 8.000000 +vertex 20.747240 -3.260331 8.000258 +vertex 21.112640 -3.794090 8.000434 +endloop +endfacet +facet normal 0.001571 -0.000141 -0.999999 +outer loop +vertex 21.112640 -3.794090 8.000434 +vertex 21.113426 -3.276976 8.000362 +vertex 22.772945 -3.352415 8.002979 +endloop +endfacet +facet normal 0.001849 -0.001191 -0.999997 +outer loop +vertex 21.112640 -3.794090 8.000434 +vertex 22.772945 -3.352415 8.002979 +vertex 22.754299 -3.795325 8.003472 +endloop +endfacet +facet normal 0.005351 -0.001338 -0.999985 +outer loop +vertex 24.311401 -3.796976 8.011806 +vertex 22.754299 -3.795325 8.003472 +vertex 22.772945 -3.352415 8.002979 +endloop +endfacet +facet normal 0.003892 -0.004703 -0.999981 +outer loop +vertex 23.345116 -3.377946 8.006075 +vertex 24.391867 -3.292191 8.009745 +vertex 24.311401 -3.796976 8.011806 +endloop +endfacet +facet normal 0.005347 -0.001337 -0.999985 +outer loop +vertex 22.772945 -3.352415 8.002979 +vertex 23.345116 -3.377946 8.006075 +vertex 24.311401 -3.796976 8.011806 +endloop +endfacet +facet normal 0.014136 -0.006335 -0.999880 +outer loop +vertex 25.563290 -3.798691 8.029513 +vertex 24.311401 -3.796976 8.011806 +vertex 24.391867 -3.292191 8.009745 +endloop +endfacet +facet normal 0.004140 -0.014740 -0.999883 +outer loop +vertex 25.696550 -3.224265 8.021599 +vertex 25.854183 -3.211885 8.022071 +vertex 25.563290 -3.798691 8.029513 +endloop +endfacet +facet normal 0.009923 -0.016077 -0.999822 +outer loop +vertex 24.391867 -3.292191 8.009745 +vertex 25.696550 -3.224265 8.021599 +vertex 25.563290 -3.798691 8.029513 +endloop +endfacet +facet normal -0.226513 -0.080775 0.970653 +outer loop +vertex 17.072617 -13.696096 9.291351 +vertex 17.714960 -13.756108 9.436255 +vertex 17.730688 -12.940925 9.507764 +endloop +endfacet +facet normal -0.239839 -0.068515 0.968392 +outer loop +vertex 17.072617 -13.696096 9.291351 +vertex 17.730688 -12.940925 9.507764 +vertex 17.062820 -12.921923 9.343699 +endloop +endfacet +facet normal -0.239097 -0.022242 0.970741 +outer loop +vertex 17.062820 -12.921923 9.343699 +vertex 17.730688 -12.940925 9.507764 +vertex 17.735132 -11.853064 9.533783 +endloop +endfacet +facet normal -0.244656 -0.018514 0.969433 +outer loop +vertex 17.062820 -12.921923 9.343699 +vertex 17.735132 -11.853064 9.533783 +vertex 17.055969 -11.850637 9.362429 +endloop +endfacet +facet normal -0.078599 -0.092845 0.992573 +outer loop +vertex 17.714960 -13.756108 9.436255 +vertex 18.594265 -13.783009 9.503368 +vertex 18.612646 -12.949788 9.582764 +endloop +endfacet +facet normal -0.085274 -0.085436 0.992688 +outer loop +vertex 17.714960 -13.756108 9.436255 +vertex 18.612646 -12.949788 9.582764 +vertex 17.730688 -12.940925 9.507764 +endloop +endfacet +facet normal -0.084963 -0.026042 0.996044 +outer loop +vertex 17.730688 -12.940925 9.507764 +vertex 18.612646 -12.949788 9.582764 +vertex 18.619249 -11.854171 9.611973 +endloop +endfacet +facet normal -0.088100 -0.023458 0.995835 +outer loop +vertex 17.730688 -12.940925 9.507764 +vertex 18.619249 -11.854171 9.611973 +vertex 17.735132 -11.853064 9.533783 +endloop +endfacet +facet normal -0.356462 -0.179492 0.916906 +outer loop +vertex 16.679167 -14.062502 9.066667 +vertex 17.038143 -14.205942 9.178142 +vertex 17.072617 -13.696096 9.291351 +endloop +endfacet +facet normal -0.387115 -0.142882 0.910893 +outer loop +vertex 16.679167 -14.062502 9.066667 +vertex 17.072617 -13.696096 9.291351 +vertex 16.602932 -13.654407 9.098280 +endloop +endfacet +facet normal -0.384431 -0.067120 0.920710 +outer loop +vertex 16.602932 -13.654407 9.098280 +vertex 17.072617 -13.696096 9.291351 +vertex 17.062820 -12.921923 9.343699 +endloop +endfacet +facet normal -0.392621 -0.060960 0.917678 +outer loop +vertex 16.602932 -13.654407 9.098280 +vertex 17.062820 -12.921923 9.343699 +vertex 16.543751 -12.910495 9.122376 +endloop +endfacet +facet normal -0.392498 -0.018588 0.919565 +outer loop +vertex 16.543751 -12.910495 9.122376 +vertex 17.062820 -12.921923 9.343699 +vertex 17.055969 -11.850637 9.362429 +endloop +endfacet +facet normal -0.395039 -0.017117 0.918505 +outer loop +vertex 16.543751 -12.910495 9.122376 +vertex 17.055969 -11.850637 9.362429 +vertex 16.520052 -11.849110 9.131964 +endloop +endfacet +facet normal -0.395063 -0.002080 0.918652 +outer loop +vertex 16.520052 -11.849110 9.131964 +vertex 17.055969 -11.850637 9.362429 +vertex 17.054993 -10.483283 9.365105 +endloop +endfacet +facet normal -0.395447 -0.001900 0.918487 +outer loop +vertex 16.520052 -11.849110 9.131964 +vertex 17.054993 -10.483283 9.365105 +vertex 16.516666 -10.483034 9.133333 +endloop +endfacet +facet normal -0.244643 -0.002518 0.969610 +outer loop +vertex 17.055969 -11.850637 9.362429 +vertex 17.735132 -11.853064 9.533783 +vertex 17.735767 -10.483404 9.537500 +endloop +endfacet +facet normal -0.245485 -0.002073 0.969398 +outer loop +vertex 17.055969 -11.850637 9.362429 +vertex 17.735767 -10.483404 9.537500 +vertex 17.054993 -10.483283 9.365105 +endloop +endfacet +facet normal -0.088099 -0.002972 0.996107 +outer loop +vertex 17.735132 -11.853064 9.533783 +vertex 18.619249 -11.854171 9.611973 +vertex 18.620193 -10.483410 9.616146 +endloop +endfacet +facet normal -0.088573 -0.002662 0.996066 +outer loop +vertex 17.735132 -11.853064 9.533783 +vertex 18.620193 -10.483410 9.616146 +vertex 17.735767 -10.483404 9.537500 +endloop +endfacet +facet normal 0.042916 -0.002991 0.999074 +outer loop +vertex 18.619249 -11.854171 9.611973 +vertex 19.768904 -11.854286 9.562587 +vertex 19.769468 -10.483306 9.566667 +endloop +endfacet +facet normal 0.043015 -0.003071 0.999070 +outer loop +vertex 18.619249 -11.854171 9.611973 +vertex 19.769468 -10.483306 9.566667 +vertex 18.620193 -10.483410 9.616146 +endloop +endfacet +facet normal 0.042213 -0.026159 0.998766 +outer loop +vertex 18.612646 -12.949788 9.582764 +vertex 19.764956 -12.951040 9.534028 +vertex 19.768904 -11.854286 9.562587 +endloop +endfacet +facet normal 0.042899 -0.026884 0.998718 +outer loop +vertex 18.612646 -12.949788 9.582764 +vertex 19.768904 -11.854286 9.562587 +vertex 18.619249 -11.854171 9.611973 +endloop +endfacet +facet normal 0.039880 -0.092783 0.994887 +outer loop +vertex 18.594265 -13.783009 9.503368 +vertex 19.754234 -13.786851 9.456511 +vertex 19.764956 -12.951040 9.534028 +endloop +endfacet +facet normal 0.041959 -0.095692 0.994526 +outer loop +vertex 18.594265 -13.783009 9.503368 +vertex 19.764956 -12.951040 9.534028 +vertex 18.612646 -12.949788 9.582764 +endloop +endfacet +facet normal 0.033635 -0.249582 0.967769 +outer loop +vertex 18.556152 -14.368310 9.348197 +vertex 19.733356 -14.375003 9.305555 +vertex 19.754234 -13.786851 9.456511 +endloop +endfacet +facet normal 0.038138 -0.258394 0.965287 +outer loop +vertex 18.556152 -14.368310 9.348197 +vertex 19.754234 -13.786851 9.456511 +vertex 18.594265 -13.783009 9.503368 +endloop +endfacet +facet normal -0.073570 -0.251088 0.965164 +outer loop +vertex 17.665802 -14.321471 9.292516 +vertex 18.556152 -14.368310 9.348197 +vertex 18.594265 -13.783009 9.503368 +endloop +endfacet +facet normal -0.081163 -0.238954 0.967633 +outer loop +vertex 17.665802 -14.321471 9.292516 +vertex 18.594265 -13.783009 9.503368 +vertex 17.714960 -13.756108 9.436255 +endloop +endfacet +facet normal -0.214355 -0.223125 0.950930 +outer loop +vertex 17.038143 -14.205942 9.178142 +vertex 17.665802 -14.321471 9.292516 +vertex 17.714960 -13.756108 9.436255 +endloop +endfacet +facet normal -0.233162 -0.195746 0.952533 +outer loop +vertex 17.038143 -14.205942 9.178142 +vertex 17.714960 -13.756108 9.436255 +vertex 17.072617 -13.696096 9.291351 +endloop +endfacet +facet normal -0.074129 -0.092546 -0.992945 +outer loop +vertex 16.490507 -13.696053 8.177488 +vertex 16.396286 -12.921580 8.112339 +vertex 17.162565 -12.940511 8.056896 +endloop +endfacet +facet normal -0.070047 -0.096169 -0.992897 +outer loop +vertex 16.490507 -13.696053 8.177488 +vertex 17.162565 -12.940511 8.056896 +vertex 17.211208 -13.756058 8.132457 +endloop +endfacet +facet normal -0.019650 -0.093399 -0.995435 +outer loop +vertex 17.211208 -13.756058 8.132457 +vertex 17.162565 -12.940511 8.056896 +vertex 18.223684 -12.949332 8.036777 +endloop +endfacet +facet normal -0.017663 -0.095869 -0.995237 +outer loop +vertex 17.211208 -13.756058 8.132457 +vertex 18.223684 -12.949332 8.036777 +vertex 18.246180 -13.782951 8.116679 +endloop +endfacet +facet normal -0.074728 -0.025524 -0.996877 +outer loop +vertex 16.396286 -12.921580 8.112339 +vertex 16.356508 -11.849483 8.087871 +vertex 17.142763 -11.851664 8.028987 +endloop +endfacet +facet normal -0.072800 -0.026879 -0.996984 +outer loop +vertex 16.396286 -12.921580 8.112339 +vertex 17.142763 -11.851664 8.028987 +vertex 17.162565 -12.940511 8.056896 +endloop +endfacet +facet normal -0.020208 -0.025985 -0.999458 +outer loop +vertex 17.162565 -12.940511 8.056896 +vertex 17.142763 -11.851664 8.028987 +vertex 18.215160 -11.852634 8.007331 +endloop +endfacet +facet normal -0.019174 -0.026985 -0.999452 +outer loop +vertex 17.162565 -12.940511 8.056896 +vertex 18.215160 -11.852634 8.007331 +vertex 18.223684 -12.949332 8.036777 +endloop +endfacet +facet normal -0.235532 -0.242939 -0.941013 +outer loop +vertex 16.245834 -14.062501 8.333333 +vertex 16.073439 -13.654376 8.271119 +vertex 16.490507 -13.696053 8.177488 +endloop +endfacet +facet normal -0.199719 -0.267532 -0.942623 +outer loop +vertex 16.245834 -14.062501 8.333333 +vertex 16.490507 -13.696053 8.177488 +vertex 16.597235 -14.205941 8.299591 +endloop +endfacet +facet normal -0.080975 -0.248118 -0.965340 +outer loop +vertex 16.597235 -14.205941 8.299591 +vertex 16.490507 -13.696053 8.177488 +vertex 17.211208 -13.756058 8.132457 +endloop +endfacet +facet normal -0.073220 -0.257975 -0.963373 +outer loop +vertex 16.597235 -14.205941 8.299591 +vertex 17.211208 -13.756058 8.132457 +vertex 17.280115 -14.321469 8.278626 +endloop +endfacet +facet normal -0.021312 -0.252667 -0.967319 +outer loop +vertex 17.280115 -14.321469 8.278626 +vertex 17.211208 -13.756058 8.132457 +vertex 18.246180 -13.782951 8.116679 +endloop +endfacet +facet normal -0.018209 -0.257842 -0.966015 +outer loop +vertex 17.280115 -14.321469 8.278626 +vertex 18.246180 -13.782951 8.116679 +vertex 18.286760 -14.368312 8.272155 +endloop +endfacet +facet normal -0.005515 -0.257059 -0.966380 +outer loop +vertex 18.286760 -14.368312 8.272155 +vertex 18.246180 -13.782951 8.116679 +vertex 19.568035 -13.786793 8.110156 +endloop +endfacet +facet normal -0.009492 -0.248886 -0.968486 +outer loop +vertex 18.286760 -14.368312 8.272155 +vertex 19.568035 -13.786793 8.110156 +vertex 19.588913 -14.375001 8.261111 +endloop +endfacet +facet normal -0.003178 -0.095497 -0.995425 +outer loop +vertex 18.246180 -13.782951 8.116679 +vertex 18.223684 -12.949332 8.036777 +vertex 19.557316 -12.950583 8.032639 +endloop +endfacet +facet normal -0.005183 -0.092370 -0.995711 +outer loop +vertex 18.246180 -13.782951 8.116679 +vertex 19.557316 -12.950583 8.032639 +vertex 19.568035 -13.786793 8.110156 +endloop +endfacet +facet normal -0.002431 -0.026859 -0.999636 +outer loop +vertex 18.223684 -12.949332 8.036777 +vertex 18.215160 -11.852634 8.007331 +vertex 19.553366 -11.852744 8.004080 +endloop +endfacet +facet normal -0.003128 -0.026016 -0.999657 +outer loop +vertex 18.223684 -12.949332 8.036777 +vertex 19.553366 -11.852744 8.004080 +vertex 19.557316 -12.950583 8.032639 +endloop +endfacet +facet normal -0.002333 -0.003066 -0.999993 +outer loop +vertex 18.215160 -11.852634 8.007331 +vertex 18.213943 -10.479768 8.003125 +vertex 19.552803 -10.479649 8.000000 +endloop +endfacet +facet normal -0.002431 -0.002972 -0.999993 +outer loop +vertex 18.215160 -11.852634 8.007331 +vertex 19.552803 -10.479649 8.000000 +vertex 19.553366 -11.852744 8.004080 +endloop +endfacet +facet normal -0.020361 -0.002948 -0.999788 +outer loop +vertex 17.142763 -11.851664 8.028987 +vertex 17.139935 -10.480088 8.025000 +vertex 18.213943 -10.479768 8.003125 +endloop +endfacet +facet normal -0.020192 -0.003081 -0.999791 +outer loop +vertex 17.142763 -11.851664 8.028987 +vertex 18.213943 -10.479768 8.003125 +vertex 18.215160 -11.852634 8.007331 +endloop +endfacet +facet normal -0.075031 -0.002858 -0.997177 +outer loop +vertex 16.356508 -11.849483 8.087871 +vertex 16.350824 -10.480552 8.084374 +vertex 17.139935 -10.480088 8.025000 +endloop +endfacet +facet normal -0.074690 -0.003053 -0.997202 +outer loop +vertex 16.356508 -11.849483 8.087871 +vertex 17.139935 -10.480088 8.025000 +vertex 17.142763 -11.851664 8.028987 +endloop +endfacet +facet normal -0.232286 -0.003385 -0.972641 +outer loop +vertex 15.874880 -11.848289 8.202797 +vertex 15.866667 -10.481091 8.200000 +vertex 16.350824 -10.480552 8.084374 +endloop +endfacet +facet normal -0.232106 -0.003448 -0.972684 +outer loop +vertex 15.874880 -11.848289 8.202797 +vertex 16.350824 -10.480552 8.084374 +vertex 16.356508 -11.849483 8.087871 +endloop +endfacet +facet normal -0.232069 -0.030489 -0.972221 +outer loop +vertex 15.932369 -12.910254 8.222376 +vertex 15.874880 -11.848289 8.202797 +vertex 16.356508 -11.849483 8.087871 +endloop +endfacet +facet normal -0.231388 -0.030777 -0.972375 +outer loop +vertex 15.932369 -12.910254 8.222376 +vertex 16.356508 -11.849483 8.087871 +vertex 16.396286 -12.921580 8.112339 +endloop +endfacet +facet normal -0.231940 -0.107299 -0.966794 +outer loop +vertex 16.073439 -13.654376 8.271119 +vertex 15.932369 -12.910254 8.222376 +vertex 16.396286 -12.921580 8.112339 +endloop +endfacet +facet normal -0.228104 -0.109138 -0.967501 +outer loop +vertex 16.073439 -13.654376 8.271119 +vertex 16.396286 -12.921580 8.112339 +vertex 16.490507 -13.696053 8.177488 +endloop +endfacet +facet normal -0.075029 0.000000 -0.997181 +outer loop +vertex 16.350824 -8.847996 8.084374 +vertex 16.350824 -7.084708 8.084374 +vertex 17.139935 -7.083379 8.025000 +endloop +endfacet +facet normal -0.075032 0.000000 -0.997181 +outer loop +vertex 16.350824 -8.847996 8.084374 +vertex 17.139935 -7.083379 8.025000 +vertex 17.139935 -8.847120 8.025000 +endloop +endfacet +facet normal -0.020362 0.000000 -0.999793 +outer loop +vertex 17.139935 -8.847120 8.025000 +vertex 17.139935 -7.083379 8.025000 +vertex 18.213943 -7.082455 8.003125 +endloop +endfacet +facet normal -0.020364 0.000000 -0.999793 +outer loop +vertex 17.139935 -8.847120 8.025000 +vertex 18.213943 -7.082455 8.003125 +vertex 18.213943 -8.846513 8.003125 +endloop +endfacet +facet normal -0.073410 -0.001135 -0.997301 +outer loop +vertex 16.350824 -7.084708 8.084374 +vertex 16.344336 -5.348505 8.082877 +vertex 17.137676 -5.346809 8.024479 +endloop +endfacet +facet normal -0.075030 -0.000396 -0.997181 +outer loop +vertex 16.350824 -7.084708 8.084374 +vertex 17.137676 -5.346809 8.024479 +vertex 17.139935 -7.083379 8.025000 +endloop +endfacet +facet normal -0.019902 -0.000326 -0.999802 +outer loop +vertex 17.139935 -7.083379 8.025000 +vertex 17.137676 -5.346809 8.024479 +vertex 18.213661 -5.345628 8.003059 +endloop +endfacet +facet normal -0.020363 -0.000041 -0.999793 +outer loop +vertex 17.139935 -7.083379 8.025000 +vertex 18.213661 -5.345628 8.003059 +vertex 18.213943 -7.082455 8.003125 +endloop +endfacet +facet normal -0.232285 0.000000 -0.972648 +outer loop +vertex 15.866667 -10.481091 8.200000 +vertex 15.866667 -8.849014 8.200000 +vertex 16.350824 -8.847996 8.084374 +endloop +endfacet +facet normal -0.232287 0.000000 -0.972647 +outer loop +vertex 15.866667 -10.481091 8.200000 +vertex 16.350824 -8.847996 8.084374 +vertex 16.350824 -10.480552 8.084374 +endloop +endfacet +facet normal -0.075030 0.000000 -0.997181 +outer loop +vertex 16.350824 -10.480552 8.084374 +vertex 16.350824 -8.847996 8.084374 +vertex 17.139935 -8.847120 8.025000 +endloop +endfacet +facet normal -0.075030 0.000000 -0.997181 +outer loop +vertex 16.350824 -10.480552 8.084374 +vertex 17.139935 -8.847120 8.025000 +vertex 17.139935 -10.480088 8.025000 +endloop +endfacet +facet normal -0.020362 0.000000 -0.999793 +outer loop +vertex 17.139935 -10.480088 8.025000 +vertex 17.139935 -8.847120 8.025000 +vertex 18.213943 -8.846513 8.003125 +endloop +endfacet +facet normal -0.020364 0.000000 -0.999793 +outer loop +vertex 17.139935 -10.480088 8.025000 +vertex 18.213943 -8.846513 8.003125 +vertex 18.213943 -10.479768 8.003125 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 -10.479768 8.003125 +vertex 18.213943 -8.846513 8.003125 +vertex 19.552803 -8.846289 8.000000 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 -10.479768 8.003125 +vertex 19.552803 -8.846289 8.000000 +vertex 19.552803 -10.479649 8.000000 +endloop +endfacet +facet normal -0.002335 0.000000 -0.999997 +outer loop +vertex 18.213943 -8.846513 8.003125 +vertex 18.213943 -7.082455 8.003125 +vertex 19.552803 -7.082114 8.000000 +endloop +endfacet +facet normal -0.002334 0.000000 -0.999997 +outer loop +vertex 18.213943 -8.846513 8.003125 +vertex 19.552803 -7.082114 8.000000 +vertex 19.552803 -8.846289 8.000000 +endloop +endfacet +facet normal -0.002284 -0.000038 -0.999997 +outer loop +vertex 18.213943 -7.082455 8.003125 +vertex 18.213661 -5.345628 8.003059 +vertex 19.552801 -5.345193 8.000000 +endloop +endfacet +facet normal -0.002333 0.000000 -0.999997 +outer loop +vertex 18.213943 -7.082455 8.003125 +vertex 19.552801 -5.345193 8.000000 +vertex 19.552803 -7.082114 8.000000 +endloop +endfacet +facet normal -0.001942 -0.000296 -0.999998 +outer loop +vertex 18.213661 -5.345628 8.003059 +vertex 18.211685 -3.794066 8.002604 +vertex 19.552803 -3.793593 8.000000 +endloop +endfacet +facet normal -0.002284 0.000000 -0.999997 +outer loop +vertex 18.213661 -5.345628 8.003059 +vertex 19.552803 -3.793593 8.000000 +vertex 19.552801 -5.345193 8.000000 +endloop +endfacet +facet normal -0.016721 -0.002520 -0.999857 +outer loop +vertex 17.137676 -5.346809 8.024479 +vertex 17.121878 -3.795351 8.020833 +vertex 18.211685 -3.794066 8.002604 +endloop +endfacet +facet normal -0.019902 -0.000319 -0.999802 +outer loop +vertex 17.137676 -5.346809 8.024479 +vertex 18.211685 -3.794066 8.002604 +vertex 18.213661 -5.345628 8.003059 +endloop +endfacet +facet normal -0.060126 -0.009772 -0.998143 +outer loop +vertex 16.344336 -5.348505 8.082877 +vertex 16.292145 -3.797194 8.070833 +vertex 17.121878 -3.795351 8.020833 +endloop +endfacet +facet normal -0.073407 -0.003091 -0.997297 +outer loop +vertex 16.344336 -5.348505 8.082877 +vertex 17.121878 -3.795351 8.020833 +vertex 17.137676 -5.346809 8.024479 +endloop +endfacet +facet normal -0.198281 -0.024701 -0.979834 +outer loop +vertex 15.857639 -5.350487 8.197917 +vertex 15.767362 -3.799347 8.177083 +vertex 16.292145 -3.797194 8.070833 +endloop +endfacet +facet normal -0.229944 -0.015290 -0.973084 +outer loop +vertex 15.857639 -5.350487 8.197917 +vertex 16.292145 -3.797194 8.070833 +vertex 16.344336 -5.348505 8.082877 +endloop +endfacet +facet normal -0.230020 -0.002364 -0.973183 +outer loop +vertex 15.866667 -7.086260 8.200000 +vertex 15.857639 -5.350487 8.197917 +vertex 16.344336 -5.348505 8.082877 +endloop +endfacet +facet normal -0.232282 -0.001707 -0.972647 +outer loop +vertex 15.866667 -7.086260 8.200000 +vertex 16.344336 -5.348505 8.082877 +vertex 16.350824 -7.084708 8.084374 +endloop +endfacet +facet normal -0.232286 0.000000 -0.972647 +outer loop +vertex 15.866667 -8.849014 8.200000 +vertex 15.866667 -7.086260 8.200000 +vertex 16.350824 -7.084708 8.084374 +endloop +endfacet +facet normal -0.232285 0.000000 -0.972648 +outer loop +vertex 15.866667 -8.849014 8.200000 +vertex 16.350824 -7.084708 8.084374 +vertex 16.350824 -8.847996 8.084374 +endloop +endfacet +facet normal -0.245487 0.000000 0.969400 +outer loop +vertex 17.054993 -8.853159 9.365105 +vertex 17.735767 -8.853391 9.537500 +vertex 17.735767 -7.092913 9.537500 +endloop +endfacet +facet normal -0.245485 -0.000000 0.969400 +outer loop +vertex 17.054993 -8.853159 9.365105 +vertex 17.735767 -7.092913 9.537500 +vertex 17.054991 -7.092562 9.365105 +endloop +endfacet +facet normal -0.245484 0.000000 0.969401 +outer loop +vertex 17.054991 -7.092562 9.365105 +vertex 17.735767 -7.092913 9.537500 +vertex 17.735767 -5.358985 9.537500 +endloop +endfacet +facet normal -0.245487 0.000000 0.969400 +outer loop +vertex 17.054991 -7.092562 9.365105 +vertex 17.735767 -5.358985 9.537500 +vertex 17.054993 -5.358536 9.365105 +endloop +endfacet +facet normal -0.088574 0.000000 0.996070 +outer loop +vertex 17.735767 -8.853391 9.537500 +vertex 18.620193 -8.853397 9.616146 +vertex 18.620193 -7.092926 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 -8.853391 9.537500 +vertex 18.620193 -7.092926 9.616146 +vertex 17.735767 -7.092913 9.537500 +endloop +endfacet +facet normal -0.088572 0.000000 0.996070 +outer loop +vertex 17.735767 -7.092913 9.537500 +vertex 18.620193 -7.092926 9.616146 +vertex 18.620193 -5.359001 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 -7.092913 9.537500 +vertex 18.620193 -5.359001 9.616146 +vertex 17.735767 -5.358985 9.537500 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 -10.483034 9.133333 +vertex 17.054993 -10.483283 9.365105 +vertex 17.054993 -8.853159 9.365105 +endloop +endfacet +facet normal -0.395446 0.000000 0.918489 +outer loop +vertex 16.516666 -10.483034 9.133333 +vertex 17.054993 -8.853159 9.365105 +vertex 16.516666 -8.852689 9.133333 +endloop +endfacet +facet normal -0.395445 -0.000001 0.918489 +outer loop +vertex 16.516666 -8.852689 9.133333 +vertex 17.054993 -8.853159 9.365105 +vertex 17.054991 -7.092562 9.365105 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 -8.852689 9.133333 +vertex 17.054991 -7.092562 9.365105 +vertex 16.516666 -7.091847 9.133333 +endloop +endfacet +facet normal -0.395448 0.000001 0.918488 +outer loop +vertex 16.516666 -7.091847 9.133333 +vertex 17.054991 -7.092562 9.365105 +vertex 17.054993 -5.358536 9.365105 +endloop +endfacet +facet normal -0.395448 0.000000 0.918488 +outer loop +vertex 16.516666 -7.091847 9.133333 +vertex 17.054993 -5.358536 9.365105 +vertex 16.516666 -5.357624 9.133333 +endloop +endfacet +facet normal -0.395446 -0.000001 0.918489 +outer loop +vertex 16.516666 -5.357624 9.133333 +vertex 17.054993 -5.358536 9.365105 +vertex 17.054991 -3.808129 9.365105 +endloop +endfacet +facet normal -0.395449 0.000000 0.918488 +outer loop +vertex 16.516666 -5.357624 9.133333 +vertex 17.054991 -3.808129 9.365105 +vertex 16.516666 -3.807135 9.133333 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 -5.358536 9.365105 +vertex 17.735767 -5.358985 9.537500 +vertex 17.735767 -3.808617 9.537500 +endloop +endfacet +facet normal -0.245485 -0.000000 0.969400 +outer loop +vertex 17.054993 -5.358536 9.365105 +vertex 17.735767 -3.808617 9.537500 +vertex 17.054991 -3.808129 9.365105 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 -5.358985 9.537500 +vertex 18.620193 -5.359001 9.616146 +vertex 18.620193 -3.808635 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 -5.358985 9.537500 +vertex 18.620193 -3.808635 9.616146 +vertex 17.735767 -3.808617 9.537500 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 -5.359001 9.616146 +vertex 19.769468 -5.358619 9.566667 +vertex 19.769468 -3.808219 9.566667 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 -5.359001 9.616146 +vertex 19.769468 -3.808219 9.566667 +vertex 18.620193 -3.808635 9.616146 +endloop +endfacet +facet normal 0.043012 0.000000 0.999075 +outer loop +vertex 18.620193 -7.092926 9.616146 +vertex 19.769468 -7.092627 9.566667 +vertex 19.769468 -5.358619 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999075 +outer loop +vertex 18.620193 -7.092926 9.616146 +vertex 19.769468 -5.358619 9.566667 +vertex 18.620193 -5.359001 9.616146 +endloop +endfacet +facet normal 0.043013 0.000000 0.999075 +outer loop +vertex 18.620193 -8.853397 9.616146 +vertex 19.769468 -8.853203 9.566667 +vertex 19.769468 -7.092627 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999074 +outer loop +vertex 18.620193 -8.853397 9.616146 +vertex 19.769468 -7.092627 9.566667 +vertex 18.620193 -7.092926 9.616146 +endloop +endfacet +facet normal 0.043013 0.000000 0.999074 +outer loop +vertex 18.620193 -10.483410 9.616146 +vertex 19.769468 -10.483306 9.566667 +vertex 19.769468 -8.853203 9.566667 +endloop +endfacet +facet normal 0.043014 0.000000 0.999074 +outer loop +vertex 18.620193 -10.483410 9.616146 +vertex 19.769468 -8.853203 9.566667 +vertex 18.620193 -8.853397 9.616146 +endloop +endfacet +facet normal -0.088574 0.000000 0.996070 +outer loop +vertex 17.735767 -10.483404 9.537500 +vertex 18.620193 -10.483410 9.616146 +vertex 18.620193 -8.853397 9.616146 +endloop +endfacet +facet normal -0.088573 0.000000 0.996070 +outer loop +vertex 17.735767 -10.483404 9.537500 +vertex 18.620193 -8.853397 9.616146 +vertex 17.735767 -8.853391 9.537500 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 -10.483283 9.365105 +vertex 17.735767 -10.483404 9.537500 +vertex 17.735767 -8.853391 9.537500 +endloop +endfacet +facet normal -0.245486 0.000000 0.969400 +outer loop +vertex 17.054993 -10.483283 9.365105 +vertex 17.735767 -8.853391 9.537500 +vertex 17.054993 -8.853159 9.365105 +endloop +endfacet +facet normal -0.242171 -0.840189 0.485218 +outer loop +vertex 16.905079 -14.500335 8.978137 +vertex 16.756197 -14.599973 8.731298 +vertex 17.455404 -14.771413 8.783410 +endloop +endfacet +facet normal -0.260777 -0.853498 0.451151 +outer loop +vertex 16.905079 -14.500335 8.978137 +vertex 17.455404 -14.771413 8.783410 +vertex 17.568832 -14.658748 9.062117 +endloop +endfacet +facet normal -0.071003 -0.914397 0.398542 +outer loop +vertex 17.568832 -14.658748 9.062117 +vertex 17.455404 -14.771413 8.783410 +vertex 18.419262 -14.834710 8.809905 +endloop +endfacet +facet normal -0.076539 -0.920191 0.383914 +outer loop +vertex 17.568832 -14.658748 9.062117 +vertex 18.419262 -14.834710 8.809905 +vertex 18.493919 -14.718086 9.104320 +endloop +endfacet +facet normal -0.205023 -0.929081 -0.307853 +outer loop +vertex 16.756197 -14.599973 8.731298 +vertex 16.664705 -14.500335 8.491524 +vertex 17.359531 -14.658748 8.506865 +endloop +endfacet +facet normal -0.204917 -0.929214 -0.307524 +outer loop +vertex 16.756197 -14.599973 8.731298 +vertex 17.359531 -14.658748 8.506865 +vertex 17.455404 -14.771413 8.783410 +endloop +endfacet +facet normal -0.052708 -0.931059 -0.361041 +outer loop +vertex 17.455404 -14.771413 8.783410 +vertex 17.359531 -14.658748 8.506865 +vertex 18.346798 -14.718087 8.515761 +endloop +endfacet +facet normal -0.051432 -0.932644 -0.357113 +outer loop +vertex 17.455404 -14.771413 8.783410 +vertex 18.346798 -14.718087 8.515761 +vertex 18.419262 -14.834710 8.809905 +endloop +endfacet +facet normal -0.414807 -0.381097 0.826257 +outer loop +vertex 16.679167 -14.062502 9.066667 +vertex 16.475306 -14.199250 8.901251 +vertex 16.905079 -14.500335 8.978137 +endloop +endfacet +facet normal -0.409180 -0.378964 0.830035 +outer loop +vertex 16.679167 -14.062502 9.066667 +vertex 16.905079 -14.500335 8.978137 +vertex 17.038143 -14.205942 9.178142 +endloop +endfacet +facet normal -0.221536 -0.477507 0.850241 +outer loop +vertex 17.038143 -14.205942 9.178142 +vertex 16.905079 -14.500335 8.978137 +vertex 17.568832 -14.658748 9.062117 +endloop +endfacet +facet normal -0.243370 -0.498430 0.832069 +outer loop +vertex 17.038143 -14.205942 9.178142 +vertex 17.568832 -14.658748 9.062117 +vertex 17.665802 -14.321471 9.292516 +endloop +endfacet +facet normal -0.073170 -0.548127 0.833189 +outer loop +vertex 17.665802 -14.321471 9.292516 +vertex 17.568832 -14.658748 9.062117 +vertex 18.493919 -14.718086 9.104320 +endloop +endfacet +facet normal -0.081027 -0.560320 0.824303 +outer loop +vertex 17.665802 -14.321471 9.292516 +vertex 18.493919 -14.718086 9.104320 +vertex 18.556152 -14.368310 9.348197 +endloop +endfacet +facet normal 0.019972 -0.574211 0.818464 +outer loop +vertex 18.556152 -14.368310 9.348197 +vertex 18.493919 -14.718086 9.104320 +vertex 19.700632 -14.726565 9.068924 +endloop +endfacet +facet normal 0.026814 -0.559897 0.828128 +outer loop +vertex 18.556152 -14.368310 9.348197 +vertex 19.700632 -14.726565 9.068924 +vertex 19.733356 -14.375003 9.305555 +endloop +endfacet +facet normal 0.001105 -0.929810 0.368039 +outer loop +vertex 18.493919 -14.718086 9.104320 +vertex 18.419262 -14.834710 8.809905 +vertex 19.661137 -14.843751 8.783334 +endloop +endfacet +facet normal 0.004617 -0.925364 0.379051 +outer loop +vertex 18.493919 -14.718086 9.104320 +vertex 19.661137 -14.843751 8.783334 +vertex 19.700632 -14.726565 9.068924 +endloop +endfacet +facet normal -0.011363 -0.930494 -0.366131 +outer loop +vertex 18.419262 -14.834710 8.809905 +vertex 18.346798 -14.718087 8.515761 +vertex 19.621639 -14.726565 8.497743 +endloop +endfacet +facet normal -0.014824 -0.925763 -0.377813 +outer loop +vertex 18.419262 -14.834710 8.809905 +vertex 19.621639 -14.726565 8.497743 +vertex 19.661137 -14.843751 8.783334 +endloop +endfacet +facet normal -0.009894 -0.572629 -0.819755 +outer loop +vertex 18.346798 -14.718087 8.515761 +vertex 18.286760 -14.368312 8.272155 +vertex 19.588913 -14.375001 8.261111 +endloop +endfacet +facet normal -0.015434 -0.559302 -0.828820 +outer loop +vertex 18.346798 -14.718087 8.515761 +vertex 19.588913 -14.375001 8.261111 +vertex 19.621639 -14.726565 8.497743 +endloop +endfacet +facet normal -0.031604 -0.565255 -0.824310 +outer loop +vertex 17.359531 -14.658748 8.506865 +vertex 17.280115 -14.321469 8.278626 +vertex 18.286760 -14.368312 8.272155 +endloop +endfacet +facet normal -0.027153 -0.574440 -0.818096 +outer loop +vertex 17.359531 -14.658748 8.506865 +vertex 18.286760 -14.368312 8.272155 +vertex 18.346798 -14.718087 8.515761 +endloop +endfacet +facet normal -0.120109 -0.561358 -0.818811 +outer loop +vertex 16.664705 -14.500335 8.491524 +vertex 16.597235 -14.205941 8.299591 +vertex 17.280115 -14.321469 8.278626 +endloop +endfacet +facet normal -0.113200 -0.574987 -0.810294 +outer loop +vertex 16.664705 -14.500335 8.491524 +vertex 17.280115 -14.321469 8.278626 +vertex 17.359531 -14.658748 8.506865 +endloop +endfacet +facet normal -0.324005 -0.627125 -0.708333 +outer loop +vertex 16.212664 -14.199250 8.469577 +vertex 16.245834 -14.062501 8.333333 +vertex 16.597235 -14.205941 8.299591 +endloop +endfacet +facet normal -0.341250 -0.566992 -0.749712 +outer loop +vertex 16.212664 -14.199250 8.469577 +vertex 16.597235 -14.205941 8.299591 +vertex 16.664705 -14.500335 8.491524 +endloop +endfacet +facet normal -0.549505 -0.831193 -0.084632 +outer loop +vertex 16.284452 -14.267941 8.678086 +vertex 16.212664 -14.199250 8.469577 +vertex 16.664705 -14.500335 8.491524 +endloop +endfacet +facet normal -0.561493 -0.817890 -0.125621 +outer loop +vertex 16.284452 -14.267941 8.678086 +vertex 16.664705 -14.500335 8.491524 +vertex 16.756197 -14.599973 8.731298 +endloop +endfacet +facet normal -0.499132 -0.610628 0.614818 +outer loop +vertex 16.475306 -14.199250 8.901251 +vertex 16.284452 -14.267941 8.678086 +vertex 16.756197 -14.599973 8.731298 +endloop +endfacet +facet normal -0.536386 -0.619208 0.573473 +outer loop +vertex 16.475306 -14.199250 8.901251 +vertex 16.756197 -14.599973 8.731298 +vertex 16.905079 -14.500335 8.978137 +endloop +endfacet +facet normal 1.000000 -0.000008 -0.000007 +outer loop +vertex 55.000004 -0.141782 3.373485 +vertex 55.000004 0.281395 2.732860 +vertex 55.000008 0.484375 3.003500 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000003 +outer loop +vertex 55.000004 -0.141782 3.373485 +vertex 55.000008 0.484375 3.003500 +vertex 55.000008 0.281395 3.515847 +endloop +endfacet +facet normal 1.000000 0.000008 -0.000006 +outer loop +vertex 55.000004 0.761863 2.168624 +vertex 55.000008 0.868634 2.732860 +vertex 55.000008 0.484375 3.003500 +endloop +endfacet +facet normal 1.000000 -0.000023 -0.000007 +outer loop +vertex 55.000004 0.761863 2.168624 +vertex 55.000008 0.484375 3.003500 +vertex 55.000004 0.281395 2.732860 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 0.767072 3.380429 +vertex 55.000008 0.281395 3.515847 +vertex 55.000008 0.484375 3.003500 +endloop +endfacet +facet normal 1.000000 -0.000009 0.000000 +outer loop +vertex 55.000008 0.767072 3.380429 +vertex 55.000008 0.484375 3.003500 +vertex 55.000008 0.868634 2.732860 +endloop +endfacet +facet normal 1.000000 0.000000 0.000005 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 55.000008 -0.500000 3.024334 +vertex 55.000004 -0.141782 3.373485 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000004 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 55.000004 -0.141782 3.373485 +vertex 55.000008 -0.546875 4.066000 +endloop +endfacet +facet normal 1.000000 0.000010 -0.000003 +outer loop +vertex 55.000008 -0.500000 3.024334 +vertex 55.000004 0.000000 2.357667 +vertex 55.000004 0.281395 2.732860 +endloop +endfacet +facet normal 1.000000 0.000020 0.000004 +outer loop +vertex 55.000008 -0.500000 3.024334 +vertex 55.000004 0.281395 2.732860 +vertex 55.000004 -0.141782 3.373485 +endloop +endfacet +facet normal 1.000000 0.000011 -0.000009 +outer loop +vertex 55.000008 -0.546875 4.066000 +vertex 55.000004 -0.141782 3.373485 +vertex 55.000008 0.281395 3.515847 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 -0.546875 4.066000 +vertex 55.000008 0.281395 3.515847 +vertex 55.000008 0.000000 4.191000 +endloop +endfacet +facet normal 1.000000 0.000008 0.000002 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 55.000008 1.281250 1.628500 +vertex 55.000004 0.761863 2.168624 +endloop +endfacet +facet normal 1.000000 -0.000009 0.000005 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 55.000004 0.761863 2.168624 +vertex 55.000008 0.500000 1.691000 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 1.281250 1.628500 +vertex 55.000008 1.375000 2.357667 +vertex 55.000008 0.868634 2.732860 +endloop +endfacet +facet normal 1.000000 -0.000011 -0.000004 +outer loop +vertex 55.000008 1.281250 1.628500 +vertex 55.000008 0.868634 2.732860 +vertex 55.000004 0.761863 2.168624 +endloop +endfacet +facet normal 1.000000 0.000000 0.000005 +outer loop +vertex 55.000008 0.500000 1.691000 +vertex 55.000004 0.761863 2.168624 +vertex 55.000004 0.281395 2.732860 +endloop +endfacet +facet normal 1.000000 0.000000 0.000003 +outer loop +vertex 55.000008 0.500000 1.691000 +vertex 55.000004 0.281395 2.732860 +vertex 55.000004 0.000000 2.357667 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000007 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 55.000008 0.546875 4.066000 +vertex 55.000008 0.767072 3.380429 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000017 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 55.000008 0.767072 3.380429 +vertex 55.000008 1.281250 3.086834 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 0.546875 4.066000 +vertex 55.000008 0.000000 4.191000 +vertex 55.000008 0.281395 3.515847 +endloop +endfacet +facet normal 1.000000 -0.000003 0.000000 +outer loop +vertex 55.000008 0.546875 4.066000 +vertex 55.000008 0.281395 3.515847 +vertex 55.000008 0.767072 3.380429 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 1.281250 3.086834 +vertex 55.000008 0.767072 3.380429 +vertex 55.000008 0.868634 2.732860 +endloop +endfacet +facet normal 1.000000 -0.000011 0.000000 +outer loop +vertex 55.000008 1.281250 3.086834 +vertex 55.000008 0.868634 2.732860 +vertex 55.000008 1.375000 2.357667 +endloop +endfacet +facet normal -0.185267 -0.973818 0.131736 +outer loop +vertex 1.539941 -2.191099 4.267486 +vertex 1.943580 -2.370726 3.507300 +vertex 2.261111 -2.373071 3.936525 +endloop +endfacet +facet normal -0.161360 -0.970018 0.181739 +outer loop +vertex 1.539941 -2.191099 4.267486 +vertex 2.261111 -2.373071 3.936525 +vertex 2.133636 -2.233862 4.566360 +endloop +endfacet +facet normal -0.052033 -0.998532 -0.015070 +outer loop +vertex 2.649281 -2.376406 2.817216 +vertex 2.909109 -2.402924 3.677179 +vertex 2.261111 -2.373071 3.936525 +endloop +endfacet +facet normal -0.007770 -0.999970 0.000285 +outer loop +vertex 2.649281 -2.376406 2.817216 +vertex 2.261111 -2.373071 3.936525 +vertex 1.943580 -2.370726 3.507300 +endloop +endfacet +facet normal -0.036345 -0.977314 0.208654 +outer loop +vertex 3.071936 -2.259743 4.608581 +vertex 2.133636 -2.233862 4.566360 +vertex 2.261111 -2.373071 3.936525 +endloop +endfacet +facet normal 0.014282 -0.988661 0.149487 +outer loop +vertex 3.071936 -2.259743 4.608581 +vertex 2.261111 -2.373071 3.936525 +vertex 2.909109 -2.402924 3.677179 +endloop +endfacet +facet normal -0.516100 -0.776904 0.360639 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 1.143440 -2.154511 3.778884 +vertex 1.539941 -2.191099 4.267486 +endloop +endfacet +facet normal -0.524418 -0.778073 0.345815 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 1.539941 -2.191099 4.267486 +vertex 1.394066 -1.776039 4.980145 +endloop +endfacet +facet normal -0.230695 -0.968710 0.091543 +outer loop +vertex 1.143440 -2.154511 3.778884 +vertex 1.609877 -2.343493 2.954529 +vertex 1.943580 -2.370726 3.507300 +endloop +endfacet +facet normal -0.224445 -0.968300 0.109629 +outer loop +vertex 1.143440 -2.154511 3.778884 +vertex 1.943580 -2.370726 3.507300 +vertex 1.539941 -2.191099 4.267486 +endloop +endfacet +facet normal -0.282420 -0.852964 0.438966 +outer loop +vertex 1.394066 -1.776039 4.980145 +vertex 1.539941 -2.191099 4.267486 +vertex 2.133636 -2.233862 4.566360 +endloop +endfacet +facet normal -0.282277 -0.852910 0.439164 +outer loop +vertex 1.394066 -1.776039 4.980145 +vertex 2.133636 -2.233862 4.566360 +vertex 2.083642 -1.867799 5.245167 +endloop +endfacet +facet normal -0.129097 -0.929282 -0.346077 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 3.804913 -2.318981 2.231933 +vertex 2.649281 -2.376406 2.817216 +endloop +endfacet +facet normal 0.197745 -0.949578 -0.243307 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 2.649281 -2.376406 2.817216 +vertex 2.251229 -2.276334 2.103142 +endloop +endfacet +facet normal -0.053123 -0.994461 -0.090688 +outer loop +vertex 3.804913 -2.318981 2.231933 +vertex 4.081018 -2.428884 3.275362 +vertex 2.909109 -2.402924 3.677179 +endloop +endfacet +facet normal 0.029515 -0.998775 -0.039717 +outer loop +vertex 3.804913 -2.318981 2.231933 +vertex 2.909109 -2.402924 3.677179 +vertex 2.649281 -2.376406 2.817216 +endloop +endfacet +facet normal -0.093136 -0.991838 -0.087081 +outer loop +vertex 2.251229 -2.276334 2.103142 +vertex 2.649281 -2.376406 2.817216 +vertex 1.943580 -2.370726 3.507300 +endloop +endfacet +facet normal 0.021777 -0.997820 -0.062305 +outer loop +vertex 2.251229 -2.276334 2.103142 +vertex 1.943580 -2.370726 3.507300 +vertex 1.609877 -2.343493 2.954529 +endloop +endfacet +facet normal -0.041653 -0.900109 0.433670 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 3.171496 -1.861331 5.445072 +vertex 3.071936 -2.259743 4.608581 +endloop +endfacet +facet normal 0.014831 -0.942051 0.335141 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 3.071936 -2.259743 4.608581 +vertex 4.454588 -2.322237 4.371733 +endloop +endfacet +facet normal -0.080858 -0.879766 0.468481 +outer loop +vertex 3.171496 -1.861331 5.445072 +vertex 2.083642 -1.867799 5.245167 +vertex 2.133636 -2.233862 4.566360 +endloop +endfacet +facet normal -0.044344 -0.899879 0.433880 +outer loop +vertex 3.171496 -1.861331 5.445072 +vertex 2.133636 -2.233862 4.566360 +vertex 3.071936 -2.259743 4.608581 +endloop +endfacet +facet normal -0.018092 -0.987748 0.155005 +outer loop +vertex 4.454588 -2.322237 4.371733 +vertex 3.071936 -2.259743 4.608581 +vertex 2.909109 -2.402924 3.677179 +endloop +endfacet +facet normal 0.009985 -0.995575 0.093441 +outer loop +vertex 4.454588 -2.322237 4.371733 +vertex 2.909109 -2.402924 3.677179 +vertex 4.081018 -2.428884 3.275362 +endloop +endfacet +facet normal -0.993309 0.019026 0.113909 +outer loop +vertex 0.194632 0.223443 3.953737 +vertex 0.108384 -0.402336 3.306158 +vertex 0.138021 -0.720486 3.617737 +endloop +endfacet +facet normal -0.991586 0.013646 0.128731 +outer loop +vertex 0.194632 0.223443 3.953737 +vertex 0.138021 -0.720486 3.617737 +vertex 0.211878 -0.430331 4.155883 +endloop +endfacet +facet normal -0.991095 -0.132667 0.011403 +outer loop +vertex 0.171079 -1.043817 2.729306 +vertex 0.204478 -1.241022 3.337755 +vertex 0.138021 -0.720486 3.617737 +endloop +endfacet +facet normal -0.995464 -0.095109 -0.002428 +outer loop +vertex 0.171079 -1.043817 2.729306 +vertex 0.138021 -0.720486 3.617737 +vertex 0.108384 -0.402336 3.306158 +endloop +endfacet +facet normal -0.959496 -0.171004 0.223886 +outer loop +vertex 0.314128 -1.133957 4.056663 +vertex 0.211878 -0.430331 4.155883 +vertex 0.138021 -0.720486 3.617737 +endloop +endfacet +facet normal -0.959260 -0.218682 0.178878 +outer loop +vertex 0.314128 -1.133957 4.056663 +vertex 0.138021 -0.720486 3.617737 +vertex 0.204478 -1.241022 3.337755 +endloop +endfacet +facet normal -0.934846 0.235015 0.266140 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.208924 0.785330 3.507765 +vertex 0.194632 0.223443 3.953737 +endloop +endfacet +facet normal -0.956068 0.248838 0.154963 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.194632 0.223443 3.953737 +vertex 0.439913 0.780122 4.573130 +endloop +endfacet +facet normal -0.994651 0.071555 0.074493 +outer loop +vertex 0.208924 0.785330 3.507765 +vertex 0.106944 0.041667 2.860445 +vertex 0.108384 -0.402336 3.306158 +endloop +endfacet +facet normal -0.995389 0.073877 0.061180 +outer loop +vertex 0.208924 0.785330 3.507765 +vertex 0.108384 -0.402336 3.306158 +vertex 0.194632 0.223443 3.953737 +endloop +endfacet +facet normal -0.947655 0.071239 0.311248 +outer loop +vertex 0.439913 0.780122 4.573130 +vertex 0.194632 0.223443 3.953737 +vertex 0.211878 -0.430331 4.155883 +endloop +endfacet +facet normal -0.959548 0.088655 0.267224 +outer loop +vertex 0.439913 0.780122 4.573130 +vertex 0.211878 -0.430331 4.155883 +vertex 0.423611 0.000000 4.773408 +endloop +endfacet +facet normal -0.963034 -0.214320 -0.163196 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.380729 -1.598091 2.220051 +vertex 0.171079 -1.043817 2.729306 +endloop +endfacet +facet normal -0.963975 -0.209317 -0.164130 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.171079 -1.043817 2.729306 +vertex 0.171354 -0.640046 2.212759 +endloop +endfacet +facet normal -0.948706 -0.312201 -0.049864 +outer loop +vertex 0.380729 -1.598091 2.220051 +vertex 0.406945 -1.789352 2.918778 +vertex 0.204478 -1.241022 3.337755 +endloop +endfacet +facet normal -0.948377 -0.313261 -0.049474 +outer loop +vertex 0.380729 -1.598091 2.220051 +vertex 0.204478 -1.241022 3.337755 +vertex 0.171079 -1.043817 2.729306 +endloop +endfacet +facet normal -0.997359 -0.056961 -0.045056 +outer loop +vertex 0.171354 -0.640046 2.212759 +vertex 0.171079 -1.043817 2.729306 +vertex 0.108384 -0.402336 3.306158 +endloop +endfacet +facet normal -0.997662 -0.050000 -0.046586 +outer loop +vertex 0.171354 -0.640046 2.212759 +vertex 0.108384 -0.402336 3.306158 +vertex 0.106944 0.041667 2.860445 +endloop +endfacet +facet normal -0.800291 -0.397577 0.448850 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 0.537691 -0.780122 4.768686 +vertex 0.314128 -1.133957 4.056663 +endloop +endfacet +facet normal -0.791815 -0.482389 0.374606 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 0.314128 -1.133957 4.056663 +vertex 0.535608 -1.766233 3.710613 +endloop +endfacet +facet normal -0.904731 -0.134748 0.404110 +outer loop +vertex 0.537691 -0.780122 4.768686 +vertex 0.423611 0.000000 4.773408 +vertex 0.211878 -0.430331 4.155883 +endloop +endfacet +facet normal -0.907576 -0.185039 0.376917 +outer loop +vertex 0.537691 -0.780122 4.768686 +vertex 0.211878 -0.430331 4.155883 +vertex 0.314128 -1.133957 4.056663 +endloop +endfacet +facet normal -0.886417 -0.418619 0.197544 +outer loop +vertex 0.535608 -1.766233 3.710613 +vertex 0.314128 -1.133957 4.056663 +vertex 0.204478 -1.241022 3.337755 +endloop +endfacet +facet normal -0.881790 -0.445001 0.156272 +outer loop +vertex 0.535608 -1.766233 3.710613 +vertex 0.204478 -1.241022 3.337755 +vertex 0.406945 -1.789352 2.918778 +endloop +endfacet +facet normal 0.181319 0.966822 0.179941 +outer loop +vertex 52.671993 1.719298 3.280712 +vertex 51.794785 1.992473 2.696870 +vertex 51.186733 2.056906 2.963377 +endloop +endfacet +facet normal 0.157565 0.949203 0.272374 +outer loop +vertex 52.671993 1.719298 3.280712 +vertex 51.186733 2.056906 2.963377 +vertex 51.447769 1.881940 3.422114 +endloop +endfacet +facet normal 0.115521 0.993281 0.006967 +outer loop +vertex 50.440521 2.149413 2.147905 +vertex 49.957466 2.201473 2.735353 +vertex 51.186733 2.056906 2.963377 +endloop +endfacet +facet normal 0.110433 0.993815 0.011685 +outer loop +vertex 50.440521 2.149413 2.147905 +vertex 51.186733 2.056906 2.963377 +vertex 51.794785 1.992473 2.696870 +endloop +endfacet +facet normal 0.088219 0.946386 0.310758 +outer loop +vertex 49.641869 2.055952 3.404844 +vertex 51.447769 1.881940 3.422114 +vertex 51.186733 2.056906 2.963377 +endloop +endfacet +facet normal 0.068771 0.967648 0.242749 +outer loop +vertex 49.641869 2.055952 3.404844 +vertex 51.186733 2.056906 2.963377 +vertex 49.957466 2.201473 2.735353 +endloop +endfacet +facet normal 0.231328 0.923403 0.306290 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 53.526051 1.599617 2.996491 +vertex 52.671993 1.719298 3.280712 +endloop +endfacet +facet normal 0.150699 0.810556 0.565941 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 52.671993 1.719298 3.280712 +vertex 53.066593 1.283501 3.799797 +endloop +endfacet +facet normal 0.208283 0.975178 0.075135 +outer loop +vertex 53.526051 1.599617 2.996491 +vertex 52.409729 1.888375 2.343264 +vertex 51.794785 1.992473 2.696870 +endloop +endfacet +facet normal 0.190815 0.967490 0.165988 +outer loop +vertex 53.526051 1.599617 2.996491 +vertex 51.794785 1.992473 2.696870 +vertex 52.671993 1.719298 3.280712 +endloop +endfacet +facet normal 0.172183 0.814978 0.553322 +outer loop +vertex 53.066593 1.283501 3.799797 +vertex 52.671993 1.719298 3.280712 +vertex 51.447769 1.881940 3.422114 +endloop +endfacet +facet normal 0.154133 0.790788 0.592366 +outer loop +vertex 53.066593 1.283501 3.799797 +vertex 51.447769 1.881940 3.422114 +vertex 51.558132 1.499486 3.903964 +endloop +endfacet +facet normal 0.127358 0.916657 -0.378840 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 48.242996 2.261178 1.679576 +vertex 50.440521 2.149413 2.147905 +endloop +endfacet +facet normal 0.017056 0.955880 -0.293263 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 50.440521 2.149413 2.147905 +vertex 51.159924 1.990409 1.671474 +endloop +endfacet +facet normal 0.089429 0.991998 -0.089126 +outer loop +vertex 48.242996 2.261178 1.679576 +vertex 47.717857 2.373972 2.408079 +vertex 49.957466 2.201473 2.735353 +endloop +endfacet +facet normal 0.059195 0.997456 -0.039721 +outer loop +vertex 48.242996 2.261178 1.679576 +vertex 49.957466 2.201473 2.735353 +vertex 50.440521 2.149413 2.147905 +endloop +endfacet +facet normal 0.153223 0.983435 -0.096846 +outer loop +vertex 51.159924 1.990409 1.671474 +vertex 50.440521 2.149413 2.147905 +vertex 51.794785 1.992473 2.696870 +endloop +endfacet +facet normal 0.122667 0.989383 -0.077939 +outer loop +vertex 51.159924 1.990409 1.671474 +vertex 51.794785 1.992473 2.696870 +vertex 52.409729 1.888375 2.343264 +endloop +endfacet +facet normal 0.074646 0.818859 0.569120 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 49.445156 1.646948 4.019125 +vertex 49.641869 2.055952 3.404844 +endloop +endfacet +facet normal 0.042905 0.894888 0.444223 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 49.641869 2.055952 3.404844 +vertex 46.986958 2.274199 3.221608 +endloop +endfacet +facet normal 0.088201 0.789948 0.606797 +outer loop +vertex 49.445156 1.646948 4.019125 +vertex 51.558132 1.499486 3.903964 +vertex 51.447769 1.881940 3.422114 +endloop +endfacet +facet normal 0.073486 0.819108 0.568913 +outer loop +vertex 49.445156 1.646948 4.019125 +vertex 51.447769 1.881940 3.422114 +vertex 49.641869 2.055952 3.404844 +endloop +endfacet +facet normal 0.063046 0.968657 0.240268 +outer loop +vertex 46.986958 2.274199 3.221608 +vertex 49.641869 2.055952 3.404844 +vertex 49.957466 2.201473 2.735353 +endloop +endfacet +facet normal 0.051438 0.984619 0.166970 +outer loop +vertex 46.986958 2.274199 3.221608 +vertex 49.957466 2.201473 2.735353 +vertex 47.717857 2.373972 2.408079 +endloop +endfacet +facet normal 0.030800 -0.006309 -0.999506 +outer loop +vertex 2.637526 -0.290024 0.511322 +vertex 1.918631 0.381511 0.484930 +vertex 2.191551 0.712577 0.491251 +endloop +endfacet +facet normal -0.028449 -0.032655 -0.999062 +outer loop +vertex 2.637526 -0.290024 0.511322 +vertex 2.191551 0.712577 0.491251 +vertex 2.830451 0.411186 0.482909 +endloop +endfacet +facet normal -0.094545 0.195170 -0.976202 +outer loop +vertex 1.460494 1.042770 0.628068 +vertex 1.971341 1.252340 0.620492 +vertex 2.191551 0.712577 0.491251 +endloop +endfacet +facet normal -0.128002 0.124306 -0.983953 +outer loop +vertex 1.460494 1.042770 0.628068 +vertex 2.191551 0.712577 0.491251 +vertex 1.918631 0.381511 0.484930 +endloop +endfacet +facet normal 0.076297 0.188834 -0.979041 +outer loop +vertex 2.813697 1.169283 0.627823 +vertex 2.830451 0.411186 0.482909 +vertex 2.191551 0.712577 0.491251 +endloop +endfacet +facet normal 0.032619 0.245301 -0.968898 +outer loop +vertex 2.813697 1.169283 0.627823 +vertex 2.191551 0.712577 0.491251 +vertex 1.971341 1.252340 0.620492 +endloop +endfacet +facet normal 0.211938 -0.298412 -0.930609 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 2.250708 -0.876450 0.611273 +vertex 2.637526 -0.290024 0.511322 +endloop +endfacet +facet normal -0.140373 -0.416817 -0.898086 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 2.637526 -0.290024 0.511322 +vertex 3.794400 -0.906173 0.616465 +endloop +endfacet +facet normal 0.053562 -0.085780 -0.994873 +outer loop +vertex 2.250708 -0.876450 0.611273 +vertex 1.605710 -0.068030 0.506844 +vertex 1.918631 0.381511 0.484930 +endloop +endfacet +facet normal -0.075041 -0.119242 -0.990025 +outer loop +vertex 2.250708 -0.876450 0.611273 +vertex 1.918631 0.381511 0.484930 +vertex 2.637526 -0.290024 0.511322 +endloop +endfacet +facet normal 0.060239 -0.056955 -0.996558 +outer loop +vertex 3.794400 -0.906173 0.616465 +vertex 2.637526 -0.290024 0.511322 +vertex 2.830451 0.411186 0.482909 +endloop +endfacet +facet normal -0.039881 -0.129627 -0.990760 +outer loop +vertex 3.794400 -0.906173 0.616465 +vertex 2.830451 0.411186 0.482909 +vertex 3.996914 -0.050026 0.496299 +endloop +endfacet +facet normal -0.401257 0.315867 -0.859779 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 1.136574 1.600775 0.984242 +vertex 1.460494 1.042770 0.628068 +endloop +endfacet +facet normal -0.403919 0.308189 -0.861318 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 1.460494 1.042770 0.628068 +vertex 1.126061 0.636751 0.639624 +endloop +endfacet +facet normal -0.190824 0.452324 -0.871200 +outer loop +vertex 1.136574 1.600775 0.984242 +vertex 1.689815 1.810828 0.972122 +vertex 1.971341 1.252340 0.620492 +endloop +endfacet +facet normal -0.195352 0.444584 -0.874175 +outer loop +vertex 1.136574 1.600775 0.984242 +vertex 1.971341 1.252340 0.620492 +vertex 1.460494 1.042770 0.628068 +endloop +endfacet +facet normal -0.158596 0.102685 -0.981989 +outer loop +vertex 1.126061 0.636751 0.639624 +vertex 1.460494 1.042770 0.628068 +vertex 1.918631 0.381511 0.484930 +endloop +endfacet +facet normal -0.169346 0.069959 -0.983071 +outer loop +vertex 1.126061 0.636751 0.639624 +vertex 1.918631 0.381511 0.484930 +vertex 1.605710 -0.068030 0.506844 +endloop +endfacet +facet normal 0.109645 0.460478 -0.880873 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 4.148535 0.817264 0.609955 +vertex 2.813697 1.169283 0.627823 +endloop +endfacet +facet normal 0.067900 0.529447 -0.845621 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 2.813697 1.169283 0.627823 +vertex 2.556761 1.841754 1.028229 +endloop +endfacet +facet normal 0.058674 0.119622 -0.991084 +outer loop +vertex 4.148535 0.817264 0.609955 +vertex 3.996914 -0.050026 0.496299 +vertex 2.830451 0.411186 0.482909 +endloop +endfacet +facet normal 0.036550 0.188409 -0.981410 +outer loop +vertex 4.148535 0.817264 0.609955 +vertex 2.830451 0.411186 0.482909 +vertex 2.813697 1.169283 0.627823 +endloop +endfacet +facet normal 0.059374 0.527350 -0.847571 +outer loop +vertex 2.556761 1.841754 1.028229 +vertex 2.813697 1.169283 0.627823 +vertex 1.971341 1.252340 0.620492 +endloop +endfacet +facet normal 0.034776 0.544975 -0.837731 +outer loop +vertex 2.556761 1.841754 1.028229 +vertex 1.971341 1.252340 0.620492 +vertex 1.689815 1.810828 0.972122 +endloop +endfacet +facet normal 0.054321 -0.000385 0.998523 +outer loop +vertex 50.247066 -0.515539 4.374654 +vertex 51.737465 0.086181 4.293806 +vertex 51.157322 0.422284 4.325496 +endloop +endfacet +facet normal 0.099741 -0.044705 0.994009 +outer loop +vertex 50.247066 -0.515539 4.374654 +vertex 51.157322 0.422284 4.325496 +vertex 49.893200 0.184938 4.441665 +endloop +endfacet +facet normal 0.091326 0.179932 0.979430 +outer loop +vertex 52.664707 0.710669 4.131961 +vertex 51.443138 0.940804 4.203588 +vertex 51.157322 0.422284 4.325496 +endloop +endfacet +facet normal 0.108925 0.094716 0.989527 +outer loop +vertex 52.664707 0.710669 4.131961 +vertex 51.157322 0.422284 4.325496 +vertex 51.737465 0.086181 4.293806 +endloop +endfacet +facet normal 0.064417 0.140482 0.987985 +outer loop +vertex 49.633720 0.952742 4.349410 +vertex 49.893200 0.184938 4.441665 +vertex 51.157322 0.422284 4.325496 +endloop +endfacet +facet normal 0.080147 0.186057 0.979265 +outer loop +vertex 49.633720 0.952742 4.349410 +vertex 51.157322 0.422284 4.325496 +vertex 51.443138 0.940804 4.203588 +endloop +endfacet +facet normal -0.018545 -0.272988 0.961839 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 50.865345 -1.042833 4.236919 +vertex 50.247066 -0.515539 4.374654 +endloop +endfacet +facet normal 0.176459 -0.509150 0.842394 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 50.247066 -0.515539 4.374654 +vertex 47.781036 -1.082973 4.548262 +endloop +endfacet +facet normal 0.012548 -0.059983 0.998121 +outer loop +vertex 50.865345 -1.042833 4.236919 +vertex 52.331310 -0.308579 4.262618 +vertex 51.737465 0.086181 4.293806 +endloop +endfacet +facet normal 0.106815 -0.132165 0.985456 +outer loop +vertex 50.865345 -1.042833 4.236919 +vertex 51.737465 0.086181 4.293806 +vertex 50.247066 -0.515539 4.374654 +endloop +endfacet +facet normal 0.082388 -0.053580 0.995159 +outer loop +vertex 47.781036 -1.082973 4.548262 +vertex 50.247066 -0.515539 4.374654 +vertex 49.893200 0.184938 4.441665 +endloop +endfacet +facet normal 0.137683 -0.147011 0.979506 +outer loop +vertex 47.781036 -1.082973 4.548262 +vertex 49.893200 0.184938 4.441665 +vertex 47.583881 -0.209556 4.707063 +endloop +endfacet +facet normal 0.114295 0.437116 0.892114 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 53.066593 1.283501 3.799797 +vertex 52.664707 0.710669 4.131961 +endloop +endfacet +facet normal 0.137123 0.321287 0.937002 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 52.664707 0.710669 4.131961 +vertex 53.516247 0.366665 4.125300 +endloop +endfacet +facet normal 0.125353 0.448738 0.884828 +outer loop +vertex 53.066593 1.283501 3.799797 +vertex 51.558132 1.499486 3.903964 +vertex 51.443138 0.940804 4.203588 +endloop +endfacet +facet normal 0.132709 0.425872 0.894998 +outer loop +vertex 53.066593 1.283501 3.799797 +vertex 51.443138 0.940804 4.203588 +vertex 52.664707 0.710669 4.131961 +endloop +endfacet +facet normal 0.069339 0.152545 0.985861 +outer loop +vertex 53.516247 0.366665 4.125300 +vertex 52.664707 0.710669 4.131961 +vertex 51.737465 0.086181 4.293806 +endloop +endfacet +facet normal 0.086202 0.051068 0.994968 +outer loop +vertex 53.516247 0.366665 4.125300 +vertex 51.737465 0.086181 4.293806 +vertex 52.331310 -0.308579 4.262618 +endloop +endfacet +facet normal 0.076994 0.391550 0.916930 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 46.970215 0.694892 4.683170 +vertex 49.633720 0.952742 4.349410 +endloop +endfacet +facet normal 0.087657 0.447287 0.890085 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 49.633720 0.952742 4.349410 +vertex 49.445156 1.646948 4.019125 +endloop +endfacet +facet normal 0.098039 0.092693 0.990856 +outer loop +vertex 46.970215 0.694892 4.683170 +vertex 47.583881 -0.209556 4.707063 +vertex 49.893200 0.184938 4.441665 +endloop +endfacet +facet normal 0.108100 0.154533 0.982056 +outer loop +vertex 46.970215 0.694892 4.683170 +vertex 49.893200 0.184938 4.441665 +vertex 49.633720 0.952742 4.349410 +endloop +endfacet +facet normal 0.074857 0.444928 0.892432 +outer loop +vertex 49.445156 1.646948 4.019125 +vertex 49.633720 0.952742 4.349410 +vertex 51.443138 0.940804 4.203588 +endloop +endfacet +facet normal 0.080225 0.458227 0.885207 +outer loop +vertex 49.445156 1.646948 4.019125 +vertex 51.443138 0.940804 4.203588 +vertex 51.558132 1.499486 3.903964 +endloop +endfacet +facet normal -0.485056 -0.030205 0.873961 +outer loop +vertex 0.883993 -0.216975 5.304646 +vertex 1.361070 0.453238 5.592591 +vertex 1.118056 0.746528 5.467853 +endloop +endfacet +facet normal -0.533801 -0.013542 0.845501 +outer loop +vertex 0.883993 -0.216975 5.304646 +vertex 1.118056 0.746528 5.467853 +vertex 0.761446 0.434430 5.237710 +endloop +endfacet +facet normal -0.291023 0.241946 0.925618 +outer loop +vertex 1.958024 1.200380 5.613314 +vertex 1.345271 1.293371 5.396353 +vertex 1.118056 0.746528 5.467853 +endloop +endfacet +facet normal -0.263567 0.184324 0.946867 +outer loop +vertex 1.958024 1.200380 5.613314 +vertex 1.118056 0.746528 5.467853 +vertex 1.361070 0.453238 5.592591 +endloop +endfacet +facet normal -0.665781 0.239685 0.706602 +outer loop +vertex 0.832530 1.140606 5.065147 +vertex 0.761446 0.434430 5.237710 +vertex 1.118056 0.746528 5.467853 +endloop +endfacet +facet normal -0.579649 0.337804 0.741549 +outer loop +vertex 0.832530 1.140606 5.065147 +vertex 1.118056 0.746528 5.467853 +vertex 1.345271 1.293371 5.396353 +endloop +endfacet +facet normal -0.728589 -0.294154 0.618572 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 1.153953 -0.776505 5.356543 +vertex 0.883993 -0.216975 5.304646 +endloop +endfacet +facet normal -0.559611 -0.364320 0.744383 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 0.883993 -0.216975 5.304646 +vertex 0.537691 -0.780122 4.768686 +endloop +endfacet +facet normal -0.428798 -0.100120 0.897835 +outer loop +vertex 1.153953 -0.776505 5.356543 +vertex 1.693982 0.028935 5.704272 +vertex 1.361070 0.453238 5.592591 +endloop +endfacet +facet normal -0.398405 -0.107739 0.910860 +outer loop +vertex 1.153953 -0.776505 5.356543 +vertex 1.361070 0.453238 5.592591 +vertex 0.883993 -0.216975 5.304646 +endloop +endfacet +facet normal -0.792535 -0.087077 0.603578 +outer loop +vertex 0.537691 -0.780122 4.768686 +vertex 0.883993 -0.216975 5.304646 +vertex 0.761446 0.434430 5.237710 +endloop +endfacet +facet normal -0.749797 -0.113591 0.651845 +outer loop +vertex 0.537691 -0.780122 4.768686 +vertex 0.761446 0.434430 5.237710 +vertex 0.423611 0.000000 4.773408 +endloop +endfacet +facet normal -0.108158 0.511312 0.852562 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 2.553637 1.874253 5.284729 +vertex 1.958024 1.200380 5.613314 +endloop +endfacet +facet normal -0.072594 0.396995 0.914945 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 1.958024 1.200380 5.613314 +vertex 2.557282 0.880763 5.799543 +endloop +endfacet +facet normal -0.194633 0.559673 0.805533 +outer loop +vertex 2.553637 1.874253 5.284729 +vertex 1.664815 1.871528 5.071865 +vertex 1.345271 1.293371 5.396353 +endloop +endfacet +facet normal -0.197768 0.565292 0.800832 +outer loop +vertex 2.553637 1.874253 5.284729 +vertex 1.345271 1.293371 5.396353 +vertex 1.958024 1.200380 5.613314 +endloop +endfacet +facet normal -0.220066 0.149091 0.964024 +outer loop +vertex 2.557282 0.880763 5.799543 +vertex 1.958024 1.200380 5.613314 +vertex 1.361070 0.453238 5.592591 +endloop +endfacet +facet normal -0.203227 0.096995 0.974316 +outer loop +vertex 2.557282 0.880763 5.799543 +vertex 1.361070 0.453238 5.592591 +vertex 1.693982 0.028935 5.704272 +endloop +endfacet +facet normal -0.838045 0.384748 0.386846 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.439913 0.780122 4.573130 +vertex 0.832530 1.140606 5.065147 +endloop +endfacet +facet normal -0.759342 0.501413 0.414710 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.832530 1.140606 5.065147 +vertex 1.052529 1.776505 4.699124 +endloop +endfacet +facet normal -0.859391 0.143921 0.490647 +outer loop +vertex 0.439913 0.780122 4.573130 +vertex 0.423611 0.000000 4.773408 +vertex 0.761446 0.434430 5.237710 +endloop +endfacet +facet normal -0.833280 0.208954 0.511842 +outer loop +vertex 0.439913 0.780122 4.573130 +vertex 0.761446 0.434430 5.237710 +vertex 0.832530 1.140606 5.065147 +endloop +endfacet +facet normal -0.562590 0.549949 0.617291 +outer loop +vertex 1.052529 1.776505 4.699124 +vertex 0.832530 1.140606 5.065147 +vertex 1.345271 1.293371 5.396353 +endloop +endfacet +facet normal -0.477260 0.615689 0.627017 +outer loop +vertex 1.052529 1.776505 4.699124 +vertex 1.345271 1.293371 5.396353 +vertex 1.664815 1.871528 5.071865 +endloop +endfacet +facet normal 0.021991 -0.005668 -0.999742 +outer loop +vertex 12.125395 -0.422749 0.552052 +vertex 10.102764 0.354268 0.503155 +vertex 10.985365 0.736143 0.520405 +endloop +endfacet +facet normal -0.005850 -0.033048 -0.999437 +outer loop +vertex 12.125395 -0.422749 0.552052 +vertex 10.985365 0.736143 0.520405 +vertex 12.626919 0.410041 0.521579 +endloop +endfacet +facet normal -0.012352 0.221554 -0.975070 +outer loop +vertex 8.320804 1.187877 0.656801 +vertex 10.215493 1.381170 0.676720 +vertex 10.985365 0.736143 0.520405 +endloop +endfacet +facet normal -0.031048 0.116599 -0.992694 +outer loop +vertex 8.320804 1.187877 0.656801 +vertex 10.985365 0.736143 0.520405 +vertex 10.102764 0.354268 0.503155 +endloop +endfacet +facet normal 0.043132 0.213605 -0.975968 +outer loop +vertex 12.492224 1.328601 0.716666 +vertex 12.626919 0.410041 0.521579 +vertex 10.985365 0.736143 0.520405 +endloop +endfacet +facet normal 0.022964 0.261264 -0.964994 +outer loop +vertex 12.492224 1.328601 0.716666 +vertex 10.985365 0.736143 0.520405 +vertex 10.215493 1.381170 0.676720 +endloop +endfacet +facet normal 0.112351 -0.356707 -0.927436 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 11.022995 -1.088743 0.674657 +vertex 12.125395 -0.422749 0.552052 +endloop +endfacet +facet normal -0.085839 -0.463142 -0.882117 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 12.125395 -0.422749 0.552052 +vertex 14.364196 -1.103987 0.691866 +endloop +endfacet +facet normal 0.032697 -0.097371 -0.994711 +outer loop +vertex 11.022995 -1.088743 0.674657 +vertex 8.954733 -0.142490 0.514045 +vertex 10.102764 0.354268 0.503155 +endloop +endfacet +facet normal -0.028179 -0.135674 -0.990353 +outer loop +vertex 11.022995 -1.088743 0.674657 +vertex 10.102764 0.354268 0.503155 +vertex 12.125395 -0.422749 0.552052 +endloop +endfacet +facet normal 0.043243 -0.062528 -0.997106 +outer loop +vertex 14.364196 -1.103987 0.691866 +vertex 12.125395 -0.422749 0.552052 +vertex 12.626919 0.410041 0.521579 +endloop +endfacet +facet normal -0.019751 -0.134098 -0.990771 +outer loop +vertex 14.364196 -1.103987 0.691866 +vertex 12.626919 0.410041 0.521579 +vertex 14.778099 -0.081578 0.545235 +endloop +endfacet +facet normal -0.032263 0.478369 -0.877566 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 6.835030 1.918170 1.109513 +vertex 8.320804 1.187877 0.656801 +endloop +endfacet +facet normal -0.062221 0.329100 -0.942243 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 8.320804 1.187877 0.656801 +vertex 6.811232 0.793336 0.618684 +endloop +endfacet +facet normal -0.027146 0.518106 -0.854885 +outer loop +vertex 6.835030 1.918170 1.109513 +vertex 9.145125 2.041365 1.110822 +vertex 10.215493 1.381170 0.676720 +endloop +endfacet +facet normal -0.038536 0.468681 -0.882526 +outer loop +vertex 6.835030 1.918170 1.109513 +vertex 10.215493 1.381170 0.676720 +vertex 8.320804 1.187877 0.656801 +endloop +endfacet +facet normal -0.014535 0.151107 -0.988411 +outer loop +vertex 6.811232 0.793336 0.618684 +vertex 8.320804 1.187877 0.656801 +vertex 10.102764 0.354268 0.503155 +endloop +endfacet +facet normal -0.029022 0.045179 -0.998557 +outer loop +vertex 6.811232 0.793336 0.618684 +vertex 10.102764 0.354268 0.503155 +vertex 8.954733 -0.142490 0.514045 +endloop +endfacet +facet normal 0.067961 0.508823 -0.858184 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 15.017767 0.994756 0.718727 +vertex 12.492224 1.328601 0.716666 +endloop +endfacet +facet normal 0.040768 0.564627 -0.824339 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 12.492224 1.328601 0.716666 +vertex 11.705572 2.128252 1.225479 +endloop +endfacet +facet normal 0.044961 0.149207 -0.987783 +outer loop +vertex 15.017767 0.994756 0.718727 +vertex 14.778099 -0.081578 0.545235 +vertex 12.626919 0.410041 0.521579 +endloop +endfacet +facet normal 0.028781 0.211701 -0.976911 +outer loop +vertex 15.017767 0.994756 0.718727 +vertex 12.626919 0.410041 0.521579 +vertex 12.492224 1.328601 0.716666 +endloop +endfacet +facet normal 0.027410 0.555686 -0.830940 +outer loop +vertex 11.705572 2.128252 1.225479 +vertex 12.492224 1.328601 0.716666 +vertex 10.215493 1.381170 0.676720 +endloop +endfacet +facet normal 0.017508 0.568987 -0.822160 +outer loop +vertex 11.705572 2.128252 1.225479 +vertex 10.215493 1.381170 0.676720 +vertex 9.145125 2.041365 1.110822 +endloop +endfacet +facet normal -0.262091 0.964859 0.018878 +outer loop +vertex 0.832788 2.175980 2.905423 +vertex 1.345435 2.325378 2.386925 +vertex 1.119097 2.270641 2.042254 +endloop +endfacet +facet normal -0.340166 0.940315 -0.009710 +outer loop +vertex 0.832788 2.175980 2.905423 +vertex 1.119097 2.270641 2.042254 +vertex 0.763476 2.143866 2.223727 +endloop +endfacet +facet normal -0.045910 0.971407 -0.232939 +outer loop +vertex 1.958174 2.226874 1.694363 +vertex 1.362232 2.158928 1.528469 +vertex 1.119097 2.270641 2.042254 +endloop +endfacet +facet normal -0.010634 0.988625 -0.150022 +outer loop +vertex 1.958174 2.226874 1.694363 +vertex 1.119097 2.270641 2.042254 +vertex 1.345435 2.325378 2.386925 +endloop +endfacet +facet normal -0.452941 0.839023 -0.301470 +outer loop +vertex 0.886872 1.968978 1.551597 +vertex 0.763476 2.143866 2.223727 +vertex 1.119097 2.270641 2.042254 +endloop +endfacet +facet normal -0.361419 0.860851 -0.358207 +outer loop +vertex 0.886872 1.968978 1.551597 +vertex 1.119097 2.270641 2.042254 +vertex 1.362232 2.158928 1.528469 +endloop +endfacet +facet normal -0.643293 0.728833 0.234471 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 1.052529 2.154190 3.576036 +vertex 0.832788 2.175980 2.905423 +endloop +endfacet +facet normal -0.634599 0.734736 0.239681 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.832788 2.175980 2.905423 +vertex 0.440260 1.766233 3.122204 +endloop +endfacet +facet normal -0.195052 0.976419 0.092523 +outer loop +vertex 1.052529 2.154190 3.576036 +vertex 1.664815 2.340921 2.896196 +vertex 1.345435 2.325378 2.386925 +endloop +endfacet +facet normal -0.189796 0.977318 0.093947 +outer loop +vertex 1.052529 2.154190 3.576036 +vertex 1.345435 2.325378 2.386925 +vertex 0.832788 2.175980 2.905423 +endloop +endfacet +facet normal -0.711112 0.701984 0.039233 +outer loop +vertex 0.440260 1.766233 3.122204 +vertex 0.832788 2.175980 2.905423 +vertex 0.763476 2.143866 2.223727 +endloop +endfacet +facet normal -0.719474 0.693746 0.032763 +outer loop +vertex 0.440260 1.766233 3.122204 +vertex 0.763476 2.143866 2.223727 +vertex 0.426389 1.789352 2.328038 +endloop +endfacet +facet normal 0.054097 0.884757 -0.462903 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 2.556761 1.841754 1.028229 +vertex 1.958174 2.226874 1.694363 +endloop +endfacet +facet normal 0.115189 0.957054 -0.266045 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 1.958174 2.226874 1.694363 +vertex 2.553637 2.266812 2.095851 +endloop +endfacet +facet normal 0.003942 0.848771 -0.528746 +outer loop +vertex 2.556761 1.841754 1.028229 +vertex 1.689815 1.810828 0.972122 +vertex 1.362232 2.158928 1.528469 +endloop +endfacet +facet normal 0.032949 0.877804 -0.477886 +outer loop +vertex 2.556761 1.841754 1.028229 +vertex 1.362232 2.158928 1.528469 +vertex 1.958174 2.226874 1.694363 +endloop +endfacet +facet normal 0.017912 0.991964 -0.125243 +outer loop +vertex 2.553637 2.266812 2.095851 +vertex 1.958174 2.226874 1.694363 +vertex 1.345435 2.325378 2.386925 +endloop +endfacet +facet normal 0.035651 0.997968 -0.052815 +outer loop +vertex 2.553637 2.266812 2.095851 +vertex 1.345435 2.325378 2.386925 +vertex 1.664815 2.340921 2.896196 +endloop +endfacet +facet normal -0.624413 0.510585 -0.591110 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 0.541146 1.598091 1.596440 +vertex 0.886872 1.968978 1.551597 +endloop +endfacet +facet normal -0.607987 0.516628 -0.602866 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 0.886872 1.968978 1.551597 +vertex 1.136574 1.600775 0.984242 +endloop +endfacet +facet normal -0.735721 0.618057 -0.276983 +outer loop +vertex 0.541146 1.598091 1.596440 +vertex 0.426389 1.789352 2.328038 +vertex 0.763476 2.143866 2.223727 +endloop +endfacet +facet normal -0.716302 0.631929 -0.295934 +outer loop +vertex 0.541146 1.598091 1.596440 +vertex 0.763476 2.143866 2.223727 +vertex 0.886872 1.968978 1.551597 +endloop +endfacet +facet normal -0.319261 0.724598 -0.610762 +outer loop +vertex 1.136574 1.600775 0.984242 +vertex 0.886872 1.968978 1.551597 +vertex 1.362232 2.158928 1.528469 +endloop +endfacet +facet normal -0.289197 0.725664 -0.624321 +outer loop +vertex 1.136574 1.600775 0.984242 +vertex 1.362232 2.158928 1.528469 +vertex 1.689815 1.810828 0.972122 +endloop +endfacet +facet normal -0.098370 -0.991958 0.079637 +outer loop +vertex 8.802366 -2.453447 5.302680 +vertex 10.254326 -2.673915 4.350039 +vertex 11.182098 -2.715471 4.978424 +endloop +endfacet +facet normal -0.091691 -0.987867 0.125347 +outer loop +vertex 8.802366 -2.453447 5.302680 +vertex 11.182098 -2.715471 4.978424 +vertex 10.676667 -2.563551 5.805993 +endloop +endfacet +facet normal -0.068967 -0.996923 -0.037261 +outer loop +vertex 12.164215 -2.729092 3.525036 +vertex 12.723382 -2.814001 4.761803 +vertex 11.182098 -2.715471 4.978424 +endloop +endfacet +facet normal -0.035057 -0.999283 -0.014324 +outer loop +vertex 12.164215 -2.729092 3.525036 +vertex 11.182098 -2.715471 4.978424 +vertex 10.254326 -2.673915 4.350039 +endloop +endfacet +facet normal -0.102259 -0.987633 0.118849 +outer loop +vertex 12.753977 -2.742223 6.108570 +vertex 10.676667 -2.563551 5.805993 +vertex 11.182098 -2.715471 4.978424 +endloop +endfacet +facet normal -0.056085 -0.996942 0.054407 +outer loop +vertex 12.753977 -2.742223 6.108570 +vertex 11.182098 -2.715471 4.978424 +vertex 12.723382 -2.814001 4.761803 +endloop +endfacet +facet normal -0.137872 -0.950809 0.277406 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 7.178772 -2.418575 4.615271 +vertex 8.802366 -2.453447 5.302680 +endloop +endfacet +facet normal -0.118476 -0.915260 0.385048 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 8.802366 -2.453447 5.302680 +vertex 8.131913 -1.975336 6.232861 +endloop +endfacet +facet normal -0.078702 -0.995832 0.046086 +outer loop +vertex 7.178772 -2.418575 4.615271 +vertex 9.061987 -2.615098 3.584776 +vertex 10.254326 -2.673915 4.350039 +endloop +endfacet +facet normal -0.071898 -0.990218 0.119581 +outer loop +vertex 7.178772 -2.418575 4.615271 +vertex 10.254326 -2.673915 4.350039 +vertex 8.802366 -2.453447 5.302680 +endloop +endfacet +facet normal -0.151552 -0.919275 0.363271 +outer loop +vertex 8.131913 -1.975336 6.232861 +vertex 8.802366 -2.453447 5.302680 +vertex 10.676667 -2.563551 5.805993 +endloop +endfacet +facet normal -0.147266 -0.913073 0.380278 +outer loop +vertex 8.131913 -1.975336 6.232861 +vertex 10.676667 -2.563551 5.805993 +vertex 10.391977 -2.143262 6.704890 +endloop +endfacet +facet normal -0.087903 -0.950882 -0.296812 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 14.374381 -2.709676 2.808274 +vertex 12.164215 -2.729092 3.525036 +endloop +endfacet +facet normal 0.069456 -0.971901 -0.224909 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 12.164215 -2.729092 3.525036 +vertex 11.040919 -2.582739 2.545703 +endloop +endfacet +facet normal -0.064487 -0.992113 -0.107483 +outer loop +vertex 14.374381 -2.709676 2.808274 +vertex 14.808001 -2.908951 4.387503 +vertex 12.723382 -2.814001 4.761803 +endloop +endfacet +facet normal -0.011732 -0.997931 -0.063208 +outer loop +vertex 14.374381 -2.709676 2.808274 +vertex 12.723382 -2.814001 4.761803 +vertex 12.164215 -2.729092 3.525036 +endloop +endfacet +facet normal -0.062176 -0.995060 -0.077388 +outer loop +vertex 11.040919 -2.582739 2.545703 +vertex 12.164215 -2.729092 3.525036 +vertex 10.254326 -2.673915 4.350039 +endloop +endfacet +facet normal -0.013181 -0.998333 -0.056193 +outer loop +vertex 11.040919 -2.582739 2.545703 +vertex 10.254326 -2.673915 4.350039 +vertex 9.061987 -2.615098 3.584776 +endloop +endfacet +facet normal -0.251464 -0.923204 0.290621 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 12.673998 -2.361658 7.248292 +vertex 12.753977 -2.742223 6.108570 +endloop +endfacet +facet normal -0.096803 -0.989875 0.103806 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 12.753977 -2.742223 6.108570 +vertex 14.999439 -2.963156 6.095771 +endloop +endfacet +facet normal -0.175573 -0.911968 0.370795 +outer loop +vertex 12.673998 -2.361658 7.248292 +vertex 10.391977 -2.143262 6.704890 +vertex 10.676667 -2.563551 5.805993 +endloop +endfacet +facet normal -0.125769 -0.943605 0.306254 +outer loop +vertex 12.673998 -2.361658 7.248292 +vertex 10.676667 -2.563551 5.805993 +vertex 12.753977 -2.742223 6.108570 +endloop +endfacet +facet normal -0.097457 -0.993709 0.055176 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 12.753977 -2.742223 6.108570 +vertex 12.723382 -2.814001 4.761803 +endloop +endfacet +facet normal -0.050154 -0.998401 -0.026060 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 12.723382 -2.814001 4.761803 +vertex 14.808001 -2.908951 4.387503 +endloop +endfacet +facet normal -0.051461 -0.998001 -0.036676 +outer loop +vertex 19.651859 -3.205200 6.661274 +vertex 21.579056 -3.249851 5.172173 +vertex 22.354593 -3.309949 5.719345 +endloop +endfacet +facet normal -0.044917 -0.998832 -0.017805 +outer loop +vertex 19.651859 -3.205200 6.661274 +vertex 22.354593 -3.309949 5.719345 +vertex 21.544149 -3.293628 6.848240 +endloop +endfacet +facet normal -0.056996 -0.994801 -0.084395 +outer loop +vertex 23.786083 -3.239633 3.923745 +vertex 23.963385 -3.355907 5.174582 +vertex 22.354593 -3.309949 5.719345 +endloop +endfacet +facet normal -0.031831 -0.997414 -0.064435 +outer loop +vertex 23.786083 -3.239633 3.923745 +vertex 22.354593 -3.309949 5.719345 +vertex 21.579056 -3.249851 5.172173 +endloop +endfacet +facet normal -0.045489 -0.998799 -0.018217 +outer loop +vertex 23.701311 -3.388976 6.689353 +vertex 21.544149 -3.293628 6.848240 +vertex 22.354593 -3.309949 5.719345 +endloop +endfacet +facet normal -0.038154 -0.998868 -0.028407 +outer loop +vertex 23.701311 -3.388976 6.689353 +vertex 22.354593 -3.309949 5.719345 +vertex 23.963385 -3.355907 5.174582 +endloop +endfacet +facet normal -0.152695 -0.988014 -0.022656 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 15.539762 -2.781838 8.288755 +vertex 15.527050 -2.781158 8.344804 +endloop +endfacet +facet normal -0.152690 -0.988015 -0.022633 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 15.527050 -2.781158 8.344804 +vertex 15.538308 -2.783193 8.357668 +endloop +endfacet +facet normal -0.045050 -0.998122 -0.041513 +outer loop +vertex 18.135849 -3.132686 6.091653 +vertex 20.531229 -3.172596 4.451771 +vertex 21.579056 -3.249851 5.172173 +endloop +endfacet +facet normal -0.039722 -0.998980 -0.021454 +outer loop +vertex 18.135849 -3.132686 6.091653 +vertex 21.579056 -3.249851 5.172173 +vertex 19.651859 -3.205200 6.661274 +endloop +endfacet +facet normal -0.090711 -0.931835 -0.351362 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 26.704079 -3.180421 3.013374 +vertex 23.786083 -3.239633 3.923745 +endloop +endfacet +facet normal 0.044079 -0.978215 -0.202863 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 23.786083 -3.239633 3.923745 +vertex 22.986065 -3.078571 2.973266 +endloop +endfacet +facet normal -0.033742 -0.991793 -0.123322 +outer loop +vertex 26.704079 -3.180421 3.013374 +vertex 26.492159 -3.367654 4.577150 +vertex 23.963385 -3.355907 5.174582 +endloop +endfacet +facet normal -0.008305 -0.995781 -0.091387 +outer loop +vertex 26.704079 -3.180421 3.013374 +vertex 23.963385 -3.355907 5.174582 +vertex 23.786083 -3.239633 3.923745 +endloop +endfacet +facet normal -0.061262 -0.991309 -0.116416 +outer loop +vertex 22.986065 -3.078571 2.973266 +vertex 23.786083 -3.239633 3.923745 +vertex 21.579056 -3.249851 5.172173 +endloop +endfacet +facet normal -0.013958 -0.996152 -0.086525 +outer loop +vertex 22.986065 -3.078571 2.973266 +vertex 21.579056 -3.249851 5.172173 +vertex 20.531229 -3.172596 4.451771 +endloop +endfacet +facet normal -0.011662 -0.999648 -0.023841 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 23.701311 -3.388976 6.689353 +vertex 23.963385 -3.355907 5.174582 +endloop +endfacet +facet normal -0.010755 -0.999608 -0.025868 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 23.963385 -3.355907 5.174582 +vertex 26.492159 -3.367654 4.577150 +endloop +endfacet +facet normal 0.010801 0.002288 -0.999939 +outer loop +vertex 24.168861 -0.458234 0.562916 +vertex 21.801546 0.458118 0.539441 +vertex 22.818352 0.906325 0.551450 +endloop +endfacet +facet normal -0.019894 -0.028087 -0.999408 +outer loop +vertex 24.168861 -0.458234 0.562916 +vertex 22.818352 0.906325 0.551450 +vertex 24.733583 0.530254 0.523895 +endloop +endfacet +facet normal -0.028836 0.235589 -0.971425 +outer loop +vertex 20.104874 1.469719 0.768631 +vertex 22.284901 1.664661 0.751195 +vertex 22.818352 0.906325 0.551450 +endloop +endfacet +facet normal -0.050065 0.140071 -0.988875 +outer loop +vertex 20.104874 1.469719 0.768631 +vertex 22.818352 0.906325 0.551450 +vertex 21.801546 0.458118 0.539441 +endloop +endfacet +facet normal 0.021616 0.182112 -0.983040 +outer loop +vertex 25.002594 1.567553 0.721974 +vertex 24.733583 0.530254 0.523895 +vertex 22.818352 0.906325 0.551450 +endloop +endfacet +facet normal -0.001330 0.253837 -0.967246 +outer loop +vertex 25.002594 1.567553 0.721974 +vertex 22.818352 0.906325 0.551450 +vertex 22.284901 1.664661 0.751195 +endloop +endfacet +facet normal 0.088396 -0.283195 -0.954980 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 23.085478 -1.280291 0.706413 +vertex 24.168861 -0.458234 0.562916 +endloop +endfacet +facet normal -0.068269 -0.447273 -0.891788 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 24.168861 -0.458234 0.562916 +vertex 27.419786 -1.179824 0.675958 +endloop +endfacet +facet normal 0.026385 -0.076248 -0.996740 +outer loop +vertex 23.085478 -1.280291 0.706413 +vertex 20.618484 -0.134188 0.553434 +vertex 21.801546 0.458118 0.539441 +endloop +endfacet +facet normal -0.037858 -0.123208 -0.991658 +outer loop +vertex 23.085478 -1.280291 0.706413 +vertex 21.801546 0.458118 0.539441 +vertex 24.168861 -0.458234 0.562916 +endloop +endfacet +facet normal 0.023045 -0.052576 -0.998351 +outer loop +vertex 27.419786 -1.179824 0.675958 +vertex 24.168861 -0.458234 0.562916 +vertex 24.733583 0.530254 0.523895 +endloop +endfacet +facet normal -0.020444 -0.120370 -0.992518 +outer loop +vertex 27.419786 -1.179824 0.675958 +vertex 24.733583 0.530254 0.523895 +vertex 27.720016 0.032394 0.522759 +endloop +endfacet +facet normal -0.041999 0.539786 -0.840754 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 19.303099 2.379055 1.392499 +vertex 20.104874 1.469719 0.768631 +endloop +endfacet +facet normal -0.082151 0.388844 -0.917634 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 20.104874 1.469719 0.768631 +vertex 18.347206 1.014438 0.733063 +endloop +endfacet +facet normal -0.040911 0.567476 -0.822373 +outer loop +vertex 19.303099 2.379055 1.392499 +vertex 21.998266 2.464462 1.317354 +vertex 22.284901 1.664661 0.751195 +endloop +endfacet +facet normal -0.054321 0.531884 -0.845073 +outer loop +vertex 19.303099 2.379055 1.392499 +vertex 22.284901 1.664661 0.751195 +vertex 20.104874 1.469719 0.768631 +endloop +endfacet +facet normal -0.026364 0.178620 -0.983565 +outer loop +vertex 18.347206 1.014438 0.733063 +vertex 20.104874 1.469719 0.768631 +vertex 21.801546 0.458118 0.539441 +endloop +endfacet +facet normal -0.045141 0.066616 -0.996757 +outer loop +vertex 18.347206 1.014438 0.733063 +vertex 21.801546 0.458118 0.539441 +vertex 20.618484 -0.134188 0.553434 +endloop +endfacet +facet normal 0.023968 0.397051 -0.917484 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 28.402740 1.203649 0.653316 +vertex 25.002594 1.567553 0.721974 +endloop +endfacet +facet normal -0.007408 0.544183 -0.838934 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 25.002594 1.567553 0.721974 +vertex 25.097067 2.476448 1.310704 +endloop +endfacet +facet normal 0.016502 0.101261 -0.994723 +outer loop +vertex 28.402740 1.203649 0.653316 +vertex 27.720016 0.032394 0.522759 +vertex 24.733583 0.530254 0.523895 +endloop +endfacet +facet normal 0.000234 0.187509 -0.982263 +outer loop +vertex 28.402740 1.203649 0.653316 +vertex 24.733583 0.530254 0.523895 +vertex 25.002594 1.567553 0.721974 +endloop +endfacet +facet normal 0.010368 0.542868 -0.839754 +outer loop +vertex 25.097067 2.476448 1.310704 +vertex 25.002594 1.567553 0.721974 +vertex 22.284901 1.664661 0.751195 +endloop +endfacet +facet normal -0.003984 0.576812 -0.816867 +outer loop +vertex 25.097067 2.476448 1.310704 +vertex 22.284901 1.664661 0.751195 +vertex 21.998266 2.464462 1.317354 +endloop +endfacet +facet normal -0.214887 0.017602 0.976480 +outer loop +vertex 6.192955 -0.172350 6.679617 +vertex 8.064869 0.641323 7.076889 +vertex 7.223382 0.924576 6.886603 +endloop +endfacet +facet normal -0.166813 -0.029270 0.985554 +outer loop +vertex 6.192955 -0.172350 6.679617 +vertex 7.223382 0.924576 6.886603 +vertex 5.782027 0.543884 6.631336 +endloop +endfacet +facet normal -0.215357 0.333843 0.917698 +outer loop +vertex 9.928230 1.566422 7.287859 +vertex 8.009556 1.544833 6.845456 +vertex 7.223382 0.924576 6.886603 +endloop +endfacet +facet normal -0.176078 0.132208 0.975458 +outer loop +vertex 9.928230 1.566422 7.287859 +vertex 7.223382 0.924576 6.886603 +vertex 8.064869 0.641323 7.076889 +endloop +endfacet +facet normal -0.238549 0.279570 0.930019 +outer loop +vertex 6.016459 1.322258 6.457483 +vertex 5.782027 0.543884 6.631336 +vertex 7.223382 0.924576 6.886603 +endloop +endfacet +facet normal -0.215922 0.334532 0.917314 +outer loop +vertex 6.016459 1.322258 6.457483 +vertex 7.223382 0.924576 6.886603 +vertex 8.009556 1.544833 6.845456 +endloop +endfacet +facet normal -0.269489 -0.373729 0.887526 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 7.198318 -0.798686 6.721142 +vertex 6.192955 -0.172350 6.679617 +endloop +endfacet +facet normal -0.043669 -0.483330 0.874349 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 6.192955 -0.172350 6.679617 +vertex 4.467706 -0.826319 6.231944 +endloop +endfacet +facet normal -0.214387 -0.110735 0.970452 +outer loop +vertex 7.198318 -0.798686 6.721142 +vertex 9.218365 0.199331 7.281278 +vertex 8.064869 0.641323 7.076889 +endloop +endfacet +facet normal -0.138807 -0.157990 0.977636 +outer loop +vertex 7.198318 -0.798686 6.721142 +vertex 8.064869 0.641323 7.076889 +vertex 6.192955 -0.172350 6.679617 +endloop +endfacet +facet normal -0.227473 -0.065013 0.971612 +outer loop +vertex 4.467706 -0.826319 6.231944 +vertex 6.192955 -0.172350 6.679617 +vertex 5.782027 0.543884 6.631336 +endloop +endfacet +facet normal -0.144871 -0.146277 0.978578 +outer loop +vertex 4.467706 -0.826319 6.231944 +vertex 5.782027 0.543884 6.631336 +vertex 4.185957 0.051183 6.321401 +endloop +endfacet +facet normal -0.279639 0.728761 0.625068 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 11.587097 2.393663 7.065519 +vertex 9.928230 1.566422 7.287859 +endloop +endfacet +facet normal -0.223054 0.171863 0.959536 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 9.928230 1.566422 7.287859 +vertex 11.600214 1.316005 7.721383 +endloop +endfacet +facet normal -0.212020 0.723188 0.657302 +outer loop +vertex 11.587097 2.393663 7.065519 +vertex 9.113427 2.193287 6.488069 +vertex 8.009556 1.544833 6.845456 +endloop +endfacet +facet normal -0.187966 0.588312 0.786484 +outer loop +vertex 11.587097 2.393663 7.065519 +vertex 8.009556 1.544833 6.845456 +vertex 9.928230 1.566422 7.287859 +endloop +endfacet +facet normal -0.214757 0.215320 0.952637 +outer loop +vertex 11.600214 1.316005 7.721383 +vertex 9.928230 1.566422 7.287859 +vertex 8.064869 0.641323 7.076889 +endloop +endfacet +facet normal -0.177720 -0.008758 0.984042 +outer loop +vertex 11.600214 1.316005 7.721383 +vertex 8.064869 0.641323 7.076889 +vertex 9.218365 0.199331 7.281278 +endloop +endfacet +facet normal -0.251593 0.565476 0.785454 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 4.172165 0.918773 6.157211 +vertex 6.016459 1.322258 6.457483 +endloop +endfacet +facet normal -0.181192 0.672813 0.717281 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 6.016459 1.322258 6.457483 +vertex 6.831068 2.026018 6.003131 +endloop +endfacet +facet normal -0.239946 0.176832 0.954545 +outer loop +vertex 4.172165 0.918773 6.157211 +vertex 4.185957 0.051183 6.321401 +vertex 5.782027 0.543884 6.631336 +endloop +endfacet +facet normal -0.212574 0.273546 0.938075 +outer loop +vertex 4.172165 0.918773 6.157211 +vertex 5.782027 0.543884 6.631336 +vertex 6.016459 1.322258 6.457483 +endloop +endfacet +facet normal -0.211694 0.691156 0.691006 +outer loop +vertex 6.831068 2.026018 6.003131 +vertex 6.016459 1.322258 6.457483 +vertex 8.009556 1.544833 6.845456 +endloop +endfacet +facet normal -0.196068 0.707802 0.678655 +outer loop +vertex 6.831068 2.026018 6.003131 +vertex 8.009556 1.544833 6.845456 +vertex 9.113427 2.193287 6.488069 +endloop +endfacet +facet normal -0.055414 0.998326 -0.016585 +outer loop +vertex 6.012343 2.489832 3.702746 +vertex 8.029861 2.588729 2.914806 +vertex 7.207201 2.535911 2.484139 +endloop +endfacet +facet normal -0.027336 0.999566 0.010993 +outer loop +vertex 6.012343 2.489832 3.702746 +vertex 7.207201 2.535911 2.484139 +vertex 5.726770 2.491796 2.814062 +endloop +endfacet +facet normal -0.024669 0.979463 -0.200110 +outer loop +vertex 9.997385 2.513603 2.030982 +vertex 8.029572 2.419185 1.811427 +vertex 7.207201 2.535911 2.484139 +endloop +endfacet +facet normal -0.009050 0.994465 -0.104676 +outer loop +vertex 9.997385 2.513603 2.030982 +vertex 7.207201 2.535911 2.484139 +vertex 8.029861 2.588729 2.914806 +endloop +endfacet +facet normal -0.070899 0.979773 -0.187130 +outer loop +vertex 6.012306 2.337867 1.899937 +vertex 5.726770 2.491796 2.814062 +vertex 7.207201 2.535911 2.484139 +endloop +endfacet +facet normal -0.049233 0.972209 -0.228879 +outer loop +vertex 6.012306 2.337867 1.899937 +vertex 7.207201 2.535911 2.484139 +vertex 8.029572 2.419185 1.811427 +endloop +endfacet +facet normal -0.139711 0.969469 0.201520 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 6.835320 2.433162 4.545931 +vertex 6.012343 2.489832 3.702746 +endloop +endfacet +facet normal 0.037649 0.932397 0.359469 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 6.012343 2.489832 3.702746 +vertex 4.161652 2.331292 4.307803 +endloop +endfacet +facet normal -0.066258 0.996717 0.046537 +outer loop +vertex 6.835320 2.433162 4.545931 +vertex 9.147440 2.632157 3.575842 +vertex 8.029861 2.588729 2.914806 +endloop +endfacet +facet normal -0.016427 0.996414 0.083002 +outer loop +vertex 6.835320 2.433162 4.545931 +vertex 8.029861 2.588729 2.914806 +vertex 6.012343 2.489832 3.702746 +endloop +endfacet +facet normal -0.076614 0.996700 0.026822 +outer loop +vertex 4.161652 2.331292 4.307803 +vertex 6.012343 2.489832 3.702746 +vertex 5.726770 2.491796 2.814062 +endloop +endfacet +facet normal -0.014082 0.995638 0.092228 +outer loop +vertex 4.161652 2.331292 4.307803 +vertex 5.726770 2.491796 2.814062 +vertex 4.101852 2.427727 3.257616 +endloop +endfacet +facet normal -0.005546 0.897442 -0.441097 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 11.705572 2.128252 1.225479 +vertex 9.997385 2.513603 2.030982 +endloop +endfacet +facet normal 0.014446 0.968220 -0.249682 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 9.997385 2.513603 2.030982 +vertex 11.713722 2.630891 2.585105 +endloop +endfacet +facet normal -0.007990 0.874773 -0.484467 +outer loop +vertex 11.705572 2.128252 1.225479 +vertex 9.145125 2.041365 1.110822 +vertex 8.029572 2.419185 1.811427 +endloop +endfacet +facet normal 0.003950 0.905314 -0.424725 +outer loop +vertex 11.705572 2.128252 1.225479 +vertex 8.029572 2.419185 1.811427 +vertex 9.997385 2.513603 2.030982 +endloop +endfacet +facet normal -0.023563 0.990341 -0.136634 +outer loop +vertex 11.713722 2.630891 2.585105 +vertex 9.997385 2.513603 2.030982 +vertex 8.029861 2.588729 2.914806 +endloop +endfacet +facet normal -0.015034 0.999078 -0.040220 +outer loop +vertex 11.713722 2.630891 2.585105 +vertex 8.029861 2.588729 2.914806 +vertex 9.147440 2.632157 3.575842 +endloop +endfacet +facet normal -0.092009 0.897832 -0.430619 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 4.161652 2.306381 2.229715 +vertex 6.012306 2.337867 1.899937 +endloop +endfacet +facet normal -0.047210 0.861043 -0.506335 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 6.012306 2.337867 1.899937 +vertex 6.835030 1.918170 1.109513 +endloop +endfacet +facet normal -0.072084 0.990024 -0.121068 +outer loop +vertex 4.161652 2.306381 2.229715 +vertex 4.101852 2.427727 3.257616 +vertex 5.726770 2.491796 2.814062 +endloop +endfacet +facet normal -0.048911 0.982323 -0.180691 +outer loop +vertex 4.161652 2.306381 2.229715 +vertex 5.726770 2.491796 2.814062 +vertex 6.012306 2.337867 1.899937 +endloop +endfacet +facet normal -0.057052 0.855963 -0.513880 +outer loop +vertex 6.835030 1.918170 1.109513 +vertex 6.012306 2.337867 1.899937 +vertex 8.029572 2.419185 1.811427 +endloop +endfacet +facet normal -0.044906 0.847665 -0.528628 +outer loop +vertex 6.835030 1.918170 1.109513 +vertex 8.029572 2.419185 1.811427 +vertex 9.145125 2.041365 1.110822 +endloop +endfacet +facet normal 0.029138 -0.029900 -0.999128 +outer loop +vertex 53.935028 0.015368 0.507757 +vertex 52.969692 0.580675 0.462687 +vertex 53.385418 0.730710 0.470321 +endloop +endfacet +facet normal -0.019652 -0.067301 -0.997539 +outer loop +vertex 53.935028 0.015368 0.507757 +vertex 53.385418 0.730710 0.470321 +vertex 54.087021 0.426941 0.476994 +endloop +endfacet +facet normal 0.020521 0.271626 -0.962184 +outer loop +vertex 52.143917 1.163148 0.565922 +vertex 53.116249 1.115542 0.573220 +vertex 53.385418 0.730710 0.470321 +endloop +endfacet +facet normal -0.029884 0.133207 -0.990638 +outer loop +vertex 52.143917 1.163148 0.565922 +vertex 53.385418 0.730710 0.470321 +vertex 52.969692 0.580675 0.462687 +endloop +endfacet +facet normal 0.125923 0.269867 -0.954628 +outer loop +vertex 54.058681 0.879590 0.601217 +vertex 54.087021 0.426941 0.476994 +vertex 53.385418 0.730710 0.470321 +endloop +endfacet +facet normal 0.109944 0.327799 -0.938328 +outer loop +vertex 54.058681 0.879590 0.601217 +vertex 53.385418 0.730710 0.470321 +vertex 53.116249 1.115542 0.573220 +endloop +endfacet +facet normal 0.126970 -0.340753 -0.931540 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 53.526051 -0.368843 0.592556 +vertex 53.935028 0.015368 0.507757 +endloop +endfacet +facet normal -0.228010 -0.620776 -0.750099 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 53.935028 0.015368 0.507757 +vertex 55.000008 -0.546875 0.649334 +endloop +endfacet +facet normal 0.039999 -0.112360 -0.992862 +outer loop +vertex 53.526051 -0.368843 0.592556 +vertex 52.409733 0.291152 0.472893 +vertex 52.969692 0.580675 0.462687 +endloop +endfacet +facet normal -0.050105 -0.164103 -0.985170 +outer loop +vertex 53.526051 -0.368843 0.592556 +vertex 52.969692 0.580675 0.462687 +vertex 53.935028 0.015368 0.507757 +endloop +endfacet +facet normal 0.077576 -0.102770 -0.991676 +outer loop +vertex 55.000008 -0.546875 0.649334 +vertex 53.935028 0.015368 0.507757 +vertex 54.087021 0.426941 0.476994 +endloop +endfacet +facet normal -0.053575 -0.222505 -0.973459 +outer loop +vertex 55.000008 -0.546875 0.649334 +vertex 54.087021 0.426941 0.476994 +vertex 55.000008 0.000000 0.524334 +endloop +endfacet +facet normal 0.017534 0.550917 -0.834376 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 51.705059 1.646369 0.875757 +vertex 52.143917 1.163148 0.565922 +endloop +endfacet +facet normal -0.043552 0.340707 -0.939160 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 52.143917 1.163148 0.565922 +vertex 51.159924 0.980571 0.545317 +endloop +endfacet +facet normal 0.066111 0.620924 -0.781078 +outer loop +vertex 51.705059 1.646369 0.875757 +vertex 52.962971 1.494856 0.861782 +vertex 53.116249 1.115542 0.573220 +endloop +endfacet +facet normal 0.033673 0.560946 -0.827168 +outer loop +vertex 51.705059 1.646369 0.875757 +vertex 53.116249 1.115542 0.573220 +vertex 52.143917 1.163148 0.565922 +endloop +endfacet +facet normal -0.009333 0.161664 -0.986802 +outer loop +vertex 51.159924 0.980571 0.545317 +vertex 52.143917 1.163148 0.565922 +vertex 52.969692 0.580675 0.462687 +endloop +endfacet +facet normal -0.037392 0.037118 -0.998611 +outer loop +vertex 51.159924 0.980571 0.545317 +vertex 52.969692 0.580675 0.462687 +vertex 52.409733 0.291152 0.472893 +endloop +endfacet +facet normal 0.255913 0.616340 -0.744738 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 55.000008 0.546875 0.649334 +vertex 54.058681 0.879590 0.601217 +endloop +endfacet +facet normal 0.250313 0.629107 -0.735913 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 54.058681 0.879590 0.601217 +vertex 54.016209 1.270190 0.920681 +endloop +endfacet +facet normal 0.152927 0.220204 -0.963392 +outer loop +vertex 55.000008 0.546875 0.649334 +vertex 55.000008 0.000000 0.524334 +vertex 54.087021 0.426941 0.476994 +endloop +endfacet +facet normal 0.144186 0.270269 -0.951927 +outer loop +vertex 55.000008 0.546875 0.649334 +vertex 54.087021 0.426941 0.476994 +vertex 54.058681 0.879590 0.601217 +endloop +endfacet +facet normal 0.181146 0.634347 -0.751525 +outer loop +vertex 54.016209 1.270190 0.920681 +vertex 54.058681 0.879590 0.601217 +vertex 53.116249 1.115542 0.573220 +endloop +endfacet +facet normal 0.178378 0.640395 -0.747044 +outer loop +vertex 54.016209 1.270190 0.920681 +vertex 53.116249 1.115542 0.573220 +vertex 52.962971 1.494856 0.861782 +endloop +endfacet +facet normal 0.132022 -0.986409 0.097817 +outer loop +vertex 51.965218 -1.882964 3.330094 +vertex 52.912369 -1.816551 2.721468 +vertex 53.356014 -1.733773 2.957440 +endloop +endfacet +facet normal 0.148050 -0.975629 0.161953 +outer loop +vertex 51.965218 -1.882964 3.330094 +vertex 53.356014 -1.733773 2.957440 +vertex 53.058926 -1.705439 3.399708 +endloop +endfacet +facet normal 0.177278 -0.983315 -0.040787 +outer loop +vertex 53.927742 -1.597909 2.166973 +vertex 54.082394 -1.592369 2.705612 +vertex 53.356014 -1.733773 2.957440 +endloop +endfacet +facet normal 0.196776 -0.980100 -0.026136 +outer loop +vertex 53.927742 -1.597909 2.166973 +vertex 53.356014 -1.733773 2.957440 +vertex 52.912369 -1.816551 2.721468 +endloop +endfacet +facet normal 0.216482 -0.954187 0.206548 +outer loop +vertex 54.051403 -1.504376 3.288356 +vertex 53.058926 -1.705439 3.399708 +vertex 53.356014 -1.733773 2.957440 +endloop +endfacet +facet normal 0.241005 -0.957672 0.157423 +outer loop +vertex 54.051403 -1.504376 3.288356 +vertex 53.356014 -1.733773 2.957440 +vertex 54.082394 -1.592369 2.705612 +endloop +endfacet +facet normal 0.074341 -0.953236 0.292942 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 50.865349 -2.053394 3.054627 +vertex 51.965218 -1.882964 3.330094 +endloop +endfacet +facet normal 0.086768 -0.920736 0.380415 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 51.965218 -1.882964 3.330094 +vertex 51.413963 -1.708487 3.878123 +endloop +endfacet +facet normal 0.122716 -0.991214 0.049349 +outer loop +vertex 50.865349 -2.053394 3.054627 +vertex 52.331310 -1.905801 2.373729 +vertex 52.912369 -1.816551 2.721468 +endloop +endfacet +facet normal 0.129476 -0.987138 0.093775 +outer loop +vertex 50.865349 -2.053394 3.054627 +vertex 52.912369 -1.816551 2.721468 +vertex 51.965218 -1.882964 3.330094 +endloop +endfacet +facet normal 0.120734 -0.904347 0.409365 +outer loop +vertex 51.413963 -1.708487 3.878123 +vertex 51.965218 -1.882964 3.330094 +vertex 53.058926 -1.705439 3.399708 +endloop +endfacet +facet normal 0.123844 -0.898992 0.420092 +outer loop +vertex 51.413963 -1.708487 3.878123 +vertex 53.058926 -1.705439 3.399708 +vertex 52.884544 -1.512283 3.864470 +endloop +endfacet +facet normal 0.055707 -0.905175 -0.421374 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 55.000008 -1.281250 1.628500 +vertex 53.927742 -1.597909 2.166973 +endloop +endfacet +facet normal 0.267480 -0.933803 -0.237626 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 53.927742 -1.597909 2.166973 +vertex 53.516243 -1.601796 1.719050 +endloop +endfacet +facet normal 0.183430 -0.975007 -0.125357 +outer loop +vertex 55.000008 -1.281250 1.628500 +vertex 55.000008 -1.375000 2.357667 +vertex 54.082394 -1.592369 2.705612 +endloop +endfacet +facet normal 0.253503 -0.965290 -0.062857 +outer loop +vertex 55.000008 -1.281250 1.628500 +vertex 54.082394 -1.592369 2.705612 +vertex 53.927742 -1.597909 2.166973 +endloop +endfacet +facet normal 0.143887 -0.981837 -0.123667 +outer loop +vertex 53.516243 -1.601796 1.719050 +vertex 53.927742 -1.597909 2.166973 +vertex 52.912369 -1.816551 2.721468 +endloop +endfacet +facet normal 0.202054 -0.975479 -0.087260 +outer loop +vertex 53.516243 -1.601796 1.719050 +vertex 52.912369 -1.816551 2.721468 +vertex 52.331310 -1.905801 2.373729 +endloop +endfacet +facet normal 0.280961 -0.863225 0.419408 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 54.006409 -1.272369 3.796018 +vertex 54.051403 -1.504376 3.288356 +endloop +endfacet +facet normal 0.289890 -0.867655 0.403905 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 54.051403 -1.504376 3.288356 +vertex 55.000008 -1.281250 3.086834 +endloop +endfacet +facet normal 0.213291 -0.871231 0.442113 +outer loop +vertex 54.006409 -1.272369 3.796018 +vertex 52.884544 -1.512283 3.864470 +vertex 53.058926 -1.705439 3.399708 +endloop +endfacet +facet normal 0.225243 -0.878447 0.421421 +outer loop +vertex 54.006409 -1.272369 3.796018 +vertex 53.058926 -1.705439 3.399708 +vertex 54.051403 -1.504376 3.288356 +endloop +endfacet +facet normal 0.257717 -0.953272 0.157650 +outer loop +vertex 55.000008 -1.281250 3.086834 +vertex 54.051403 -1.504376 3.288356 +vertex 54.082394 -1.592369 2.705612 +endloop +endfacet +facet normal 0.272575 -0.954280 0.122692 +outer loop +vertex 55.000008 -1.281250 3.086834 +vertex 54.082394 -1.592369 2.705612 +vertex 55.000008 -1.375000 2.357667 +endloop +endfacet +facet normal 0.167439 0.067301 0.983583 +outer loop +vertex 31.040508 -0.715920 7.494724 +vertex 35.292370 0.246160 6.705083 +vertex 33.537426 0.758488 6.968779 +endloop +endfacet +facet normal 0.181584 0.042938 0.982438 +outer loop +vertex 31.040508 -0.715920 7.494724 +vertex 33.537426 0.758488 6.968779 +vertex 30.348871 0.441312 7.571981 +endloop +endfacet +facet normal 0.181361 0.207236 0.961333 +outer loop +vertex 38.358265 1.213073 5.961304 +vertex 34.627728 1.534933 6.595708 +vertex 33.537426 0.758488 6.968779 +endloop +endfacet +facet normal 0.188967 0.147609 0.970826 +outer loop +vertex 38.358265 1.213073 5.961304 +vertex 33.537426 0.758488 6.968779 +vertex 35.292370 0.246160 6.705083 +endloop +endfacet +facet normal 0.173255 0.117851 0.977801 +outer loop +vertex 30.465677 1.665626 7.403723 +vertex 30.348871 0.441312 7.571981 +vertex 33.537426 0.758488 6.968779 +endloop +endfacet +facet normal 0.192844 0.191595 0.962342 +outer loop +vertex 30.465677 1.665626 7.403723 +vertex 33.537426 0.758488 6.968779 +vertex 34.627728 1.534933 6.595708 +endloop +endfacet +facet normal 0.164600 0.019458 0.986168 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 37.555817 -0.354681 6.339149 +vertex 35.292370 0.246160 6.705083 +endloop +endfacet +facet normal 0.184416 -0.008358 0.982813 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 35.292370 0.246160 6.705083 +vertex 31.040508 -0.715920 7.494724 +endloop +endfacet +facet normal 0.162435 0.031240 0.986225 +outer loop +vertex 26.000004 -1.750000 8.357668 +vertex 31.040508 -0.715920 7.494724 +vertex 30.348871 0.441312 7.571981 +endloop +endfacet +facet normal 0.177786 0.000000 0.984069 +outer loop +vertex 26.000004 -1.750000 8.357668 +vertex 30.348871 0.441312 7.571981 +vertex 26.000004 0.000000 8.357668 +endloop +endfacet +facet normal 0.148415 0.534277 0.832178 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 39.717136 2.070559 5.168432 +vertex 38.358265 1.213073 5.961304 +endloop +endfacet +facet normal 0.184967 0.343754 0.920663 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 38.358265 1.213073 5.961304 +vertex 41.577522 0.722469 5.497714 +endloop +endfacet +facet normal 0.177666 0.526795 0.831217 +outer loop +vertex 39.717136 2.070559 5.168432 +vertex 35.210911 2.342078 5.959519 +vertex 34.627728 1.534933 6.595708 +endloop +endfacet +facet normal 0.187058 0.490538 0.851106 +outer loop +vertex 39.717136 2.070559 5.168432 +vertex 34.627728 1.534933 6.595708 +vertex 38.358265 1.213073 5.961304 +endloop +endfacet +facet normal 0.169836 0.203241 0.964287 +outer loop +vertex 41.577522 0.722469 5.497714 +vertex 38.358265 1.213073 5.961304 +vertex 35.292370 0.246160 6.705083 +endloop +endfacet +facet normal 0.181541 0.087342 0.979497 +outer loop +vertex 41.577522 0.722469 5.497714 +vertex 35.292370 0.246160 6.705083 +vertex 37.555817 -0.354681 6.339149 +endloop +endfacet +facet normal 0.177787 0.000000 0.984069 +outer loop +vertex 26.000004 1.750000 8.357668 +vertex 26.000004 0.000000 8.357668 +vertex 30.348871 0.441312 7.571981 +endloop +endfacet +facet normal 0.209605 0.113473 0.971180 +outer loop +vertex 26.000004 1.750000 8.357668 +vertex 30.348871 0.441312 7.571981 +vertex 30.465677 1.665626 7.403723 +endloop +endfacet +facet normal 0.184607 0.437281 0.880174 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 30.465677 1.665626 7.403723 +vertex 34.627728 1.534933 6.595708 +endloop +endfacet +facet normal 0.202851 0.511551 0.834965 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 34.627728 1.534933 6.595708 +vertex 35.210911 2.342078 5.959519 +endloop +endfacet +facet normal 0.081666 0.991806 0.098240 +outer loop +vertex 38.388279 2.646122 4.363545 +vertex 35.528660 2.952158 3.651049 +vertex 33.655098 3.034144 4.380816 +endloop +endfacet +facet normal 0.081322 0.985299 0.150242 +outer loop +vertex 38.388279 2.646122 4.363545 +vertex 33.655098 3.034144 4.380816 +vertex 34.646252 2.840508 5.114205 +endloop +endfacet +facet normal 0.054177 0.997517 -0.044985 +outer loop +vertex 31.805523 3.078282 3.132048 +vertex 30.597834 3.201355 4.406643 +vertex 33.655098 3.034144 4.380816 +endloop +endfacet +facet normal 0.036450 0.999161 -0.018671 +outer loop +vertex 31.805523 3.078282 3.132048 +vertex 33.655098 3.034144 4.380816 +vertex 35.528660 2.952158 3.651049 +endloop +endfacet +facet normal 0.087986 0.986031 0.141429 +outer loop +vertex 30.497274 3.120947 5.740185 +vertex 34.646252 2.840508 5.114205 +vertex 33.655098 3.034144 4.380816 +endloop +endfacet +facet normal 0.055040 0.996416 0.064231 +outer loop +vertex 30.497274 3.120947 5.740185 +vertex 33.655098 3.034144 4.380816 +vertex 30.597834 3.201355 4.406643 +endloop +endfacet +facet normal 0.115900 0.931453 0.344909 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 41.623692 2.542653 3.555777 +vertex 38.388279 2.646122 4.363545 +endloop +endfacet +facet normal 0.110299 0.885594 0.451174 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 38.388279 2.646122 4.363545 +vertex 39.717136 2.070559 5.168432 +endloop +endfacet +facet normal 0.067884 0.995692 0.063155 +outer loop +vertex 41.623692 2.542653 3.555777 +vertex 37.925159 2.841050 2.826803 +vertex 35.528660 2.952158 3.651049 +endloop +endfacet +facet normal 0.068602 0.986544 0.148409 +outer loop +vertex 41.623692 2.542653 3.555777 +vertex 35.528660 2.952158 3.651049 +vertex 38.388279 2.646122 4.363545 +endloop +endfacet +facet normal 0.131547 0.896208 0.423683 +outer loop +vertex 39.717136 2.070559 5.168432 +vertex 38.388279 2.646122 4.363545 +vertex 34.646252 2.840508 5.114205 +endloop +endfacet +facet normal 0.130413 0.889713 0.437496 +outer loop +vertex 39.717136 2.070559 5.168432 +vertex 34.646252 2.840508 5.114205 +vertex 35.210911 2.342078 5.959519 +endloop +endfacet +facet normal 0.039131 0.932981 -0.357793 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 27.830294 3.080580 2.703281 +vertex 31.805523 3.078282 3.132048 +endloop +endfacet +facet normal -0.044689 0.954225 -0.295733 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 31.805523 3.078282 3.132048 +vertex 34.121059 2.869947 2.109917 +endloop +endfacet +facet normal 0.038652 0.990359 -0.133021 +outer loop +vertex 27.830294 3.080580 2.703281 +vertex 26.798485 3.337256 4.314457 +vertex 30.597834 3.201355 4.406643 +endloop +endfacet +facet normal 0.009935 0.996178 -0.086776 +outer loop +vertex 27.830294 3.080580 2.703281 +vertex 30.597834 3.201355 4.406643 +vertex 31.805523 3.078282 3.132048 +endloop +endfacet +facet normal 0.047068 0.994265 -0.096028 +outer loop +vertex 34.121059 2.869947 2.109917 +vertex 31.805523 3.078282 3.132048 +vertex 35.528660 2.952158 3.651049 +endloop +endfacet +facet normal 0.021258 0.997134 -0.072607 +outer loop +vertex 34.121059 2.869947 2.109917 +vertex 35.528660 2.952158 3.651049 +vertex 37.925159 2.841050 2.826803 +endloop +endfacet +facet normal 0.149574 0.892197 0.426161 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 35.210911 2.342078 5.959519 +vertex 34.646252 2.840508 5.114205 +endloop +endfacet +facet normal 0.116003 0.928432 0.352926 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 34.646252 2.840508 5.114205 +vertex 30.497274 3.120947 5.740185 +endloop +endfacet +facet normal 0.073450 0.995143 0.065542 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 30.497274 3.120947 5.740185 +vertex 30.597834 3.201355 4.406643 +endloop +endfacet +facet normal 0.036465 0.998875 -0.030321 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 30.597834 3.201355 4.406643 +vertex 26.798485 3.337256 4.314457 +endloop +endfacet +facet normal -0.040504 0.998778 -0.028303 +outer loop +vertex 21.356569 3.284150 6.661976 +vertex 19.487246 3.166282 5.177697 +vertex 18.679420 3.148952 5.722180 +endloop +endfacet +facet normal -0.045724 0.998864 -0.013447 +outer loop +vertex 21.356569 3.284150 6.661976 +vertex 18.679420 3.148952 5.722180 +vertex 19.461210 3.199903 6.848686 +endloop +endfacet +facet normal -0.036095 0.997273 -0.064373 +outer loop +vertex 17.431246 2.988852 3.941785 +vertex 17.108587 3.057137 5.180579 +vertex 18.679420 3.148952 5.722180 +endloop +endfacet +facet normal -0.055571 0.997166 -0.050709 +outer loop +vertex 17.431246 2.988852 3.941785 +vertex 18.679420 3.148952 5.722180 +vertex 19.487246 3.166282 5.177697 +endloop +endfacet +facet normal -0.045336 0.998878 -0.013715 +outer loop +vertex 17.307825 3.099989 6.690114 +vertex 19.461210 3.199903 6.848686 +vertex 18.679420 3.148952 5.722180 +endloop +endfacet +facet normal -0.050904 0.998469 -0.021626 +outer loop +vertex 17.307825 3.099989 6.690114 +vertex 18.679420 3.148952 5.722180 +vertex 17.108587 3.057137 5.180579 +endloop +endfacet +facet normal -0.044284 0.998359 -0.036314 +outer loop +vertex 22.875492 3.349853 6.092598 +vertex 20.559479 3.187713 4.459331 +vertex 19.487246 3.166282 5.177697 +endloop +endfacet +facet normal -0.049537 0.998629 -0.016914 +outer loop +vertex 22.875492 3.349853 6.092598 +vertex 19.487246 3.166282 5.177697 +vertex 21.356569 3.284150 6.661976 +endloop +endfacet +facet normal 0.027002 0.955533 -0.293645 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 15.109202 2.798170 3.107773 +vertex 17.431246 2.988852 3.941785 +endloop +endfacet +facet normal -0.116626 0.975840 -0.184755 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 17.431246 2.988852 3.941785 +vertex 18.347837 2.920243 3.000808 +endloop +endfacet +facet normal -0.034878 0.995267 -0.090702 +outer loop +vertex 15.109202 2.798170 3.107773 +vertex 15.010111 2.935162 4.649097 +vertex 17.108587 3.057137 5.180579 +endloop +endfacet +facet normal -0.056758 0.995953 -0.069682 +outer loop +vertex 15.109202 2.798170 3.107773 +vertex 17.108587 3.057137 5.180579 +vertex 17.431246 2.988852 3.941785 +endloop +endfacet +facet normal -0.026645 0.994781 -0.098487 +outer loop +vertex 18.347837 2.920243 3.000808 +vertex 17.431246 2.988852 3.941785 +vertex 19.487246 3.166282 5.177697 +endloop +endfacet +facet normal -0.070482 0.994650 -0.075527 +outer loop +vertex 18.347837 2.920243 3.000808 +vertex 19.487246 3.166282 5.177697 +vertex 20.559479 3.187713 4.459331 +endloop +endfacet +facet normal -0.152667 0.987945 0.025655 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 15.538307 2.783193 8.357668 +vertex 15.527117 2.781798 8.344819 +endloop +endfacet +facet normal -0.134205 0.983559 0.120833 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 15.527117 2.781798 8.344819 +vertex 15.437763 2.799557 8.101022 +endloop +endfacet +facet normal -0.056032 0.998429 -0.000117 +outer loop +vertex 17.658047 3.119799 8.010885 +vertex 17.307825 3.099989 6.690114 +vertex 14.959599 2.968169 6.376157 +endloop +endfacet +facet normal -0.149732 0.976245 0.156610 +outer loop +vertex 17.098806 3.033245 8.015749 +vertex 17.658047 3.119799 8.010885 +vertex 14.959599 2.968169 6.376157 +endloop +endfacet +facet normal -0.071194 0.990627 0.116577 +outer loop +vertex 14.959599 2.968169 6.376157 +vertex 15.458333 2.770834 8.357668 +vertex 15.437763 2.799557 8.101022 +endloop +endfacet +facet normal -0.146431 0.977434 0.152256 +outer loop +vertex 16.196001 2.892864 8.048681 +vertex 17.098806 3.033245 8.015749 +vertex 14.959599 2.968169 6.376157 +endloop +endfacet +facet normal -0.112465 0.985441 0.127508 +outer loop +vertex 15.437763 2.799557 8.101022 +vertex 16.196001 2.892864 8.048681 +vertex 14.959599 2.968169 6.376157 +endloop +endfacet +facet normal -0.053193 0.998357 -0.021320 +outer loop +vertex 14.959599 2.968169 6.376157 +vertex 17.307825 3.099989 6.690114 +vertex 17.108587 3.057137 5.180579 +endloop +endfacet +facet normal -0.052808 0.998392 -0.020625 +outer loop +vertex 14.959599 2.968169 6.376157 +vertex 17.108587 3.057137 5.180579 +vertex 15.010111 2.935162 4.649097 +endloop +endfacet +facet normal 0.000002 0.000000 1.000000 +outer loop +vertex 15.000001 -1.500000 8.357668 +vertex 15.458335 -2.770834 8.357668 +vertex 15.533590 -2.727700 8.357668 +endloop +endfacet +facet normal -0.000003 0.000000 1.000000 +outer loop +vertex 15.000001 -1.500000 8.357668 +vertex 15.533590 -2.727700 8.357668 +vertex 15.518668 -2.552154 8.357668 +endloop +endfacet +facet normal 0.000002 0.000000 1.000000 +outer loop +vertex 15.434759 -1.559115 8.357668 +vertex 15.427221 -1.296539 8.357668 +vertex 15.000001 -1.500000 8.357668 +endloop +endfacet +facet normal 0.000003 0.000000 1.000000 +outer loop +vertex 15.518668 -2.552154 8.357668 +vertex 15.434759 -1.559115 8.357668 +vertex 15.000001 -1.500000 8.357668 +endloop +endfacet +facet normal -0.000001 0.000000 1.000000 +outer loop +vertex 15.000001 0.000000 8.357668 +vertex 15.000001 -1.500000 8.357668 +vertex 15.427221 -1.296539 8.357668 +endloop +endfacet +facet normal 0.000005 0.000000 1.000000 +outer loop +vertex 15.000001 0.000000 8.357668 +vertex 15.427221 -1.296539 8.357668 +vertex 15.402760 -0.736852 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.398325 0.000000 8.357668 +vertex 15.399197 0.125639 8.357668 +vertex 15.000001 0.000000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.402760 -0.736852 8.357668 +vertex 15.398325 0.000000 8.357668 +vertex 15.000001 0.000000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.538307 2.783193 8.357668 +vertex 15.458333 2.770834 8.357668 +vertex 15.000001 1.500000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.000001 1.500000 8.357668 +vertex 15.435318 1.565411 8.357668 +vertex 15.518668 2.552154 8.357668 +endloop +endfacet +facet normal -0.000007 0.000000 1.000000 +outer loop +vertex 15.000001 1.500000 8.357668 +vertex 15.518668 2.552154 8.357668 +vertex 15.538307 2.783193 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.435318 1.565411 8.357668 +vertex 15.000001 1.500000 8.357668 +vertex 15.000001 0.000000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.000001 0.000000 8.357668 +vertex 15.399197 0.125639 8.357668 +vertex 15.402760 0.736852 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.434760 1.559115 8.357668 +vertex 15.435318 1.565411 8.357668 +vertex 15.000001 0.000000 8.357668 +endloop +endfacet +facet normal 0.000003 0.000000 1.000000 +outer loop +vertex 15.000001 0.000000 8.357668 +vertex 15.402760 0.736852 8.357668 +vertex 15.434760 1.559115 8.357668 +endloop +endfacet +facet normal 0.000078 0.016928 -0.999857 +outer loop +vertex 43.736702 0.010560 0.445198 +vertex 40.305779 0.778190 0.457927 +vertex 42.050312 1.064699 0.462914 +endloop +endfacet +facet normal -0.006061 0.007108 -0.999956 +outer loop +vertex 43.736702 0.010560 0.445198 +vertex 42.050312 1.064699 0.462914 +vertex 44.715622 0.691151 0.444102 +endloop +endfacet +facet normal -0.010645 0.174425 -0.984613 +outer loop +vertex 37.501717 1.626225 0.611567 +vertex 41.149567 1.655752 0.577358 +vertex 42.050312 1.064699 0.462914 +endloop +endfacet +facet normal -0.017296 0.122588 -0.992307 +outer loop +vertex 37.501717 1.626225 0.611567 +vertex 42.050312 1.064699 0.462914 +vertex 40.305779 0.778190 0.457927 +endloop +endfacet +facet normal 0.015202 0.158187 -0.987292 +outer loop +vertex 44.902733 1.422932 0.564232 +vertex 44.715622 0.691151 0.444102 +vertex 42.050312 1.064699 0.462914 +endloop +endfacet +facet normal 0.009205 0.203594 -0.979012 +outer loop +vertex 44.902733 1.422932 0.564232 +vertex 42.050312 1.064699 0.462914 +vertex 41.149567 1.655752 0.577358 +endloop +endfacet +facet normal 0.023074 -0.155230 -0.987609 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 41.912167 -0.620574 0.501772 +vertex 43.736702 0.010560 0.445198 +endloop +endfacet +facet normal -0.024671 -0.225012 -0.974044 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 43.736702 0.010560 0.445198 +vertex 47.104279 -0.592443 0.499200 +endloop +endfacet +facet normal 0.001842 -0.029217 -0.999571 +outer loop +vertex 41.912167 -0.620574 0.501772 +vertex 38.036938 0.306147 0.467543 +vertex 40.305779 0.778190 0.457927 +endloop +endfacet +facet normal -0.014413 -0.047859 -0.998750 +outer loop +vertex 41.912167 -0.620574 0.501772 +vertex 40.305779 0.778190 0.457927 +vertex 43.736702 0.010560 0.445198 +endloop +endfacet +facet normal 0.012519 -0.019616 -0.999729 +outer loop +vertex 47.104279 -0.592443 0.499200 +vertex 43.736702 0.010560 0.445198 +vertex 44.715622 0.691151 0.444102 +endloop +endfacet +facet normal -0.008058 -0.057846 -0.998293 +outer loop +vertex 47.104279 -0.592443 0.499200 +vertex 44.715622 0.691151 0.444102 +vertex 47.743988 0.219856 0.446967 +endloop +endfacet +facet normal -0.034582 0.395028 -0.918018 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 36.144936 2.362384 0.979452 +vertex 37.501717 1.626225 0.611567 +endloop +endfacet +facet normal -0.039478 0.360558 -0.931901 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 37.501717 1.626225 0.611567 +vertex 34.217098 1.287853 0.619794 +endloop +endfacet +facet normal -0.010260 0.438657 -0.898596 +outer loop +vertex 36.144936 2.362384 0.979452 +vertex 40.623978 2.257716 0.877214 +vertex 41.149567 1.655752 0.577358 +endloop +endfacet +facet normal -0.011944 0.429291 -0.903087 +outer loop +vertex 36.144936 2.362384 0.979452 +vertex 41.149567 1.655752 0.577358 +vertex 37.501717 1.626225 0.611567 +endloop +endfacet +facet normal -0.015658 0.127888 -0.991665 +outer loop +vertex 34.217098 1.287853 0.619794 +vertex 37.501717 1.626225 0.611567 +vertex 40.305779 0.778190 0.457927 +endloop +endfacet +facet normal -0.020111 0.076352 -0.996878 +outer loop +vertex 34.217098 1.287853 0.619794 +vertex 40.305779 0.778190 0.457927 +vertex 38.036938 0.306147 0.467543 +endloop +endfacet +facet normal 0.041462 0.392034 -0.919016 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 48.246258 1.017376 0.542076 +vertex 44.902733 1.422932 0.564232 +endloop +endfacet +facet normal 0.035618 0.440952 -0.896824 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 44.902733 1.422932 0.564232 +vertex 44.908054 2.060014 0.877686 +endloop +endfacet +facet normal 0.017659 0.107426 -0.994056 +outer loop +vertex 48.246258 1.017376 0.542076 +vertex 47.743988 0.219856 0.446967 +vertex 44.715622 0.691151 0.444102 +endloop +endfacet +facet normal 0.012721 0.158811 -0.987227 +outer loop +vertex 48.246258 1.017376 0.542076 +vertex 44.715622 0.691151 0.444102 +vertex 44.902733 1.422932 0.564232 +endloop +endfacet +facet normal 0.024230 0.441181 -0.897091 +outer loop +vertex 44.908054 2.060014 0.877686 +vertex 44.902733 1.422932 0.564232 +vertex 41.149567 1.655752 0.577358 +endloop +endfacet +facet normal 0.021356 0.460648 -0.887326 +outer loop +vertex 44.908054 2.060014 0.877686 +vertex 41.149567 1.655752 0.577358 +vertex 40.623978 2.257716 0.877214 +endloop +endfacet +facet normal 0.050301 -0.995695 -0.077854 +outer loop +vertex 44.083652 -2.607777 2.596714 +vertex 40.060291 -2.776363 2.153366 +vertex 41.591160 -2.669416 1.774674 +endloop +endfacet +facet normal 0.045011 -0.997079 -0.061711 +outer loop +vertex 44.083652 -2.607777 2.596714 +vertex 41.591160 -2.669416 1.774674 +vertex 44.463726 -2.547658 1.902582 +endloop +endfacet +facet normal 0.024502 -0.939142 -0.342654 +outer loop +vertex 36.286263 -2.738104 1.583596 +vertex 39.933842 -2.550894 1.331318 +vertex 41.591160 -2.669416 1.774674 +endloop +endfacet +facet normal 0.019783 -0.980233 -0.196857 +outer loop +vertex 36.286263 -2.738104 1.583596 +vertex 41.591160 -2.669416 1.774674 +vertex 40.060291 -2.776363 2.153366 +endloop +endfacet +facet normal 0.056317 -0.919188 -0.389771 +outer loop +vertex 43.666763 -2.336409 1.289248 +vertex 44.463726 -2.547658 1.902582 +vertex 41.591160 -2.669416 1.774674 +endloop +endfacet +facet normal 0.047362 -0.906565 -0.419399 +outer loop +vertex 43.666763 -2.336409 1.289248 +vertex 41.591160 -2.669416 1.774674 +vertex 39.933842 -2.550894 1.331318 +endloop +endfacet +facet normal 0.091668 -0.981384 0.168765 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 42.552837 -2.614465 3.389307 +vertex 44.083652 -2.607777 2.596714 +endloop +endfacet +facet normal 0.050249 -0.956931 0.285932 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 44.083652 -2.607777 2.596714 +vertex 47.784302 -2.359905 2.775923 +endloop +endfacet +facet normal 0.052838 -0.998310 0.024212 +outer loop +vertex 42.552837 -2.614465 3.389307 +vertex 37.887997 -2.875850 2.791907 +vertex 40.060291 -2.776363 2.153366 +endloop +endfacet +facet normal 0.035231 -0.997599 0.059627 +outer loop +vertex 42.552837 -2.614465 3.389307 +vertex 40.060291 -2.776363 2.153366 +vertex 44.083652 -2.607777 2.596714 +endloop +endfacet +facet normal 0.069089 -0.996432 -0.048472 +outer loop +vertex 47.784302 -2.359905 2.775923 +vertex 44.083652 -2.607777 2.596714 +vertex 44.463726 -2.547658 1.902582 +endloop +endfacet +facet normal 0.060172 -0.998087 -0.014212 +outer loop +vertex 47.784302 -2.359905 2.775923 +vertex 44.463726 -2.547658 1.902582 +vertex 47.610008 -2.359042 1.977433 +endloop +endfacet +facet normal -0.003913 -0.823107 -0.567873 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 33.108257 -2.339576 1.027848 +vertex 36.286263 -2.738104 1.583596 +endloop +endfacet +facet normal -0.008918 -0.933554 -0.358327 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 36.286263 -2.738104 1.583596 +vertex 33.042091 -2.926533 2.155251 +endloop +endfacet +facet normal 0.007482 -0.733841 -0.679280 +outer loop +vertex 33.108257 -2.339576 1.027848 +vertex 37.724285 -2.146670 0.870290 +vertex 39.933842 -2.550894 1.331318 +endloop +endfacet +facet normal 0.001003 -0.809926 -0.586532 +outer loop +vertex 33.108257 -2.339576 1.027848 +vertex 39.933842 -2.550894 1.331318 +vertex 36.286263 -2.738104 1.583596 +endloop +endfacet +facet normal 0.020887 -0.978733 -0.204074 +outer loop +vertex 33.042091 -2.926533 2.155251 +vertex 36.286263 -2.738104 1.583596 +vertex 40.060291 -2.776363 2.153366 +endloop +endfacet +facet normal 0.021297 -0.996340 -0.082783 +outer loop +vertex 33.042091 -2.926533 2.155251 +vertex 40.060291 -2.776363 2.153366 +vertex 37.887997 -2.875850 2.791907 +endloop +endfacet +facet normal 0.040971 -0.617963 -0.785139 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 47.087532 -2.123862 1.300462 +vertex 43.666763 -2.336409 1.289248 +endloop +endfacet +facet normal 0.045439 -0.625789 -0.778668 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 43.666763 -2.336409 1.289248 +vertex 41.873085 -1.866991 0.807323 +endloop +endfacet +facet normal 0.064363 -0.926222 -0.371443 +outer loop +vertex 47.087532 -2.123862 1.300462 +vertex 47.610008 -2.359042 1.977433 +vertex 44.463726 -2.547658 1.902582 +endloop +endfacet +facet normal 0.058331 -0.918106 -0.392018 +outer loop +vertex 47.087532 -2.123862 1.300462 +vertex 44.463726 -2.547658 1.902582 +vertex 43.666763 -2.336409 1.289248 +endloop +endfacet +facet normal 0.029403 -0.659127 -0.751457 +outer loop +vertex 41.873085 -1.866991 0.807323 +vertex 43.666763 -2.336409 1.289248 +vertex 39.933842 -2.550894 1.331318 +endloop +endfacet +facet normal 0.033597 -0.666132 -0.745077 +outer loop +vertex 41.873085 -1.866991 0.807323 +vertex 39.933842 -2.550894 1.331318 +vertex 37.724285 -2.146670 0.870290 +endloop +endfacet +facet normal 1.000000 -0.000008 0.000002 +outer loop +vertex 55.000004 -0.761863 2.546710 +vertex 55.000004 -0.868634 1.982474 +vertex 55.000008 -0.484375 1.711834 +endloop +endfacet +facet normal 1.000000 0.000016 0.000007 +outer loop +vertex 55.000004 -0.761863 2.546710 +vertex 55.000008 -0.484375 1.711834 +vertex 55.000004 -0.281395 1.982474 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 -0.767072 1.334905 +vertex 55.000008 -0.281395 1.199488 +vertex 55.000008 -0.484375 1.711834 +endloop +endfacet +facet normal 1.000000 -0.000009 0.000005 +outer loop +vertex 55.000008 -0.767072 1.334905 +vertex 55.000008 -0.484375 1.711834 +vertex 55.000004 -0.868634 1.982474 +endloop +endfacet +facet normal 1.000000 0.000016 0.000007 +outer loop +vertex 55.000004 0.141782 1.341849 +vertex 55.000004 -0.281395 1.982474 +vertex 55.000008 -0.484375 1.711834 +endloop +endfacet +facet normal 1.000000 0.000000 0.000003 +outer loop +vertex 55.000004 0.141782 1.341849 +vertex 55.000008 -0.484375 1.711834 +vertex 55.000008 -0.281395 1.199488 +endloop +endfacet +facet normal 1.000000 0.000016 -0.000002 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 55.000008 -1.281250 3.086834 +vertex 55.000004 -0.761863 2.546710 +endloop +endfacet +facet normal 1.000000 -0.000009 -0.000005 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 55.000004 -0.761863 2.546710 +vertex 55.000008 -0.500000 3.024334 +endloop +endfacet +facet normal 1.000000 0.000009 -0.000001 +outer loop +vertex 55.000008 -1.281250 3.086834 +vertex 55.000008 -1.375000 2.357667 +vertex 55.000004 -0.868634 1.982474 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000001 +outer loop +vertex 55.000008 -1.281250 3.086834 +vertex 55.000004 -0.868634 1.982474 +vertex 55.000004 -0.761863 2.546710 +endloop +endfacet +facet normal 1.000000 -0.000020 -0.000005 +outer loop +vertex 55.000008 -0.500000 3.024334 +vertex 55.000004 -0.761863 2.546710 +vertex 55.000004 -0.281395 1.982474 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000003 +outer loop +vertex 55.000008 -0.500000 3.024334 +vertex 55.000004 -0.281395 1.982474 +vertex 55.000004 0.000000 2.357667 +endloop +endfacet +facet normal 1.000000 0.000000 -0.000004 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 55.000008 -0.546875 0.649334 +vertex 55.000008 -0.767072 1.334905 +endloop +endfacet +facet normal 1.000000 0.000017 -0.000009 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 55.000008 -0.767072 1.334905 +vertex 55.000008 -1.281250 1.628500 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 -0.546875 0.649334 +vertex 55.000008 0.000000 0.524334 +vertex 55.000008 -0.281395 1.199488 +endloop +endfacet +facet normal 1.000000 -0.000003 0.000000 +outer loop +vertex 55.000008 -0.546875 0.649334 +vertex 55.000008 -0.281395 1.199488 +vertex 55.000008 -0.767072 1.334905 +endloop +endfacet +facet normal 1.000000 0.000025 0.000006 +outer loop +vertex 55.000008 -1.281250 1.628500 +vertex 55.000008 -0.767072 1.334905 +vertex 55.000004 -0.868634 1.982474 +endloop +endfacet +facet normal 1.000000 0.000011 0.000001 +outer loop +vertex 55.000008 -1.281250 1.628500 +vertex 55.000004 -0.868634 1.982474 +vertex 55.000008 -1.375000 2.357667 +endloop +endfacet +facet normal 1.000000 0.000009 -0.000005 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 55.000008 0.500000 1.691000 +vertex 55.000004 0.141782 1.341849 +endloop +endfacet +facet normal 1.000000 0.000000 0.000004 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 55.000004 0.141782 1.341849 +vertex 55.000008 0.546875 0.649334 +endloop +endfacet +facet normal 1.000000 -0.000010 0.000003 +outer loop +vertex 55.000008 0.500000 1.691000 +vertex 55.000004 0.000000 2.357667 +vertex 55.000004 -0.281395 1.982474 +endloop +endfacet +facet normal 1.000000 -0.000020 -0.000004 +outer loop +vertex 55.000008 0.500000 1.691000 +vertex 55.000004 -0.281395 1.982474 +vertex 55.000004 0.141782 1.341849 +endloop +endfacet +facet normal 1.000000 0.000003 0.000009 +outer loop +vertex 55.000008 0.546875 0.649334 +vertex 55.000004 0.141782 1.341849 +vertex 55.000008 -0.281395 1.199488 +endloop +endfacet +facet normal 1.000000 0.000000 0.000000 +outer loop +vertex 55.000008 0.546875 0.649334 +vertex 55.000008 -0.281395 1.199488 +vertex 55.000008 0.000000 0.524334 +endloop +endfacet +facet normal -0.347592 -0.937177 0.029666 +outer loop +vertex 0.871076 -2.176219 3.139536 +vertex 0.727360 -2.144018 2.472892 +vertex 1.043808 -2.271605 2.150067 +endloop +endfacet +facet normal -0.280931 -0.958747 0.043383 +outer loop +vertex 0.871076 -2.176219 3.139536 +vertex 1.043808 -2.271605 2.150067 +vertex 1.281674 -2.327258 2.460489 +endloop +endfacet +facet normal -0.483806 -0.826760 -0.287052 +outer loop +vertex 0.725749 -1.969235 1.815254 +vertex 1.173840 -2.160953 1.612211 +vertex 1.043808 -2.271605 2.150067 +endloop +endfacet +facet normal -0.545184 -0.810521 -0.214080 +outer loop +vertex 0.725749 -1.969235 1.815254 +vertex 1.043808 -2.271605 2.150067 +vertex 0.727360 -2.144018 2.472892 +endloop +endfacet +facet normal -0.039823 -0.988382 -0.146683 +outer loop +vertex 1.698624 -2.233000 1.712161 +vertex 1.281674 -2.327258 2.460489 +vertex 1.043808 -2.271605 2.150067 +endloop +endfacet +facet normal -0.091052 -0.970845 -0.221743 +outer loop +vertex 1.698624 -2.233000 1.712161 +vertex 1.043808 -2.271605 2.150067 +vertex 1.173840 -2.160953 1.612211 +endloop +endfacet +facet normal -0.445325 -0.830489 0.334624 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 0.535608 -1.766233 3.710613 +vertex 0.871076 -2.176219 3.139536 +endloop +endfacet +facet normal -0.620246 -0.729234 0.288985 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 0.871076 -2.176219 3.139536 +vertex 1.143440 -2.154511 3.778884 +endloop +endfacet +facet normal -0.650841 -0.748414 0.127605 +outer loop +vertex 0.535608 -1.766233 3.710613 +vertex 0.406945 -1.789352 2.918778 +vertex 0.727360 -2.144018 2.472892 +endloop +endfacet +facet normal -0.685904 -0.718842 0.113147 +outer loop +vertex 0.535608 -1.766233 3.710613 +vertex 0.727360 -2.144018 2.472892 +vertex 0.871076 -2.176219 3.139536 +endloop +endfacet +facet normal -0.178805 -0.977787 0.109370 +outer loop +vertex 1.143440 -2.154511 3.778884 +vertex 0.871076 -2.176219 3.139536 +vertex 1.281674 -2.327258 2.460489 +endloop +endfacet +facet normal -0.207227 -0.972565 0.105706 +outer loop +vertex 1.143440 -2.154511 3.778884 +vertex 1.281674 -2.327258 2.460489 +vertex 1.609877 -2.343493 2.954529 +endloop +endfacet +facet normal -0.836686 -0.437568 -0.329379 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.752797 -1.601241 1.257680 +vertex 0.725749 -1.969235 1.815254 +endloop +endfacet +facet normal -0.841795 -0.437141 -0.316685 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.725749 -1.969235 1.815254 +vertex 0.380729 -1.598091 2.220051 +endloop +endfacet +facet normal -0.501130 -0.699600 -0.509341 +outer loop +vertex 0.752797 -1.601241 1.257680 +vertex 1.258488 -1.814558 1.053140 +vertex 1.173840 -2.160953 1.612211 +endloop +endfacet +facet normal -0.520777 -0.700671 -0.487700 +outer loop +vertex 0.752797 -1.601241 1.257680 +vertex 1.173840 -2.160953 1.612211 +vertex 0.725749 -1.969235 1.815254 +endloop +endfacet +facet normal -0.801061 -0.578984 -0.151916 +outer loop +vertex 0.380729 -1.598091 2.220051 +vertex 0.725749 -1.969235 1.815254 +vertex 0.727360 -2.144018 2.472892 +endloop +endfacet +facet normal -0.809904 -0.572781 -0.126400 +outer loop +vertex 0.380729 -1.598091 2.220051 +vertex 0.727360 -2.144018 2.472892 +vertex 0.406945 -1.789352 2.918778 +endloop +endfacet +facet normal 0.138145 -0.943925 -0.299870 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 2.251229 -2.276334 2.103142 +vertex 1.698624 -2.233000 1.712161 +endloop +endfacet +facet normal 0.054306 -0.878761 -0.474162 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 1.698624 -2.233000 1.712161 +vertex 1.937340 -1.854676 1.038357 +endloop +endfacet +facet normal 0.032398 -0.997998 -0.054319 +outer loop +vertex 2.251229 -2.276334 2.103142 +vertex 1.609877 -2.343493 2.954529 +vertex 1.281674 -2.327258 2.460489 +endloop +endfacet +facet normal 0.007619 -0.992649 -0.120787 +outer loop +vertex 2.251229 -2.276334 2.103142 +vertex 1.281674 -2.327258 2.460489 +vertex 1.698624 -2.233000 1.712161 +endloop +endfacet +facet normal -0.024677 -0.867934 -0.496066 +outer loop +vertex 1.937340 -1.854676 1.038357 +vertex 1.698624 -2.233000 1.712161 +vertex 1.173840 -2.160953 1.612211 +endloop +endfacet +facet normal -0.061487 -0.844255 -0.532402 +outer loop +vertex 1.937340 -1.854676 1.038357 +vertex 1.173840 -2.160953 1.612211 +vertex 1.258488 -1.814558 1.053140 +endloop +endfacet +facet normal -0.989048 0.147517 -0.004837 +outer loop +vertex 0.194890 1.137827 2.886624 +vertex 0.213908 1.243482 2.220033 +vertex 0.139063 0.736111 2.050549 +endloop +endfacet +facet normal -0.992676 0.120519 0.008377 +outer loop +vertex 0.194890 1.137827 2.886624 +vertex 0.139063 0.736111 2.050549 +vertex 0.108548 0.460785 2.395634 +endloop +endfacet +facet normal -0.955710 0.170720 -0.239736 +outer loop +vertex 0.318853 1.047687 1.555693 +vertex 0.206508 0.432790 1.565678 +vertex 0.139063 0.736111 2.050549 +endloop +endfacet +facet normal -0.953775 0.211887 -0.213113 +outer loop +vertex 0.318853 1.047687 1.555693 +vertex 0.139063 0.736111 2.050549 +vertex 0.213908 1.243482 2.220033 +endloop +endfacet +facet normal -0.995369 -0.009567 -0.095650 +outer loop +vertex 0.171337 -0.132326 1.801552 +vertex 0.108548 0.460785 2.395634 +vertex 0.139063 0.736111 2.050549 +endloop +endfacet +facet normal -0.990176 0.003280 -0.139785 +outer loop +vertex 0.171337 -0.132326 1.801552 +vertex 0.139063 0.736111 2.050549 +vertex 0.206508 0.432790 1.565678 +endloop +endfacet +facet normal -0.936978 0.296676 0.184540 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.440260 1.766233 3.122204 +vertex 0.194890 1.137827 2.886624 +endloop +endfacet +facet normal -0.938109 0.291692 0.186730 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 0.194890 1.137827 2.886624 +vertex 0.208924 0.785330 3.507765 +endloop +endfacet +facet normal -0.933334 0.358014 0.026725 +outer loop +vertex 0.440260 1.766233 3.122204 +vertex 0.426389 1.789352 2.328038 +vertex 0.213908 1.243482 2.220033 +endloop +endfacet +facet normal -0.934793 0.353970 0.029434 +outer loop +vertex 0.440260 1.766233 3.122204 +vertex 0.213908 1.243482 2.220033 +vertex 0.194890 1.137827 2.886624 +endloop +endfacet +facet normal -0.994680 0.078319 0.066920 +outer loop +vertex 0.208924 0.785330 3.507765 +vertex 0.194890 1.137827 2.886624 +vertex 0.108548 0.460785 2.395634 +endloop +endfacet +facet normal -0.994693 0.078095 0.066986 +outer loop +vertex 0.208924 0.785330 3.507765 +vertex 0.108548 0.460785 2.395634 +vertex 0.106944 0.041667 2.860445 +endloop +endfacet +facet normal -0.767130 0.340593 -0.543606 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 0.539062 0.645255 0.992794 +vertex 0.318853 1.047687 1.555693 +endloop +endfacet +facet normal -0.765771 0.349253 -0.540016 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 0.318853 1.047687 1.555693 +vertex 0.541146 1.598091 1.596440 +endloop +endfacet +facet normal -0.878046 0.142192 -0.456964 +outer loop +vertex 0.539062 0.645255 0.992794 +vertex 0.409722 -0.000000 1.040537 +vertex 0.206508 0.432790 1.565678 +endloop +endfacet +facet normal -0.878248 0.153103 -0.453035 +outer loop +vertex 0.539062 0.645255 0.992794 +vertex 0.206508 0.432790 1.565678 +vertex 0.318853 1.047687 1.555693 +endloop +endfacet +facet normal -0.890632 0.378373 -0.252206 +outer loop +vertex 0.541146 1.598091 1.596440 +vertex 0.318853 1.047687 1.555693 +vertex 0.213908 1.243482 2.220033 +endloop +endfacet +facet normal -0.887071 0.393161 -0.241929 +outer loop +vertex 0.541146 1.598091 1.596440 +vertex 0.213908 1.243482 2.220033 +vertex 0.426389 1.789352 2.328038 +endloop +endfacet +facet normal -0.964031 -0.167303 -0.206530 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.171354 -0.640046 2.212759 +vertex 0.171337 -0.132326 1.801552 +endloop +endfacet +facet normal -0.962614 -0.165977 -0.214069 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.171337 -0.132326 1.801552 +vertex 0.381076 -0.645255 1.256104 +endloop +endfacet +facet normal -0.997662 -0.049007 -0.047631 +outer loop +vertex 0.171354 -0.640046 2.212759 +vertex 0.106944 0.041667 2.860445 +vertex 0.108548 0.460785 2.395634 +endloop +endfacet +facet normal -0.997185 -0.047215 -0.058256 +outer loop +vertex 0.171354 -0.640046 2.212759 +vertex 0.108548 0.460785 2.395634 +vertex 0.171337 -0.132326 1.801552 +endloop +endfacet +facet normal -0.950782 -0.067091 -0.302510 +outer loop +vertex 0.381076 -0.645255 1.256104 +vertex 0.171337 -0.132326 1.801552 +vertex 0.206508 0.432790 1.565678 +endloop +endfacet +facet normal -0.947141 -0.063041 -0.314562 +outer loop +vertex 0.381076 -0.645255 1.256104 +vertex 0.206508 0.432790 1.565678 +vertex 0.409722 -0.000000 1.040537 +endloop +endfacet +facet normal 0.176119 0.983530 0.040642 +outer loop +vertex 53.935028 1.596290 2.549231 +vertex 54.087021 1.591341 2.010366 +vertex 53.385422 1.727238 1.761988 +endloop +endfacet +facet normal 0.195731 0.980302 0.026412 +outer loop +vertex 53.935028 1.596290 2.549231 +vertex 53.385422 1.727238 1.761988 +vertex 52.969688 1.803813 2.000689 +endloop +endfacet +facet normal 0.215299 0.954276 -0.207373 +outer loop +vertex 54.058685 1.502757 1.427992 +vertex 53.116249 1.692702 1.323606 +vertex 53.385422 1.727238 1.761988 +endloop +endfacet +facet normal 0.241210 0.957624 -0.157398 +outer loop +vertex 54.058685 1.502757 1.427992 +vertex 53.385422 1.727238 1.761988 +vertex 54.087021 1.591341 2.010366 +endloop +endfacet +facet normal 0.122349 0.987069 -0.103565 +outer loop +vertex 52.143909 1.843897 1.407164 +vertex 52.969688 1.803813 2.000689 +vertex 53.385422 1.727238 1.761988 +endloop +endfacet +facet normal 0.138040 0.977132 -0.161736 +outer loop +vertex 52.143909 1.843897 1.407164 +vertex 53.385422 1.727238 1.761988 +vertex 53.116249 1.692702 1.323606 +endloop +endfacet +facet normal 0.055059 0.905206 0.421391 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 55.000008 1.281250 3.086834 +vertex 53.935028 1.596290 2.549231 +endloop +endfacet +facet normal 0.267736 0.933666 0.237874 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 53.935028 1.596290 2.549231 +vertex 53.526051 1.599617 2.996491 +endloop +endfacet +facet normal 0.183352 0.975022 0.125359 +outer loop +vertex 55.000008 1.281250 3.086834 +vertex 55.000008 1.375000 2.357667 +vertex 54.087021 1.591341 2.010366 +endloop +endfacet +facet normal 0.253856 0.965205 0.062737 +outer loop +vertex 55.000008 1.281250 3.086834 +vertex 54.087021 1.591341 2.010366 +vertex 53.935028 1.596290 2.549231 +endloop +endfacet +facet normal 0.141701 0.982330 0.122265 +outer loop +vertex 53.526051 1.599617 2.996491 +vertex 53.935028 1.596290 2.549231 +vertex 52.969688 1.803813 2.000689 +endloop +endfacet +facet normal 0.201020 0.975649 0.087751 +outer loop +vertex 53.526051 1.599617 2.996491 +vertex 52.969688 1.803813 2.000689 +vertex 52.409729 1.888375 2.343264 +endloop +endfacet +facet normal 0.281243 0.863203 -0.419266 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 54.016209 1.270190 0.920681 +vertex 54.058685 1.502757 1.427992 +endloop +endfacet +facet normal 0.290181 0.867573 -0.403871 +outer loop +vertex 55.000008 1.000000 1.024334 +vertex 54.058685 1.502757 1.427992 +vertex 55.000008 1.281250 1.628500 +endloop +endfacet +facet normal 0.210646 0.871327 -0.443190 +outer loop +vertex 54.016209 1.270190 0.920681 +vertex 52.962971 1.494856 0.861782 +vertex 53.116249 1.692702 1.323606 +endloop +endfacet +facet normal 0.223803 0.878738 -0.421582 +outer loop +vertex 54.016209 1.270190 0.920681 +vertex 53.116249 1.692702 1.323606 +vertex 54.058685 1.502757 1.427992 +endloop +endfacet +facet normal 0.257870 0.953249 -0.157542 +outer loop +vertex 55.000008 1.281250 1.628500 +vertex 54.058685 1.502757 1.427992 +vertex 54.087021 1.591341 2.010366 +endloop +endfacet +facet normal 0.272780 0.954222 -0.122685 +outer loop +vertex 55.000008 1.281250 1.628500 +vertex 54.087021 1.591341 2.010366 +vertex 55.000008 1.375000 2.357667 +endloop +endfacet +facet normal 0.044312 0.934547 -0.353069 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 51.159924 1.990409 1.671474 +vertex 52.143909 1.843897 1.407164 +endloop +endfacet +facet normal 0.049913 0.922009 -0.383936 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 52.143909 1.843897 1.407164 +vertex 51.705059 1.646369 0.875757 +endloop +endfacet +facet normal 0.113147 0.991773 -0.059865 +outer loop +vertex 51.159924 1.990409 1.671474 +vertex 52.409729 1.888375 2.343264 +vertex 52.969688 1.803813 2.000689 +endloop +endfacet +facet normal 0.120095 0.987674 -0.100388 +outer loop +vertex 51.159924 1.990409 1.671474 +vertex 52.969688 1.803813 2.000689 +vertex 52.143909 1.843897 1.407164 +endloop +endfacet +facet normal 0.103964 0.901163 -0.420828 +outer loop +vertex 51.705059 1.646369 0.875757 +vertex 52.143909 1.843897 1.407164 +vertex 53.116249 1.692702 1.323606 +endloop +endfacet +facet normal 0.103885 0.901285 -0.420588 +outer loop +vertex 51.705059 1.646369 0.875757 +vertex 53.116249 1.692702 1.323606 +vertex 52.962971 1.494856 0.861782 +endloop +endfacet +facet normal -0.079497 -0.242369 -0.966922 +outer loop +vertex 1.698774 -1.194074 0.641540 +vertex 1.174004 -1.261905 0.701687 +vertex 1.044850 -0.745563 0.582879 +endloop +endfacet +facet normal -0.037206 -0.182741 -0.982457 +outer loop +vertex 1.698774 -1.194074 0.641540 +vertex 1.044850 -0.745563 0.582879 +vertex 1.282836 -0.479353 0.524350 +endloop +endfacet +facet normal -0.539801 -0.241520 -0.806401 +outer loop +vertex 0.726007 -1.050028 0.887499 +vertex 0.729391 -0.434278 0.700814 +vertex 1.044850 -0.745563 0.582879 +endloop +endfacet +facet normal -0.485511 -0.309565 -0.817587 +outer loop +vertex 0.726007 -1.050028 0.887499 +vertex 1.044850 -0.745563 0.582879 +vertex 1.174004 -1.261905 0.701687 +endloop +endfacet +facet normal -0.254551 0.014972 -0.966943 +outer loop +vertex 0.873956 0.129967 0.641424 +vertex 1.282836 -0.479353 0.524350 +vertex 1.044850 -0.745563 0.582879 +endloop +endfacet +facet normal -0.356281 -0.007064 -0.934352 +outer loop +vertex 0.873956 0.129967 0.641424 +vertex 1.044850 -0.745563 0.582879 +vertex 0.729391 -0.434278 0.700814 +endloop +endfacet +facet normal 0.076213 -0.493064 -0.866648 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 1.937340 -1.854676 1.038357 +vertex 1.698774 -1.194074 0.641540 +endloop +endfacet +facet normal 0.152938 -0.353691 -0.922774 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 1.698774 -1.194074 0.641540 +vertex 2.250708 -0.876450 0.611273 +endloop +endfacet +facet normal -0.050272 -0.541403 -0.839259 +outer loop +vertex 1.937340 -1.854676 1.038357 +vertex 1.258488 -1.814558 1.053140 +vertex 1.174004 -1.261905 0.701687 +endloop +endfacet +facet normal -0.030096 -0.522661 -0.852009 +outer loop +vertex 1.937340 -1.854676 1.038357 +vertex 1.174004 -1.261905 0.701687 +vertex 1.698774 -1.194074 0.641540 +endloop +endfacet +facet normal 0.029280 -0.145119 -0.988981 +outer loop +vertex 2.250708 -0.876450 0.611273 +vertex 1.698774 -1.194074 0.641540 +vertex 1.282836 -0.479353 0.524350 +endloop +endfacet +facet normal 0.054443 -0.085079 -0.994886 +outer loop +vertex 2.250708 -0.876450 0.611273 +vertex 1.282836 -0.479353 0.524350 +vertex 1.605710 -0.068030 0.506844 +endloop +endfacet +facet normal -0.841401 -0.321327 -0.434504 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.381076 -0.645255 1.256104 +vertex 0.726007 -1.050028 0.887499 +endloop +endfacet +facet normal -0.836848 -0.332655 -0.434772 +outer loop +vertex 0.388889 -1.203704 1.653963 +vertex 0.726007 -1.050028 0.887499 +vertex 0.752797 -1.601241 1.257680 +endloop +endfacet +facet normal -0.809887 -0.153210 -0.566225 +outer loop +vertex 0.381076 -0.645255 1.256104 +vertex 0.409722 -0.000000 1.040537 +vertex 0.729391 -0.434278 0.700814 +endloop +endfacet +facet normal -0.805090 -0.168042 -0.568851 +outer loop +vertex 0.381076 -0.645255 1.256104 +vertex 0.729391 -0.434278 0.700814 +vertex 0.726007 -1.050028 0.887499 +endloop +endfacet +facet normal -0.521874 -0.492910 -0.696194 +outer loop +vertex 0.752797 -1.601241 1.257680 +vertex 0.726007 -1.050028 0.887499 +vertex 1.174004 -1.261905 0.701687 +endloop +endfacet +facet normal -0.499400 -0.517985 -0.694472 +outer loop +vertex 0.752797 -1.601241 1.257680 +vertex 1.174004 -1.261905 0.701687 +vertex 1.258488 -1.814558 1.053140 +endloop +endfacet +facet normal -0.489586 0.240574 -0.838111 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 1.126061 0.636751 0.639624 +vertex 0.873956 0.129967 0.641424 +endloop +endfacet +facet normal -0.506824 0.235979 -0.829122 +outer loop +vertex 0.777778 1.203704 1.005815 +vertex 0.873956 0.129967 0.641424 +vertex 0.539062 0.645255 0.992794 +endloop +endfacet +facet normal -0.155176 0.079900 -0.984650 +outer loop +vertex 1.126061 0.636751 0.639624 +vertex 1.605710 -0.068030 0.506844 +vertex 1.282836 -0.479353 0.524350 +endloop +endfacet +facet normal -0.164661 0.078421 -0.983228 +outer loop +vertex 1.126061 0.636751 0.639624 +vertex 1.282836 -0.479353 0.524350 +vertex 0.873956 0.129967 0.641424 +endloop +endfacet +facet normal -0.653249 0.088217 -0.751986 +outer loop +vertex 0.539062 0.645255 0.992794 +vertex 0.873956 0.129967 0.641424 +vertex 0.729391 -0.434278 0.700814 +endloop +endfacet +facet normal -0.672584 0.080388 -0.735642 +outer loop +vertex 0.539062 0.645255 0.992794 +vertex 0.729391 -0.434278 0.700814 +vertex 0.409722 -0.000000 1.040537 +endloop +endfacet +facet normal 0.041553 -0.274803 0.960602 +outer loop +vertex 51.965214 -1.202216 4.176255 +vertex 53.058922 -1.128279 4.150095 +vertex 53.356018 -0.737245 4.249107 +endloop +endfacet +facet normal -0.011195 -0.122011 0.992466 +outer loop +vertex 51.965214 -1.202216 4.176255 +vertex 53.356018 -0.737245 4.249107 +vertex 52.912373 -0.593412 4.261785 +endloop +endfacet +facet normal 0.128077 -0.269565 0.954427 +outer loop +vertex 54.051399 -0.881209 4.115131 +vertex 54.082394 -0.427969 4.238985 +vertex 53.356018 -0.737245 4.249107 +endloop +endfacet +facet normal 0.113803 -0.324253 0.939100 +outer loop +vertex 54.051399 -0.881209 4.115131 +vertex 53.356018 -0.737245 4.249107 +vertex 53.058922 -1.128279 4.150095 +endloop +endfacet +facet normal 0.037141 0.026509 0.998958 +outer loop +vertex 53.927742 -0.016987 4.208736 +vertex 52.912373 -0.593412 4.261785 +vertex 53.356018 -0.737245 4.249107 +endloop +endfacet +facet normal -0.014965 0.067793 0.997587 +outer loop +vertex 53.927742 -0.016987 4.208736 +vertex 53.356018 -0.737245 4.249107 +vertex 54.082394 -0.427969 4.238985 +endloop +endfacet +facet normal 0.058644 -0.553201 0.830981 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 51.413963 -1.708487 3.878123 +vertex 51.965214 -1.202216 4.176255 +endloop +endfacet +facet normal 0.004120 -0.330760 0.943706 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 51.965214 -1.202216 4.176255 +vertex 50.865345 -1.042833 4.236919 +endloop +endfacet +facet normal 0.090004 -0.620383 0.779117 +outer loop +vertex 51.413963 -1.708487 3.878123 +vertex 52.884544 -1.512283 3.864470 +vertex 53.058922 -1.128279 4.150095 +endloop +endfacet +facet normal 0.057222 -0.552129 0.831793 +outer loop +vertex 51.413963 -1.708487 3.878123 +vertex 53.058922 -1.128279 4.150095 +vertex 51.965214 -1.202216 4.176255 +endloop +endfacet +facet normal 0.027912 -0.181522 0.982991 +outer loop +vertex 50.865345 -1.042833 4.236919 +vertex 51.965214 -1.202216 4.176255 +vertex 52.912373 -0.593412 4.261785 +endloop +endfacet +facet normal -0.007944 -0.019132 0.999785 +outer loop +vertex 50.865345 -1.042833 4.236919 +vertex 52.912373 -0.593412 4.261785 +vertex 52.331310 -0.308579 4.262618 +endloop +endfacet +facet normal 0.255804 -0.616356 0.744761 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 55.000008 -0.546875 4.066000 +vertex 54.051399 -0.881209 4.115131 +endloop +endfacet +facet normal 0.250243 -0.629145 0.735904 +outer loop +vertex 55.000008 -1.000000 3.691000 +vertex 54.051399 -0.881209 4.115131 +vertex 54.006409 -1.272369 3.796018 +endloop +endfacet +facet normal 0.153076 -0.220201 0.963369 +outer loop +vertex 55.000008 -0.546875 4.066000 +vertex 55.000008 0.000000 4.191000 +vertex 54.082394 -0.427969 4.238985 +endloop +endfacet +facet normal 0.144470 -0.270011 0.951957 +outer loop +vertex 55.000008 -0.546875 4.066000 +vertex 54.082394 -0.427969 4.238985 +vertex 54.051399 -0.881209 4.115131 +endloop +endfacet +facet normal 0.184278 -0.633950 0.751099 +outer loop +vertex 54.006409 -1.272369 3.796018 +vertex 54.051399 -0.881209 4.115131 +vertex 53.058922 -1.128279 4.150095 +endloop +endfacet +facet normal 0.182211 -0.638753 0.747525 +outer loop +vertex 54.006409 -1.272369 3.796018 +vertex 53.058922 -1.128279 4.150095 +vertex 52.884544 -1.512283 3.864470 +endloop +endfacet +facet normal 0.127756 0.339678 0.931825 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 53.516247 0.366665 4.125300 +vertex 53.927742 -0.016987 4.208736 +endloop +endfacet +facet normal -0.226665 0.620976 0.750341 +outer loop +vertex 55.000015 1.000000 3.691000 +vertex 53.927742 -0.016987 4.208736 +vertex 55.000008 0.546875 4.066000 +endloop +endfacet +facet normal 0.053967 0.107189 0.992773 +outer loop +vertex 53.516247 0.366665 4.125300 +vertex 52.331310 -0.308579 4.262618 +vertex 52.912373 -0.593412 4.261785 +endloop +endfacet +facet normal -0.043596 0.167437 0.984918 +outer loop +vertex 53.516247 0.366665 4.125300 +vertex 52.912373 -0.593412 4.261785 +vertex 53.927742 -0.016987 4.208736 +endloop +endfacet +facet normal 0.078160 0.102397 0.991668 +outer loop +vertex 55.000008 0.546875 4.066000 +vertex 53.927742 -0.016987 4.208736 +vertex 54.082394 -0.427969 4.238985 +endloop +endfacet +facet normal -0.052872 0.222516 0.973494 +outer loop +vertex 55.000008 0.546875 4.066000 +vertex 54.082394 -0.427969 4.238985 +vertex 55.000008 0.000000 4.191000 +endloop +endfacet +facet normal -0.354704 -0.234511 0.905091 +outer loop +vertex 1.547216 -1.136979 5.509017 +vertex 2.138190 -1.286114 5.701979 +vertex 2.290046 -0.728202 5.906047 +endloop +endfacet +facet normal -0.402864 -0.145558 0.903611 +outer loop +vertex 1.547216 -1.136979 5.509017 +vertex 2.290046 -0.728202 5.906047 +vertex 2.000845 -0.410237 5.828329 +endloop +endfacet +facet normal -0.147066 -0.220833 0.964160 +outer loop +vertex 3.079537 -1.179202 5.923172 +vertex 2.968978 -0.413501 6.081686 +vertex 2.290046 -0.728202 5.906047 +endloop +endfacet +facet normal -0.187207 -0.292097 0.937887 +outer loop +vertex 3.079537 -1.179202 5.923172 +vertex 2.290046 -0.728202 5.906047 +vertex 2.138190 -1.286114 5.701979 +endloop +endfacet +facet normal -0.230089 0.028490 0.972753 +outer loop +vertex 2.833053 0.292040 6.004605 +vertex 2.000845 -0.410237 5.828329 +vertex 2.290046 -0.728202 5.906047 +endloop +endfacet +facet normal -0.272804 0.052395 0.960642 +outer loop +vertex 2.833053 0.292040 6.004605 +vertex 2.290046 -0.728202 5.906047 +vertex 2.968978 -0.413501 6.081686 +endloop +endfacet +facet normal -0.607856 -0.414717 0.677142 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 1.394066 -1.776039 4.980145 +vertex 1.547216 -1.136979 5.509017 +endloop +endfacet +facet normal -0.622161 -0.392137 0.677601 +outer loop +vertex 0.840000 -1.566667 4.611000 +vertex 1.547216 -1.136979 5.509017 +vertex 1.153953 -0.776505 5.356543 +endloop +endfacet +facet normal -0.361797 -0.554571 0.749369 +outer loop +vertex 1.394066 -1.776039 4.980145 +vertex 2.083642 -1.867799 5.245167 +vertex 2.138190 -1.286114 5.701979 +endloop +endfacet +facet normal -0.381163 -0.533510 0.755038 +outer loop +vertex 1.394066 -1.776039 4.980145 +vertex 2.138190 -1.286114 5.701979 +vertex 1.547216 -1.136979 5.509017 +endloop +endfacet +facet normal -0.446405 -0.111459 0.887863 +outer loop +vertex 1.153953 -0.776505 5.356543 +vertex 1.547216 -1.136979 5.509017 +vertex 2.000845 -0.410237 5.828329 +endloop +endfacet +facet normal -0.461265 -0.072506 0.884295 +outer loop +vertex 1.153953 -0.776505 5.356543 +vertex 2.000845 -0.410237 5.828329 +vertex 1.693982 0.028935 5.704272 +endloop +endfacet +facet normal -0.065862 -0.497668 0.864863 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 4.467706 -0.826319 6.231944 +vertex 3.079537 -1.179202 5.923172 +endloop +endfacet +facet normal -0.094530 -0.579898 0.809186 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 3.079537 -1.179202 5.923172 +vertex 3.171496 -1.861331 5.445072 +endloop +endfacet +facet normal -0.137956 -0.144190 0.979886 +outer loop +vertex 4.467706 -0.826319 6.231944 +vertex 4.185957 0.051183 6.321401 +vertex 2.968978 -0.413501 6.081686 +endloop +endfacet +facet normal -0.157608 -0.221957 0.962234 +outer loop +vertex 4.467706 -0.826319 6.231944 +vertex 2.968978 -0.413501 6.081686 +vertex 3.079537 -1.179202 5.923172 +endloop +endfacet +facet normal -0.123155 -0.580665 0.804774 +outer loop +vertex 3.171496 -1.861331 5.445072 +vertex 3.079537 -1.179202 5.923172 +vertex 2.138190 -1.286114 5.701979 +endloop +endfacet +facet normal -0.140668 -0.603298 0.785012 +outer loop +vertex 3.171496 -1.861331 5.445072 +vertex 2.138190 -1.286114 5.701979 +vertex 2.083642 -1.867799 5.245167 +endloop +endfacet +facet normal -0.022637 0.319377 0.947357 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 2.557282 0.880763 5.799543 +vertex 2.833053 0.292040 6.004605 +endloop +endfacet +facet normal -0.346115 0.555417 0.756119 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 2.833053 0.292040 6.004605 +vertex 4.172165 0.918773 6.157211 +endloop +endfacet +facet normal -0.222871 0.117637 0.967724 +outer loop +vertex 2.557282 0.880763 5.799543 +vertex 1.693982 0.028935 5.704272 +vertex 2.000845 -0.410237 5.828329 +endloop +endfacet +facet normal -0.336073 0.165526 0.927176 +outer loop +vertex 2.557282 0.880763 5.799543 +vertex 2.000845 -0.410237 5.828329 +vertex 2.833053 0.292040 6.004605 +endloop +endfacet +facet normal -0.149265 0.078927 0.985642 +outer loop +vertex 4.172165 0.918773 6.157211 +vertex 2.833053 0.292040 6.004605 +vertex 2.968978 -0.413501 6.081686 +endloop +endfacet +facet normal -0.254484 0.175919 0.950942 +outer loop +vertex 4.172165 0.918773 6.157211 +vertex 2.968978 -0.413501 6.081686 +vertex 4.185957 0.051183 6.321401 +endloop +endfacet +facet normal 0.019993 -0.243746 -0.969633 +outer loop +vertex 9.308265 -1.414347 0.689740 +vertex 7.432445 -1.480433 0.667675 +vertex 6.950618 -0.894483 0.510445 +endloop +endfacet +facet normal 0.041964 -0.150350 -0.987742 +outer loop +vertex 9.308265 -1.414347 0.689740 +vertex 6.950618 -0.894483 0.510445 +vertex 7.816463 -0.594631 0.501587 +endloop +endfacet +facet normal -0.039810 -0.206707 -0.977593 +outer loop +vertex 5.514132 -1.298165 0.654298 +vertex 5.571075 -0.536503 0.490929 +vertex 6.950618 -0.894483 0.510445 +endloop +endfacet +facet normal -0.019320 -0.273907 -0.961562 +outer loop +vertex 5.514132 -1.298165 0.654298 +vertex 6.950618 -0.894483 0.510445 +vertex 7.432445 -1.480433 0.667675 +endloop +endfacet +facet normal -0.008654 -0.004548 -0.999952 +outer loop +vertex 5.970649 0.172968 0.514071 +vertex 7.816463 -0.594631 0.501587 +vertex 6.950618 -0.894483 0.510445 +endloop +endfacet +facet normal 0.019719 0.021498 -0.999574 +outer loop +vertex 5.970649 0.172968 0.514071 +vertex 6.950618 -0.894483 0.510445 +vertex 5.571075 -0.536503 0.490929 +endloop +endfacet +facet normal 0.039007 -0.544753 -0.837689 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 10.028108 -2.122718 1.183917 +vertex 9.308265 -1.414347 0.689740 +endloop +endfacet +facet normal 0.072566 -0.423970 -0.902765 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 9.308265 -1.414347 0.689740 +vertex 11.022995 -1.088743 0.674657 +endloop +endfacet +facet normal 0.018704 -0.579360 -0.814857 +outer loop +vertex 10.028108 -2.122718 1.183917 +vertex 7.706276 -2.079218 1.099694 +vertex 7.432445 -1.480433 0.667675 +endloop +endfacet +facet normal 0.029242 -0.551751 -0.833496 +outer loop +vertex 10.028108 -2.122718 1.183917 +vertex 7.432445 -1.480433 0.667675 +vertex 9.308265 -1.414347 0.689740 +endloop +endfacet +facet normal 0.025427 -0.179458 -0.983437 +outer loop +vertex 11.022995 -1.088743 0.674657 +vertex 9.308265 -1.414347 0.689740 +vertex 7.816463 -0.594631 0.501587 +endloop +endfacet +facet normal 0.041784 -0.077744 -0.996097 +outer loop +vertex 11.022995 -1.088743 0.674657 +vertex 7.816463 -0.594631 0.501587 +vertex 8.954733 -0.142490 0.514045 +endloop +endfacet +facet normal -0.082683 -0.448631 -0.889884 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 3.794400 -0.906173 0.616465 +vertex 5.514132 -1.298165 0.654298 +endloop +endfacet +facet normal -0.043684 -0.560753 -0.826830 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 5.514132 -1.298165 0.654298 +vertex 5.514146 -1.967330 1.108122 +endloop +endfacet +facet normal -0.043195 -0.128838 -0.990724 +outer loop +vertex 3.794400 -0.906173 0.616465 +vertex 3.996914 -0.050026 0.496299 +vertex 5.571075 -0.536503 0.490929 +endloop +endfacet +facet normal -0.025854 -0.207801 -0.977829 +outer loop +vertex 3.794400 -0.906173 0.616465 +vertex 5.571075 -0.536503 0.490929 +vertex 5.514132 -1.298165 0.654298 +endloop +endfacet +facet normal -0.047506 -0.560655 -0.826686 +outer loop +vertex 5.514146 -1.967330 1.108122 +vertex 5.514132 -1.298165 0.654298 +vertex 7.432445 -1.480433 0.667675 +endloop +endfacet +facet normal -0.033447 -0.594788 -0.803187 +outer loop +vertex 5.514146 -1.967330 1.108122 +vertex 7.432445 -1.480433 0.667675 +vertex 7.706276 -2.079218 1.099694 +endloop +endfacet +facet normal -0.085594 0.277348 -0.956949 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 6.811232 0.793336 0.618684 +vertex 5.970649 0.172968 0.514071 +endloop +endfacet +facet normal 0.116135 0.459484 -0.880561 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 5.970649 0.172968 0.514071 +vertex 4.148535 0.817264 0.609955 +endloop +endfacet +facet normal -0.017477 0.071479 -0.997289 +outer loop +vertex 6.811232 0.793336 0.618684 +vertex 8.954733 -0.142490 0.514045 +vertex 7.816463 -0.594631 0.501587 +endloop +endfacet +facet normal 0.040235 0.112897 -0.992792 +outer loop +vertex 6.811232 0.793336 0.618684 +vertex 7.816463 -0.594631 0.501587 +vertex 5.970649 0.172968 0.514071 +endloop +endfacet +facet normal -0.034199 0.051816 -0.998071 +outer loop +vertex 4.148535 0.817264 0.609955 +vertex 5.970649 0.172968 0.514071 +vertex 5.571075 -0.536503 0.490929 +endloop +endfacet +facet normal 0.034895 0.123858 -0.991686 +outer loop +vertex 4.148535 0.817264 0.609955 +vertex 5.571075 -0.536503 0.490929 +vertex 3.996914 -0.050026 0.496299 +endloop +endfacet +facet normal -0.177314 0.972452 0.151314 +outer loop +vertex 1.413100 2.191117 4.134416 +vertex 1.957623 2.235735 4.485753 +vertex 2.211112 2.373071 3.900183 +endloop +endfacet +facet normal -0.192322 0.975924 0.102886 +outer loop +vertex 1.413100 2.191117 4.134416 +vertex 2.211112 2.373071 3.900183 +vertex 1.957623 2.368998 3.464983 +endloop +endfacet +facet normal 0.012141 0.988030 0.153781 +outer loop +vertex 2.818975 2.265612 4.542609 +vertex 2.888844 2.402780 3.655801 +vertex 2.211112 2.373071 3.900183 +endloop +endfacet +facet normal -0.047645 0.976865 0.208483 +outer loop +vertex 2.818975 2.265612 4.542609 +vertex 2.211112 2.373071 3.900183 +vertex 1.957623 2.235735 4.485753 +endloop +endfacet +facet normal -0.006157 0.999964 -0.005773 +outer loop +vertex 2.818976 2.370519 2.809858 +vertex 1.957623 2.368998 3.464983 +vertex 2.211112 2.373071 3.900183 +endloop +endfacet +facet normal -0.055802 0.997881 -0.033447 +outer loop +vertex 2.818976 2.370519 2.809858 +vertex 2.211112 2.373071 3.900183 +vertex 2.888844 2.402780 3.655801 +endloop +endfacet +facet normal -0.591580 0.781961 0.196391 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 1.052529 1.776505 4.699124 +vertex 1.413100 2.191117 4.134416 +endloop +endfacet +facet normal -0.579142 0.747851 0.324520 +outer loop +vertex 0.560000 1.566667 4.051001 +vertex 1.413100 2.191117 4.134416 +vertex 1.052529 2.154190 3.576036 +endloop +endfacet +facet normal -0.353218 0.863464 0.360093 +outer loop +vertex 1.052529 1.776505 4.699124 +vertex 1.664815 1.871528 5.071865 +vertex 1.957623 2.235735 4.485753 +endloop +endfacet +facet normal -0.334023 0.848772 0.409897 +outer loop +vertex 1.052529 1.776505 4.699124 +vertex 1.957623 2.235735 4.485753 +vertex 1.413100 2.191117 4.134416 +endloop +endfacet +facet normal -0.221081 0.972093 0.078474 +outer loop +vertex 1.052529 2.154190 3.576036 +vertex 1.413100 2.191117 4.134416 +vertex 1.957623 2.368998 3.464983 +endloop +endfacet +facet normal -0.222661 0.972618 0.066613 +outer loop +vertex 1.052529 2.154190 3.576036 +vertex 1.957623 2.368998 3.464983 +vertex 1.664815 2.340921 2.896196 +endloop +endfacet +facet normal 0.017576 0.932290 0.361283 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 4.161652 2.331292 4.307803 +vertex 2.818975 2.265612 4.542609 +endloop +endfacet +facet normal -0.053358 0.891025 0.450808 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 2.818975 2.265612 4.542609 +vertex 2.553637 1.874253 5.284729 +endloop +endfacet +facet normal 0.009362 0.995815 0.090909 +outer loop +vertex 4.161652 2.331292 4.307803 +vertex 4.101852 2.427727 3.257616 +vertex 2.888844 2.402780 3.655801 +endloop +endfacet +facet normal -0.021913 0.988270 0.151135 +outer loop +vertex 4.161652 2.331292 4.307803 +vertex 2.888844 2.402780 3.655801 +vertex 2.818975 2.265612 4.542609 +endloop +endfacet +facet normal -0.060539 0.891686 0.448589 +outer loop +vertex 2.553637 1.874253 5.284729 +vertex 2.818975 2.265612 4.542609 +vertex 1.957623 2.235735 4.485753 +endloop +endfacet +facet normal -0.117852 0.868776 0.480976 +outer loop +vertex 2.553637 1.874253 5.284729 +vertex 1.957623 2.235735 4.485753 +vertex 1.664815 1.871528 5.071865 +endloop +endfacet +facet normal 0.155860 0.967636 -0.198467 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 2.553637 2.266812 2.095851 +vertex 2.818976 2.370519 2.809858 +endloop +endfacet +facet normal -0.144269 0.890113 -0.432302 +outer loop +vertex 4.272728 1.792930 1.135445 +vertex 2.818976 2.370519 2.809858 +vertex 4.161652 2.306381 2.229715 +endloop +endfacet +facet normal 0.026538 0.997666 -0.062909 +outer loop +vertex 2.553637 2.266812 2.095851 +vertex 1.664815 2.340921 2.896196 +vertex 1.957623 2.368998 3.464983 +endloop +endfacet +facet normal -0.086627 0.989971 -0.111598 +outer loop +vertex 2.553637 2.266812 2.095851 +vertex 1.957623 2.368998 3.464983 +vertex 2.818976 2.370519 2.809858 +endloop +endfacet +facet normal 0.030174 0.998721 -0.040579 +outer loop +vertex 4.161652 2.306381 2.229715 +vertex 2.818976 2.370519 2.809858 +vertex 2.888844 2.402780 3.655801 +endloop +endfacet +facet normal -0.059923 0.990907 -0.120465 +outer loop +vertex 4.161652 2.306381 2.229715 +vertex 2.888844 2.402780 3.655801 +vertex 4.101852 2.427727 3.257616 +endloop +endfacet +facet normal -0.024915 -0.999333 0.026680 +outer loop +vertex 6.154458 -2.483407 3.738467 +vertex 5.628341 -2.494678 2.824974 +vertex 6.979553 -2.537423 2.485754 +endloop +endfacet +facet normal -0.051736 -0.998620 0.008983 +outer loop +vertex 6.154458 -2.483407 3.738467 +vertex 6.979553 -2.537423 2.485754 +vertex 7.876621 -2.580005 2.918531 +endloop +endfacet +facet normal -0.046870 -0.980837 -0.189108 +outer loop +vertex 5.521408 -2.354881 1.900373 +vertex 7.436999 -2.427878 1.804205 +vertex 6.979553 -2.537423 2.485754 +endloop +endfacet +facet normal -0.066795 -0.987667 -0.141609 +outer loop +vertex 5.521408 -2.354881 1.900373 +vertex 6.979553 -2.537423 2.485754 +vertex 5.628341 -2.494678 2.824974 +endloop +endfacet +facet normal -0.001211 -0.995435 -0.095434 +outer loop +vertex 9.316480 -2.494374 2.007067 +vertex 7.876621 -2.580005 2.918531 +vertex 6.979553 -2.537423 2.485754 +endloop +endfacet +facet normal -0.016568 -0.985391 -0.169501 +outer loop +vertex 9.316480 -2.494374 2.007067 +vertex 6.979553 -2.537423 2.485754 +vertex 7.436999 -2.427878 1.804205 +endloop +endfacet +facet normal 0.031593 -0.945072 0.325333 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 4.454588 -2.322237 4.371733 +vertex 6.154458 -2.483407 3.738467 +endloop +endfacet +facet normal -0.154841 -0.955382 0.251534 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 6.154458 -2.483407 3.738467 +vertex 7.178772 -2.418575 4.615271 +endloop +endfacet +facet normal -0.012858 -0.994788 0.101147 +outer loop +vertex 4.454588 -2.322237 4.371733 +vertex 4.081018 -2.428884 3.275362 +vertex 5.628341 -2.494678 2.824974 +endloop +endfacet +facet normal -0.073964 -0.995749 0.054885 +outer loop +vertex 4.454588 -2.322237 4.371733 +vertex 5.628341 -2.494678 2.824974 +vertex 6.154458 -2.483407 3.738467 +endloop +endfacet +facet normal -0.013366 -0.995919 0.089256 +outer loop +vertex 7.178772 -2.418575 4.615271 +vertex 6.154458 -2.483407 3.738467 +vertex 7.876621 -2.580005 2.918531 +endloop +endfacet +facet normal -0.067176 -0.995483 0.067083 +outer loop +vertex 7.178772 -2.418575 4.615271 +vertex 7.876621 -2.580005 2.918531 +vertex 9.061987 -2.615098 3.584776 +endloop +endfacet +facet normal -0.059376 -0.896912 -0.438204 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 5.514146 -1.967330 1.108122 +vertex 5.521408 -2.354881 1.900373 +endloop +endfacet +facet normal -0.090043 -0.926304 -0.365858 +outer loop +vertex 3.212122 -1.828283 1.135445 +vertex 5.521408 -2.354881 1.900373 +vertex 3.804913 -2.318981 2.231933 +endloop +endfacet +facet normal -0.047082 -0.887985 -0.457456 +outer loop +vertex 5.514146 -1.967330 1.108122 +vertex 7.706276 -2.079218 1.099694 +vertex 7.436999 -2.427878 1.804205 +endloop +endfacet +facet normal -0.056189 -0.897066 -0.438309 +outer loop +vertex 5.514146 -1.967330 1.108122 +vertex 7.436999 -2.427878 1.804205 +vertex 5.521408 -2.354881 1.900373 +endloop +endfacet +facet normal -0.048457 -0.988414 -0.143842 +outer loop +vertex 3.804913 -2.318981 2.231933 +vertex 5.521408 -2.354881 1.900373 +vertex 5.628341 -2.494678 2.824974 +endloop +endfacet +facet normal -0.067534 -0.993932 -0.086819 +outer loop +vertex 3.804913 -2.318981 2.231933 +vertex 5.628341 -2.494678 2.824974 +vertex 4.081018 -2.428884 3.275362 +endloop +endfacet +facet normal 0.037829 -0.959677 -0.278547 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 11.040919 -2.582739 2.545703 +vertex 9.316480 -2.494374 2.007067 +endloop +endfacet +facet normal 0.013280 -0.915584 -0.401908 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 9.316480 -2.494374 2.007067 +vertex 10.028108 -2.122718 1.183917 +endloop +endfacet +facet normal -0.005840 -0.999091 -0.042236 +outer loop +vertex 11.040919 -2.582739 2.545703 +vertex 9.061987 -2.615098 3.584776 +vertex 7.876621 -2.580005 2.918531 +endloop +endfacet +facet normal -0.014561 -0.993107 -0.116304 +outer loop +vertex 11.040919 -2.582739 2.545703 +vertex 7.876621 -2.580005 2.918531 +vertex 9.316480 -2.494374 2.007067 +endloop +endfacet +facet normal 0.011175 -0.914939 -0.403438 +outer loop +vertex 10.028108 -2.122718 1.183917 +vertex 9.316480 -2.494374 2.007067 +vertex 7.436999 -2.427878 1.804205 +endloop +endfacet +facet normal -0.000692 -0.896144 -0.443763 +outer loop +vertex 10.028108 -2.122718 1.183917 +vertex 7.436999 -2.427878 1.804205 +vertex 7.706276 -2.079218 1.099694 +endloop +endfacet +facet normal -0.040521 -0.997608 -0.056007 +outer loop +vertex 17.317537 -3.084208 5.004945 +vertex 16.903872 -2.995256 3.719807 +vertex 18.486927 -3.029857 3.190796 +endloop +endfacet +facet normal -0.046639 -0.997112 -0.059936 +outer loop +vertex 17.317537 -3.084208 5.004945 +vertex 18.486927 -3.029857 3.190796 +vertex 19.410824 -3.104883 3.720019 +endloop +endfacet +facet normal -0.053940 -0.980851 -0.187143 +outer loop +vertex 16.758194 -2.787295 2.417759 +vertex 19.016983 -2.881913 2.262618 +vertex 18.486927 -3.029857 3.190796 +endloop +endfacet +facet normal -0.071516 -0.986171 -0.149509 +outer loop +vertex 16.758194 -2.787295 2.417759 +vertex 18.486927 -3.029857 3.190796 +vertex 16.903872 -2.995256 3.719807 +endloop +endfacet +facet normal -0.011128 -0.992556 -0.121283 +outer loop +vertex 21.142828 -2.968985 2.448945 +vertex 19.410824 -3.104883 3.720019 +vertex 18.486927 -3.029857 3.190796 +endloop +endfacet +facet normal -0.025313 -0.984870 -0.171436 +outer loop +vertex 21.142828 -2.968985 2.448945 +vertex 18.486927 -3.029857 3.190796 +vertex 19.016983 -2.881913 2.262618 +endloop +endfacet +facet normal -0.053978 -0.998535 -0.003898 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 17.317537 -3.084208 5.004945 +vertex 18.135849 -3.132686 6.091653 +endloop +endfacet +facet normal -0.053604 -0.993996 0.095390 +outer loop +vertex 18.135849 -3.132686 6.091653 +vertex 15.780942 -2.814433 8.084641 +vertex 15.436029 -2.794299 8.100622 +endloop +endfacet +facet normal -0.053600 -0.993996 0.095388 +outer loop +vertex 15.436029 -2.794299 8.100622 +vertex 15.539762 -2.781838 8.288755 +vertex 15.458335 -2.770834 8.357668 +endloop +endfacet +facet normal -0.053602 -0.993996 0.095392 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 18.135849 -3.132686 6.091653 +vertex 15.436029 -2.794299 8.100622 +endloop +endfacet +facet normal -0.053597 -0.993996 0.095394 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 14.999439 -2.963156 6.095771 +vertex 15.436029 -2.794299 8.100622 +endloop +endfacet +facet normal -0.049442 -0.998435 -0.026141 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 14.808001 -2.908951 4.387503 +vertex 16.903872 -2.995256 3.719807 +endloop +endfacet +facet normal -0.073363 -0.996274 -0.045343 +outer loop +vertex 14.999439 -2.963156 6.095771 +vertex 16.903872 -2.995256 3.719807 +vertex 17.317537 -3.084208 5.004945 +endloop +endfacet +facet normal -0.025465 -0.999353 -0.025406 +outer loop +vertex 18.135849 -3.132686 6.091653 +vertex 17.317537 -3.084208 5.004945 +vertex 19.410824 -3.104883 3.720019 +endloop +endfacet +facet normal -0.039013 -0.998704 -0.032681 +outer loop +vertex 18.135849 -3.132686 6.091653 +vertex 19.410824 -3.104883 3.720019 +vertex 20.531229 -3.172596 4.451771 +endloop +endfacet +facet normal -0.048406 -0.919580 -0.389908 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 16.677395 -2.328955 1.346818 +vertex 16.758194 -2.787295 2.417759 +endloop +endfacet +facet normal -0.080440 -0.949832 -0.302241 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 16.758194 -2.787295 2.417759 +vertex 14.374381 -2.709676 2.808274 +endloop +endfacet +facet normal -0.050820 -0.907086 -0.417866 +outer loop +vertex 16.677395 -2.328955 1.346818 +vertex 19.305408 -2.463877 1.320084 +vertex 19.016983 -2.881913 2.262618 +endloop +endfacet +facet normal -0.065183 -0.919157 -0.388461 +outer loop +vertex 16.677395 -2.328955 1.346818 +vertex 19.016983 -2.881913 2.262618 +vertex 16.758194 -2.787295 2.417759 +endloop +endfacet +facet normal -0.056911 -0.986856 -0.151252 +outer loop +vertex 14.374381 -2.709676 2.808274 +vertex 16.758194 -2.787295 2.417759 +vertex 16.903872 -2.995256 3.719807 +endloop +endfacet +facet normal -0.074213 -0.991724 -0.104763 +outer loop +vertex 14.374381 -2.709676 2.808274 +vertex 16.903872 -2.995256 3.719807 +vertex 14.808001 -2.908951 4.387503 +endloop +endfacet +facet normal 0.017575 -0.964533 -0.263376 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 22.986065 -3.078571 2.973266 +vertex 21.142828 -2.968985 2.448945 +endloop +endfacet +facet normal -0.006963 -0.923448 -0.383662 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 21.142828 -2.968985 2.448945 +vertex 22.056328 -2.526146 1.366480 +endloop +endfacet +facet normal -0.009040 -0.996881 -0.078405 +outer loop +vertex 22.986065 -3.078571 2.973266 +vertex 20.531229 -3.172596 4.451771 +vertex 19.410824 -3.104883 3.720019 +endloop +endfacet +facet normal -0.020734 -0.990740 -0.134179 +outer loop +vertex 22.986065 -3.078571 2.973266 +vertex 19.410824 -3.104883 3.720019 +vertex 21.142828 -2.968985 2.448945 +endloop +endfacet +facet normal -0.004391 -0.924230 -0.381811 +outer loop +vertex 22.056328 -2.526146 1.366480 +vertex 21.142828 -2.968985 2.448945 +vertex 19.016983 -2.881913 2.262618 +endloop +endfacet +facet normal -0.013758 -0.912470 -0.408912 +outer loop +vertex 22.056328 -2.526146 1.366480 +vertex 19.016983 -2.881913 2.262618 +vertex 19.305408 -2.463877 1.320084 +endloop +endfacet +facet normal 0.009464 -0.265393 -0.964094 +outer loop +vertex 21.152800 -1.671543 0.753253 +vertex 19.011806 -1.749285 0.753637 +vertex 18.485788 -1.047572 0.555308 +endloop +endfacet +facet normal 0.039705 -0.143966 -0.988786 +outer loop +vertex 21.152800 -1.671543 0.753253 +vertex 18.485788 -1.047572 0.555308 +vertex 19.435152 -0.680933 0.540048 +endloop +endfacet +facet normal -0.045444 -0.214786 -0.975603 +outer loop +vertex 16.746109 -1.541689 0.745126 +vertex 16.898190 -0.628839 0.537072 +vertex 18.485788 -1.047572 0.555308 +endloop +endfacet +facet normal -0.022762 -0.287675 -0.957458 +outer loop +vertex 16.746109 -1.541689 0.745126 +vertex 18.485788 -1.047572 0.555308 +vertex 19.011806 -1.749285 0.753637 +endloop +endfacet +facet normal -0.017933 0.004821 -0.999828 +outer loop +vertex 17.376574 0.244999 0.581435 +vertex 19.435152 -0.680933 0.540048 +vertex 18.485788 -1.047572 0.555308 +endloop +endfacet +facet normal 0.021717 0.038830 -0.999010 +outer loop +vertex 17.376574 0.244999 0.581435 +vertex 18.485788 -1.047572 0.555308 +vertex 16.898190 -0.628839 0.537072 +endloop +endfacet +facet normal 0.003884 -0.580278 -0.814409 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 22.056328 -2.526146 1.366480 +vertex 21.152800 -1.671543 0.753253 +endloop +endfacet +facet normal 0.052271 -0.369286 -0.927845 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 21.152800 -1.671543 0.753253 +vertex 23.085478 -1.280291 0.706413 +endloop +endfacet +facet normal -0.000852 -0.621409 -0.783486 +outer loop +vertex 22.056328 -2.526146 1.366480 +vertex 19.305408 -2.463877 1.320084 +vertex 19.011806 -1.749285 0.753637 +endloop +endfacet +facet normal 0.020495 -0.568482 -0.822441 +outer loop +vertex 22.056328 -2.526146 1.366480 +vertex 19.011806 -1.749285 0.753637 +vertex 21.152800 -1.671543 0.753253 +endloop +endfacet +facet normal 0.014057 -0.187031 -0.982253 +outer loop +vertex 23.085478 -1.280291 0.706413 +vertex 21.152800 -1.671543 0.753253 +vertex 19.435152 -0.680933 0.540048 +endloop +endfacet +facet normal 0.036513 -0.054596 -0.997841 +outer loop +vertex 23.085478 -1.280291 0.706413 +vertex 19.435152 -0.680933 0.540048 +vertex 20.618484 -0.134188 0.553434 +endloop +endfacet +facet normal -0.068268 -0.478058 -0.875671 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 14.364196 -1.103987 0.691866 +vertex 16.746109 -1.541689 0.745126 +endloop +endfacet +facet normal -0.032443 -0.605127 -0.795467 +outer loop +vertex 13.235235 -2.149359 1.350582 +vertex 16.746109 -1.541689 0.745126 +vertex 16.677395 -2.328955 1.346818 +endloop +endfacet +facet normal -0.036678 -0.127304 -0.991185 +outer loop +vertex 14.364196 -1.103987 0.691866 +vertex 14.778099 -0.081578 0.545235 +vertex 16.898190 -0.628839 0.537072 +endloop +endfacet +facet normal -0.018478 -0.219254 -0.975493 +outer loop +vertex 14.364196 -1.103987 0.691866 +vertex 16.898190 -0.628839 0.537072 +vertex 16.746109 -1.541689 0.745126 +endloop +endfacet +facet normal -0.052309 -0.603519 -0.795631 +outer loop +vertex 16.677395 -2.328955 1.346818 +vertex 16.746109 -1.541689 0.745126 +vertex 19.011806 -1.749285 0.753637 +endloop +endfacet +facet normal -0.040268 -0.630799 -0.774901 +outer loop +vertex 16.677395 -2.328955 1.346818 +vertex 19.011806 -1.749285 0.753637 +vertex 19.305408 -2.463877 1.320084 +endloop +endfacet +facet normal -0.114675 0.329351 -0.937218 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 18.347206 1.014438 0.733063 +vertex 17.376574 0.244999 0.581435 +endloop +endfacet +facet normal 0.107539 0.496098 -0.861581 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 17.376574 0.244999 0.581435 +vertex 15.017767 0.994756 0.718727 +endloop +endfacet +facet normal -0.031693 0.092959 -0.995165 +outer loop +vertex 18.347206 1.014438 0.733063 +vertex 20.618484 -0.134188 0.553434 +vertex 19.435152 -0.680933 0.540048 +endloop +endfacet +facet normal 0.043249 0.140367 -0.989155 +outer loop +vertex 18.347206 1.014438 0.733063 +vertex 19.435152 -0.680933 0.540048 +vertex 17.376574 0.244999 0.581435 +endloop +endfacet +facet normal -0.035721 0.070167 -0.996895 +outer loop +vertex 15.017767 0.994756 0.718727 +vertex 17.376574 0.244999 0.581435 +vertex 16.898190 -0.628839 0.537072 +endloop +endfacet +facet normal 0.035271 0.151376 -0.987847 +outer loop +vertex 15.017767 0.994756 0.718727 +vertex 16.898190 -0.628839 0.537072 +vertex 14.778099 -0.081578 0.545235 +endloop +endfacet +facet normal -0.207907 -0.234473 0.949630 +outer loop +vertex 8.814745 -1.206727 6.949300 +vertex 10.684258 -1.438964 7.301260 +vertex 11.230325 -0.752508 7.590306 +endloop +endfacet +facet normal -0.221349 -0.176374 0.959112 +outer loop +vertex 8.814745 -1.206727 6.949300 +vertex 11.230325 -0.752508 7.590306 +vertex 10.351794 -0.324887 7.466191 +endloop +endfacet +facet normal -0.182416 -0.136784 0.973660 +outer loop +vertex 12.766925 -1.493038 7.774157 +vertex 12.825415 -0.437871 7.933350 +vertex 11.230325 -0.752508 7.590306 +endloop +endfacet +facet normal -0.221337 -0.223614 0.949214 +outer loop +vertex 12.766925 -1.493038 7.774157 +vertex 11.230325 -0.752508 7.590306 +vertex 10.684258 -1.438964 7.301260 +endloop +endfacet +facet normal -0.175156 -0.074923 0.981686 +outer loop +vertex 12.479491 0.556369 7.913082 +vertex 10.351794 -0.324887 7.466191 +vertex 11.230325 -0.752508 7.590306 +endloop +endfacet +facet normal -0.200587 -0.049845 0.978407 +outer loop +vertex 12.479491 0.556369 7.913082 +vertex 11.230325 -0.752508 7.590306 +vertex 12.825415 -0.437871 7.933350 +endloop +endfacet +facet normal -0.175617 -0.583240 0.793089 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 8.131913 -1.975336 6.232861 +vertex 8.814745 -1.206727 6.949300 +endloop +endfacet +facet normal -0.231487 -0.428725 0.873275 +outer loop +vertex 5.333334 -1.828283 5.721304 +vertex 8.814745 -1.206727 6.949300 +vertex 7.198318 -0.798686 6.721142 +endloop +endfacet +facet normal -0.207574 -0.580538 0.787330 +outer loop +vertex 8.131913 -1.975336 6.232861 +vertex 10.391977 -2.143262 6.704890 +vertex 10.684258 -1.438964 7.301260 +endloop +endfacet +facet normal -0.219985 -0.553386 0.803349 +outer loop +vertex 8.131913 -1.975336 6.232861 +vertex 10.684258 -1.438964 7.301260 +vertex 8.814745 -1.206727 6.949300 +endloop +endfacet +facet normal -0.191770 -0.225608 0.955157 +outer loop +vertex 7.198318 -0.798686 6.721142 +vertex 8.814745 -1.206727 6.949300 +vertex 10.351794 -0.324887 7.466191 +endloop +endfacet +facet normal -0.211874 -0.115797 0.970413 +outer loop +vertex 7.198318 -0.798686 6.721142 +vertex 10.351794 -0.324887 7.466191 +vertex 9.218365 0.199331 7.281278 +endloop +endfacet +facet normal -0.252034 -0.090897 0.963440 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 15.000001 -1.500000 8.357668 +vertex 12.766925 -1.493038 7.774157 +endloop +endfacet +facet normal -0.387026 -0.446878 0.806542 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 12.766925 -1.493038 7.774157 +vertex 12.673998 -2.361658 7.248292 +endloop +endfacet +facet normal -0.191514 0.000000 0.981490 +outer loop +vertex 15.000001 -1.500000 8.357668 +vertex 15.000001 0.000000 8.357668 +vertex 12.825415 -0.437871 7.933350 +endloop +endfacet +facet normal -0.251025 -0.130785 0.959105 +outer loop +vertex 15.000001 -1.500000 8.357668 +vertex 12.825415 -0.437871 7.933350 +vertex 12.766925 -1.493038 7.774157 +endloop +endfacet +facet normal -0.205027 -0.490744 0.846838 +outer loop +vertex 12.673998 -2.361658 7.248292 +vertex 12.766925 -1.493038 7.774157 +vertex 10.684258 -1.438964 7.301260 +endloop +endfacet +facet normal -0.241796 -0.566639 0.787690 +outer loop +vertex 12.673998 -2.361658 7.248292 +vertex 10.684258 -1.438964 7.301260 +vertex 10.391977 -2.143262 6.704890 +endloop +endfacet +facet normal -0.178002 0.042065 0.983131 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 11.600214 1.316005 7.721383 +vertex 12.479491 0.556369 7.913082 +endloop +endfacet +facet normal -0.199291 0.071876 0.977301 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 12.479491 0.556369 7.913082 +vertex 15.000001 1.500000 8.357668 +endloop +endfacet +facet normal -0.171271 -0.022875 0.984958 +outer loop +vertex 11.600214 1.316005 7.721383 +vertex 9.218365 0.199331 7.281278 +vertex 10.351794 -0.324887 7.466191 +endloop +endfacet +facet normal -0.207969 0.006110 0.978116 +outer loop +vertex 11.600214 1.316005 7.721383 +vertex 10.351794 -0.324887 7.466191 +vertex 12.479491 0.556369 7.913082 +endloop +endfacet +facet normal -0.160593 -0.035767 0.986373 +outer loop +vertex 15.000001 1.500000 8.357668 +vertex 12.479491 0.556369 7.913082 +vertex 12.825415 -0.437871 7.933350 +endloop +endfacet +facet normal -0.191514 0.000000 0.981490 +outer loop +vertex 15.000001 1.500000 8.357668 +vertex 12.825415 -0.437871 7.933350 +vertex 15.000001 0.000000 8.357668 +endloop +endfacet +facet normal -0.084059 0.991947 0.094739 +outer loop +vertex 8.334797 2.473336 5.217071 +vertex 10.207706 2.583551 5.724866 +vertex 11.047479 2.728813 4.949031 +endloop +endfacet +facet normal -0.088305 0.994612 0.054306 +outer loop +vertex 8.334797 2.473336 5.217071 +vertex 11.047479 2.728813 4.949031 +vertex 10.230559 2.689977 4.331943 +endloop +endfacet +facet normal -0.053528 0.997617 0.043540 +outer loop +vertex 12.456796 2.756068 6.057173 +vertex 12.761621 2.829240 4.755350 +vertex 11.047479 2.728813 4.949031 +endloop +endfacet +facet normal -0.089260 0.992013 0.089121 +outer loop +vertex 12.456796 2.756068 6.057173 +vertex 11.047479 2.728813 4.949031 +vertex 10.207706 2.583551 5.724866 +endloop +endfacet +facet normal -0.036886 0.999221 -0.014055 +outer loop +vertex 12.535257 2.764018 3.547343 +vertex 10.230559 2.689977 4.331943 +vertex 11.047479 2.728813 4.949031 +endloop +endfacet +facet normal -0.063164 0.997119 -0.042001 +outer loop +vertex 12.535257 2.764018 3.547343 +vertex 11.047479 2.728813 4.949031 +vertex 12.761621 2.829240 4.755350 +endloop +endfacet +facet normal -0.119460 0.943694 0.308497 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 6.831068 2.026018 6.003131 +vertex 8.334797 2.473336 5.217071 +endloop +endfacet +facet normal -0.128361 0.964920 0.229027 +outer loop +vertex 4.272728 1.863636 5.509182 +vertex 8.334797 2.473336 5.217071 +vertex 6.835320 2.433162 4.545931 +endloop +endfacet +facet normal -0.131981 0.946425 0.294720 +outer loop +vertex 6.831068 2.026018 6.003131 +vertex 9.113427 2.193287 6.488069 +vertex 10.207706 2.583551 5.724866 +endloop +endfacet +facet normal -0.133205 0.949137 0.285299 +outer loop +vertex 6.831068 2.026018 6.003131 +vertex 10.207706 2.583551 5.724866 +vertex 8.334797 2.473336 5.217071 +endloop +endfacet +facet normal -0.069128 0.993074 0.095003 +outer loop +vertex 6.835320 2.433162 4.545931 +vertex 8.334797 2.473336 5.217071 +vertex 10.230559 2.689977 4.331943 +endloop +endfacet +facet normal -0.073565 0.996864 0.029151 +outer loop +vertex 6.835320 2.433162 4.545931 +vertex 10.230559 2.689977 4.331943 +vertex 9.147440 2.632157 3.575842 +endloop +endfacet +facet normal -0.099396 0.987373 0.123348 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 14.959599 2.968169 6.376157 +vertex 12.456796 2.756068 6.057173 +endloop +endfacet +facet normal -0.162862 0.964840 0.206300 +outer loop +vertex 15.458333 2.770834 8.357668 +vertex 12.456796 2.756068 6.057173 +vertex 11.587097 2.393663 7.065519 +endloop +endfacet +facet normal -0.048012 0.998637 -0.020490 +outer loop +vertex 14.959599 2.968169 6.376157 +vertex 15.010111 2.935162 4.649097 +vertex 12.761621 2.829240 4.755350 +endloop +endfacet +facet normal -0.088838 0.995426 0.035149 +outer loop +vertex 14.959599 2.968169 6.376157 +vertex 12.761621 2.829240 4.755350 +vertex 12.456796 2.756068 6.057173 +endloop +endfacet +facet normal -0.110743 0.961846 0.250177 +outer loop +vertex 11.587097 2.393663 7.065519 +vertex 12.456796 2.756068 6.057173 +vertex 10.207706 2.583551 5.724866 +endloop +endfacet +facet normal -0.142479 0.949067 0.281020 +outer loop +vertex 11.587097 2.393663 7.065519 +vertex 10.207706 2.583551 5.724866 +vertex 9.113427 2.193287 6.488069 +endloop +endfacet +facet normal 0.038270 0.984884 -0.168934 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 11.713722 2.630891 2.585105 +vertex 12.535257 2.764018 3.547343 +endloop +endfacet +facet normal -0.066287 0.946914 -0.314580 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 12.535257 2.764018 3.547343 +vertex 15.109202 2.798170 3.107773 +endloop +endfacet +facet normal -0.018665 0.998594 -0.049625 +outer loop +vertex 11.713722 2.630891 2.585105 +vertex 9.147440 2.632157 3.575842 +vertex 10.230559 2.689977 4.331943 +endloop +endfacet +facet normal -0.061045 0.994469 -0.085468 +outer loop +vertex 11.713722 2.630891 2.585105 +vertex 10.230559 2.689977 4.331943 +vertex 12.535257 2.764018 3.547343 +endloop +endfacet +facet normal -0.021759 0.998520 -0.049835 +outer loop +vertex 15.109202 2.798170 3.107773 +vertex 12.535257 2.764018 3.547343 +vertex 12.761621 2.829240 4.755350 +endloop +endfacet +facet normal -0.051180 0.994473 -0.091679 +outer loop +vertex 15.109202 2.798170 3.107773 +vertex 12.761621 2.829240 4.755350 +vertex 15.010111 2.935162 4.649097 +endloop +endfacet +facet normal 0.054722 -0.170693 -0.983804 +outer loop +vertex 52.674423 -0.709498 0.593456 +vertex 51.466869 -0.919875 0.562790 +vertex 51.196529 -0.417554 0.460599 +endloop +endfacet +facet normal 0.065258 -0.120434 -0.990574 +outer loop +vertex 52.674423 -0.709498 0.593456 +vertex 51.196529 -0.417554 0.460599 +vertex 51.796329 -0.097145 0.461157 +endloop +endfacet +facet normal -0.010837 -0.123996 -0.992224 +outer loop +vertex 49.705631 -0.887627 0.535626 +vertex 49.976562 -0.176008 0.443738 +vertex 51.196529 -0.417554 0.460599 +endloop +endfacet +facet normal 0.011591 -0.193348 -0.981062 +outer loop +vertex 49.705631 -0.887627 0.535626 +vertex 51.196529 -0.417554 0.460599 +vertex 51.466869 -0.919875 0.562790 +endloop +endfacet +facet normal -0.003247 0.007819 -0.999964 +outer loop +vertex 50.442947 0.478647 0.470053 +vertex 51.796329 -0.097145 0.461157 +vertex 51.196529 -0.417554 0.460599 +endloop +endfacet +facet normal 0.019077 0.026585 -0.999465 +outer loop +vertex 50.442947 0.478647 0.470053 +vertex 51.196529 -0.417554 0.460599 +vertex 49.976562 -0.176008 0.443738 +endloop +endfacet +facet normal 0.111522 -0.433531 -0.894211 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 53.069855 -1.279745 0.919241 +vertex 52.674423 -0.709498 0.593456 +endloop +endfacet +facet normal 0.131659 -0.331615 -0.934183 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 52.674423 -0.709498 0.593456 +vertex 53.526051 -0.368843 0.592556 +endloop +endfacet +facet normal 0.102334 -0.431799 -0.896146 +outer loop +vertex 53.069855 -1.279745 0.919241 +vertex 51.584263 -1.469445 0.841000 +vertex 51.466869 -0.919875 0.562790 +endloop +endfacet +facet normal 0.099437 -0.440718 -0.892121 +outer loop +vertex 53.069855 -1.279745 0.919241 +vertex 51.466869 -0.919875 0.562790 +vertex 52.674423 -0.709498 0.593456 +endloop +endfacet +facet normal 0.053660 -0.136761 -0.989150 +outer loop +vertex 53.526051 -0.368843 0.592556 +vertex 52.674423 -0.709498 0.593456 +vertex 51.796329 -0.097145 0.461157 +endloop +endfacet +facet normal 0.064366 -0.071597 -0.995355 +outer loop +vertex 53.526051 -0.368843 0.592556 +vertex 51.796329 -0.097145 0.461157 +vertex 52.409733 0.291152 0.472893 +endloop +endfacet +facet normal -0.013460 -0.238450 -0.971061 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 47.104279 -0.592443 0.499200 +vertex 49.705631 -0.887627 0.535626 +endloop +endfacet +facet normal 0.011553 -0.381650 -0.924235 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 49.705631 -0.887627 0.535626 +vertex 49.558994 -1.541163 0.803662 +endloop +endfacet +facet normal -0.011256 -0.055335 -0.998405 +outer loop +vertex 47.104279 -0.592443 0.499200 +vertex 47.743988 0.219856 0.446967 +vertex 49.976562 -0.176008 0.443738 +endloop +endfacet +facet normal -0.000617 -0.127831 -0.991796 +outer loop +vertex 47.104279 -0.592443 0.499200 +vertex 49.976562 -0.176008 0.443738 +vertex 49.705631 -0.887627 0.535626 +endloop +endfacet +facet normal 0.007287 -0.380846 -0.924610 +outer loop +vertex 49.558994 -1.541163 0.803662 +vertex 49.705631 -0.887627 0.535626 +vertex 51.466869 -0.919875 0.562790 +endloop +endfacet +facet normal 0.032282 -0.445925 -0.894488 +outer loop +vertex 49.558994 -1.541163 0.803662 +vertex 51.466869 -0.919875 0.562790 +vertex 51.584263 -1.469445 0.841000 +endloop +endfacet +facet normal -0.083283 0.263095 -0.961169 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 51.159924 0.980571 0.545317 +vertex 50.442947 0.478647 0.470053 +endloop +endfacet +facet normal 0.060146 0.369231 -0.927389 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 50.442947 0.478647 0.470053 +vertex 48.246258 1.017376 0.542076 +endloop +endfacet +facet normal -0.022004 0.064914 -0.997648 +outer loop +vertex 51.159924 0.980571 0.545317 +vertex 52.409733 0.291152 0.472893 +vertex 51.796329 -0.097145 0.461157 +endloop +endfacet +facet normal 0.035397 0.098564 -0.994501 +outer loop +vertex 51.159924 0.980571 0.545317 +vertex 51.796329 -0.097145 0.461157 +vertex 50.442947 0.478647 0.470053 +endloop +endfacet +facet normal -0.019487 0.054012 -0.998350 +outer loop +vertex 48.246258 1.017376 0.542076 +vertex 50.442947 0.478647 0.470053 +vertex 49.976562 -0.176008 0.443738 +endloop +endfacet +facet normal 0.017615 0.107453 -0.994054 +outer loop +vertex 48.246258 1.017376 0.542076 +vertex 49.976562 -0.176008 0.443738 +vertex 47.743988 0.219856 0.446967 +endloop +endfacet +facet normal 0.103997 -0.993227 -0.051811 +outer loop +vertex 50.249496 -2.189126 2.495076 +vertex 49.912304 -2.191964 1.872657 +vertex 51.167130 -2.052176 1.711607 +endloop +endfacet +facet normal 0.108150 -0.993027 -0.046913 +outer loop +vertex 50.249496 -2.189126 2.495076 +vertex 51.167130 -2.052176 1.711607 +vertex 51.739006 -2.003437 1.998294 +endloop +endfacet +facet normal 0.067192 -0.926506 -0.370233 +outer loop +vertex 49.697483 -1.986714 1.281070 +vertex 51.462238 -1.861012 1.286779 +vertex 51.167130 -2.052176 1.711607 +endloop +endfacet +facet normal 0.059813 -0.936137 -0.346512 +outer loop +vertex 49.697483 -1.986714 1.281070 +vertex 51.167130 -2.052176 1.711607 +vertex 49.912304 -2.191964 1.872657 +endloop +endfacet +facet normal 0.178912 -0.964775 -0.192870 +outer loop +vertex 52.667137 -1.718126 1.432071 +vertex 51.739006 -2.003437 1.998294 +vertex 51.167130 -2.052176 1.711607 +endloop +endfacet +facet normal 0.149342 -0.936386 -0.317613 +outer loop +vertex 52.667137 -1.718126 1.432071 +vertex 51.167130 -2.052176 1.711607 +vertex 51.462238 -1.861012 1.286779 +endloop +endfacet +facet normal 0.097164 -0.957821 0.270441 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 47.784302 -2.359905 2.775923 +vertex 50.249496 -2.189126 2.495076 +endloop +endfacet +facet normal 0.041678 -0.980515 0.191974 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 50.249496 -2.189126 2.495076 +vertex 50.865349 -2.053394 3.054627 +endloop +endfacet +facet normal 0.071613 -0.997293 -0.016709 +outer loop +vertex 47.784302 -2.359905 2.775923 +vertex 47.610008 -2.359042 1.977433 +vertex 49.912304 -2.191964 1.872657 +endloop +endfacet +facet normal 0.065565 -0.997367 -0.030972 +outer loop +vertex 47.784302 -2.359905 2.775923 +vertex 49.912304 -2.191964 1.872657 +vertex 50.249496 -2.189126 2.495076 +endloop +endfacet +facet normal 0.148268 -0.986023 0.075995 +outer loop +vertex 50.865349 -2.053394 3.054627 +vertex 50.249496 -2.189126 2.495076 +vertex 51.739006 -2.003437 1.998294 +endloop +endfacet +facet normal 0.126570 -0.990270 0.057849 +outer loop +vertex 50.865349 -2.053394 3.054627 +vertex 51.739006 -2.003437 1.998294 +vertex 52.331310 -1.905801 2.373729 +endloop +endfacet +facet normal -0.000003 -0.731078 -0.682293 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 49.558994 -1.541163 0.803662 +vertex 49.697483 -1.986714 1.281070 +endloop +endfacet +facet normal 0.027440 -0.631728 -0.774704 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 49.697483 -1.986714 1.281070 +vertex 47.087532 -2.123862 1.300462 +endloop +endfacet +facet normal 0.038818 -0.755992 -0.653429 +outer loop +vertex 49.558994 -1.541163 0.803662 +vertex 51.584263 -1.469445 0.841000 +vertex 51.462238 -1.861012 1.286779 +endloop +endfacet +facet normal 0.053673 -0.722210 -0.689588 +outer loop +vertex 49.558994 -1.541163 0.803662 +vertex 51.462238 -1.861012 1.286779 +vertex 49.697483 -1.986714 1.281070 +endloop +endfacet +facet normal 0.046763 -0.938340 -0.342535 +outer loop +vertex 47.087532 -2.123862 1.300462 +vertex 49.697483 -1.986714 1.281070 +vertex 49.912304 -2.191964 1.872657 +endloop +endfacet +facet normal 0.051025 -0.930529 -0.362647 +outer loop +vertex 47.087532 -2.123862 1.300462 +vertex 49.912304 -2.191964 1.872657 +vertex 47.610008 -2.359042 1.977433 +endloop +endfacet +facet normal 0.230432 -0.923203 -0.307567 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 53.516243 -1.601796 1.719050 +vertex 52.667137 -1.718126 1.432071 +endloop +endfacet +facet normal 0.148033 -0.806161 -0.572879 +outer loop +vertex 55.000004 -1.000000 1.024333 +vertex 52.667137 -1.718126 1.432071 +vertex 53.069855 -1.279745 0.919241 +endloop +endfacet +facet normal 0.208521 -0.975108 -0.075387 +outer loop +vertex 53.516243 -1.601796 1.719050 +vertex 52.331310 -1.905801 2.373729 +vertex 51.739006 -2.003437 1.998294 +endloop +endfacet +facet normal 0.191047 -0.966100 -0.173645 +outer loop +vertex 53.516243 -1.601796 1.719050 +vertex 51.739006 -2.003437 1.998294 +vertex 52.667137 -1.718126 1.432071 +endloop +endfacet +facet normal 0.163964 -0.809735 -0.563422 +outer loop +vertex 53.069855 -1.279745 0.919241 +vertex 52.667137 -1.718126 1.432071 +vertex 51.462238 -1.861012 1.286779 +endloop +endfacet +facet normal 0.130727 -0.762334 -0.633843 +outer loop +vertex 53.069855 -1.279745 0.919241 +vertex 51.462238 -1.861012 1.286779 +vertex 51.584263 -1.469445 0.841000 +endloop +endfacet +facet normal 0.184375 -0.318695 0.929752 +outer loop +vertex 36.147072 -1.831106 6.448225 +vertex 40.017311 -1.760391 5.704975 +vertex 41.560101 -1.103757 5.624108 +endloop +endfacet +facet normal 0.165458 -0.122603 0.978566 +outer loop +vertex 36.147072 -1.831106 6.448225 +vertex 41.560101 -1.103757 5.624108 +vertex 39.862602 -0.822579 5.946353 +endloop +endfacet +facet normal 0.183797 -0.244654 0.952031 +outer loop +vertex 44.075966 -1.492789 5.038428 +vertex 44.441277 -0.694113 5.173146 +vertex 41.560101 -1.103757 5.624108 +endloop +endfacet +facet normal 0.173716 -0.292338 0.940405 +outer loop +vertex 44.075966 -1.492789 5.038428 +vertex 41.560101 -1.103757 5.624108 +vertex 40.017311 -1.760391 5.704975 +endloop +endfacet +facet normal 0.180735 -0.035334 0.982897 +outer loop +vertex 43.582516 0.046202 5.293565 +vertex 39.862602 -0.822579 5.946353 +vertex 41.560101 -1.103757 5.624108 +endloop +endfacet +facet normal 0.152392 0.016036 0.988190 +outer loop +vertex 43.582516 0.046202 5.293565 +vertex 41.560101 -1.103757 5.624108 +vertex 44.441277 -0.694113 5.173146 +endloop +endfacet +facet normal 0.172939 -0.712636 0.679884 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 37.831306 -2.456728 5.531137 +vertex 40.017311 -1.760391 5.704975 +endloop +endfacet +facet normal 0.166410 -0.557232 0.813511 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 40.017311 -1.760391 5.704975 +vertex 36.147072 -1.831106 6.448225 +endloop +endfacet +facet normal 0.187405 -0.213269 0.958851 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 36.147072 -1.831106 6.448225 +vertex 39.862602 -0.822579 5.946353 +endloop +endfacet +facet normal 0.168591 0.003703 0.985679 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 39.862602 -0.822579 5.946353 +vertex 37.555817 -0.354681 6.339149 +endloop +endfacet +facet normal 0.168089 -0.512462 0.842097 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 47.781036 -1.082973 4.548262 +vertex 44.075966 -1.492789 5.038428 +endloop +endfacet +facet normal 0.136009 -0.619029 0.773502 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 44.075966 -1.492789 5.038428 +vertex 42.545757 -2.222376 4.723609 +endloop +endfacet +facet normal 0.166339 -0.139921 0.976091 +outer loop +vertex 47.781036 -1.082973 4.548262 +vertex 47.583881 -0.209556 4.707063 +vertex 44.441277 -0.694113 5.173146 +endloop +endfacet +facet normal 0.152744 -0.231906 0.960671 +outer loop +vertex 47.781036 -1.082973 4.548262 +vertex 44.441277 -0.694113 5.173146 +vertex 44.075966 -1.492789 5.038428 +endloop +endfacet +facet normal 0.163914 -0.660109 0.733068 +outer loop +vertex 42.545757 -2.222376 4.723609 +vertex 44.075966 -1.492789 5.038428 +vertex 40.017311 -1.760391 5.704975 +endloop +endfacet +facet normal 0.157177 -0.673692 0.722104 +outer loop +vertex 42.545757 -2.222376 4.723609 +vertex 40.017311 -1.760391 5.704975 +vertex 37.831306 -2.456728 5.531137 +endloop +endfacet +facet normal 0.198517 0.307649 0.930561 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 41.577522 0.722469 5.497714 +vertex 43.582516 0.046202 5.293565 +endloop +endfacet +facet normal 0.086579 0.404526 0.910419 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 43.582516 0.046202 5.293565 +vertex 46.970215 0.694892 4.683170 +endloop +endfacet +facet normal 0.183268 0.081065 0.979715 +outer loop +vertex 41.577522 0.722469 5.497714 +vertex 37.555817 -0.354681 6.339149 +vertex 39.862602 -0.822579 5.946353 +endloop +endfacet +facet normal 0.142676 0.126675 0.981630 +outer loop +vertex 41.577522 0.722469 5.497714 +vertex 39.862602 -0.822579 5.946353 +vertex 43.582516 0.046202 5.293565 +endloop +endfacet +facet normal 0.170270 0.037342 0.984690 +outer loop +vertex 46.970215 0.694892 4.683170 +vertex 43.582516 0.046202 5.293565 +vertex 44.441277 -0.694113 5.173146 +endloop +endfacet +facet normal 0.128644 0.113311 0.985196 +outer loop +vertex 46.970215 0.694892 4.683170 +vertex 44.441277 -0.694113 5.173146 +vertex 47.583881 -0.209556 4.707063 +endloop +endfacet +facet normal 0.039897 0.997857 0.051864 +outer loop +vertex 43.657722 2.568572 2.769760 +vertex 44.696526 2.563327 2.071559 +vertex 42.040512 2.681520 1.840692 +endloop +endfacet +facet normal 0.050933 0.998167 0.032691 +outer loop +vertex 43.657722 2.568572 2.769760 +vertex 42.040512 2.681520 1.840692 +vertex 40.275978 2.759886 2.197073 +endloop +endfacet +facet normal 0.046625 0.963783 -0.262580 +outer loop +vertex 44.900307 2.428381 1.419360 +vertex 41.148026 2.594087 1.361296 +vertex 42.040512 2.681520 1.840692 +endloop +endfacet +facet normal 0.059680 0.981049 -0.184341 +outer loop +vertex 44.900307 2.428381 1.419360 +vertex 42.040512 2.681520 1.840692 +vertex 44.696526 2.563327 2.071559 +endloop +endfacet +facet normal 0.017645 0.991277 -0.130608 +outer loop +vertex 37.490425 2.725876 1.562616 +vertex 40.275978 2.759886 2.197073 +vertex 42.040512 2.681520 1.840692 +endloop +endfacet +facet normal 0.022990 0.975085 -0.220638 +outer loop +vertex 37.490425 2.725876 1.562616 +vertex 42.040512 2.681520 1.840692 +vertex 41.148026 2.594087 1.361296 +endloop +endfacet +facet normal 0.023267 0.907364 0.419702 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 46.986958 2.274199 3.221608 +vertex 43.657722 2.568572 2.769760 +endloop +endfacet +facet normal 0.118241 0.934128 0.336784 +outer loop +vertex 45.262634 1.792930 4.357667 +vertex 43.657722 2.568572 2.769760 +vertex 41.623692 2.542653 3.555777 +endloop +endfacet +facet normal 0.043934 0.986073 0.160405 +outer loop +vertex 46.986958 2.274199 3.221608 +vertex 47.717857 2.373972 2.408079 +vertex 44.696526 2.563327 2.071559 +endloop +endfacet +facet normal 0.073819 0.992003 0.102378 +outer loop +vertex 46.986958 2.274199 3.221608 +vertex 44.696526 2.563327 2.071559 +vertex 43.657722 2.568572 2.769760 +endloop +endfacet +facet normal 0.035158 0.991699 0.123681 +outer loop +vertex 41.623692 2.542653 3.555777 +vertex 43.657722 2.568572 2.769760 +vertex 40.275978 2.759886 2.197073 +endloop +endfacet +facet normal 0.060704 0.993274 0.098594 +outer loop +vertex 41.623692 2.542653 3.555777 +vertex 40.275978 2.759886 2.197073 +vertex 37.925159 2.841050 2.826803 +endloop +endfacet +facet normal 0.055260 0.826010 -0.560941 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 44.908054 2.060014 0.877686 +vertex 44.900307 2.428381 1.419360 +endloop +endfacet +facet normal 0.078874 0.895661 -0.437687 +outer loop +vertex 49.363640 1.792930 0.923323 +vertex 44.900307 2.428381 1.419360 +vertex 48.242996 2.261178 1.679576 +endloop +endfacet +facet normal 0.037049 0.801405 -0.596974 +outer loop +vertex 44.908054 2.060014 0.877686 +vertex 40.623978 2.257716 0.877214 +vertex 41.148026 2.594087 1.361296 +endloop +endfacet +facet normal 0.045179 0.826363 -0.561323 +outer loop +vertex 44.908054 2.060014 0.877686 +vertex 41.148026 2.594087 1.361296 +vertex 44.900307 2.428381 1.419360 +endloop +endfacet +facet normal 0.063333 0.981034 -0.183196 +outer loop +vertex 48.242996 2.261178 1.679576 +vertex 44.900307 2.428381 1.419360 +vertex 44.696526 2.563327 2.071559 +endloop +endfacet +facet normal 0.073402 0.992204 -0.100712 +outer loop +vertex 48.242996 2.261178 1.679576 +vertex 44.696526 2.563327 2.071559 +vertex 47.717857 2.373972 2.408079 +endloop +endfacet +facet normal -0.022142 0.924819 -0.379763 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 34.121059 2.869947 2.109917 +vertex 37.490425 2.725876 1.562616 +endloop +endfacet +facet normal -0.013562 0.862312 -0.506196 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 37.490425 2.725876 1.562616 +vertex 36.144936 2.362384 0.979452 +endloop +endfacet +facet normal 0.018682 0.998089 -0.058900 +outer loop +vertex 34.121059 2.869947 2.109917 +vertex 37.925159 2.841050 2.826803 +vertex 40.275978 2.759886 2.197073 +endloop +endfacet +facet normal 0.019679 0.990031 -0.139468 +outer loop +vertex 34.121059 2.869947 2.109917 +vertex 40.275978 2.759886 2.197073 +vertex 37.490425 2.725876 1.562616 +endloop +endfacet +facet normal 0.001296 0.847296 -0.531120 +outer loop +vertex 36.144936 2.362384 0.979452 +vertex 37.490425 2.725876 1.562616 +vertex 41.148026 2.594087 1.361296 +endloop +endfacet +facet normal 0.005994 0.818142 -0.574985 +outer loop +vertex 36.144936 2.362384 0.979452 +vertex 41.148026 2.594087 1.361296 +vertex 40.623978 2.257716 0.877214 +endloop +endfacet +facet normal -0.035335 0.996555 -0.075031 +outer loop +vertex 23.709536 3.370836 5.004611 +vertex 24.248417 3.292628 3.712090 +vertex 22.616539 3.195441 3.189772 +endloop +endfacet +facet normal -0.029504 0.996475 -0.078536 +outer loop +vertex 23.709536 3.370836 5.004611 +vertex 22.616539 3.195441 3.189772 +vertex 21.666407 3.209487 3.724936 +endloop +endfacet +facet normal -0.010336 0.973966 -0.226459 +outer loop +vertex 24.699970 3.032526 2.394014 +vertex 22.188141 2.974855 2.260623 +vertex 22.616539 3.195441 3.189772 +endloop +endfacet +facet normal 0.003188 0.981282 -0.192549 +outer loop +vertex 24.699970 3.032526 2.394014 +vertex 22.616539 3.195441 3.189772 +vertex 24.248417 3.292628 3.712090 +endloop +endfacet +facet normal -0.064025 0.988135 -0.139607 +outer loop +vertex 20.080088 2.928699 2.465025 +vertex 21.666407 3.209487 3.724936 +vertex 22.616539 3.195441 3.189772 +endloop +endfacet +facet normal -0.041972 0.976276 -0.212422 +outer loop +vertex 20.080088 2.928699 2.465025 +vertex 22.616539 3.195441 3.189772 +vertex 22.188141 2.974855 2.260623 +endloop +endfacet +facet normal -0.007926 0.999149 -0.040472 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 26.798485 3.337256 4.314457 +vertex 24.248417 3.292628 3.712090 +endloop +endfacet +facet normal 0.009354 0.998358 -0.056509 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 24.248417 3.292628 3.712090 +vertex 23.709536 3.370836 5.004611 +endloop +endfacet +facet normal -0.061375 0.997727 -0.027808 +outer loop +vertex 22.875492 3.349853 6.092598 +vertex 23.709536 3.370836 5.004611 +vertex 21.666407 3.209487 3.724936 +endloop +endfacet +facet normal -0.043999 0.998357 -0.036718 +outer loop +vertex 22.875492 3.349853 6.092598 +vertex 21.666407 3.209487 3.724936 +vertex 20.559479 3.187713 4.459331 +endloop +endfacet +facet normal 0.003337 0.890130 -0.455693 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 25.097067 2.476448 1.310704 +vertex 24.699970 3.032526 2.394014 +endloop +endfacet +facet normal 0.023317 0.924814 -0.379705 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 24.699970 3.032526 2.394014 +vertex 27.830294 3.080580 2.703281 +endloop +endfacet +facet normal -0.004423 0.879868 -0.475198 +outer loop +vertex 25.097067 2.476448 1.310704 +vertex 21.998266 2.464462 1.317354 +vertex 22.188141 2.974855 2.260623 +endloop +endfacet +facet normal 0.003754 0.890191 -0.455572 +outer loop +vertex 25.097067 2.476448 1.310704 +vertex 22.188141 2.974855 2.260623 +vertex 24.699970 3.032526 2.394014 +endloop +endfacet +facet normal 0.003935 0.981328 -0.192301 +outer loop +vertex 27.830294 3.080580 2.703281 +vertex 24.699970 3.032526 2.394014 +vertex 24.248417 3.292628 3.712090 +endloop +endfacet +facet normal 0.017294 0.989061 -0.146492 +outer loop +vertex 27.830294 3.080580 2.703281 +vertex 24.248417 3.292628 3.712090 +vertex 26.798485 3.337256 4.314457 +endloop +endfacet +facet normal -0.081501 0.965257 -0.248268 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 18.347837 2.920243 3.000808 +vertex 20.080088 2.928699 2.465025 +endloop +endfacet +facet normal -0.034191 0.899259 -0.436078 +outer loop +vertex 15.508273 2.303025 1.533252 +vertex 20.080088 2.928699 2.465025 +vertex 19.303099 2.379055 1.392499 +endloop +endfacet +facet normal -0.070078 0.994632 -0.076136 +outer loop +vertex 18.347837 2.920243 3.000808 +vertex 20.559479 3.187713 4.459331 +vertex 21.666407 3.209487 3.724936 +endloop +endfacet +facet normal -0.052418 0.986695 -0.153901 +outer loop +vertex 18.347837 2.920243 3.000808 +vertex 21.666407 3.209487 3.724936 +vertex 20.080088 2.928699 2.465025 +endloop +endfacet +facet normal -0.060562 0.905436 -0.420139 +outer loop +vertex 19.303099 2.379055 1.392499 +vertex 20.080088 2.928699 2.465025 +vertex 22.188141 2.974855 2.260623 +endloop +endfacet +facet normal -0.041035 0.882197 -0.469088 +outer loop +vertex 19.303099 2.379055 1.392499 +vertex 22.188141 2.974855 2.260623 +vertex 21.998266 2.464462 1.317354 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 15.458335 -2.770834 8.357668 +vertex 15.538308 -2.783193 8.357668 +vertex 15.533590 -2.727700 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 25.813204 -1.764621 8.357668 +vertex 26.000004 -1.750000 8.357668 +vertex 26.000004 0.000000 8.357668 +endloop +endfacet +facet normal -0.000008 0.000000 1.000000 +outer loop +vertex 26.000004 0.000000 8.357668 +vertex 25.816088 -0.050794 8.357668 +vertex 25.815907 -0.737309 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 25.814472 -1.560014 8.357668 +vertex 25.813204 -1.764621 8.357668 +vertex 26.000004 0.000000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 26.000004 0.000000 8.357668 +vertex 25.815907 -0.737309 8.357668 +vertex 25.814472 -1.560014 8.357668 +endloop +endfacet +facet normal -0.000003 0.000000 1.000000 +outer loop +vertex 26.000004 0.000000 8.357668 +vertex 26.000004 1.750000 8.357668 +vertex 25.813848 1.660502 8.357668 +endloop +endfacet +facet normal 0.000013 0.000000 1.000000 +outer loop +vertex 26.000004 0.000000 8.357668 +vertex 25.813848 1.660502 8.357668 +vertex 25.814472 1.560014 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 25.816114 0.000000 8.357668 +vertex 25.816088 -0.050794 8.357668 +vertex 26.000004 0.000000 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 26.000004 0.000000 8.357668 +vertex 25.814472 1.560014 8.357668 +vertex 25.815907 0.737309 8.357668 +endloop +endfacet +facet normal 0.000000 0.000000 1.000000 +outer loop +vertex 25.815907 0.737309 8.357668 +vertex 25.816114 0.000000 8.357668 +vertex 26.000004 0.000000 8.357668 +endloop +endfacet +facet normal 0.001470 -0.143936 -0.989586 +outer loop +vertex 39.094803 -1.120972 0.539543 +vertex 35.647991 -1.432970 0.579802 +vertex 34.200043 -0.732904 0.475826 +endloop +endfacet +facet normal 0.008056 -0.062250 -0.998028 +outer loop +vertex 39.094803 -1.120972 0.539543 +vertex 34.200043 -0.732904 0.475826 +vertex 35.827679 -0.267875 0.459959 +endloop +endfacet +facet normal -0.021287 -0.152833 -0.988023 +outer loop +vertex 31.578562 -1.461071 0.644944 +vertex 31.257000 -0.427498 0.491994 +vertex 34.200043 -0.732904 0.475826 +endloop +endfacet +facet normal -0.014538 -0.176252 -0.984238 +outer loop +vertex 31.578562 -1.461071 0.644944 +vertex 34.200043 -0.732904 0.475826 +vertex 35.647991 -1.432970 0.579802 +endloop +endfacet +facet normal -0.013702 0.013843 -0.999810 +outer loop +vertex 32.156475 0.529089 0.521305 +vertex 35.827679 -0.267875 0.459959 +vertex 34.200043 -0.732904 0.475826 +endloop +endfacet +facet normal -0.002107 0.032606 -0.999466 +outer loop +vertex 32.156475 0.529089 0.521305 +vertex 34.200043 -0.732904 0.475826 +vertex 31.257000 -0.427498 0.491994 +endloop +endfacet +facet normal 0.011105 -0.300928 -0.953582 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 41.873085 -1.866991 0.807323 +vertex 39.094803 -1.120972 0.539543 +endloop +endfacet +facet normal 0.018277 -0.177180 -0.984009 +outer loop +vertex 45.986675 -1.473333 0.731000 +vertex 39.094803 -1.120972 0.539543 +vertex 41.912167 -0.620574 0.501772 +endloop +endfacet +facet normal 0.009590 -0.352907 -0.935609 +outer loop +vertex 41.873085 -1.866991 0.807323 +vertex 37.724285 -2.146670 0.870290 +vertex 35.647991 -1.432970 0.579802 +endloop +endfacet +facet normal 0.014902 -0.288180 -0.957460 +outer loop +vertex 41.873085 -1.866991 0.807323 +vertex 35.647991 -1.432970 0.579802 +vertex 39.094803 -1.120972 0.539543 +endloop +endfacet +facet normal 0.001875 -0.085762 -0.996314 +outer loop +vertex 41.912167 -0.620574 0.501772 +vertex 39.094803 -1.120972 0.539543 +vertex 35.827679 -0.267875 0.459959 +endloop +endfacet +facet normal 0.006244 -0.010821 -0.999922 +outer loop +vertex 41.912167 -0.620574 0.501772 +vertex 35.827679 -0.267875 0.459959 +vertex 38.036938 0.306147 0.467543 +endloop +endfacet +facet normal -0.037686 -0.459402 -0.887429 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 27.419786 -1.179824 0.675958 +vertex 31.578562 -1.461071 0.644944 +endloop +endfacet +facet normal -0.038934 -0.455450 -0.889410 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 31.578562 -1.461071 0.644944 +vertex 33.108257 -2.339576 1.027848 +endloop +endfacet +facet normal -0.024165 -0.119452 -0.992546 +outer loop +vertex 27.419786 -1.179824 0.675958 +vertex 27.720016 0.032394 0.522759 +vertex 31.257000 -0.427498 0.491994 +endloop +endfacet +facet normal -0.017631 -0.151731 -0.988265 +outer loop +vertex 27.419786 -1.179824 0.675958 +vertex 31.257000 -0.427498 0.491994 +vertex 31.578562 -1.461071 0.644944 +endloop +endfacet +facet normal -0.011675 -0.416542 -0.909041 +outer loop +vertex 33.108257 -2.339576 1.027848 +vertex 31.578562 -1.461071 0.644944 +vertex 35.647991 -1.432970 0.579802 +endloop +endfacet +facet normal -0.013914 -0.411410 -0.911344 +outer loop +vertex 33.108257 -2.339576 1.027848 +vertex 35.647991 -1.432970 0.579802 +vertex 37.724285 -2.146670 0.870290 +endloop +endfacet +facet normal -0.062065 0.292420 -0.954274 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 34.217098 1.287853 0.619794 +vertex 32.156475 0.529089 0.521305 +endloop +endfacet +facet normal 0.036104 0.381657 -0.923599 +outer loop +vertex 30.224367 2.412967 1.224249 +vertex 32.156475 0.529089 0.521305 +vertex 28.402740 1.203649 0.653316 +endloop +endfacet +facet normal -0.018264 0.083456 -0.996344 +outer loop +vertex 34.217098 1.287853 0.619794 +vertex 38.036938 0.306147 0.467543 +vertex 35.827679 -0.267875 0.459959 +endloop +endfacet +facet normal 0.007171 0.109542 -0.993956 +outer loop +vertex 34.217098 1.287853 0.619794 +vertex 35.827679 -0.267875 0.459959 +vertex 32.156475 0.529089 0.521305 +endloop +endfacet +facet normal -0.025328 0.054402 -0.998198 +outer loop +vertex 28.402740 1.203649 0.653316 +vertex 32.156475 0.529089 0.521305 +vertex 31.257000 -0.427498 0.491994 +endloop +endfacet +facet normal 0.005356 0.107696 -0.994169 +outer loop +vertex 28.402740 1.203649 0.653316 +vertex 31.257000 -0.427498 0.491994 +vertex 27.720016 0.032394 0.522759 +endloop +endfacet +facet normal 0.065139 -0.992975 0.098780 +outer loop +vertex 39.476257 -2.712869 4.191426 +vertex 35.635860 -2.889090 4.952492 +vertex 33.974491 -3.061083 4.319118 +endloop +endfacet +facet normal 0.064169 -0.996860 0.046393 +outer loop +vertex 39.476257 -2.712869 4.191426 +vertex 33.974491 -3.061083 4.319118 +vertex 35.673954 -2.984780 3.608021 +endloop +endfacet +facet normal 0.048487 -0.998051 0.039288 +outer loop +vertex 31.104549 -3.148645 5.636679 +vertex 30.579285 -3.223353 4.387111 +vertex 33.974491 -3.061083 4.319118 +endloop +endfacet +facet normal 0.069964 -0.993805 0.086351 +outer loop +vertex 31.104549 -3.148645 5.636679 +vertex 33.974491 -3.061083 4.319118 +vertex 35.635860 -2.889090 4.952492 +endloop +endfacet +facet normal 0.033576 -0.999072 -0.026961 +outer loop +vertex 31.234276 -3.121788 3.156084 +vertex 35.673954 -2.984780 3.608021 +vertex 33.974491 -3.061083 4.319118 +endloop +endfacet +facet normal 0.046511 -0.997260 -0.057531 +outer loop +vertex 31.234276 -3.121788 3.156084 +vertex 33.974491 -3.061083 4.319118 +vertex 30.579285 -3.223353 4.387111 +endloop +endfacet +facet normal 0.087711 -0.931605 0.352731 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 42.545757 -2.222376 4.723609 +vertex 39.476257 -2.712869 4.191426 +endloop +endfacet +facet normal 0.086649 -0.973212 0.212956 +outer loop +vertex 48.166676 -2.000000 3.913223 +vertex 39.476257 -2.712869 4.191426 +vertex 42.552837 -2.614465 3.389307 +endloop +endfacet +facet normal 0.101486 -0.942296 0.319029 +outer loop +vertex 42.545757 -2.222376 4.723609 +vertex 37.831306 -2.456728 5.531137 +vertex 35.635860 -2.889090 4.952492 +endloop +endfacet +facet normal 0.101432 -0.951147 0.291600 +outer loop +vertex 42.545757 -2.222376 4.723609 +vertex 35.635860 -2.889090 4.952492 +vertex 39.476257 -2.712869 4.191426 +endloop +endfacet +facet normal 0.056520 -0.993885 0.094858 +outer loop +vertex 42.552837 -2.614465 3.389307 +vertex 39.476257 -2.712869 4.191426 +vertex 35.673954 -2.984780 3.608021 +endloop +endfacet +facet normal 0.054186 -0.998436 0.013738 +outer loop +vertex 42.552837 -2.614465 3.389307 +vertex 35.673954 -2.984780 3.608021 +vertex 37.887997 -2.875850 2.791907 +endloop +endfacet +facet normal 0.034076 -0.999087 -0.025763 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 26.492159 -3.367654 4.577150 +vertex 30.579285 -3.223353 4.387111 +endloop +endfacet +facet normal 0.061829 -0.997519 0.033649 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 30.579285 -3.223353 4.387111 +vertex 31.104549 -3.148645 5.636679 +endloop +endfacet +facet normal 0.094295 -0.961011 0.259935 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 31.104549 -3.148645 5.636679 +vertex 35.635860 -2.889090 4.952492 +endloop +endfacet +facet normal 0.111006 -0.950793 0.289259 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 35.635860 -2.889090 4.952492 +vertex 37.831306 -2.456728 5.531137 +endloop +endfacet +facet normal -0.025109 -0.971689 -0.234924 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 33.042091 -2.926533 2.155251 +vertex 31.234276 -3.121788 3.156084 +endloop +endfacet +facet normal 0.023137 -0.936595 -0.349649 +outer loop +vertex 26.752544 -2.589665 1.434137 +vertex 31.234276 -3.121788 3.156084 +vertex 26.704079 -3.180421 3.013374 +endloop +endfacet +facet normal 0.020573 -0.996801 -0.077235 +outer loop +vertex 33.042091 -2.926533 2.155251 +vertex 37.887997 -2.875850 2.791907 +vertex 35.673954 -2.984780 3.608021 +endloop +endfacet +facet normal 0.042510 -0.992246 -0.116794 +outer loop +vertex 33.042091 -2.926533 2.155251 +vertex 35.673954 -2.984780 3.608021 +vertex 31.234276 -3.121788 3.156084 +endloop +endfacet +facet normal 0.015242 -0.997130 -0.074158 +outer loop +vertex 26.704079 -3.180421 3.013374 +vertex 31.234276 -3.121788 3.156084 +vertex 30.579285 -3.223353 4.387111 +endloop +endfacet +facet normal 0.029716 -0.992937 -0.114859 +outer loop +vertex 26.704079 -3.180421 3.013374 +vertex 30.579285 -3.223353 4.387111 +vertex 26.492159 -3.367654 4.577150 +endloop +endfacet +facet normal -0.045029 0.998983 -0.002168 +outer loop +vertex 19.461210 3.199903 6.848686 +vertex 20.253862 3.238130 8.000117 +vertex 21.113426 3.276875 8.000361 +endloop +endfacet +facet normal -0.045408 0.998965 -0.002823 +outer loop +vertex 22.772949 3.352315 8.002979 +vertex 22.854446 3.356021 8.003421 +vertex 21.356569 3.284150 6.661976 +endloop +endfacet +facet normal -0.045409 0.998967 -0.001621 +outer loop +vertex 19.461210 3.199903 6.848686 +vertex 21.113426 3.276875 8.000361 +vertex 22.772949 3.352315 8.002979 +endloop +endfacet +facet normal -0.044751 0.998992 -0.003514 +outer loop +vertex 22.772949 3.352315 8.002979 +vertex 21.356569 3.284150 6.661976 +vertex 19.461210 3.199903 6.848686 +endloop +endfacet +facet normal -0.012835 -0.007060 -0.999893 +outer loop +vertex 17.121878 -3.795351 8.020833 +vertex 17.098297 -3.016442 8.015636 +vertex 18.147570 -3.141765 8.003051 +endloop +endfacet +facet normal -0.016735 -0.000960 -0.999860 +outer loop +vertex 18.147570 -3.141765 8.003051 +vertex 18.206795 -3.144454 8.002062 +vertex 18.211685 -3.794066 8.002604 +endloop +endfacet +facet normal -0.016724 -0.000958 -0.999860 +outer loop +vertex 18.147570 -3.141765 8.003051 +vertex 18.211685 -3.794066 8.002604 +vertex 17.121878 -3.795351 8.020833 +endloop +endfacet +facet normal 0.264648 -0.001304 0.964344 +outer loop +vertex 26.150230 -3.802635 8.262934 +vertex 26.155199 -2.887410 8.262807 +vertex 25.946306 -2.553310 8.320586 +endloop +endfacet +facet normal 0.264650 0.000001 0.964345 +outer loop +vertex 25.946306 -2.553310 8.320586 +vertex 25.414009 -2.554101 8.466667 +vertex 25.414011 -3.803846 8.466667 +endloop +endfacet +facet normal 0.266708 -0.000942 0.963777 +outer loop +vertex 25.946306 -2.553310 8.320586 +vertex 25.414011 -3.803846 8.466667 +vertex 26.150230 -3.802635 8.262934 +endloop +endfacet +facet normal -0.044557 -0.999000 -0.003685 +outer loop +vertex 23.701311 -3.388976 6.689353 +vertex 23.345116 -3.377946 8.006075 +vertex 22.772945 -3.352415 8.002979 +endloop +endfacet +facet normal -0.045407 -0.998965 -0.002538 +outer loop +vertex 21.113426 -3.276976 8.000362 +vertex 20.747240 -3.260331 8.000258 +vertex 21.544149 -3.293628 6.848240 +endloop +endfacet +facet normal -0.045404 -0.998960 -0.004285 +outer loop +vertex 23.701311 -3.388976 6.689353 +vertex 22.772945 -3.352415 8.002979 +vertex 21.113426 -3.276976 8.000362 +endloop +endfacet +facet normal -0.044314 -0.999015 -0.002128 +outer loop +vertex 21.113426 -3.276976 8.000362 +vertex 21.544149 -3.293628 6.848240 +vertex 23.701311 -3.388976 6.689353 +endloop +endfacet +facet normal 0.262625 0.000457 0.964898 +outer loop +vertex 25.815907 0.737309 8.357668 +vertex 25.814472 1.560014 8.357668 +vertex 25.414009 1.560457 8.466666 +endloop +endfacet +facet normal 0.261761 0.000002 0.965133 +outer loop +vertex 25.815907 0.737309 8.357668 +vertex 25.414009 1.560457 8.466666 +vertex 25.414009 0.737545 8.466667 +endloop +endfacet +facet normal -0.045748 -0.998949 -0.002773 +outer loop +vertex 21.544149 -3.293628 6.848240 +vertex 20.747240 -3.260331 8.000258 +vertex 19.549599 -3.205483 8.000000 +endloop +endfacet +facet normal -0.045409 -0.998962 -0.003690 +outer loop +vertex 18.206795 -3.144454 8.002062 +vertex 18.147570 -3.141765 8.003051 +vertex 19.651859 -3.205200 6.661274 +endloop +endfacet +facet normal -0.045406 -0.998966 -0.002179 +outer loop +vertex 21.544149 -3.293628 6.848240 +vertex 19.549599 -3.205483 8.000000 +vertex 18.206795 -3.144454 8.002062 +endloop +endfacet +facet normal -0.046229 -0.998920 -0.004567 +outer loop +vertex 18.206795 -3.144454 8.002062 +vertex 19.651859 -3.205200 6.661274 +vertex 21.544149 -3.293628 6.848240 +endloop +endfacet +facet normal 0.095073 0.059732 -0.993677 +outer loop +vertex 26.344246 3.115694 8.063178 +vertex 25.864864 3.190336 8.021798 +vertex 25.563290 3.798691 8.029513 +endloop +endfacet +facet normal 0.054163 0.012717 -0.998451 +outer loop +vertex 26.344246 3.115694 8.063178 +vertex 25.563290 3.798691 8.029513 +vertex 26.235046 3.800150 8.065972 +endloop +endfacet +facet normal 0.054085 0.992060 -0.113543 +outer loop +vertex 21.356569 3.284150 6.661976 +vertex 22.854446 3.356021 8.003421 +vertex 24.394966 3.272750 8.009665 +endloop +endfacet +facet normal 0.081522 0.996369 -0.024541 +outer loop +vertex 24.394966 3.272750 8.009665 +vertex 25.143169 3.211766 8.019117 +vertex 22.875492 3.349853 6.092598 +endloop +endfacet +facet normal -0.021684 0.998120 0.057330 +outer loop +vertex 24.394966 3.272750 8.009665 +vertex 22.875492 3.349853 6.092598 +vertex 21.356569 3.284150 6.661976 +endloop +endfacet +facet normal 0.000009 0.000000 1.000000 +outer loop +vertex 25.813204 -1.764621 8.357668 +vertex 25.811184 -2.342206 8.357668 +vertex 26.000004 -1.750000 8.357668 +endloop +endfacet +facet normal -0.646731 0.003771 0.762709 +outer loop +vertex 15.679340 0.737497 8.592188 +vertex 15.402760 0.736852 8.357668 +vertex 15.399197 0.125639 8.357668 +endloop +endfacet +facet normal -0.642932 0.004442 0.765911 +outer loop +vertex 15.399197 0.125639 8.357668 +vertex 15.398325 0.000000 8.357668 +vertex 15.677083 0.000000 8.591667 +endloop +endfacet +facet normal -0.643740 0.001431 0.765243 +outer loop +vertex 15.399197 0.125639 8.357668 +vertex 15.677083 0.000000 8.591667 +vertex 15.679340 0.737497 8.592188 +endloop +endfacet +facet normal 0.261757 -0.000068 0.965134 +outer loop +vertex 25.414009 -0.737545 8.466667 +vertex 25.815907 -0.737309 8.357668 +vertex 25.816088 -0.050794 8.357668 +endloop +endfacet +facet normal 0.261630 -0.000135 0.965168 +outer loop +vertex 25.816088 -0.050794 8.357668 +vertex 25.816114 0.000000 8.357668 +vertex 25.414011 0.000000 8.466667 +endloop +endfacet +facet normal 0.261647 -0.000002 0.965164 +outer loop +vertex 25.816088 -0.050794 8.357668 +vertex 25.414011 0.000000 8.466667 +vertex 25.414009 -0.737545 8.466667 +endloop +endfacet +facet normal -0.037314 -0.007796 -0.999273 +outer loop +vertex 16.194366 -2.877492 8.048306 +vertex 17.098297 -3.016442 8.015636 +vertex 17.121878 -3.795351 8.020833 +endloop +endfacet +facet normal -0.060056 -0.030823 -0.997719 +outer loop +vertex 16.194366 -2.877492 8.048306 +vertex 17.121878 -3.795351 8.020833 +vertex 16.292145 -3.797194 8.070833 +endloop +endfacet +facet normal -0.013682 0.996071 0.087501 +outer loop +vertex 22.875492 3.349853 6.092598 +vertex 25.143169 3.211766 8.019117 +vertex 25.666662 3.218757 8.021394 +endloop +endfacet +facet normal -0.041527 0.996154 0.077157 +outer loop +vertex 25.666662 3.218757 8.021394 +vertex 26.406237 3.403702 6.031664 +vertex 23.709536 3.370836 5.004611 +endloop +endfacet +facet normal 0.021972 0.999106 0.036112 +outer loop +vertex 25.666662 3.218757 8.021394 +vertex 23.709536 3.370836 5.004611 +vertex 22.875492 3.349853 6.092598 +endloop +endfacet +facet normal 0.374263 0.015226 0.927198 +outer loop +vertex 26.157015 2.552997 8.262760 +vertex 26.450327 2.551991 8.144380 +vertex 26.471371 2.578741 8.135446 +endloop +endfacet +facet normal 0.425797 0.017492 0.904649 +outer loop +vertex 26.471371 2.578741 8.135446 +vertex 26.418255 3.801430 8.136806 +vertex 26.150230 3.802635 8.262934 +endloop +endfacet +facet normal 0.375244 0.001908 0.926924 +outer loop +vertex 26.471371 2.578741 8.135446 +vertex 26.150230 3.802635 8.262934 +vertex 26.157015 2.552997 8.262760 +endloop +endfacet +facet normal 0.261756 0.000075 0.965134 +outer loop +vertex 25.816114 0.000000 8.357668 +vertex 25.815907 0.737309 8.357668 +vertex 25.414009 0.737545 8.466667 +endloop +endfacet +facet normal 0.261631 0.000000 0.965168 +outer loop +vertex 25.816114 0.000000 8.357668 +vertex 25.414009 0.737545 8.466667 +vertex 25.414011 0.000000 8.466667 +endloop +endfacet +facet normal 0.262627 0.001635 0.964896 +outer loop +vertex 25.414009 1.560457 8.466666 +vertex 25.814472 1.560014 8.357668 +vertex 25.813848 1.660502 8.357668 +endloop +endfacet +facet normal 0.263724 0.001634 0.964597 +outer loop +vertex 25.811188 2.342197 8.357667 +vertex 26.159176 2.236687 8.262705 +vertex 26.157015 2.552997 8.262760 +endloop +endfacet +facet normal 0.262766 0.001027 0.964859 +outer loop +vertex 25.414009 1.560457 8.466666 +vertex 25.813848 1.660502 8.357668 +vertex 25.811188 2.342197 8.357667 +endloop +endfacet +facet normal 0.264650 0.000006 0.964344 +outer loop +vertex 25.811188 2.342197 8.357667 +vertex 26.157015 2.552997 8.262760 +vertex 25.414009 2.554101 8.466667 +endloop +endfacet +facet normal 0.264654 -0.000002 0.964343 +outer loop +vertex 25.811188 2.342197 8.357667 +vertex 25.414009 2.554101 8.466667 +vertex 25.414009 1.560457 8.466666 +endloop +endfacet +facet normal 0.179891 -0.057355 0.982013 +outer loop +vertex 26.000004 -1.750000 8.357668 +vertex 25.811184 -2.342206 8.357668 +vertex 25.946306 -2.553310 8.320586 +endloop +endfacet +facet normal 0.171538 -0.039531 0.984384 +outer loop +vertex 26.155199 -2.887410 8.262807 +vertex 26.175591 -3.047504 8.252825 +vertex 32.798649 -1.584844 7.157436 +endloop +endfacet +facet normal 0.179874 -0.057356 0.982016 +outer loop +vertex 26.000004 -1.750000 8.357668 +vertex 25.946306 -2.553310 8.320586 +vertex 26.155199 -2.887410 8.262807 +endloop +endfacet +facet normal 0.174462 -0.029048 0.984235 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 31.040508 -0.715920 7.494724 +vertex 26.000004 -1.750000 8.357668 +endloop +endfacet +facet normal 0.174927 -0.058104 0.982866 +outer loop +vertex 26.155199 -2.887410 8.262807 +vertex 32.798649 -1.584844 7.157436 +vertex 26.000004 -1.750000 8.357668 +endloop +endfacet +facet normal -0.046076 0.998934 -0.002765 +outer loop +vertex 17.307825 3.099989 6.690114 +vertex 17.658047 3.119799 8.010885 +vertex 18.206800 3.145085 8.002063 +endloop +endfacet +facet normal -0.045408 0.998967 -0.001906 +outer loop +vertex 19.549603 3.206118 8.000000 +vertex 20.253862 3.238130 8.000117 +vertex 19.461210 3.199903 6.848686 +endloop +endfacet +facet normal -0.045409 0.998963 -0.003222 +outer loop +vertex 17.307825 3.099989 6.690114 +vertex 18.206800 3.145085 8.002063 +vertex 19.549603 3.206118 8.000000 +endloop +endfacet +facet normal -0.046213 0.998930 -0.001845 +outer loop +vertex 19.549603 3.206118 8.000000 +vertex 19.461210 3.199903 6.848686 +vertex 17.307825 3.099989 6.690114 +endloop +endfacet +facet normal 0.534949 -0.088776 -0.840207 +outer loop +vertex 26.235046 -3.800150 8.065972 +vertex 26.337137 -3.160255 8.063360 +vertex 26.443146 -3.132867 8.127961 +endloop +endfacet +facet normal 0.889279 -0.039139 -0.455688 +outer loop +vertex 26.443146 -3.132867 8.127961 +vertex 26.448036 -3.115854 8.136044 +vertex 26.418255 -3.801430 8.136806 +endloop +endfacet +facet normal 0.360335 -0.025751 -0.932467 +outer loop +vertex 26.443146 -3.132867 8.127961 +vertex 26.418255 -3.801430 8.136806 +vertex 26.235046 -3.800150 8.065972 +endloop +endfacet +facet normal -0.646510 -0.028257 0.762382 +outer loop +vertex 15.679340 -0.737497 8.592188 +vertex 15.402760 -0.736852 8.357668 +vertex 15.427221 -1.296539 8.357668 +endloop +endfacet +facet normal -0.674858 -0.019374 0.737694 +outer loop +vertex 15.427221 -1.296539 8.357668 +vertex 15.434759 -1.559115 8.357668 +vertex 15.695139 -1.560369 8.595834 +endloop +endfacet +facet normal -0.669606 -0.009564 0.742655 +outer loop +vertex 15.427221 -1.296539 8.357668 +vertex 15.695139 -1.560369 8.595834 +vertex 15.679340 -0.737497 8.592188 +endloop +endfacet +facet normal 0.050350 -0.990036 0.131507 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 25.696550 -3.224265 8.021599 +vertex 24.391867 -3.292191 8.009745 +endloop +endfacet +facet normal 0.081508 -0.996209 0.030395 +outer loop +vertex 24.391867 -3.292191 8.009745 +vertex 23.345116 -3.377946 8.006075 +vertex 23.701311 -3.388976 6.689353 +endloop +endfacet +facet normal 0.001676 -0.997386 0.072232 +outer loop +vertex 24.391867 -3.292191 8.009745 +vertex 23.701311 -3.388976 6.689353 +vertex 26.488607 -3.412277 6.302927 +endloop +endfacet +facet normal 0.115149 -0.968319 0.221582 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 26.443146 -3.132867 8.127961 +vertex 26.337137 -3.160255 8.063360 +endloop +endfacet +facet normal 0.077055 -0.986654 0.143447 +outer loop +vertex 25.854183 -3.211885 8.022071 +vertex 25.696550 -3.224265 8.021599 +vertex 26.488607 -3.412277 6.302927 +endloop +endfacet +facet normal 0.094369 -0.986971 0.130319 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 26.337137 -3.160255 8.063360 +vertex 25.854183 -3.211885 8.022071 +endloop +endfacet +facet normal 0.092770 -0.960406 0.262706 +outer loop +vertex 26.488607 -3.412277 6.302927 +vertex 31.104549 -3.148645 5.636679 +vertex 32.833088 -2.745604 6.499722 +endloop +endfacet +facet normal 0.098669 -0.983587 0.151064 +outer loop +vertex 25.854183 -3.211885 8.022071 +vertex 26.488607 -3.412277 6.302927 +vertex 32.833088 -2.745604 6.499722 +endloop +endfacet +facet normal 0.087076 -0.017958 -0.996040 +outer loop +vertex 25.854183 -3.211885 8.022071 +vertex 26.337137 -3.160255 8.063360 +vertex 26.235046 -3.800150 8.065972 +endloop +endfacet +facet normal 0.054068 -0.039456 -0.997757 +outer loop +vertex 25.854183 -3.211885 8.022071 +vertex 26.235046 -3.800150 8.065972 +vertex 25.563290 -3.798691 8.029513 +endloop +endfacet +facet normal -0.646558 0.025163 0.762450 +outer loop +vertex 15.434760 1.559115 8.357668 +vertex 15.402760 0.736852 8.357668 +vertex 15.679340 0.737497 8.592188 +endloop +endfacet +facet normal -0.674927 0.009687 0.737821 +outer loop +vertex 15.434760 1.559115 8.357668 +vertex 15.679340 0.737497 8.592188 +vertex 15.695139 1.560369 8.595834 +endloop +endfacet +facet normal 0.287964 0.091813 0.953230 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 26.429348 3.098415 8.098086 +vertex 26.471371 2.578741 8.135446 +endloop +endfacet +facet normal 0.287966 0.091854 0.953225 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 26.471371 2.578741 8.135446 +vertex 26.450327 2.551991 8.144380 +endloop +endfacet +facet normal 0.195991 0.434585 0.879047 +outer loop +vertex 26.000004 1.750000 8.357668 +vertex 30.465677 1.665626 7.403723 +vertex 30.651365 2.674705 6.863454 +endloop +endfacet +facet normal 0.287964 0.091816 0.953230 +outer loop +vertex 26.159176 2.236687 8.262705 +vertex 25.811188 2.342197 8.357667 +vertex 26.000004 1.750000 8.357668 +endloop +endfacet +facet normal 0.287964 0.091814 0.953230 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 26.450327 2.551991 8.144380 +vertex 26.159176 2.236687 8.262705 +endloop +endfacet +facet normal 0.287964 0.091817 0.953229 +outer loop +vertex 26.159176 2.236687 8.262705 +vertex 26.000004 1.750000 8.357668 +vertex 30.651365 2.674705 6.863454 +endloop +endfacet +facet normal -0.016121 0.000954 -0.999870 +outer loop +vertex 18.211685 3.794066 8.002604 +vertex 18.206800 3.145085 8.002063 +vertex 17.658047 3.119799 8.010885 +endloop +endfacet +facet normal -0.009774 0.006966 -0.999928 +outer loop +vertex 17.658047 3.119799 8.010885 +vertex 17.098806 3.033245 8.015749 +vertex 17.121878 3.795351 8.020833 +endloop +endfacet +facet normal -0.016724 0.001451 -0.999859 +outer loop +vertex 17.658047 3.119799 8.010885 +vertex 17.121878 3.795351 8.020833 +vertex 18.211685 3.794066 8.002604 +endloop +endfacet +facet normal 0.261755 -0.000457 0.965134 +outer loop +vertex 25.814472 -1.560014 8.357668 +vertex 25.815907 -0.737309 8.357668 +vertex 25.414009 -0.737545 8.466667 +endloop +endfacet +facet normal 0.262631 -0.000000 0.964896 +outer loop +vertex 25.814472 -1.560014 8.357668 +vertex 25.414009 -0.737545 8.466667 +vertex 25.414009 -1.560457 8.466666 +endloop +endfacet +facet normal -0.119174 -0.989068 -0.086848 +outer loop +vertex 19.651859 -3.205200 6.661274 +vertex 18.147570 -3.141765 8.003051 +vertex 17.098297 -3.016442 8.015636 +endloop +endfacet +facet normal -0.152691 -0.988015 -0.022647 +outer loop +vertex 16.194366 -2.877492 8.048306 +vertex 15.780942 -2.814433 8.084641 +vertex 18.135849 -3.132686 6.091653 +endloop +endfacet +facet normal -0.155594 -0.975192 -0.157450 +outer loop +vertex 19.651859 -3.205200 6.661274 +vertex 17.098297 -3.016442 8.015636 +vertex 16.194366 -2.877492 8.048306 +endloop +endfacet +facet normal -0.070236 -0.995714 0.060173 +outer loop +vertex 16.194366 -2.877492 8.048306 +vertex 18.135849 -3.132686 6.091653 +vertex 19.651859 -3.205200 6.661274 +endloop +endfacet +facet normal 0.264651 -0.000001 0.964344 +outer loop +vertex 25.414009 -2.554101 8.466667 +vertex 25.946306 -2.553310 8.320586 +vertex 25.811184 -2.342206 8.357668 +endloop +endfacet +facet normal 0.262626 -0.001634 0.964896 +outer loop +vertex 25.813204 -1.764621 8.357668 +vertex 25.814472 -1.560014 8.357668 +vertex 25.414009 -1.560457 8.466666 +endloop +endfacet +facet normal 0.265109 -0.000928 0.964218 +outer loop +vertex 25.414009 -2.554101 8.466667 +vertex 25.811184 -2.342206 8.357668 +vertex 25.813204 -1.764621 8.357668 +endloop +endfacet +facet normal 0.263406 0.000002 0.964685 +outer loop +vertex 25.813204 -1.764621 8.357668 +vertex 25.414009 -1.560457 8.466666 +vertex 25.414009 -2.554101 8.466667 +endloop +endfacet +facet normal -0.040914 0.028815 -0.998747 +outer loop +vertex 17.098806 3.033245 8.015749 +vertex 16.196001 2.892864 8.048681 +vertex 16.292145 3.797194 8.070833 +endloop +endfacet +facet normal -0.060129 0.008479 -0.998155 +outer loop +vertex 17.098806 3.033245 8.015749 +vertex 16.292145 3.797194 8.070833 +vertex 17.121878 3.795351 8.020833 +endloop +endfacet +facet normal 0.390536 -0.015941 0.920450 +outer loop +vertex 26.418255 -3.801430 8.136806 +vertex 26.448036 -3.115854 8.136044 +vertex 26.175591 -3.047504 8.252825 +endloop +endfacet +facet normal 0.425774 -0.002187 0.904827 +outer loop +vertex 26.175591 -3.047504 8.252825 +vertex 26.155199 -2.887410 8.262807 +vertex 26.150230 -3.802635 8.262934 +endloop +endfacet +facet normal 0.425804 -0.002187 0.904813 +outer loop +vertex 26.175591 -3.047504 8.252825 +vertex 26.150230 -3.802635 8.262934 +vertex 26.418255 -3.801430 8.136806 +endloop +endfacet +facet normal 0.374271 0.002397 0.927316 +outer loop +vertex 26.159176 2.236687 8.262705 +vertex 26.450327 2.551991 8.144380 +vertex 26.157015 2.552997 8.262760 +endloop +endfacet +facet normal -0.001574 0.000845 -0.999998 +outer loop +vertex 19.549603 3.206118 8.000000 +vertex 18.206800 3.145085 8.002063 +vertex 18.211685 3.794066 8.002604 +endloop +endfacet +facet normal -0.001941 0.000011 -0.999998 +outer loop +vertex 19.549603 3.206118 8.000000 +vertex 18.211685 3.794066 8.002604 +vertex 19.552803 3.793593 8.000000 +endloop +endfacet +facet normal -0.646730 -0.003892 0.762709 +outer loop +vertex 15.398325 0.000000 8.357668 +vertex 15.402760 -0.736852 8.357668 +vertex 15.679340 -0.737497 8.592188 +endloop +endfacet +facet normal -0.642936 -0.001426 0.765918 +outer loop +vertex 15.398325 0.000000 8.357668 +vertex 15.679340 -0.737497 8.592188 +vertex 15.677083 0.000000 8.591667 +endloop +endfacet +facet normal 0.000000 0.000001 1.000000 +outer loop +vertex 25.811188 2.342197 8.357667 +vertex 25.813848 1.660502 8.357668 +vertex 26.000004 1.750000 8.357668 +endloop +endfacet +facet normal 0.889208 0.039135 -0.455825 +outer loop +vertex 26.418255 3.801430 8.136806 +vertex 26.471371 2.578741 8.135446 +vertex 26.429348 3.098415 8.098086 +endloop +endfacet +facet normal 0.390112 0.065989 -0.918400 +outer loop +vertex 26.429348 3.098415 8.098086 +vertex 26.344246 3.115694 8.063178 +vertex 26.235046 3.800150 8.065972 +endloop +endfacet +facet normal 0.359681 0.056970 -0.931335 +outer loop +vertex 26.429348 3.098415 8.098086 +vertex 26.235046 3.800150 8.065972 +vertex 26.418255 3.801430 8.136806 +endloop +endfacet +facet normal -0.051486 -0.092620 -0.994370 +outer loop +vertex 15.767362 -3.799347 8.177083 +vertex 15.436029 -2.794299 8.100622 +vertex 15.780942 -2.814433 8.084641 +endloop +endfacet +facet normal -0.092678 -0.034228 -0.995108 +outer loop +vertex 15.780942 -2.814433 8.084641 +vertex 16.194366 -2.877492 8.048306 +vertex 16.292145 -3.797194 8.070833 +endloop +endfacet +facet normal -0.197299 -0.088913 -0.976303 +outer loop +vertex 15.780942 -2.814433 8.084641 +vertex 16.292145 -3.797194 8.070833 +vertex 15.767362 -3.799347 8.177083 +endloop +endfacet +facet normal -0.081004 0.102134 -0.991467 +outer loop +vertex 16.196001 2.892864 8.048681 +vertex 15.437763 2.799557 8.101022 +vertex 15.767362 3.799347 8.177083 +endloop +endfacet +facet normal -0.198061 0.045040 -0.979154 +outer loop +vertex 16.196001 2.892864 8.048681 +vertex 15.767362 3.799347 8.177083 +vertex 16.292145 3.797194 8.070833 +endloop +endfacet +facet normal -0.755974 0.063856 0.651480 +outer loop +vertex 15.731250 2.553990 8.604167 +vertex 15.518668 2.552154 8.357668 +vertex 15.435318 1.565411 8.357668 +endloop +endfacet +facet normal -0.673871 0.059786 0.736426 +outer loop +vertex 15.435318 1.565411 8.357668 +vertex 15.434760 1.559115 8.357668 +vertex 15.695139 1.560369 8.595834 +endloop +endfacet +facet normal -0.675412 0.018365 0.737212 +outer loop +vertex 15.435318 1.565411 8.357668 +vertex 15.695139 1.560369 8.595834 +vertex 15.731250 2.553990 8.604167 +endloop +endfacet +facet normal 0.140188 0.979719 0.143172 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 25.666662 3.218757 8.021394 +vertex 25.864864 3.190336 8.021798 +endloop +endfacet +facet normal 0.140189 0.979720 0.143165 +outer loop +vertex 26.344246 3.115694 8.063178 +vertex 26.429348 3.098415 8.098086 +vertex 30.651365 2.674705 6.863454 +endloop +endfacet +facet normal 0.140192 0.979718 0.143174 +outer loop +vertex 26.406237 3.403702 6.031664 +vertex 25.864864 3.190336 8.021798 +vertex 26.344246 3.115694 8.063178 +endloop +endfacet +facet normal 0.089702 0.929763 0.357062 +outer loop +vertex 30.651365 2.674705 6.863454 +vertex 30.497274 3.120947 5.740185 +vertex 26.406237 3.403702 6.031664 +endloop +endfacet +facet normal 0.140190 0.979719 0.143173 +outer loop +vertex 26.344246 3.115694 8.063178 +vertex 30.651365 2.674705 6.863454 +vertex 26.406237 3.403702 6.031664 +endloop +endfacet +facet normal -0.755974 -0.063878 0.651478 +outer loop +vertex 15.434759 -1.559115 8.357668 +vertex 15.518668 -2.552154 8.357668 +vertex 15.731250 -2.553990 8.604167 +endloop +endfacet +facet normal -0.674866 -0.018339 0.737712 +outer loop +vertex 15.434759 -1.559115 8.357668 +vertex 15.731250 -2.553990 8.604167 +vertex 15.695139 -1.560369 8.595834 +endloop +endfacet +facet normal 0.244400 -0.472541 0.846743 +outer loop +vertex 32.798649 -1.584844 7.157436 +vertex 26.175591 -3.047504 8.252825 +vertex 26.448036 -3.115854 8.136044 +endloop +endfacet +facet normal 0.244393 -0.472518 0.846758 +outer loop +vertex 26.448036 -3.115854 8.136044 +vertex 26.443146 -3.132867 8.127961 +vertex 32.833088 -2.745604 6.499722 +endloop +endfacet +facet normal 0.147050 -0.484320 0.862445 +outer loop +vertex 32.833088 -2.745604 6.499722 +vertex 36.147072 -1.831106 6.448225 +vertex 32.798649 -1.584844 7.157436 +endloop +endfacet +facet normal 0.244400 -0.472535 0.846746 +outer loop +vertex 26.448036 -3.115854 8.136044 +vertex 32.833088 -2.745604 6.499722 +vertex 32.798649 -1.584844 7.157436 +endloop +endfacet +facet normal -0.001535 -0.000008 -0.999999 +outer loop +vertex 18.206795 -3.144454 8.002062 +vertex 19.549599 -3.205483 8.000000 +vertex 19.552803 -3.793593 8.000000 +endloop +endfacet +facet normal -0.001942 -0.000848 -0.999998 +outer loop +vertex 18.206795 -3.144454 8.002062 +vertex 19.552803 -3.793593 8.000000 +vertex 18.211685 -3.794066 8.002604 +endloop +endfacet +endsolid Exported from Blender-2.80 (sub 74) diff --git a/tests/pytest/meshes.py b/tests/pytest/meshes.py new file mode 100644 index 00000000..d1c9a7dc --- /dev/null +++ b/tests/pytest/meshes.py @@ -0,0 +1,11 @@ +import pytest + +@pytest.fixture +def unit_mesh_2d(): + import netgen.geom2d as g2d + return g2d.unit_square.GenerateMesh(maxh=0.2) + +@pytest.fixture +def unit_mesh_3d(): + import netgen.csg as csg + return csg.unit_cube.GenerateMesh(maxh=0.2) diff --git a/tests/pytest/results.json b/tests/pytest/results.json new file mode 100644 index 00000000..3cdc607c --- /dev/null +++ b/tests/pytest/results.json @@ -0,0 +1,3425 @@ +{ + "boundarycondition.geo": [ + { + "angles_tet": [ + 27.291, + 136.38 + ], + "angles_trig": [ + 23.577, + 123.09 + ], + "ne1d": 74, + "ne2d": 52, + "ne3d": 48, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 7, 7, 16, 3, 6, 1, 1, 0, 1]", + "total_badness": 74.651719788 + }, + { + "angles_tet": [ + 31.383, + 127.91 + ], + "angles_trig": [ + 28.041, + 119.87 + ], + "ne1d": 59, + "ne2d": 37, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.161528768 + }, + { + "angles_tet": [ + 32.461, + 127.16 + ], + "angles_trig": [ + 28.547, + 120.05 + ], + "ne1d": 59, + "ne2d": 37, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.098288788 + }, + { + "angles_tet": [ + 27.292, + 136.38 + ], + "angles_trig": [ + 23.578, + 123.09 + ], + "ne1d": 74, + "ne2d": 52, + "ne3d": 48, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 7, 7, 16, 3, 6, 1, 1, 0, 1]", + "total_badness": 74.651710944 + }, + { + "angles_tet": [ + 27.415, + 131.66 + ], + "angles_trig": [ + 26.455, + 111.47 + ], + "ne1d": 118, + "ne2d": 126, + "ne3d": 141, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 18, 11, 17, 30, 19, 19, 14, 7, 2]", + "total_badness": 196.01215512 + }, + { + "angles_tet": [ + 26.405, + 131.02 + ], + "angles_trig": [ + 24.196, + 110.45 + ], + "ne1d": 181, + "ne2d": 291, + "ne3d": 459, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 9, 18, 30, 44, 69, 111, 100, 54, 18]", + "total_badness": 575.46697618 + } + ], + "boxcyl.geo": [ + { + "angles_tet": [ + 21.213, + 142.56 + ], + "angles_trig": [ + 22.379, + 121.98 + ], + "ne1d": 190, + "ne2d": 450, + "ne3d": 834, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 24, 93, 74, 76, 85, 110, 106, 100, 99, 45, 19]", + "total_badness": 1200.3294639 + }, + { + "angles_tet": [ + 19.341, + 145.29 + ], + "angles_trig": [ + 22.325, + 120.0 + ], + "ne1d": 94, + "ne2d": 108, + "ne3d": 112, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 4, 5, 15, 14, 6, 14, 6, 10, 7, 10, 3, 15, 2, 0]", + "total_badness": 200.73145455 + }, + { + "angles_tet": [ + 14.751, + 158.04 + ], + "angles_trig": [ + 19.228, + 140.0 + ], + "ne1d": 136, + "ne2d": 204, + "ne3d": 326, + "quality_histogram": "[0, 0, 0, 2, 2, 7, 8, 10, 14, 12, 12, 22, 26, 34, 50, 43, 45, 26, 10, 3]", + "total_badness": 530.84214658 + }, + { + "angles_tet": [ + 21.211, + 138.67 + ], + "angles_trig": [ + 22.376, + 121.98 + ], + "ne1d": 190, + "ne2d": 450, + "ne3d": 833, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 24, 86, 78, 71, 79, 124, 107, 92, 99, 51, 21]", + "total_badness": 1192.8552253 + }, + { + "angles_tet": [ + 26.153, + 141.36 + ], + "angles_trig": [ + 25.575, + 114.94 + ], + "ne1d": 284, + "ne2d": 922, + "ne3d": 3853, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 15, 42, 118, 219, 455, 671, 787, 798, 574, 170]", + "total_badness": 4779.2253231 + }, + { + "angles_tet": [ + 25.158, + 143.56 + ], + "angles_trig": [ + 26.346, + 116.86 + ], + "ne1d": 456, + "ne2d": 2480, + "ne3d": 18633, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 17, 93, 295, 810, 1622, 2827, 3994, 4471, 3391, 1108]", + "total_badness": 22509.04709 + } + ], + "circle_on_cube.geo": [ + { + "angles_tet": [ + 25.073, + 134.64 + ], + "angles_trig": [ + 20.125, + 122.26 + ], + "ne1d": 94, + "ne2d": 162, + "ne3d": 616, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 4, 13, 15, 36, 61, 59, 78, 112, 98, 85, 43, 12]", + "total_badness": 838.94349075 + }, + { + "angles_tet": [ + 15.603, + 140.66 + ], + "angles_trig": [ + 19.788, + 123.88 + ], + "ne1d": 40, + "ne2d": 30, + "ne3d": 43, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 0, 2, 5, 9, 7, 5, 5, 3, 3, 2, 0, 0, 1, 0]", + "total_badness": 82.109638474 + }, + { + "angles_tet": [ + 20.678, + 133.74 + ], + "angles_trig": [ + 23.119, + 112.86 + ], + "ne1d": 62, + "ne2d": 76, + "ne3d": 155, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 9, 16, 26, 34, 29, 13, 10, 8, 0]", + "total_badness": 220.46268417 + }, + { + "angles_tet": [ + 25.158, + 131.6 + ], + "angles_trig": [ + 23.161, + 113.1 + ], + "ne1d": 94, + "ne2d": 162, + "ne3d": 587, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 22, 44, 62, 62, 114, 104, 94, 57, 15]", + "total_badness": 769.56121713 + }, + { + "angles_tet": [ + 22.472, + 138.67 + ], + "angles_trig": [ + 26.461, + 115.01 + ], + "ne1d": 138, + "ne2d": 370, + "ne3d": 2009, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 13, 21, 56, 130, 220, 339, 438, 428, 295, 67]", + "total_badness": 2495.8001047 + }, + { + "angles_tet": [ + 25.429, + 141.91 + ], + "angles_trig": [ + 26.66, + 115.7 + ], + "ne1d": 224, + "ne2d": 922, + "ne3d": 12168, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 8, 17, 52, 227, 553, 1180, 1901, 2590, 2911, 2091, 637]", + "total_badness": 14779.532725 + } + ], + "cone.geo": [ + { + "angles_tet": [ + 14.938, + 141.57 + ], + "angles_trig": [ + 16.548, + 122.02 + ], + "ne1d": 64, + "ne2d": 718, + "ne3d": 1191, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 2, 18, 41, 51, 83, 122, 133, 145, 163, 130, 138, 89, 60, 15]", + "total_badness": 1802.9323748 + }, + { + "angles_tet": [ + 8.3849, + 167.59 + ], + "angles_trig": [ + 10.826, + 151.58 + ], + "ne1d": 32, + "ne2d": 208, + "ne3d": 486, + "quality_histogram": "[0, 0, 1, 4, 5, 16, 34, 28, 32, 34, 49, 35, 37, 42, 39, 42, 26, 28, 26, 8]", + "total_badness": 915.87761832 + }, + { + "angles_tet": [ + 7.5064, + 168.59 + ], + "angles_trig": [ + 9.0552, + 153.87 + ], + "ne1d": 48, + "ne2d": 420, + "ne3d": 618, + "quality_histogram": "[0, 0, 4, 11, 7, 17, 9, 23, 39, 60, 71, 85, 85, 55, 56, 27, 42, 19, 8, 0]", + "total_badness": 1183.5702625 + }, + { + "angles_tet": [ + 17.166, + 143.86 + ], + "angles_trig": [ + 19.54, + 120.27 + ], + "ne1d": 64, + "ne2d": 718, + "ne3d": 1186, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 8, 22, 42, 92, 106, 123, 168, 148, 166, 127, 103, 62, 17]", + "total_badness": 1745.7492969 + }, + { + "angles_tet": [ + 25.516, + 138.4 + ], + "angles_trig": [ + 25.119, + 121.58 + ], + "ne1d": 96, + "ne2d": 1648, + "ne3d": 4372, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 16, 60, 127, 237, 407, 551, 707, 802, 764, 543, 156]", + "total_badness": 5622.2033105 + }, + { + "angles_tet": [ + 20.726, + 143.6 + ], + "angles_trig": [ + 23.171, + 123.6 + ], + "ne1d": 160, + "ne2d": 4738, + "ne3d": 27128, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 6, 18, 86, 216, 621, 1451, 2693, 4334, 5762, 5891, 4576, 1473]", + "total_badness": 33206.092666 + } + ], + "cube.geo": [ + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 27.354, + 136.27 + ], + "angles_trig": [ + 21.671, + 126.93 + ], + "ne1d": 48, + "ne2d": 36, + "ne3d": 57, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 1, 10, 12, 9, 12, 4, 1, 2, 0]", + "total_badness": 83.840161698 + }, + { + "angles_tet": [ + 31.709, + 132.62 + ], + "angles_trig": [ + 28.796, + 108.23 + ], + "ne1d": 72, + "ne2d": 92, + "ne3d": 138, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 11, 8, 8, 25, 23, 30, 17, 10]", + "total_badness": 173.92559042 + } + ], + "cubeandring.geo": [ + { + "angles_tet": [ + 5.9072, + 170.37 + ], + "angles_trig": [ + 11.614, + 156.34 + ], + "ne1d": 262, + "ne2d": 652, + "ne3d": 2038, + "quality_histogram": "[0, 2, 7, 27, 57, 102, 101, 108, 81, 61, 60, 83, 136, 187, 241, 238, 234, 170, 107, 36]", + "total_badness": 3798.8151666 + }, + { + "angles_tet": [ + 17.294, + 155.18 + ], + "angles_trig": [ + 22.715, + 110.61 + ], + "ne1d": 134, + "ne2d": 142, + "ne3d": 205, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 0, 1, 0, 3, 7, 17, 26, 37, 30, 28, 34, 15, 2, 4]", + "total_badness": 293.89415407 + }, + { + "angles_tet": [ + 14.206, + 159.84 + ], + "angles_trig": [ + 20.057, + 131.52 + ], + "ne1d": 190, + "ne2d": 242, + "ne3d": 501, + "quality_histogram": "[0, 0, 0, 0, 2, 0, 0, 5, 4, 14, 39, 54, 41, 82, 64, 82, 54, 37, 23, 0]", + "total_badness": 745.18627676 + }, + { + "angles_tet": [ + 5.9072, + 170.37 + ], + "angles_trig": [ + 11.709, + 156.34 + ], + "ne1d": 262, + "ne2d": 652, + "ne3d": 1894, + "quality_histogram": "[0, 2, 5, 20, 38, 79, 95, 106, 70, 32, 41, 60, 94, 145, 217, 266, 250, 198, 121, 55]", + "total_badness": 3344.7627877 + }, + { + "angles_tet": [ + 21.707, + 139.77 + ], + "angles_trig": [ + 23.443, + 118.77 + ], + "ne1d": 378, + "ne2d": 1360, + "ne3d": 7586, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 9, 46, 126, 290, 603, 925, 1250, 1521, 1436, 1062, 317]", + "total_badness": 9530.8442156 + }, + { + "angles_tet": [ + 23.791, + 144.45 + ], + "angles_trig": [ + 26.716, + 123.99 + ], + "ne1d": 624, + "ne2d": 3860, + "ne3d": 38096, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 26, 61, 218, 722, 1809, 3652, 6020, 8063, 8757, 6666, 2099]", + "total_badness": 46320.985555 + } + ], + "cubeandspheres.geo": [ + { + "angles_tet": [ + 23.615, + 137.71 + ], + "angles_trig": [ + 29.501, + 107.25 + ], + "ne1d": 144, + "ne2d": 144, + "ne3d": 92, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 3, 2, 17, 19, 12, 18, 4, 6, 2, 0]", + "total_badness": 137.495802 + }, + { + "angles_tet": [ + 24.423, + 137.5 + ], + "angles_trig": [ + 30.884, + 106.1 + ], + "ne1d": 144, + "ne2d": 138, + "ne3d": 83, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 7, 5, 24, 16, 11, 15, 1, 1, 1, 0]", + "total_badness": 126.26836349 + }, + { + "angles_tet": [ + 23.888, + 137.32 + ], + "angles_trig": [ + 31.068, + 105.7 + ], + "ne1d": 144, + "ne2d": 138, + "ne3d": 83, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 6, 5, 23, 16, 8, 19, 0, 3, 0, 0]", + "total_badness": 126.93839932 + }, + { + "angles_tet": [ + 23.615, + 137.71 + ], + "angles_trig": [ + 29.501, + 107.25 + ], + "ne1d": 144, + "ne2d": 144, + "ne3d": 92, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 6, 3, 2, 17, 19, 12, 18, 4, 6, 2, 0]", + "total_badness": 137.495802 + }, + { + "angles_tet": [ + 26.936, + 139.31 + ], + "angles_trig": [ + 22.269, + 127.24 + ], + "ne1d": 264, + "ne2d": 344, + "ne3d": 304, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 20, 20, 42, 39, 43, 18, 34, 42, 30, 14, 1]", + "total_badness": 450.93390985 + }, + { + "angles_tet": [ + 15.335, + 146.31 + ], + "angles_trig": [ + 18.471, + 128.1 + ], + "ne1d": 428, + "ne2d": 906, + "ne3d": 1041, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 4, 24, 50, 35, 98, 106, 102, 105, 178, 167, 69, 59, 26, 18]", + "total_badness": 1617.9736122 + } + ], + "cubemcyl.geo": [ + { + "angles_tet": [ + 17.552, + 149.02 + ], + "angles_trig": [ + 19.505, + 129.13 + ], + "ne1d": 142, + "ne2d": 2400, + "ne3d": 20169, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 5, 25, 79, 231, 483, 876, 1588, 2360, 2911, 3368, 3435, 2711, 1628, 469]", + "total_badness": 27089.560986 + }, + { + "angles_tet": [ + 15.294, + 163.36 + ], + "angles_trig": [ + 13.852, + 128.58 + ], + "ne1d": 64, + "ne2d": 556, + "ne3d": 3011, + "quality_histogram": "[0, 0, 0, 1, 3, 1, 14, 25, 40, 85, 116, 209, 333, 406, 462, 445, 441, 279, 120, 31]", + "total_badness": 4347.4002113 + }, + { + "angles_tet": [ + 17.198, + 146.78 + ], + "angles_trig": [ + 19.119, + 125.8 + ], + "ne1d": 102, + "ne2d": 1280, + "ne3d": 8063, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 5, 27, 79, 200, 404, 723, 1075, 1303, 1418, 1197, 941, 556, 133]", + "total_badness": 10967.392706 + }, + { + "angles_tet": [ + 19.398, + 141.79 + ], + "angles_trig": [ + 21.638, + 126.55 + ], + "ne1d": 142, + "ne2d": 2400, + "ne3d": 19151, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 3, 14, 55, 170, 418, 1024, 1805, 2657, 3451, 3664, 3317, 2042, 530]", + "total_badness": 24613.208269 + }, + { + "angles_tet": [ + 21.795, + 146.06 + ], + "angles_trig": [ + 22.867, + 125.23 + ], + "ne1d": 210, + "ne2d": 5460, + "ne3d": 88820, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 11, 51, 248, 703, 2183, 5117, 9583, 14386, 18836, 19401, 14091, 4209]", + "total_badness": 109216.91549 + }, + { + "angles_tet": [ + 23.058, + 143.66 + ], + "angles_trig": [ + 23.971, + 124.25 + ], + "ne1d": 362, + "ne2d": 15082, + "ne3d": 520159, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 9, 85, 499, 2149, 7346, 21113, 47200, 79734, 110083, 123828, 97201, 30912]", + "total_badness": 627466.22084 + } + ], + "cubemsphere.geo": [ + { + "angles_tet": [ + 22.156, + 150.39 + ], + "angles_trig": [ + 20.064, + 125.29 + ], + "ne1d": 90, + "ne2d": 570, + "ne3d": 4523, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 9, 41, 75, 170, 331, 494, 702, 821, 761, 645, 383, 89]", + "total_badness": 6000.2419679 + }, + { + "angles_tet": [ + 10.055, + 158.35 + ], + "angles_trig": [ + 7.7708, + 147.23 + ], + "ne1d": 44, + "ne2d": 142, + "ne3d": 313, + "quality_histogram": "[0, 0, 1, 2, 9, 23, 31, 23, 34, 37, 31, 32, 22, 30, 18, 10, 4, 3, 1, 2]", + "total_badness": 701.96510542 + }, + { + "angles_tet": [ + 13.22, + 146.51 + ], + "angles_trig": [ + 16.161, + 132.51 + ], + "ne1d": 68, + "ne2d": 272, + "ne3d": 1429, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 0, 2, 7, 15, 38, 92, 155, 214, 253, 241, 169, 143, 76, 23]", + "total_badness": 1985.519867 + }, + { + "angles_tet": [ + 24.16, + 139.58 + ], + "angles_trig": [ + 20.668, + 120.71 + ], + "ne1d": 90, + "ne2d": 570, + "ne3d": 4315, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 36, 65, 214, 404, 593, 811, 842, 765, 470, 102]", + "total_badness": 5519.2080716 + }, + { + "angles_tet": [ + 25.558, + 139.27 + ], + "angles_trig": [ + 21.95, + 123.73 + ], + "ne1d": 146, + "ne2d": 1366, + "ne3d": 17357, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 8, 45, 131, 383, 1002, 1818, 2868, 3691, 3818, 2778, 814]", + "total_badness": 21308.513568 + }, + { + "angles_tet": [ + 24.327, + 140.29 + ], + "angles_trig": [ + 25.758, + 120.76 + ], + "ne1d": 248, + "ne2d": 4248, + "ne3d": 112960, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 17, 100, 493, 1726, 4923, 10719, 17771, 23842, 26353, 20492, 6522]", + "total_badness": 136671.30101 + } + ], + "cylinder.geo": [ + { + "angles_tet": [ + 19.076, + 144.67 + ], + "angles_trig": [ + 25.583, + 111.5 + ], + "ne1d": 52, + "ne2d": 286, + "ne3d": 407, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 2, 4, 10, 32, 48, 50, 81, 56, 52, 42, 14, 14]", + "total_badness": 567.546292 + }, + { + "angles_tet": [ + 24.676, + 151.98 + ], + "angles_trig": [ + 24.811, + 126.7 + ], + "ne1d": 24, + "ne2d": 66, + "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": [ + 8.4923, + 161.34 + ], + "angles_trig": [ + 20.122, + 127.45 + ], + "ne1d": 36, + "ne2d": 152, + "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.67848726 + }, + { + "angles_tet": [ + 19.058, + 144.69 + ], + "angles_trig": [ + 25.588, + 111.45 + ], + "ne1d": 52, + "ne2d": 286, + "ne3d": 407, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 0, 3, 10, 31, 45, 55, 73, 61, 53, 46, 15, 13]", + "total_badness": 563.90833945 + }, + { + "angles_tet": [ + 21.985, + 136.08 + ], + "angles_trig": [ + 24.141, + 119.73 + ], + "ne1d": 76, + "ne2d": 636, + "ne3d": 1183, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 9, 29, 61, 96, 141, 201, 195, 208, 129, 88, 25]", + "total_badness": 1595.7683733 + }, + { + "angles_tet": [ + 27.151, + 138.19 + ], + "angles_trig": [ + 27.89, + 120.16 + ], + "ne1d": 124, + "ne2d": 1666, + "ne3d": 7963, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 21, 55, 143, 336, 696, 1178, 1725, 1848, 1439, 521]", + "total_badness": 9633.6668545 + } + ], + "cylsphere.geo": [ + { + "angles_tet": [ + 16.89, + 146.66 + ], + "angles_trig": [ + 17.583, + 116.74 + ], + "ne1d": 104, + "ne2d": 494, + "ne3d": 707, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 5, 7, 19, 31, 60, 96, 109, 89, 101, 53, 69, 48, 15, 3]", + "total_badness": 1103.8873525 + }, + { + "angles_tet": [ + 11.146, + 163.27 + ], + "angles_trig": [ + 14.484, + 148.23 + ], + "ne1d": 48, + "ne2d": 100, + "ne3d": 104, + "quality_histogram": "[0, 0, 0, 2, 1, 4, 13, 15, 9, 10, 14, 5, 5, 2, 6, 10, 8, 0, 0, 0]", + "total_badness": 228.55775864 + }, + { + "angles_tet": [ + 16.975, + 146.47 + ], + "angles_trig": [ + 17.533, + 120.59 + ], + "ne1d": 104, + "ne2d": 494, + "ne3d": 706, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 5, 6, 17, 30, 59, 98, 99, 97, 96, 68, 65, 45, 16, 3]", + "total_badness": 1096.9819246 + }, + { + "angles_tet": [ + 19.739, + 142.01 + ], + "angles_trig": [ + 21.005, + 119.84 + ], + "ne1d": 152, + "ne2d": 1082, + "ne3d": 2842, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 5, 22, 43, 84, 156, 233, 309, 459, 535, 529, 378, 87]", + "total_badness": 3653.5906442 + }, + { + "angles_tet": [ + 25.231, + 139.3 + ], + "angles_trig": [ + 25.146, + 122.8 + ], + "ne1d": 248, + "ne2d": 2810, + "ne3d": 17714, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 29, 100, 280, 741, 1663, 2724, 3885, 4147, 3098, 1032]", + "total_badness": 21466.883949 + } + ], + "ellipsoid.geo": [ + { + "angles_tet": [ + 18.985, + 146.67 + ], + "angles_trig": [ + 18.356, + 122.93 + ], + "ne1d": 0, + "ne2d": 694, + "ne3d": 1271, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 7, 34, 67, 108, 149, 145, 161, 175, 147, 107, 92, 59, 18]", + "total_badness": 1927.202371 + }, + { + "angles_tet": [ + 4.4724, + 169.99 + ], + "angles_trig": [ + 9.3765, + 160.0 + ], + "ne1d": 0, + "ne2d": 156, + "ne3d": 536, + "quality_histogram": "[0, 13, 28, 52, 60, 66, 49, 44, 44, 37, 27, 35, 22, 18, 13, 9, 7, 6, 6, 0]", + "total_badness": 1855.3951762 + }, + { + "angles_tet": [ + 20.08, + 138.43 + ], + "angles_trig": [ + 19.842, + 116.21 + ], + "ne1d": 0, + "ne2d": 384, + "ne3d": 588, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 7, 17, 40, 68, 74, 90, 109, 64, 52, 37, 22, 7]", + "total_badness": 870.44417377 + }, + { + "angles_tet": [ + 22.423, + 143.66 + ], + "angles_trig": [ + 19.734, + 119.91 + ], + "ne1d": 0, + "ne2d": 694, + "ne3d": 1259, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 3, 20, 61, 90, 124, 145, 171, 157, 147, 147, 103, 75, 16]", + "total_badness": 1857.1598634 + }, + { + "angles_tet": [ + 21.995, + 138.77 + ], + "angles_trig": [ + 25.46, + 115.64 + ], + "ne1d": 0, + "ne2d": 1578, + "ne3d": 5402, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 7, 41, 142, 273, 437, 588, 900, 1031, 1032, 733, 215]", + "total_badness": 6841.327071 + }, + { + "angles_tet": [ + 21.744, + 144.6 + ], + "angles_trig": [ + 26.751, + 121.56 + ], + "ne1d": 0, + "ne2d": 4212, + "ne3d": 37347, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 9, 52, 185, 546, 1522, 3262, 5723, 7899, 8903, 6950, 2294]", + "total_badness": 45064.497844 + } + ], + "ellipticcone.geo": [ + { + "angles_tet": [ + 23.263, + 146.25 + ], + "angles_trig": [ + 22.188, + 124.34 + ], + "ne1d": 174, + "ne2d": 1492, + "ne3d": 4957, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 8, 34, 88, 187, 310, 511, 694, 881, 895, 745, 469, 135]", + "total_badness": 6507.6297349 + }, + { + "angles_tet": [ + 20.274, + 150.89 + ], + "angles_trig": [ + 22.128, + 124.89 + ], + "ne1d": 86, + "ne2d": 336, + "ne3d": 469, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 0, 6, 20, 36, 44, 66, 67, 67, 47, 50, 44, 16, 5]", + "total_badness": 695.59119444 + }, + { + "angles_tet": [ + 16.545, + 153.27 + ], + "angles_trig": [ + 16.861, + 134.96 + ], + "ne1d": 130, + "ne2d": 794, + "ne3d": 1476, + "quality_histogram": "[0, 0, 0, 0, 1, 0, 19, 28, 50, 58, 53, 90, 145, 165, 181, 203, 199, 155, 101, 28]", + "total_badness": 2177.7084872 + }, + { + "angles_tet": [ + 24.549, + 137.43 + ], + "angles_trig": [ + 22.188, + 117.33 + ], + "ne1d": 174, + "ne2d": 1492, + "ne3d": 4748, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 7, 32, 96, 200, 389, 599, 887, 945, 888, 521, 181]", + "total_badness": 6023.4146593 + }, + { + "angles_tet": [ + 19.964, + 146.92 + ], + "angles_trig": [ + 22.162, + 126.99 + ], + "ne1d": 258, + "ne2d": 3318, + "ne3d": 13093, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 1, 13, 35, 131, 267, 525, 918, 1525, 2178, 2534, 2516, 1864, 585]", + "total_badness": 16495.184175 + }, + { + "angles_tet": [ + 20.933, + 146.0 + ], + "angles_trig": [ + 22.947, + 128.99 + ], + "ne1d": 432, + "ne2d": 9184, + "ne3d": 68625, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 8, 20, 51, 215, 569, 1425, 3426, 6873, 10848, 14353, 15147, 11893, 3795]", + "total_badness": 83844.770837 + } + ], + "ellipticcyl.geo": [ + { + "angles_tet": [ + 20.908, + 145.52 + ], + "angles_trig": [ + 21.34, + 121.52 + ], + "ne1d": 156, + "ne2d": 942, + "ne3d": 2141, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 3, 8, 29, 62, 101, 159, 256, 306, 360, 377, 282, 155, 43]", + "total_badness": 2890.2110231 + }, + { + "angles_tet": [ + 16.477, + 144.27 + ], + "angles_trig": [ + 21.842, + 119.59 + ], + "ne1d": 76, + "ne2d": 200, + "ne3d": 241, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 2, 11, 23, 16, 32, 31, 40, 30, 30, 14, 6, 4, 0]", + "total_badness": 387.30490812 + }, + { + "angles_tet": [ + 24.683, + 136.88 + ], + "angles_trig": [ + 24.591, + 114.49 + ], + "ne1d": 116, + "ne2d": 542, + "ne3d": 1031, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 16, 45, 82, 99, 163, 195, 190, 115, 99, 23]", + "total_badness": 1364.8877139 + }, + { + "angles_tet": [ + 21.397, + 134.44 + ], + "angles_trig": [ + 21.803, + 118.55 + ], + "ne1d": 156, + "ne2d": 942, + "ne3d": 2091, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 15, 36, 73, 126, 232, 306, 377, 391, 305, 180, 48]", + "total_badness": 2749.9153281 + }, + { + "angles_tet": [ + 21.299, + 145.92 + ], + "angles_trig": [ + 22.971, + 123.45 + ], + "ne1d": 232, + "ne2d": 2102, + "ne3d": 7936, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 2, 7, 36, 102, 243, 506, 936, 1318, 1618, 1632, 1161, 374]", + "total_badness": 9862.7920315 + }, + { + "angles_tet": [ + 24.388, + 140.11 + ], + "angles_trig": [ + 24.731, + 114.31 + ], + "ne1d": 388, + "ne2d": 5914, + "ne3d": 54280, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 57, 235, 715, 1972, 4653, 8290, 11507, 13163, 10250, 3423]", + "total_badness": 65288.837508 + } + ], + "extrusion.geo": [ + { + "angles_tet": [ + 6.6753, + 171.52 + ], + "angles_trig": [ + 11.356, + 151.93 + ], + "ne1d": 172, + "ne2d": 284, + "ne3d": 239, + "quality_histogram": "[0, 0, 4, 51, 44, 23, 0, 0, 0, 0, 2, 0, 5, 18, 18, 29, 21, 10, 9, 5]", + "total_badness": 756.94679757 + }, + { + "angles_tet": [ + 11.453, + 162.3 + ], + "angles_trig": [ + 14.644, + 140.86 + ], + "ne1d": 104, + "ne2d": 126, + "ne3d": 99, + "quality_histogram": "[0, 0, 0, 1, 7, 18, 15, 11, 11, 10, 5, 5, 5, 1, 3, 2, 3, 2, 0, 0]", + "total_badness": 262.54448973 + }, + { + "angles_tet": [ + 14.092, + 161.67 + ], + "angles_trig": [ + 16.092, + 147.39 + ], + "ne1d": 134, + "ne2d": 176, + "ne3d": 147, + "quality_histogram": "[0, 0, 0, 0, 2, 10, 15, 24, 11, 24, 11, 9, 10, 11, 7, 3, 5, 3, 1, 1]", + "total_badness": 324.38705634 + }, + { + "angles_tet": [ + 6.6753, + 171.52 + ], + "angles_trig": [ + 11.356, + 151.93 + ], + "ne1d": 172, + "ne2d": 284, + "ne3d": 239, + "quality_histogram": "[0, 0, 4, 51, 44, 23, 0, 0, 0, 0, 2, 0, 5, 18, 18, 29, 21, 10, 9, 5]", + "total_badness": 756.94679757 + }, + { + "angles_tet": [ + 13.66, + 140.84 + ], + "angles_trig": [ + 16.325, + 118.98 + ], + "ne1d": 276, + "ne2d": 544, + "ne3d": 623, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 4, 11, 18, 30, 47, 70, 73, 69, 81, 73, 75, 52, 17, 3]", + "total_badness": 953.76990304 + } + ], + "fichera.geo": [ + { + "angles_tet": [ + 31.625, + 128.51 + ], + "angles_trig": [ + 35.264, + 92.7 + ], + "ne1d": 50, + "ne2d": 36, + "ne3d": 32, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 2, 8, 8, 5, 0, 0, 0, 0]", + "total_badness": 48.255148991 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 109.47 + ], + "ne1d": 42, + "ne2d": 24, + "ne3d": 18, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 10, 6, 1, 0, 0, 0, 0]", + "total_badness": 26.546480075 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 109.47 + ], + "ne1d": 42, + "ne2d": 24, + "ne3d": 18, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 10, 6, 1, 0, 0, 0, 0]", + "total_badness": 26.546480075 + }, + { + "angles_tet": [ + 31.625, + 128.51 + ], + "angles_trig": [ + 35.264, + 92.7 + ], + "ne1d": 50, + "ne2d": 36, + "ne3d": 32, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7, 2, 8, 8, 5, 0, 0, 0, 0]", + "total_badness": 48.255148991 + }, + { + "angles_tet": [ + 28.158, + 128.52 + ], + "angles_trig": [ + 28.353, + 114.07 + ], + "ne1d": 96, + "ne2d": 108, + "ne3d": 194, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 6, 12, 26, 25, 34, 34, 35, 12, 7]", + "total_badness": 254.28246256 + }, + { + "angles_tet": [ + 28.713, + 135.86 + ], + "angles_trig": [ + 27.552, + 105.69 + ], + "ne1d": 144, + "ne2d": 264, + "ne3d": 484, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 12, 23, 37, 65, 105, 72, 87, 59, 23]", + "total_badness": 613.06762292 + } + ], + "hinge.stl": [ + { + "angles_tet": [ + 15.803, + 152.56 + ], + "angles_trig": [ + 19.22, + 131.45 + ], + "ne1d": 456, + "ne2d": 1066, + "ne3d": 1694, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 6, 9, 23, 41, 74, 105, 136, 200, 243, 292, 219, 204, 110, 31]", + "total_badness": 2383.0874819 + }, + { + "angles_tet": [ + 4.8415, + 163.34 + ], + "angles_trig": [ + 8.9881, + 149.17 + ], + "ne1d": 298, + "ne2d": 502, + "ne3d": 610, + "quality_histogram": "[0, 0, 3, 5, 10, 16, 27, 39, 43, 38, 55, 65, 64, 51, 49, 62, 38, 24, 16, 5]", + "total_badness": 1160.8468965 + }, + { + "angles_tet": [ + 12.929, + 152.0 + ], + "angles_trig": [ + 10.914, + 145.17 + ], + "ne1d": 370, + "ne2d": 758, + "ne3d": 982, + "quality_histogram": "[0, 0, 0, 0, 0, 7, 6, 25, 33, 29, 47, 79, 112, 128, 147, 142, 96, 68, 50, 13]", + "total_badness": 1492.3556478 + }, + { + "angles_tet": [ + 17.097, + 147.54 + ], + "angles_trig": [ + 18.124, + 131.28 + ], + "ne1d": 516, + "ne2d": 1454, + "ne3d": 2329, + "quality_histogram": "[0, 0, 0, 0, 0, 2, 2, 13, 22, 39, 68, 145, 210, 272, 358, 330, 328, 308, 185, 47]", + "total_badness": 3218.2251987 + }, + { + "angles_tet": [ + 10.908, + 160.75 + ], + "angles_trig": [ + 24.909, + 127.95 + ], + "ne1d": 722, + "ne2d": 2768, + "ne3d": 6516, + "quality_histogram": "[0, 0, 0, 0, 1, 0, 0, 3, 16, 30, 61, 162, 306, 578, 835, 999, 1088, 1188, 967, 282]", + "total_badness": 8307.9517383 + }, + { + "angles_tet": [ + 19.776, + 144.38 + ], + "angles_trig": [ + 22.289, + 122.14 + ], + "ne1d": 1862, + "ne2d": 18540, + "ne3d": 125397, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 4, 21, 93, 374, 1009, 2638, 6483, 12401, 19519, 26284, 28157, 21536, 6877]", + "total_badness": 153172.81544 + } + ], + "lense.in2d": [ + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 80, + "ne2d": 376, + "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": 86, + "ne2d": 308, + "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": 86, + "ne2d": 360, + "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": 80, + "ne2d": 376, + "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": 83, + "ne2d": 429, + "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": 84, + "ne2d": 462, + "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 + } + ], + "lshape3d.geo": [ + { + "angles_tet": [ + 35.202, + 125.39 + ], + "angles_trig": [ + 35.225, + 109.34 + ], + "ne1d": 44, + "ne2d": 28, + "ne3d": 24, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 20, 0, 2, 0, 0, 0, 0]", + "total_badness": 36.197580222 + }, + { + "angles_tet": [ + 30.0, + 120.0 + ], + "angles_trig": [ + 26.565, + 90.0 + ], + "ne1d": 36, + "ne2d": 20, + "ne3d": 12, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 6, 1, 0, 0, 0, 0, 0]", + "total_badness": 18.961481515 + }, + { + "angles_tet": [ + 30.0, + 120.0 + ], + "angles_trig": [ + 26.565, + 90.0 + ], + "ne1d": 36, + "ne2d": 20, + "ne3d": 12, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 6, 1, 0, 0, 0, 0, 0]", + "total_badness": 18.961481515 + }, + { + "angles_tet": [ + 35.202, + 125.39 + ], + "angles_trig": [ + 35.225, + 109.34 + ], + "ne1d": 44, + "ne2d": 28, + "ne3d": 24, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 20, 0, 2, 0, 0, 0, 0]", + "total_badness": 36.197580222 + }, + { + "angles_tet": [ + 31.097, + 125.11 + ], + "angles_trig": [ + 28.101, + 92.426 + ], + "ne1d": 80, + "ne2d": 66, + "ne3d": 73, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 20, 13, 2, 3, 6]", + "total_badness": 97.336071703 + }, + { + "angles_tet": [ + 25.594, + 129.87 + ], + "angles_trig": [ + 24.835, + 109.77 + ], + "ne1d": 122, + "ne2d": 192, + "ne3d": 302, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 10, 16, 35, 31, 45, 51, 66, 34, 10]", + "total_badness": 388.25333392 + } + ], + "manyholes.geo": [ + { + "angles_tet": [ + 17.962, + 147.8 + ], + "angles_trig": [ + 17.912, + 139.33 + ], + "ne1d": 5886, + "ne2d": 45882, + "ne3d": 174631, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 7, 64, 229, 668, 1859, 5592, 10791, 18495, 26482, 29904, 31051, 26889, 18192, 4407]", + "total_badness": 227663.03541 + }, + { + "angles_tet": [ + 13.226, + 153.55 + ], + "angles_trig": [ + 14.377, + 130.91 + ], + "ne1d": 2746, + "ne2d": 10428, + "ne3d": 23614, + "quality_histogram": "[0, 0, 0, 1, 2, 8, 39, 141, 333, 732, 1350, 2122, 2750, 3219, 3149, 3069, 2743, 2192, 1438, 326]", + "total_badness": 34314.343654 + }, + { + "angles_tet": [ + 12.344, + 154.1 + ], + "angles_trig": [ + 12.439, + 133.91 + ], + "ne1d": 4106, + "ne2d": 23238, + "ne3d": 63123, + "quality_histogram": "[0, 0, 0, 0, 31, 60, 174, 310, 596, 1169, 2077, 3620, 5748, 7925, 9100, 9947, 9228, 7455, 4552, 1131]", + "total_badness": 87899.222784 + } + ], + "manyholes2.geo": [ + { + "angles_tet": [ + 10.467, + 152.55 + ], + "angles_trig": [ + 16.373, + 136.58 + ], + "ne1d": 10202, + "ne2d": 41054, + "ne3d": 104058, + "quality_histogram": "[0, 0, 0, 0, 2, 14, 88, 229, 686, 1719, 3645, 6682, 9826, 12717, 14060, 15164, 15249, 13389, 8410, 2178]", + "total_badness": 143298.73232 + } + ], + "matrix.geo": [ + { + "angles_tet": [ + 8.9391, + 167.45 + ], + "angles_trig": [ + 9.9849, + 158.68 + ], + "ne1d": 174, + "ne2d": 1070, + "ne3d": 4599, + "quality_histogram": "[0, 0, 10, 93, 172, 67, 25, 59, 126, 165, 272, 371, 440, 515, 548, 537, 528, 411, 204, 56]", + "total_badness": 7959.8830096 + }, + { + "angles_tet": [ + 6.4945, + 166.83 + ], + "angles_trig": [ + 8.2716, + 155.6 + ], + "ne1d": 106, + "ne2d": 314, + "ne3d": 872, + "quality_histogram": "[0, 0, 4, 34, 45, 54, 80, 72, 92, 87, 91, 73, 67, 53, 44, 32, 21, 20, 3, 0]", + "total_badness": 2091.3790714 + }, + { + "angles_tet": [ + 6.3225, + 170.81 + ], + "angles_trig": [ + 10.133, + 155.67 + ], + "ne1d": 132, + "ne2d": 588, + "ne3d": 1799, + "quality_histogram": "[0, 0, 2, 17, 33, 76, 152, 120, 84, 98, 127, 150, 170, 203, 187, 133, 118, 64, 55, 10]", + "total_badness": 3522.631959 + }, + { + "angles_tet": [ + 8.9391, + 167.45 + ], + "angles_trig": [ + 9.9849, + 158.68 + ], + "ne1d": 174, + "ne2d": 1070, + "ne3d": 4497, + "quality_histogram": "[0, 0, 10, 93, 172, 65, 16, 60, 101, 137, 258, 314, 387, 500, 529, 561, 555, 411, 252, 76]", + "total_badness": 7683.1109366 + }, + { + "angles_tet": [ + 13.101, + 145.56 + ], + "angles_trig": [ + 15.887, + 143.02 + ], + "ne1d": 248, + "ne2d": 2256, + "ne3d": 16133, + "quality_histogram": "[0, 0, 0, 0, 0, 7, 22, 55, 90, 186, 318, 576, 969, 1526, 2057, 2534, 2791, 2579, 1841, 582]", + "total_badness": 21280.729551 + }, + { + "angles_tet": [ + 18.113, + 145.19 + ], + "angles_trig": [ + 17.821, + 130.51 + ], + "ne1d": 418, + "ne2d": 5914, + "ne3d": 101287, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 6, 8, 42, 111, 349, 979, 2418, 5321, 10105, 15903, 20923, 22509, 17204, 5408]", + "total_badness": 124144.15591 + } + ], + "ortho.geo": [ + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 35.264, + 125.26 + ], + "angles_trig": [ + 35.264, + 90.0 + ], + "ne1d": 24, + "ne2d": 12, + "ne3d": 6, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 1, 0, 0, 0, 0]", + "total_badness": 9.1401272869 + }, + { + "angles_tet": [ + 27.31, + 136.31 + ], + "angles_trig": [ + 32.958, + 102.54 + ], + "ne1d": 48, + "ne2d": 36, + "ne3d": 57, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 2, 9, 11, 10, 12, 4, 1, 2, 0]", + "total_badness": 83.838340747 + }, + { + "angles_tet": [ + 27.731, + 134.89 + ], + "angles_trig": [ + 28.064, + 104.8 + ], + "ne1d": 72, + "ne2d": 104, + "ne3d": 157, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 7, 9, 22, 17, 29, 25, 28, 10, 6]", + "total_badness": 206.30371107 + } + ], + "part1.stl": [ + { + "angles_tet": [ + 22.083, + 138.04 + ], + "angles_trig": [ + 24.223, + 119.8 + ], + "ne1d": 170, + "ne2d": 400, + "ne3d": 1023, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 5, 16, 55, 75, 124, 152, 176, 176, 136, 86, 20]", + "total_badness": 1363.6686182 + }, + { + "angles_tet": [ + 10.722, + 160.47 + ], + "angles_trig": [ + 13.01, + 146.61 + ], + "ne1d": 134, + "ne2d": 254, + "ne3d": 457, + "quality_histogram": "[0, 0, 0, 4, 1, 3, 9, 7, 11, 19, 29, 44, 53, 56, 60, 63, 43, 31, 20, 4]", + "total_badness": 726.0384133 + }, + { + "angles_tet": [ + 20.846, + 134.73 + ], + "angles_trig": [ + 22.401, + 115.69 + ], + "ne1d": 194, + "ne2d": 554, + "ne3d": 1600, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 17, 63, 93, 166, 229, 275, 308, 246, 159, 37]", + "total_badness": 2088.862264 + }, + { + "angles_tet": [ + 21.368, + 141.27 + ], + "angles_trig": [ + 26.65, + 112.07 + ], + "ne1d": 266, + "ne2d": 958, + "ne3d": 4158, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 5, 9, 19, 53, 126, 298, 501, 705, 822, 870, 591, 159]", + "total_badness": 5194.9903517 + }, + { + "angles_tet": [ + 24.374, + 139.79 + ], + "angles_trig": [ + 25.767, + 121.5 + ], + "ne1d": 674, + "ne2d": 6330, + "ne3d": 73017, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 3, 15, 118, 382, 1197, 3223, 6772, 11399, 15438, 17121, 13196, 4153]", + "total_badness": 88445.46456 + } + ], + "period.geo": [ + { + "angles_tet": [ + 14.172, + 145.15 + ], + "angles_trig": [ + 17.555, + 130.0 + ], + "ne1d": 344, + "ne2d": 1040, + "ne3d": 3043, + "quality_histogram": "[0, 0, 0, 0, 0, 6, 18, 25, 51, 71, 144, 251, 302, 421, 423, 407, 390, 306, 179, 49]", + "total_badness": 4402.5390324 + }, + { + "angles_tet": [ + 7.025, + 170.6 + ], + "angles_trig": [ + 11.507, + 140.67 + ], + "ne1d": 160, + "ne2d": 234, + "ne3d": 417, + "quality_histogram": "[0, 0, 2, 4, 4, 12, 17, 17, 26, 32, 44, 44, 48, 39, 27, 37, 33, 20, 8, 3]", + "total_badness": 780.76742775 + }, + { + "angles_tet": [ + 9.6632, + 163.12 + ], + "angles_trig": [ + 13.809, + 147.97 + ], + "ne1d": 232, + "ne2d": 494, + "ne3d": 1159, + "quality_histogram": "[0, 0, 1, 5, 14, 20, 42, 39, 60, 72, 87, 117, 127, 158, 104, 117, 72, 73, 42, 9]", + "total_badness": 2023.1948662 + }, + { + "angles_tet": [ + 14.172, + 145.15 + ], + "angles_trig": [ + 17.555, + 130.0 + ], + "ne1d": 344, + "ne2d": 1040, + "ne3d": 3001, + "quality_histogram": "[0, 0, 0, 0, 0, 6, 17, 24, 42, 58, 124, 222, 276, 398, 439, 428, 393, 327, 202, 45]", + "total_badness": 4283.328223 + }, + { + "angles_tet": [ + 20.132, + 145.06 + ], + "angles_trig": [ + 23.036, + 125.82 + ], + "ne1d": 480, + "ne2d": 2200, + "ne3d": 11579, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 5, 6, 37, 79, 241, 508, 972, 1450, 2025, 2203, 2142, 1482, 429]", + "total_badness": 14696.222297 + }, + { + "angles_tet": [ + 20.751, + 144.48 + ], + "angles_trig": [ + 20.259, + 128.89 + ], + "ne1d": 820, + "ne2d": 6174, + "ne3d": 68319, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 4, 13, 57, 176, 519, 1477, 3598, 7004, 10873, 14367, 15055, 11494, 3682]", + "total_badness": 83579.590629 + } + ], + "plane.stl": [ + { + "angles_tet": [ + 1.2146, + 170.22 + ], + "angles_trig": [ + 1.8906, + 147.74 + ], + "ne1d": 892, + "ne2d": 2226, + "ne3d": 7016, + "quality_histogram": "[3, 12, 31, 39, 43, 61, 53, 62, 97, 110, 236, 389, 555, 829, 983, 1053, 1003, 852, 486, 119]", + "total_badness": 10717.291909 + }, + { + "angles_tet": [ + 1.6463, + 171.19 + ], + "angles_trig": [ + 1.7241, + 168.81 + ], + "ne1d": 572, + "ne2d": 748, + "ne3d": 932, + "quality_histogram": "[2, 29, 56, 54, 80, 83, 65, 78, 62, 79, 75, 65, 56, 52, 34, 28, 18, 10, 4, 2]", + "total_badness": 3148.0282985 + }, + { + "angles_tet": [ + 1.1094, + 171.74 + ], + "angles_trig": [ + 3.1957, + 172.05 + ], + "ne1d": 724, + "ne2d": 1340, + "ne3d": 2375, + "quality_histogram": "[2, 17, 32, 56, 42, 52, 55, 65, 90, 131, 177, 192, 236, 274, 291, 265, 213, 124, 54, 7]", + "total_badness": 4744.9473584 + }, + { + "angles_tet": [ + 1.2337, + 165.93 + ], + "angles_trig": [ + 1.932, + 150.35 + ], + "ne1d": 956, + "ne2d": 2328, + "ne3d": 7371, + "quality_histogram": "[3, 8, 27, 48, 50, 50, 51, 60, 73, 92, 154, 275, 478, 757, 1051, 1198, 1207, 995, 647, 147]", + "total_badness": 10885.708005 + }, + { + "angles_tet": [ + 1.1634, + 165.93 + ], + "angles_trig": [ + 4.1049, + 148.28 + ], + "ne1d": 1554, + "ne2d": 5646, + "ne3d": 29373, + "quality_histogram": "[2, 6, 10, 11, 20, 49, 62, 47, 81, 131, 233, 473, 1030, 2096, 3516, 4912, 6007, 5650, 3941, 1096]", + "total_badness": 37671.804771 + }, + { + "angles_tet": [ + 1.2313, + 163.56 + ], + "angles_trig": [ + 1.2728, + 155.0 + ], + "ne1d": 2992, + "ne2d": 22730, + "ne3d": 271701, + "quality_histogram": "[4, 8, 13, 10, 12, 24, 23, 53, 88, 211, 645, 1879, 5291, 12837, 26732, 43337, 57290, 61716, 46823, 14705]", + "total_badness": 331770.62487 + } + ], + "revolution.geo": [ + { + "angles_tet": [ + 18.192, + 146.62 + ], + "angles_trig": [ + 16.784, + 125.93 + ], + "ne1d": 320, + "ne2d": 2790, + "ne3d": 7801, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 19, 85, 222, 383, 609, 790, 928, 1072, 1122, 1033, 869, 535, 133]", + "total_badness": 11028.386222 + }, + { + "angles_tet": [ + 15.884, + 148.19 + ], + "angles_trig": [ + 16.462, + 128.75 + ], + "ne1d": 160, + "ne2d": 658, + "ne3d": 1014, + "quality_histogram": "[0, 0, 0, 0, 0, 1, 10, 39, 45, 86, 105, 135, 148, 143, 94, 77, 55, 39, 27, 10]", + "total_badness": 1687.852505 + }, + { + "angles_tet": [ + 19.465, + 143.15 + ], + "angles_trig": [ + 21.41, + 127.26 + ], + "ne1d": 240, + "ne2d": 1584, + "ne3d": 3587, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 6, 53, 114, 213, 346, 472, 525, 493, 421, 409, 290, 188, 56]", + "total_badness": 5235.8043244 + }, + { + "angles_tet": [ + 16.533, + 145.17 + ], + "angles_trig": [ + 16.65, + 126.12 + ], + "ne1d": 320, + "ne2d": 2790, + "ne3d": 7588, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 3, 3, 53, 130, 283, 474, 732, 845, 1029, 1136, 1138, 955, 650, 157]", + "total_badness": 10417.772443 + }, + { + "angles_tet": [ + 19.966, + 146.33 + ], + "angles_trig": [ + 22.508, + 127.48 + ], + "ne1d": 480, + "ne2d": 6314, + "ne3d": 31852, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 4, 10, 39, 160, 534, 1078, 2215, 3774, 5529, 6417, 6291, 4543, 1258]", + "total_badness": 39852.048962 + }, + { + "angles_tet": [ + 24.204, + 141.06 + ], + "angles_trig": [ + 25.313, + 123.86 + ], + "ne1d": 800, + "ne2d": 16950, + "ne3d": 198677, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 5, 71, 314, 1001, 3314, 8614, 18130, 30449, 42143, 46676, 36551, 11409]", + "total_badness": 240440.79682 + } + ], + "sculpture.geo": [ + { + "angles_tet": [ + 17.893, + 145.39 + ], + "angles_trig": [ + 26.076, + 107.8 + ], + "ne1d": 192, + "ne2d": 358, + "ne3d": 399, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 2, 5, 7, 20, 31, 46, 60, 68, 73, 56, 20, 8, 2]", + "total_badness": 577.70426627 + }, + { + "angles_tet": [ + 26.065, + 137.71 + ], + "angles_trig": [ + 29.005, + 98.684 + ], + "ne1d": 102, + "ne2d": 126, + "ne3d": 110, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 13, 17, 25, 18, 13, 13, 0]", + "total_badness": 145.42684912 + }, + { + "angles_tet": [ + 23.253, + 133.46 + ], + "angles_trig": [ + 31.14, + 92.518 + ], + "ne1d": 144, + "ne2d": 202, + "ne3d": 188, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 5, 16, 19, 34, 43, 47, 19, 1]", + "total_badness": 236.63013908 + }, + { + "angles_tet": [ + 17.893, + 145.39 + ], + "angles_trig": [ + 26.076, + 107.8 + ], + "ne1d": 192, + "ne2d": 358, + "ne3d": 399, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 2, 5, 7, 20, 31, 46, 60, 68, 73, 56, 20, 8, 2]", + "total_badness": 577.70426673 + }, + { + "angles_tet": [ + 14.571, + 148.55 + ], + "angles_trig": [ + 20.892, + 127.8 + ], + "ne1d": 288, + "ne2d": 912, + "ne3d": 1234, + "quality_histogram": "[0, 0, 0, 0, 2, 2, 10, 23, 35, 68, 98, 124, 108, 127, 127, 127, 154, 133, 80, 16]", + "total_badness": 1875.8247719 + }, + { + "angles_tet": [ + 16.0, + 149.5 + ], + "angles_trig": [ + 17.232, + 118.94 + ], + "ne1d": 480, + "ne2d": 2314, + "ne3d": 6590, + "quality_histogram": "[0, 0, 0, 0, 2, 3, 11, 9, 20, 24, 53, 89, 214, 406, 753, 1055, 1353, 1311, 962, 325]", + "total_badness": 8284.1518654 + } + ], + "shaft.geo": [ + { + "angles_tet": [ + 8.1571, + 162.65 + ], + "angles_trig": [ + 9.7076, + 147.95 + ], + "ne1d": 708, + "ne2d": 1656, + "ne3d": 2629, + "quality_histogram": "[0, 0, 2, 4, 12, 13, 24, 56, 76, 149, 278, 365, 333, 240, 233, 285, 255, 188, 94, 22]", + "total_badness": 4239.9806978 + }, + { + "angles_tet": [ + 8.5237, + 165.9 + ], + "angles_trig": [ + 10.094, + 128.25 + ], + "ne1d": 410, + "ne2d": 542, + "ne3d": 688, + "quality_histogram": "[0, 0, 0, 2, 1, 4, 6, 11, 18, 32, 41, 56, 73, 85, 81, 83, 66, 73, 43, 13]", + "total_badness": 1047.4282101 + }, + { + "angles_tet": [ + 9.2737, + 159.17 + ], + "angles_trig": [ + 13.813, + 149.46 + ], + "ne1d": 510, + "ne2d": 912, + "ne3d": 1645, + "quality_histogram": "[0, 0, 0, 3, 5, 17, 16, 48, 65, 87, 108, 139, 179, 177, 238, 195, 186, 104, 53, 25]", + "total_badness": 2610.9306806 + }, + { + "angles_tet": [ + 13.139, + 162.65 + ], + "angles_trig": [ + 15.194, + 147.25 + ], + "ne1d": 708, + "ne2d": 1656, + "ne3d": 2585, + "quality_histogram": "[0, 0, 0, 1, 4, 4, 10, 22, 43, 120, 259, 375, 332, 266, 243, 291, 287, 189, 115, 24]", + "total_badness": 3972.0391087 + }, + { + "angles_tet": [ + 15.284, + 147.53 + ], + "angles_trig": [ + 20.193, + 122.49 + ], + "ne1d": 1138, + "ne2d": 4104, + "ne3d": 10879, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 2, 22, 68, 162, 312, 550, 917, 1327, 1807, 2027, 1905, 1346, 433]", + "total_badness": 13982.205496 + }, + { + "angles_tet": [ + 23.051, + 146.15 + ], + "angles_trig": [ + 26.463, + 118.44 + ], + "ne1d": 1792, + "ne2d": 10502, + "ne3d": 63623, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 3, 36, 112, 441, 1261, 3055, 6105, 9988, 13378, 14460, 11103, 3679]", + "total_badness": 77412.92414 + } + ], + "sphere.geo": [ + { + "angles_tet": [ + 41.444, + 94.535 + ], + "angles_trig": [ + 23.218, + 78.391 + ], + "ne1d": 0, + "ne2d": 108, + "ne3d": 108, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 26, 39, 19, 11, 4, 2, 0, 0, 0, 0]", + "total_badness": 187.15399646 + }, + { + "angles_tet": [ + 27.382, + 145.2 + ], + "angles_trig": [ + 37.429, + 87.848 + ], + "ne1d": 0, + "ne2d": 28, + "ne3d": 28, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 3, 1, 1, 2, 6, 4, 8]", + "total_badness": 35.610383187 + }, + { + "angles_tet": [ + 46.477, + 88.039 + ], + "angles_trig": [ + 30.095, + 74.952 + ], + "ne1d": 0, + "ne2d": 64, + "ne3d": 64, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 15, 32, 10, 4, 1, 0]", + "total_badness": 82.818407116 + }, + { + "angles_tet": [ + 41.444, + 94.535 + ], + "angles_trig": [ + 23.218, + 78.391 + ], + "ne1d": 0, + "ne2d": 108, + "ne3d": 108, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 26, 39, 19, 11, 4, 2, 0, 0, 0, 0]", + "total_badness": 187.15399646 + }, + { + "angles_tet": [ + 22.983, + 128.09 + ], + "angles_trig": [ + 22.196, + 111.76 + ], + "ne1d": 0, + "ne2d": 256, + "ne3d": 363, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 2, 19, 33, 58, 54, 54, 32, 38, 31, 24, 12, 6]", + "total_badness": 551.13017475 + }, + { + "angles_tet": [ + 30.456, + 130.64 + ], + "angles_trig": [ + 29.846, + 110.98 + ], + "ne1d": 0, + "ne2d": 658, + "ne3d": 2272, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 24, 67, 135, 263, 417, 457, 440, 354, 113]", + "total_badness": 2808.9045443 + } + ], + "sphereincube.geo": [ + { + "angles_tet": [ + 9.8889, + 167.24 + ], + "angles_trig": [ + 12.223, + 153.03 + ], + "ne1d": 46, + "ne2d": 170, + "ne3d": 349, + "quality_histogram": "[0, 0, 0, 8, 48, 42, 29, 16, 30, 20, 25, 24, 22, 20, 6, 13, 16, 20, 7, 3]", + "total_badness": 882.88818519 + }, + { + "angles_tet": [ + 7.3975, + 162.99 + ], + "angles_trig": [ + 4.6299, + 163.28 + ], + "ne1d": 24, + "ne2d": 34, + "ne3d": 79, + "quality_histogram": "[0, 0, 2, 2, 10, 6, 15, 12, 9, 9, 5, 5, 1, 1, 0, 0, 2, 0, 0, 0]", + "total_badness": 239.01564453 + }, + { + "angles_tet": [ + 6.4483, + 169.86 + ], + "angles_trig": [ + 9.6618, + 150.58 + ], + "ne1d": 30, + "ne2d": 100, + "ne3d": 242, + "quality_histogram": "[0, 0, 6, 15, 20, 43, 45, 12, 21, 10, 21, 8, 12, 4, 4, 6, 4, 7, 3, 1]", + "total_badness": 732.39744465 + }, + { + "angles_tet": [ + 9.8889, + 167.24 + ], + "angles_trig": [ + 12.223, + 153.03 + ], + "ne1d": 46, + "ne2d": 170, + "ne3d": 349, + "quality_histogram": "[0, 0, 0, 8, 48, 42, 29, 16, 30, 20, 25, 24, 22, 20, 6, 13, 16, 20, 7, 3]", + "total_badness": 882.88818519 + }, + { + "angles_tet": [ + 14.198, + 139.87 + ], + "angles_trig": [ + 16.51, + 128.49 + ], + "ne1d": 74, + "ne2d": 412, + "ne3d": 1683, + "quality_histogram": "[0, 0, 0, 0, 0, 5, 3, 12, 21, 29, 71, 92, 153, 214, 236, 260, 224, 186, 131, 46]", + "total_badness": 2358.043084 + }, + { + "angles_tet": [ + 24.367, + 140.35 + ], + "angles_trig": [ + 22.231, + 124.31 + ], + "ne1d": 122, + "ne2d": 1066, + "ne3d": 13989, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 7, 23, 83, 256, 466, 907, 1562, 2200, 2835, 2874, 2099, 676]", + "total_badness": 17429.478381 + } + ], + "square.in2d": [ + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 27, + "ne2d": 79, + "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": 28, + "ne2d": 62, + "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": 26, + "ne2d": 70, + "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": 79, + "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": 36, + "ne2d": 128, + "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": 42, + "ne2d": 238, + "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 + } + ], + "squarecircle.in2d": [ + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 32, + "ne2d": 144, + "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": 114, + "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": 134, + "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": 144, + "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": 42, + "ne2d": 308, + "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": 76, + "ne2d": 809, + "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 + } + ], + "squarehole.in2d": [ + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 32, + "ne2d": 120, + "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": 82, + "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": 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 + }, + { + "angles_tet": [ + 0.0, + 0.0 + ], + "angles_trig": [ + 0.0, + 0.0 + ], + "ne1d": 32, + "ne2d": 120, + "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": 42, + "ne2d": 258, + "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": 76, + "ne2d": 674, + "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 + } + ], + "torus.geo": [ + { + "angles_tet": [ + 19.194, + 148.12 + ], + "angles_trig": [ + 19.92, + 127.08 + ], + "ne1d": 0, + "ne2d": 2526, + "ne3d": 5587, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 9, 75, 208, 373, 547, 678, 782, 752, 738, 585, 470, 285, 83]", + "total_badness": 8168.6605532 + }, + { + "angles_tet": [ + 2.7079, + 172.97 + ], + "angles_trig": [ + 4.6723, + 166.19 + ], + "ne1d": 0, + "ne2d": 648, + "ne3d": 3007, + "quality_histogram": "[8, 144, 319, 360, 402, 367, 276, 245, 184, 169, 139, 98, 83, 58, 46, 41, 31, 22, 10, 5]", + "total_badness": 12976.808797 + }, + { + "angles_tet": [ + 17.153, + 145.79 + ], + "angles_trig": [ + 21.25, + 118.2 + ], + "ne1d": 0, + "ne2d": 1430, + "ne3d": 2733, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 4, 11, 39, 130, 214, 321, 436, 454, 400, 279, 240, 155, 49]", + "total_badness": 3874.9869154 + }, + { + "angles_tet": [ + 21.147, + 145.15 + ], + "angles_trig": [ + 20.446, + 123.07 + ], + "ne1d": 0, + "ne2d": 2526, + "ne3d": 5473, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 2, 34, 119, 265, 466, 597, 763, 810, 767, 682, 534, 342, 92]", + "total_badness": 7745.4881518 + }, + { + "angles_tet": [ + 21.754, + 144.45 + ], + "angles_trig": [ + 23.239, + 124.02 + ], + "ne1d": 0, + "ne2d": 5824, + "ne3d": 25317, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 9, 41, 114, 347, 765, 1598, 2782, 4135, 5379, 5234, 3786, 1126]", + "total_badness": 31434.764854 + }, + { + "angles_tet": [ + 21.699, + 145.02 + ], + "angles_trig": [ + 23.154, + 120.78 + ], + "ne1d": 0, + "ne2d": 16198, + "ne3d": 174686, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 1, 4, 58, 234, 858, 2518, 6918, 15184, 26272, 37074, 41964, 33110, 10491]", + "total_badness": 210586.77039 + } + ], + "trafo.geo": [ + { + "angles_tet": [ + 7.5246, + 167.34 + ], + "angles_trig": [ + 14.916, + 132.02 + ], + "ne1d": 690, + "ne2d": 1662, + "ne3d": 5132, + "quality_histogram": "[0, 0, 1, 0, 2, 12, 24, 42, 108, 200, 269, 367, 431, 571, 685, 733, 586, 527, 446, 128]", + "total_badness": 7423.4819931 + }, + { + "angles_tet": [ + 2.8238, + 174.1 + ], + "angles_trig": [ + 7.7605, + 156.22 + ], + "ne1d": 390, + "ne2d": 516, + "ne3d": 1342, + "quality_histogram": "[0, 1, 5, 13, 16, 44, 80, 117, 122, 153, 159, 135, 123, 113, 85, 80, 54, 28, 11, 3]", + "total_badness": 2768.1650274 + }, + { + "angles_tet": [ + 7.8932, + 164.55 + ], + "angles_trig": [ + 14.15, + 148.05 + ], + "ne1d": 512, + "ne2d": 864, + "ne3d": 2363, + "quality_histogram": "[0, 0, 0, 3, 9, 11, 42, 63, 119, 151, 184, 204, 316, 393, 341, 237, 138, 87, 42, 23]", + "total_badness": 3893.8304292 + }, + { + "angles_tet": [ + 7.5246, + 167.34 + ], + "angles_trig": [ + 14.916, + 132.02 + ], + "ne1d": 690, + "ne2d": 1662, + "ne3d": 5067, + "quality_histogram": "[0, 0, 1, 0, 1, 8, 17, 35, 109, 188, 260, 357, 413, 536, 678, 734, 604, 538, 451, 137]", + "total_badness": 7266.2053478 + }, + { + "angles_tet": [ + 18.048, + 145.94 + ], + "angles_trig": [ + 17.539, + 126.69 + ], + "ne1d": 1050, + "ne2d": 3670, + "ne3d": 17479, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 2, 13, 27, 55, 173, 512, 1412, 2098, 2269, 2479, 2612, 2725, 2398, 704]", + "total_badness": 22725.68654 + }, + { + "angles_tet": [ + 13.821, + 149.28 + ], + "angles_trig": [ + 19.234, + 128.69 + ], + "ne1d": 1722, + "ne2d": 9990, + "ne3d": 84843, + "quality_histogram": "[0, 0, 0, 0, 1, 3, 47, 1412, 705, 384, 688, 1183, 2414, 5544, 8737, 13008, 16461, 17017, 12962, 4277]", + "total_badness": 108418.01981 + } + ], + "twobricks.geo": [ + { + "angles_tet": [ + 26.301, + 137.72 + ], + "angles_trig": [ + 24.205, + 111.42 + ], + "ne1d": 72, + "ne2d": 50, + "ne3d": 46, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 4, 14, 7, 6, 3, 1, 0, 0]", + "total_badness": 70.226764001 + }, + { + "angles_tet": [ + 34.114, + 126.16 + ], + "angles_trig": [ + 29.435, + 120.21 + ], + "ne1d": 56, + "ne2d": 34, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.050418036 + }, + { + "angles_tet": [ + 34.449, + 125.92 + ], + "angles_trig": [ + 29.602, + 120.18 + ], + "ne1d": 56, + "ne2d": 34, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 2, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.041320435 + }, + { + "angles_tet": [ + 26.301, + 137.72 + ], + "angles_trig": [ + 24.205, + 111.42 + ], + "ne1d": 72, + "ne2d": 50, + "ne3d": 46, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 4, 14, 7, 6, 3, 1, 0, 0]", + "total_badness": 70.226762635 + }, + { + "angles_tet": [ + 23.292, + 131.34 + ], + "angles_trig": [ + 27.682, + 108.04 + ], + "ne1d": 116, + "ne2d": 132, + "ne3d": 174, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 10, 12, 17, 36, 23, 30, 18, 15, 8]", + "total_badness": 232.20307583 + }, + { + "angles_tet": [ + 28.202, + 131.83 + ], + "angles_trig": [ + 27.743, + 109.15 + ], + "ne1d": 186, + "ne2d": 330, + "ne3d": 561, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 22, 35, 52, 81, 98, 100, 102, 55, 12]", + "total_badness": 726.84401009 + } + ], + "twocubes.geo": [ + { + "angles_tet": [ + 26.301, + 137.72 + ], + "angles_trig": [ + 24.205, + 111.42 + ], + "ne1d": 72, + "ne2d": 50, + "ne3d": 46, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 4, 14, 7, 6, 3, 1, 0, 0]", + "total_badness": 70.226764001 + }, + { + "angles_tet": [ + 34.114, + 126.16 + ], + "angles_trig": [ + 29.435, + 120.21 + ], + "ne1d": 56, + "ne2d": 34, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 4, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.050418036 + }, + { + "angles_tet": [ + 34.449, + 125.92 + ], + "angles_trig": [ + 29.602, + 120.18 + ], + "ne1d": 56, + "ne2d": 34, + "ne3d": 22, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 2, 4, 0, 0, 8, 0, 0, 0, 0]", + "total_badness": 35.041320435 + }, + { + "angles_tet": [ + 26.301, + 137.72 + ], + "angles_trig": [ + 24.205, + 111.42 + ], + "ne1d": 72, + "ne2d": 50, + "ne3d": 46, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 5, 4, 14, 7, 6, 3, 1, 0, 0]", + "total_badness": 70.226762635 + }, + { + "angles_tet": [ + 23.292, + 131.34 + ], + "angles_trig": [ + 27.682, + 108.04 + ], + "ne1d": 116, + "ne2d": 132, + "ne3d": 174, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 10, 12, 17, 36, 23, 30, 18, 15, 8]", + "total_badness": 232.20307583 + }, + { + "angles_tet": [ + 28.202, + 131.83 + ], + "angles_trig": [ + 27.743, + 109.15 + ], + "ne1d": 186, + "ne2d": 330, + "ne3d": 561, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 22, 35, 52, 81, 98, 100, 102, 55, 12]", + "total_badness": 726.84401009 + } + ], + "twocyl.geo": [ + { + "angles_tet": [ + 17.425, + 151.3 + ], + "angles_trig": [ + 21.178, + 117.72 + ], + "ne1d": 144, + "ne2d": 408, + "ne3d": 577, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 6, 9, 16, 24, 56, 67, 84, 115, 83, 63, 36, 11, 6]", + "total_badness": 849.57493713 + }, + { + "angles_tet": [ + 19.604, + 153.38 + ], + "angles_trig": [ + 25.599, + 115.69 + ], + "ne1d": 68, + "ne2d": 98, + "ne3d": 138, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 6, 8, 8, 17, 22, 22, 25, 15, 7, 2]", + "total_badness": 193.53502944 + }, + { + "angles_tet": [ + 12.305, + 161.08 + ], + "angles_trig": [ + 11.495, + 149.57 + ], + "ne1d": 102, + "ne2d": 234, + "ne3d": 412, + "quality_histogram": "[0, 0, 0, 1, 7, 7, 15, 28, 44, 24, 19, 33, 36, 36, 36, 28, 56, 26, 12, 4]", + "total_badness": 744.59871397 + }, + { + "angles_tet": [ + 17.51, + 141.43 + ], + "angles_trig": [ + 21.505, + 117.52 + ], + "ne1d": 144, + "ne2d": 408, + "ne3d": 575, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 4, 7, 10, 20, 57, 65, 76, 116, 90, 74, 40, 14, 2]", + "total_badness": 830.26839459 + }, + { + "angles_tet": [ + 20.993, + 137.7 + ], + "angles_trig": [ + 22.158, + 117.49 + ], + "ne1d": 214, + "ne2d": 904, + "ne3d": 1847, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 8, 28, 77, 127, 199, 270, 350, 335, 247, 164, 41]", + "total_badness": 2434.6179175 + }, + { + "angles_tet": [ + 21.528, + 142.94 + ], + "angles_trig": [ + 26.329, + 116.56 + ], + "ne1d": 350, + "ne2d": 2358, + "ne3d": 13357, + "quality_histogram": "[0, 0, 0, 0, 0, 0, 0, 0, 1, 5, 23, 78, 239, 564, 1230, 2137, 2846, 3149, 2364, 721]", + "total_badness": 16204.662169 + } + ] +} \ No newline at end of file diff --git a/tests/pytest/test_array.py b/tests/pytest/test_array.py new file mode 100644 index 00000000..fc3efd09 --- /dev/null +++ b/tests/pytest/test_array.py @@ -0,0 +1,33 @@ +from pyngcore import * +from numpy import sort, array + +def test_array_numpy(): + a = Array_I_S(5) + a[:] = 0 + a[3:] = 2 + assert(sum(a) == 4) + a[1] = 5 + b = sort(a) + assert(all(b == array([0,0,2,2,5]))) + assert(all(a == array([0,5,0,2,2]))) + a.NumPy().sort() + assert(all(a == array([0,0,2,2,5]))) + +def test_mesh_elements_numpy_array_access(): + from netgen.csg import unit_cube + mesh = unit_cube.GenerateMesh() + np_els = mesh.Elements3D().NumPy() + vol_nodes = np_els["nodes"] + indices = np_els["index"] + nps = np_els["np"] + for nodes, el, index, np in zip(vol_nodes, mesh.Elements3D(), indices, nps): + for n1, n2 in zip(nodes, el.vertices): + assert n1 == n2 + for n in nodes[len(el.vertices):]: + assert n == 0 + assert el.index == index + assert len(el.vertices) == np + +if __name__ == "__main__": + test_array_numpy() + test_mesh_elements_numpy_array_access() diff --git a/tests/pytest/test_bitarray.py b/tests/pytest/test_bitarray.py new file mode 100644 index 00000000..ed3d6fd8 --- /dev/null +++ b/tests/pytest/test_bitarray.py @@ -0,0 +1,35 @@ +from pyngcore import BitArray + +def test_bitarray(): + a = BitArray(498) + assert len(a) == 498 + + a.Set() + for b in a: + assert b == True + + a.Clear(23) + assert a[22] == True + assert a[23] == False + assert a[24] == True + + a.Clear() + for b in a: + assert b == False + + a.Set(23) + assert a[22] == False + assert a[23] == True + assert a[24] == False + + a.Clear() + a[100:200:9] = True + for i in range(len(a)): + assert a[i] == bool(100<=i and i<200 and i%9==100%9) + + ac = ~a + + for b,bc in zip(a,ac): + assert b == (not bc) + + diff --git a/tests/pytest/test_boundarylayer.py b/tests/pytest/test_boundarylayer.py new file mode 100644 index 00000000..e0208c14 --- /dev/null +++ b/tests/pytest/test_boundarylayer.py @@ -0,0 +1,177 @@ + +import pytest +from netgen.csg import * + +geometries=[unit_cube] + +try: + import netgen.occ as occ + box = occ.Box( (0,0,0), (1,1,1) ) + box.faces.Min(occ.Y).name = "back" + box.faces.Max(occ.Y).name = "front" + box.faces.Min(occ.X).name = "left" + box.faces.Max(occ.X).name = "right" + box.faces.Min(occ.Z).name = "bottom" + box.faces.Max(occ.Z).name = "top" + geometries.append(occ.OCCGeometry(box)) +except ImportError: + pass + +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]) +@pytest.mark.parametrize("geo", geometries) +def test_boundarylayer(outside, geo, capfd): + mesh = geo.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) + +# not working yet +@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_gui.py b/tests/pytest/test_gui.py deleted file mode 100644 index 6e32488f..00000000 --- a/tests/pytest/test_gui.py +++ /dev/null @@ -1,11 +0,0 @@ -import netgen -import pytest - -def test_gui(): - try: - from tkinter import Tk - win = Tk() - except: - pytest.skip("can't create a window") - import netgen.gui - diff --git a/tests/pytest/test_meshclass.py b/tests/pytest/test_meshclass.py new file mode 100644 index 00000000..0144422f --- /dev/null +++ b/tests/pytest/test_meshclass.py @@ -0,0 +1,22 @@ +import pyngcore +import netgen + +from meshes import unit_mesh_3d + +def test_element_arrays(unit_mesh_3d): + mesh = unit_mesh_3d + el0 = mesh.Elements0D() + el1 = mesh.Elements1D() + el2 = mesh.Elements2D() + el3 = mesh.Elements3D() + p = mesh.Points() + + assert len(el2) > 0 + assert len(el3) > 0 + assert len(p) > 0 + + for el in el2: + assert len(el.vertices) == 3 + + for el in el3: + assert len(el.vertices) == 4 diff --git a/tests/pytest/test_mpi4py.py b/tests/pytest/test_mpi4py.py new file mode 100644 index 00000000..4d68eda6 --- /dev/null +++ b/tests/pytest/test_mpi4py.py @@ -0,0 +1,22 @@ +import pytest +import netgen.meshing + +mpi4py = pytest.importorskip("mpi4py") +_ = pytest.importorskip("pytest_mpi") + +@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_occ.py b/tests/pytest/test_occ.py new file mode 100644 index 00000000..510775cb --- /dev/null +++ b/tests/pytest/test_occ.py @@ -0,0 +1,43 @@ +import pytest +import math +from pytest import approx +from pytest_check import check + +def check_volume(shape, volume, dim=3): + from netgen.occ import OCCGeometry + geo = OCCGeometry(shape, dim=dim) + + m = geo.GenerateMesh() + assert len(m.Elements2D()) > 0 + assert len(m.Elements1D()) > 0 + if dim==3: + assert len(m.Elements3D()) > 0 + + ngs = pytest.importorskip("ngsolve") + mesh = ngs.Mesh(m) + mesh.Curve(5) + with check: assert ngs.Integrate(1.0, mesh) == approx(volume) + +def test_rect_with_two_holes(): + occ = pytest.importorskip("netgen.occ") + + face = occ.WorkPlane().Rectangle(7,4) \ + .Circle(2,2,1).Reverse() \ + .Circle(5,2,1).Reverse().Face() + check_volume(face, 7*4-2*math.pi, 2) + +def test_unit_square(): + occ = pytest.importorskip("netgen.occ") + check_volume(occ.unit_square.shape, 1, dim=2) + +def test_box_and_cyl(): + occ = pytest.importorskip("netgen.occ") + box = occ.Box(occ.Pnt(0,0,0), occ.Pnt(1,1,1)) + check_volume(box, 1) + r = 0.3 + h = 0.5 + vcyl = r*r*math.pi*h + cyl = occ.Cylinder(occ.Pnt(1,0.5,0.5), occ.X, r=r, h=h) + check_volume(cyl, vcyl) + fused = box+cyl + check_volume(fused, 1+vcyl) diff --git a/tests/pytest/test_occ_identifications.py b/tests/pytest/test_occ_identifications.py new file mode 100644 index 00000000..d15abc5e --- /dev/null +++ b/tests/pytest/test_occ_identifications.py @@ -0,0 +1,76 @@ +import pytest + +from netgen.meshing import IdentificationType +idtype = IdentificationType.CLOSESURFACES + +def test_two_boxes(): + occ = pytest.importorskip("netgen.occ") + inner = occ.Box((0,0,0), (1,1,1)) + trafo = occ.gp_Trsf().Scale(inner.center, 1.1) + outer = trafo(inner) + + inner.Identify(outer, "", idtype, trafo) + shape = occ.Glue([outer-inner, inner]) + + geo = occ.OCCGeometry(shape) + mesh = geo.GenerateMesh(maxh=0.3) + have_prisms = False + + for el in mesh.Elements3D(): + if len(el.vertices)==6: + have_prisms = True + break + + assert have_prisms + +def test_two_circles(): + occ = pytest.importorskip("netgen.occ") + circ1 = occ.WorkPlane().Circle(1).Face() + trafo = occ.gp_Trsf().Scale(circ1.center, 1.1) + + circ2 = trafo(circ1) + circ1.edges[0].Identify(circ2.edges[0], "", idtype, trafo) + circ2 -= circ1 + shape = occ.Glue([circ1, circ2]) + + geo = occ.OCCGeometry(shape, 2) + mesh = geo.GenerateMesh(maxh=0.2) + have_quads = False + + for el in mesh.Elements2D(): + if len(el.vertices)==4: + have_quads = True + break + + assert have_quads + +def test_cut_identified_face(): + occ = pytest.importorskip("netgen.occ") + from netgen.occ import Z, Box, Cylinder, Glue, OCCGeometry + box = Box((-1,-1,0), (1,1,1)) + cyl = Cylinder( (0,0,0), Z, 0.5, 1 ) + + box.faces.Min(Z).Identify(box.faces.Max(Z), "", idtype) + shape = Glue([cyl, box]) + geo = OCCGeometry(shape) + mesh = geo.GenerateMesh(maxh=0.5) + + for el in mesh.Elements3D(): + assert len(el.vertices)==6 + +def test_identify_multiple_faces(): + occ = pytest.importorskip("netgen.occ") + from netgen.occ import Z, Box, Cylinder, Glue, OCCGeometry, gp_Trsf + box = Box((-1,-1,0), (1,1,1)) + cyl = Cylinder( (0,0,0), Z, 0.5, 1 ) + + shape = Glue([box, cyl]) + bot_faces = shape.faces[Z < 0.1] + top_faces = shape.faces[Z > 0.1] + bot_faces.Identify(top_faces, "", idtype, gp_Trsf.Translation((0,0,1))) + + geo = OCCGeometry(shape) + mesh = geo.GenerateMesh(maxh=0.3) + + for el in mesh.Elements3D(): + assert len(el.vertices)==6 diff --git a/tests/pytest/test_pickling.py b/tests/pytest/test_pickling.py index 13e20f9e..3e15f806 100644 --- a/tests/pytest/test_pickling.py +++ b/tests/pytest/test_pickling.py @@ -48,11 +48,11 @@ def test_pickle_stl(): def test_pickle_occ(): try: - import netgen.NgOCC as occ + import netgen.occ as occ except: import pytest pytest.skip("can't import occ") - geo = occ.LoadOCCGeometry("../../tutorials/frame.step") + geo = occ.OCCGeometry("../../tutorials/frame.step") geo_dump = pickle.dumps(geo) geo2 = pickle.loads(geo_dump) vd1 = geo._visualizationData() @@ -87,19 +87,23 @@ def test_pickle_geom2d(): def test_pickle_mesh(): import netgen.csg as csg - geo = csg.CSGeometry() + geo1 = csg.CSGeometry() + geo2 = csg.CSGeometry() brick = csg.OrthoBrick(csg.Pnt(-3,-3,-3), csg.Pnt(3,3,3)) - mesh = geo.GenerateMesh(maxh=0.2) - assert geo == mesh.GetGeometry() - dump = pickle.dumps([geo,mesh]) - geo2, mesh2 = pickle.loads(dump) - assert geo2 == mesh2.GetGeometry() - mesh.Save("msh1.vol.gz") - mesh2.Save("msh2.vol.gz") - import filecmp, os - assert filecmp.cmp("msh1.vol.gz", "msh2.vol.gz") - os.remove("msh1.vol.gz") - os.remove("msh2.vol.gz") + geo2.Add(brick) + + for geo in [geo1, geo2]: + mesh = geo.GenerateMesh(maxh=2) + assert geo == mesh.GetGeometry() + dump = pickle.dumps([geo,mesh]) + geo2, mesh2 = pickle.loads(dump) + assert geo2 == mesh2.GetGeometry() + mesh.Save("msh1.vol.gz") + mesh2.Save("msh2.vol.gz") + import filecmp, os + assert filecmp.cmp("msh1.vol.gz", "msh2.vol.gz") + os.remove("msh1.vol.gz") + os.remove("msh2.vol.gz") if __name__ == "__main__": test_pickle_mesh() diff --git a/tests/pytest/test_savemesh.py b/tests/pytest/test_savemesh.py index 6b7b7e2c..e98f2a79 100644 --- a/tests/pytest/test_savemesh.py +++ b/tests/pytest/test_savemesh.py @@ -28,7 +28,7 @@ def CreateGeo(): def test_BBNDsave(): mesh = CreateGeo().GenerateMesh(maxh=0.4,perfstepsend = meshing.MeshingStep.MESHSURFACE) for i in range(2): - mesh.GenerateVolumeMesh(mp = MeshingParameters(only3D_domain=i+1,maxh=0.4)) + mesh.GenerateVolumeMesh(only3D_domain_nr=i+1,maxh=0.4) mesh.SetGeometry(None) mesh.Save("test.vol") mesh2 = meshing.Mesh() 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 new file mode 100644 index 00000000..e7c4d81a --- /dev/null +++ b/tests/pytest/test_tutorials.py @@ -0,0 +1,160 @@ + +import os, pytest +from netgen.meshing import meshsize, MeshingParameters, SetMessageImportance +import netgen.csg as csg +import netgen.stl as stl +import netgen.geom2d as geom2d +from pyngcore import TaskManager +import json +try: + import netgen.occ as occ + has_occ = occ.occ_version >= "7.4.0" + has_occ = False # skip occ tests (current development on interface) +except ImportError: + has_occ = False + +SetMessageImportance(0) + +def round(x, digits=11): + try: + return float(("{:."+str(digits)+"g}").format(x)) + except: #list + return [float(("{:."+str(digits)+"g}").format(y)) for y in x] + + +def getData(mesh, mp): + out = {} + out['ne1d'] = len(mesh.Elements1D()) + out['ne2d'] = len(mesh.Elements2D()) + out['ne3d'] = len(mesh.Elements3D()) + # round badness to avoid fluctuations in last digits + out["total_badness"] = round(mesh.CalcTotalBadness(mp)) + angles = mesh.CalcMinMaxAngle() + out["angles_trig"] = round(angles["trig"], 5) + out["angles_tet"] = round(angles["tet"], 5) + out["quality_histogram"] = str(list(mesh.GetQualityHistogram())) + return out + +def checkData(mesh, mp, ref): + data = getData(mesh, mp) + assert ref['ne1d'] == data['ne1d'] + assert ref['ne2d'] == data['ne2d'] + assert ref['ne3d'] == data['ne3d'] + assert json.loads(ref['quality_histogram']) == pytest.approx(json.loads(data['quality_histogram']), abs=1, rel=0.4) + assert ref['total_badness'] == pytest.approx(data['total_badness'], rel=1e-5) + assert ref['angles_trig'] == pytest.approx(data['angles_trig'], rel=1e-4) + assert ref['angles_tet'] == pytest.approx(data['angles_tet'], rel=1e-4) + +# get tutorials +def getFiles(fileEnding): + r, d, files = next(os.walk(os.path.join("..","..","tutorials"))) + return [f for f in files if f.endswith(fileEnding)] + +# get additional tests +def getAdditionalFiles(fileEnding): + r, d, files = next(os.walk("geofiles")) + return [f for f in files if f.endswith(fileEnding)] + +@pytest.fixture +def refdata(): + return json.load(open('results.json','r')) + + +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 == "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": + return standard[0:2] + standard[3:] # coarse gives inconsistent reults (other mesh on MacOS) + if filename == "part1.stl": + return standard[0:1] + standard[2:] # very coarse does not work + return standard + +_geofiles = getFiles(".in2d") + getFiles(".geo") + getFiles(".stl") +if has_occ: + _geofiles += getFiles(".step") +_geofiles.sort() +_additional_testfiles = getAdditionalFiles(".stl") +if has_occ: + _additional_testfiles += getAdditionalFiles(".step") +_additional_testfiles.sort() + +def generateMesh(filename, mp): + folder = os.path.join("..","..","tutorials") if filename in _geofiles else "geofiles" + if filename.endswith(".geo"): + geo = csg.CSGeometry(os.path.join(folder, filename)) + elif filename.endswith(".stl"): + geo = stl.STLGeometry(os.path.join(folder, filename)) + elif filename.endswith(".step"): + geo = occ.OCCGeometry(os.path.join(folder, filename)) + elif filename.endswith(".in2d"): + geo = geom2d.SplineGeometry(os.path.join(folder, filename)) + return geo.GenerateMesh(mp) + +def isSlowTest(filename): + return filename in ["cubemcyl.geo", "frame.step", "revolution.geo", "manyholes.geo", "torus.geo", + "cubemsphere.geo", "manyholes2.geo", "matrix.geo", "trafo.geo", "ellipticcone.geo", + "period.geo", "shaft.geo", "cubeandring.geo", "ellipticcyl.geo", + "ellipsoid.geo", "cone.geo", "plane.stl"] + +def getParameters(): + res = [] + for f in _geofiles + _additional_testfiles: + for i,mp in enumerate(getMeshingparameters(f)): + if isSlowTest(f): + res.append( pytest.param(f, mp, i, marks=pytest.mark.slow ) ) + else: + res.append( (f, mp, i) ) + return res + +@pytest.mark.parametrize(("filename", "mp", "i"), getParameters()) +def test_geoFiles(filename, mp, i, refdata): + ref = refdata[filename] + import filecmp + print("load geo", filename) + mp = MeshingParameters(mp, parallel_meshing=False) + mesh = generateMesh(filename, mp) + mesh.Save(filename+'_seq.vol.gz') + with TaskManager(): + mesh_par = generateMesh(filename, mp) + mesh_par.Save(filename+'_par.vol.gz') + + assert filecmp.cmp(filename+'_seq.vol.gz', filename+'_par.vol.gz') + checkData(mesh, mp, ref[i]) + + +def generateResultFile(): + import re, time + data = {} + with TaskManager(): + for _file in _geofiles + _additional_testfiles: + print("generate "+_file) + start = time.time() + mps = getMeshingparameters(_file) + if not mps: + continue + meshdata = [] + for mp in mps: + 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") + + s = json.dumps(data, sort_keys=True, indent=4) + open("results.json", "w").write(s) + print("done") + +if __name__ == "__main__": + generateResultFile() diff --git a/tutorials/cubeandspheres.geo b/tutorials/cubeandspheres.geo index d5ad4f65..baac4bc9 100644 --- a/tutorials/cubeandspheres.geo +++ b/tutorials/cubeandspheres.geo @@ -11,7 +11,7 @@ solid cube = plane (0, 0, 0; 0, 0, -1) and plane (1, 1, 1; 0, 1, 0) and plane (1, 1, 1; 1, 0, 0); -# two shperes +# two spheres solid sph1 = sphere (0.5, 0.5, 0.5; 0.58); solid sph2 = sphere (0.5, 0.5, 0.5; 0.75); 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"