diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index eabde0a8..e972b99b 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -10,17 +10,25 @@ runs: using: "composite" steps: + - name: Remove unused Homebrew taps + shell: bash + if: ${{ startsWith(inputs.os, 'macos-') }} + run: | + brew uninstall --formula azure/bicep/bicep || true + brew untap aws/tap azure/bicep || true + - name: 🩹 Enable Long Paths shell: bash if: ${{ inputs.os == 'windows-latest' }} run: git config --system core.longpaths true - name: 🖥️ Enable MSVC Dev Console - uses: ilammy/msvc-dev-cmd@v1 + if: ${{ inputs.os == 'windows-latest' }} + uses: microsoft/setup-msbuild@v3 - name: 📋 Install OpenMP shell: bash - if: ${{ inputs.os == 'macos-latest' }} + if: ${{ startsWith(inputs.os, 'macos-') }} run: | brew install libomp echo "OpenMP_ROOT=$(brew --prefix)/opt/libomp" >> $GITHUB_ENV @@ -36,7 +44,7 @@ runs: echo "CMAKE_GIT_COMMAND=$(which git)" >> "$GITHUB_ENV" - name: Install Git LFS and configure (macOS) - if: ${{ inputs.os == 'macos-latest' }} + if: ${{ startsWith(inputs.os, 'macos-') }} shell: bash run: | brew install git-lfs || true @@ -47,7 +55,6 @@ runs: - name: 💻 Setup Deployment Target shell: bash - if: ${{ inputs.os == 'macos-latest' }} + if: ${{ startsWith(inputs.os, 'macos-') }} run: | echo "MACOSX_DEPLOYMENT_TARGET=15.0" >> $GITHUB_ENV - diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4dae3029..19162df5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,6 +18,7 @@ jobs: - Debug os: + - macos-15-intel - macos-latest - windows-latest @@ -40,7 +41,7 @@ jobs: steps: - name: 📥 Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: 🖥️ Setup Environment uses: ./.github/actions/setup @@ -48,7 +49,7 @@ jobs: os: ${{ matrix.os }} - name: 📋 Install MacOS Dependencies - if: ${{ matrix.os == 'macos-latest' }} + if: ${{ startsWith(matrix.os, 'macos-') }} run: brew install vtk - name: 🖥️ Setup vcpkg (Windows) @@ -65,15 +66,16 @@ jobs: New-Item -ItemType Directory -Force -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" - - name: 🦥 Cache vcpkg binary (Windows) + - name: 🦥 Cache vcpkg installed if: ${{ matrix.os == 'windows-latest' }} - uses: actions/cache@v4 + id: vcpkg-installed-cache + uses: actions/cache@v5 with: - path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} - key: vcpkg-${{ matrix.os }}-${{ matrix.config }}-${{ env.VCPKG_COMMIT }} + path: ${{ github.workspace }}/vcpkg_installed + key: vcpkg-installed-${{ matrix.os }}-${{ env.VCPKG_COMMIT }}-${{ hashFiles('vcpkg.json') }} - name: 🛠️ Build Dependencies (Windows) - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.os == 'windows-latest' && steps.vcpkg-installed-cache.outputs.cache-hit != 'true' }} shell: pwsh run: | ./vcpkg/vcpkg install --triplet x64-windows @@ -81,7 +83,7 @@ jobs: - name: 🏗️ Compile (Windows) if: ${{ matrix.os == 'windows-latest' }} run: | - cmake -DVIENNALS_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -B build + cmake -DVIENNALS_BUILD_TESTS=ON -DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows -DVCPKG_INSTALLED_DIR=${{ github.workspace }}/vcpkg_installed -DVCPKG_MANIFEST_INSTALL=OFF -B build cmake --build build --config ${{ matrix.config }} - name: 🏗️ Compile (other) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dcea44bb..aa3efba7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -25,7 +25,7 @@ jobs: run: pacman --noconfirm -Syu doxygen git - name: 📥 Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: submodules: "recursive" diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 850a285c..44963bfb 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -18,7 +18,7 @@ jobs: steps: - name: 📥 Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: set-safe-directory: true @@ -35,6 +35,6 @@ jobs: - name: 🔍 Check Format run: | - git config --global --add safe.directory $(pwd) # "set-safe-directory" seems bugged in actions/checkout@v4 + git config --global --add safe.directory $(pwd) # "set-safe-directory" seems bugged in actions/checkout@v5 cmake -B build cmake --build build --target format-check diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9d542965..8f2faaac 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -24,6 +24,7 @@ jobs: - os: ubuntu-latest container: ghcr.io/viennatools/vienna-builder:cuda-suite-python-nocuda - os: windows-latest + - os: macos-15-intel - os: macos-latest runs-on: ${{ matrix.os }} @@ -37,7 +38,7 @@ jobs: steps: - name: 📥 Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: 🖥️ Setup Environment uses: ./.github/actions/setup @@ -59,28 +60,23 @@ jobs: New-Item -ItemType Directory -Force -Path "${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" - name: 📋 Install MacOS Dependencies - if: ${{ matrix.os == 'macos-latest' }} + if: ${{ startsWith(matrix.os, 'macos-') }} run: brew install vtk - - name: 🦥 Cache vcpkg binary + - name: 🦥 Cache vcpkg installed if: ${{ matrix.os == 'windows-latest' }} - uses: actions/cache@v4 + id: vcpkg-installed-cache + uses: actions/cache@v5 with: - path: ${{ env.VCPKG_DEFAULT_BINARY_CACHE }} - key: vcpkg-${{ matrix.os }}-Release-${{ env.VCPKG_COMMIT }} + path: ${{ github.workspace }}/vcpkg_installed + key: vcpkg-installed-${{ matrix.os }}-${{ env.VCPKG_COMMIT }}-${{ hashFiles('vcpkg.json') }} - name: 🛠️ Build VTK (Windows) - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.os == 'windows-latest' && steps.vcpkg-installed-cache.outputs.cache-hit != 'true' }} shell: pwsh run: | ./vcpkg/vcpkg install --triplet x64-windows - - name: 🛠️ Disable IPO - if: ${{ matrix.os == 'ubuntu-latest' }} - run: | - sed -i 's/\(DVIENNALS_BUILD_PYTHON=ON"\)/\1,"-DUSE_IPO=off"/g' pyproject.toml - cat pyproject.toml - - name: 🐍 Build and check Python Module (Windows) if: ${{ matrix.os == 'windows-latest' }} shell: pwsh @@ -89,9 +85,13 @@ jobs: $toolchain = "${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake".Replace('\', '/') .\venv\Scripts\python -m pip install --upgrade pip + $vcpkgInstalled = "${{ github.workspace }}/vcpkg_installed".Replace('\', '/') + .\venv\Scripts\python -m pip install . ` --config-settings=cmake.define.CMAKE_TOOLCHAIN_FILE="$toolchain" ` - --config-settings=cmake.define.VCPKG_TARGET_TRIPLET="x64-windows" + --config-settings=cmake.define.VCPKG_TARGET_TRIPLET="x64-windows" ` + --config-settings=cmake.define.VCPKG_INSTALLED_DIR="$vcpkgInstalled" ` + --config-settings=cmake.define.VCPKG_MANIFEST_INSTALL=OFF .\venv\Scripts\python -c "import viennals; print(viennals.__doc__)" - name: 🐍 Build and check Python Module (Other) @@ -102,7 +102,7 @@ jobs: ./venv/bin/python -c "import viennals; print(viennals.__doc__)" - name: 📦 Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Pre-Built (${{ matrix.os }}) path: venv @@ -114,13 +114,18 @@ jobs: matrix: include: - os: ubuntu-latest - skip: "cp36-* cp37-* cp38-* cp39-* pp37-* pp38-* pp39-* *-manylinux_i686 *-musllinux_*" + skip: "cp38-* cp39-* *-manylinux_i686 *-musllinux_*" - os: windows-latest - skip: "cp36-* cp37-* cp38-* cp39-* pp37-* pp38-* pp39-*" + skip: "cp38-* cp39-*" + + - os: macos-15-intel + archs: x86_64 + skip: "cp38-* cp39-* cp310-*" - os: macos-latest - skip: "cp36-* cp37-* cp38-* cp39-* cp310-* pp37-* pp38-* pp39-*" + archs: arm64 + skip: "cp38-* cp39-* cp310-*" runs-on: ${{ matrix.os }} @@ -132,7 +137,7 @@ jobs: steps: - name: 📥 Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: 🖥️ Setup Environment uses: ./.github/actions/setup @@ -142,31 +147,21 @@ jobs: - name: 🛞 CIBuildWheel run: pip install cibuildwheel==3.4.0 --break-system-packages - - name: 🛠️ Package Config (Windows) - if: ${{ matrix.os == 'windows-latest' }} - shell: pwsh - run: | - sed -i 's/\(DVIENNALS_BUILD_PYTHON=ON"\)/\1,"-DVIENNALS_PACKAGE_PYTHON=ON","-DVIENNALS_IS_CI=ON","-DVIENNALS_VTK_RENDERING=OFF"/g' pyproject.toml - type pyproject.toml - - - name: 🛠️ Package Config (MacOs) - if: ${{ matrix.os == 'macos-latest' }} - run: | - sed -i .bak 's/\(DVIENNALS_BUILD_PYTHON=ON"\)/\1,"-DVIENNALS_PACKAGE_PYTHON=ON","-DVIENNALS_IS_CI=ON","-DVIENNALS_VTK_RENDERING=OFF"/g' pyproject.toml - cat pyproject.toml - - - name: 🛠️ Package Config (Linux) - if: ${{ matrix.os == 'ubuntu-latest' }} - run: | - sed -i 's/\(DVIENNALS_BUILD_PYTHON=ON"\)/\1,"-DVIENNALS_PACKAGE_PYTHON=ON","-DVIENNALS_IS_CI=ON","-DVIENNALS_VTK_RENDERING=OFF"/g' pyproject.toml - cat pyproject.toml - - name: 🏗️ Build Wheels (Linux) if: ${{ matrix.os == 'ubuntu-latest' }} run: python -m cibuildwheel --output-dir wheelhouse env: CIBW_SKIP: ${{ matrix.skip }} - CIBW_MANYLINUX_X86_64_IMAGE: ghcr.io/viennatools/vienna-builder:cuda-python-nocuda + CIBW_TEST_COMMAND: >- + python -c "import viennals; + print(viennals.__file__); + print(viennals.d2); + print(viennals.d3)" + CIBW_CONFIG_SETTINGS: >- + cmake.define.VIENNALS_PACKAGE_PYTHON=ON + cmake.define.VIENNALS_IS_CI=ON + cmake.define.VIENNALS_VTK_RENDERING=ON + cmake.define.USE_IPO=OFF - name: 🏗️ Build Wheels (Other) if: ${{ matrix.os != 'ubuntu-latest' }} @@ -174,9 +169,66 @@ jobs: env: CIBW_SKIP: ${{ matrix.skip }} CIBW_ARCHS_WINDOWS: auto64 + CIBW_ARCHS_MACOS: ${{ matrix.archs }} + CIBW_TEST_COMMAND: >- + python -c "import viennals; + print(viennals.__file__); + print(viennals.d2); + print(viennals.d3)" + CIBW_CONFIG_SETTINGS: >- + cmake.define.VIENNALS_PACKAGE_PYTHON=ON + cmake.define.VIENNALS_IS_CI=ON + cmake.define.VIENNALS_VTK_RENDERING=ON + + - name: 🔎 Inspect wheel contents + shell: bash + run: | + python - <<'PY' + import glob + import sys + import zipfile + + missing = [] + + for wheel in glob.glob("wheelhouse/*.whl"): + print("\n" + wheel) + with zipfile.ZipFile(wheel) as zf: + entries = sorted(zf.infolist(), key=lambda item: item.file_size, reverse=True) + for entry in entries[:40]: + size_mb = entry.file_size / 1024 / 1024 + print(f"{size_mb:8.2f} MB {entry.filename}") + + names = [entry.filename for entry in entries] + if "win_amd64" in wheel: + libs = [name for name in names if name.startswith("viennals.libs/")] + openmp = [ + name + for name in libs + if name.lower() == "viennals.libs/libomp140.x86_64.dll" + ] + core = next(name for name in names if name.startswith("viennals/_core") and name.endswith(".pyd")) + core_bytes = zf.read(core) + + print("\nWindows runtime libraries:") + for name in libs: + print(f" {name}") + + if not libs: + missing.append(f"{wheel}: missing viennals.libs/") + if not openmp: + missing.append(f"{wheel}: missing viennals.libs/libomp140.x86_64.dll") + if b"omp_set_nested" in core_bytes: + missing.append(f"{wheel}: _core.pyd still references deprecated omp_set_nested") + + if missing: + print("\nWheel runtime checks failed:", file=sys.stderr) + for item in missing: + print(f" {item}", file=sys.stderr) + raise SystemExit(1) + PY - name: 📦 Upload Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wheels-${{ matrix.os }} path: ./wheelhouse/*.whl diff --git a/CMakeLists.txt b/CMakeLists.txt index ad7abdd1..79b008dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,37 @@ if(VIENNALS_BUILD_PYTHON) endif() if(MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -openmp:llvm /bigobj") + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.30) + set(OpenMP_RUNTIME_MSVC + "llvm" + CACHE STRING "MSVC OpenMP runtime selected for FindOpenMP" FORCE) + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -openmp:llvm") + endif() + + add_compile_options("$<$:/bigobj>") +endif() + +# Reduce wheel size when packaging Python wheels. +# These flags are set before dependencies are added, so VTK is built with them too. +if(MSVC AND VIENNALS_PACKAGE_PYTHON) + string(APPEND CMAKE_C_FLAGS_RELEASE " /O2 /Gy /Gw") + string(APPEND CMAKE_CXX_FLAGS_RELEASE " /O2 /Gy /Gw") + string(APPEND CMAKE_MODULE_LINKER_FLAGS_RELEASE " /OPT:REF /OPT:ICF") + string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " /OPT:REF /OPT:ICF") + string(APPEND CMAKE_EXE_LINKER_FLAGS_RELEASE " /OPT:REF /OPT:ICF") +endif() + +if((CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") AND VIENNALS_PACKAGE_PYTHON) + string(APPEND CMAKE_C_FLAGS_RELEASE " -O3 -DNDEBUG -ffunction-sections -fdata-sections") + string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O3 -DNDEBUG -ffunction-sections -fdata-sections") + if(APPLE) + string(APPEND CMAKE_MODULE_LINKER_FLAGS_RELEASE " -Wl,-dead_strip") + string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -Wl,-dead_strip") + else() + string(APPEND CMAKE_MODULE_LINKER_FLAGS_RELEASE " -Wl,--gc-sections") + string(APPEND CMAKE_SHARED_LINKER_FLAGS_RELEASE " -Wl,--gc-sections") + endif() endif() # -------------------------------------------------------------------------------------------------------- @@ -217,22 +247,40 @@ elseif(VIENNALS_USE_VTK) # Common VTK build options set(VTK_OPTIONS "BUILD_SHARED_LIBS OFF" + "VTK_INSTALL_SDK OFF" + "VTK_BUILD_TESTING OFF" + "BUILD_TESTING OFF" + "VTK_BUILD_ALL_MODULES OFF" + "VTK_ENABLE_REMOTE_MODULES OFF" "VTK_LEGACY_REMOVE ON" - "VTK_SMP_IMPLEMENTATION_TYPE Sequential" + # VTK threading backend. + # OpenMP gives full parallelism on all platforms. On MSVC, CMake may still + # report an old OpenMP spec date, but OpenMP_RUNTIME_MSVC=llvm selects the LLVM runtime. + # makes LLVM OpenMP 5.0 the actual runtime — the cmake atomics warning is + # + # STDThread is intentionally kept OFF. vtkSMPToolsImpl.txx declares the + # explicit specialisation of IsParallelScope() for STDThread, but + # vtkSMPToolsAPI.cxx only includes vtkSMPToolsImpl.h — it never sees that + # declaration. GCC/Clang resolve this via COMDAT weak/strong symbols; + # MSVC emits an import reference that the linker cannot satisfy, producing + # LNK2001 for vtkSMPToolsImpl<1>::IsParallelScope. + "VTK_SMP_IMPLEMENTATION_TYPE OpenMP" "VTK_SMP_ENABLE_STDTHREAD OFF" - "VTK_SMP_ENABLE_OPENMP OFF" + "VTK_SMP_ENABLE_OPENMP ON" "VTK_SMP_ENABLE_TBB OFF" - "VTK_GROUP_ENABLE_Rendering NO" - "VTK_GROUP_ENABLE_Imaging NO" - "VTK_GROUP_ENABLE_Views NO" - "VTK_GROUP_ENABLE_Web NO" + # Keep the build small, but do not hard-forbid dependency resolution. + "VTK_GROUP_ENABLE_Rendering DONT_WANT" + "VTK_GROUP_ENABLE_Imaging DONT_WANT" + "VTK_GROUP_ENABLE_Views DONT_WANT" + "VTK_GROUP_ENABLE_Web DONT_WANT" + # These should stay hard-disabled for wheels. "VTK_GROUP_ENABLE_Qt NO" "VTK_GROUP_ENABLE_MPI NO" - "VTK_GROUP_ENABLE_Parallel NO" - "VTK_MODULE_ENABLE_VTK_ParallelDIY NO" - "VTK_MODULE_ENABLE_VTK_ParallelCore NO" + # No VTK Python wrapping; ViennaLS uses pybind11. "VTK_ENABLE_WRAPPING NO" + # Avoid optional geospatial/projection dependency. "VTK_MODULE_ENABLE_VTK_libproj NO" + # Required VTK core/IO/filter modules. "VTK_MODULE_ENABLE_VTK_CommonExecutionModel YES" "VTK_MODULE_ENABLE_VTK_CommonMisc YES" "VTK_MODULE_ENABLE_VTK_CommonSystem YES" @@ -246,12 +294,15 @@ elseif(VIENNALS_USE_VTK) "VTK_MODULE_ENABLE_VTK_FiltersCore YES" "VTK_MODULE_ENABLE_VTK_FiltersGeneral YES" "VTK_MODULE_ENABLE_VTK_FiltersGeometry YES" - "VTK_MODULE_ENABLE_VTK_RenderingCore YES" - "CMAKE_PROJECT_NAME VTK") + "VTK_GROUP_ENABLE_Parallel DONT_WANT" + "VTK_MODULE_ENABLE_VTK_ParallelDIY DONT_WANT" + "VTK_MODULE_ENABLE_VTK_ParallelCore DONT_WANT") + if(VIENNALS_VTK_RENDERING) list( APPEND VTK_OPTIONS + "VTK_MODULE_ENABLE_VTK_RenderingCore YES" "VTK_MODULE_ENABLE_VTK_RenderingOpenGL2 YES" "VTK_MODULE_ENABLE_VTK_RenderingUI YES" "VTK_MODULE_ENABLE_VTK_InteractionStyle YES" @@ -265,14 +316,65 @@ elseif(VIENNALS_USE_VTK) list(APPEND VTK_OPTIONS "VTK_MODULE_USE_EXTERNAL_VTK_png ON" "VTK_MODULE_USE_EXTERNAL_VTK_zlib ON") endif() + # Guarantee STDTHREAD stays OFF even if VTK's option() fires before CPM's + # cache entry is written. See comment in VTK_OPTIONS above for why this + # matters on MSVC. + # Guarantee VTK uses OpenMP SMP for package builds. + set(VTK_SMP_IMPLEMENTATION_TYPE + "OpenMP" + CACHE STRING "" FORCE) + set(VTK_SMP_ENABLE_STDTHREAD + OFF + CACHE BOOL "" FORCE) + set(VTK_SMP_ENABLE_OPENMP + ON + CACHE BOOL "" FORCE) + set(VTK_SMP_ENABLE_TBB + OFF + CACHE BOOL "" FORCE) + + if(NOT VIENNALS_PACKAGE_PYTHON) + find_package(VTK 9.0.0 QUIET) + endif() + + if(VTK_FOUND) + message(STATUS "[ViennaLS] Using system VTK ${VTK_VERSION}") + else() + message(STATUS "[ViennaLS] Using bundled VTK v9.3.1") + + # Apply CPM-style options manually because DOWNLOAD_ONLY does not configure VTK. + foreach(_vtk_option IN LISTS VTK_OPTIONS) + if(_vtk_option MATCHES "^([^ ]+) +(.*)$") + set("${CMAKE_MATCH_1}" + "${CMAKE_MATCH_2}" + CACHE STRING "" FORCE) + else() + message(FATAL_ERROR "[ViennaLS] Invalid VTK option: ${_vtk_option}") + endif() + endforeach() + + CPMAddPackage( + NAME VTK + GIT_TAG v9.3.1 + GIT_REPOSITORY "https://gitlab.kitware.com/vtk/vtk" + DOWNLOAD_ONLY YES) + + if(VTK_ADDED) + viennals_patch_vtk_msvc_stdext("${VTK_SOURCE_DIR}") + viennals_patch_vtk_openmp_nested("${VTK_SOURCE_DIR}") + + if(VIENNALS_BUILD_PYTHON) + add_subdirectory("${VTK_SOURCE_DIR}" "${VTK_BINARY_DIR}" EXCLUDE_FROM_ALL) + else() + add_subdirectory("${VTK_SOURCE_DIR}" "${VTK_BINARY_DIR}") + endif() + endif() + endif() - CPMFindPackage( - NAME VTK - GIT_TAG v9.3.1 - VERSION 9.0.0 - GIT_REPOSITORY "https://gitlab.kitware.com/vtk/vtk" - EXCLUDE_FROM_ALL ${VIENNALS_BUILD_PYTHON} - OPTIONS ${VTK_OPTIONS}) + message(STATUS "[ViennaLS] VTK_SMP_IMPLEMENTATION_TYPE=${VTK_SMP_IMPLEMENTATION_TYPE}") + message(STATUS "[ViennaLS] VTK_SMP_ENABLE_OPENMP=${VTK_SMP_ENABLE_OPENMP}") + message(STATUS "[ViennaLS] VTK_SMP_ENABLE_STDTHREAD=${VTK_SMP_ENABLE_STDTHREAD}") + message(STATUS "[ViennaLS] VTK_SMP_ENABLE_TBB=${VTK_SMP_ENABLE_TBB}") target_compile_definitions(${PROJECT_NAME} INTERFACE VIENNALS_USE_VTK=1) @@ -289,16 +391,15 @@ elseif(VIENNALS_USE_VTK) VTK::IOXML VTK::FiltersCore VTK::FiltersGeneral - VTK::FiltersGeometry - CACHE INTERNAL "VTK Libraries") + VTK::FiltersGeometry) if(VIENNALS_VTK_RENDERING) list( APPEND VTK_LIBRARIES VTK::RenderingCore VTK::RenderingOpenGL2 - VTK::RenderingUI VTK::InteractionStyle + VTK::RenderingUI VTK::RenderingFreeType VTK::RenderingAnnotation VTK::IOImage) diff --git a/cmake/vtk.cmake b/cmake/vtk.cmake index 9ed5faf4..8bd0ad34 100644 --- a/cmake/vtk.cmake +++ b/cmake/vtk.cmake @@ -58,3 +58,107 @@ macro(import_vtk_python) message(STATUS "[ViennaLS] Successfully created VTK::PythonLibs target") endmacro() + +function(viennals_patch_vtk_msvc_stdext VTK_SOURCE_DIR) + if(NOT MSVC) + return() + endif() + + set(_vtk_fmt_header "${VTK_SOURCE_DIR}/ThirdParty/diy2/vtkdiy2/include/vtkdiy2/fmt/format.h") + + if(NOT EXISTS "${_vtk_fmt_header}") + message( + WARNING + "[ViennaLS] Could not find VTK diy2/fmt header for MSVC stdext patch: ${_vtk_fmt_header}") + return() + endif() + + file(READ "${_vtk_fmt_header}" _vtk_fmt_contents) + + set(_patched_guard "#if defined(_SECURE_SCL) && (!defined(_MSC_VER) || _MSC_VER < 1951)") + + string(FIND "${_vtk_fmt_contents}" "${_patched_guard}" _already_patched) + + if(NOT _already_patched EQUAL -1) + message(STATUS "[ViennaLS] VTK MSVC stdext patch already applied") + return() + endif() + + set(_old_guard "#ifdef _SECURE_SCL") + + string(FIND "${_vtk_fmt_contents}" "${_old_guard}" _old_guard_pos) + + if(_old_guard_pos EQUAL -1) + message( + WARNING + "[ViennaLS] VTK MSVC stdext patch was not applied; expected guard not found in ${_vtk_fmt_header}" + ) + return() + endif() + + string(REPLACE "${_old_guard}" "${_patched_guard}" _vtk_fmt_contents "${_vtk_fmt_contents}") + + file(WRITE "${_vtk_fmt_header}" "${_vtk_fmt_contents}") + + message(STATUS "[ViennaLS] Applied VTK MSVC stdext patch") +endfunction() + +function(viennals_patch_vtk_openmp_nested VTK_SOURCE_DIR) + file(GLOB_RECURSE _vtk_smp_openmp_sources "${VTK_SOURCE_DIR}/Common/Core/SMP/OpenMP/*.cxx" + "${VTK_SOURCE_DIR}/Common/Core/SMP/OpenMP/*.txx" + "${VTK_SOURCE_DIR}/Common/Core/SMP/OpenMP/*.h") + + if(NOT _vtk_smp_openmp_sources) + message(WARNING "[ViennaLS] Could not find VTK OpenMP SMP sources for omp_set_nested patch") + return() + endif() + + set(_vtk_smp_openmp_patch_count 0) + + foreach(_vtk_smp_openmp IN LISTS _vtk_smp_openmp_sources) + file(READ "${_vtk_smp_openmp}" _vtk_smp_contents) + + string(FIND "${_vtk_smp_contents}" "omp_set_nested(" _has_set_nested) + string(FIND "${_vtk_smp_contents}" "omp_get_nested()" _has_get_nested) + + if(_has_set_nested EQUAL -1 AND _has_get_nested EQUAL -1) + continue() + endif() + + if(NOT _has_set_nested EQUAL -1) + # omp_set_nested is deprecated. Use max-active-levels directly so the + # deprecated call is removed even when a compiler reports an old _OPENMP + # macro while linking against a modern runtime. + string(REGEX REPLACE [[omp_set_nested\(([^)]*)\);]] [[/* VIENNALS_PATCH_OMP_SET_NESTED */ + omp_set_max_active_levels((\1) ? 1024 : 1);]] _vtk_smp_contents "${_vtk_smp_contents}") + + endif() + + if(NOT _has_get_nested EQUAL -1) + string( + REGEX + REPLACE "omp_get_nested\\(\\)" + "/* VIENNALS_PATCH_OMP_GET_NESTED */ (omp_get_max_active_levels() > 1)" + _vtk_smp_contents "${_vtk_smp_contents}") + + endif() + + string(FIND "${_vtk_smp_contents}" "omp_set_nested(" _still_has_set_nested) + if(NOT _still_has_set_nested EQUAL -1) + message( + FATAL_ERROR + "[ViennaLS] VTK OpenMP nested-parallelism patch did not remove omp_set_nested from ${_vtk_smp_openmp}" + ) + endif() + + file(WRITE "${_vtk_smp_openmp}" "${_vtk_smp_contents}") + math(EXPR _vtk_smp_openmp_patch_count "${_vtk_smp_openmp_patch_count} + 1") + message(STATUS "[ViennaLS] Patched VTK OpenMP nested-parallelism source: ${_vtk_smp_openmp}") + endforeach() + + if(_vtk_smp_openmp_patch_count EQUAL 0) + message(STATUS "[ViennaLS] VTK OpenMP nested-parallelism patch not needed") + else() + message(STATUS "[ViennaLS] Applied VTK OpenMP nested-parallelism patch") + endif() +endfunction() diff --git a/include/viennals/lsMesh.hpp b/include/viennals/lsMesh.hpp index 4f1b3868..6f95e5ff 100644 --- a/include/viennals/lsMesh.hpp +++ b/include/viennals/lsMesh.hpp @@ -179,11 +179,12 @@ template class Mesh { if (it != newNodes.end()) { adjusted = true; // if duplicate point, save it to be replaced - unsigned nodeId = std::distance(newNodes.begin(), it); + unsigned nodeId = + static_cast(std::distance(newNodes.begin(), it)); duplicates.emplace_back(i, nodeId); } else { if (adjusted) - duplicates.push_back(std::make_pair(i, newNodes.size())); + duplicates.emplace_back(i, static_cast(newNodes.size())); newNodes.push_back(nodes[i]); } } diff --git a/pyproject.toml b/pyproject.toml index ff4bfaa4..4b5cc346 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "scikit-build-core", + "scikit-build-core>=0.12.2", "pybind11" ] build-backend = "scikit_build_core.build" @@ -21,4 +21,4 @@ Issues = "https://github.com/ViennaTools/ViennaLS/issues" [tool.scikit-build] build-dir = "build" cmake.args = ["-DVIENNALS_BUILD_PYTHON=ON"] -wheel.packages = ["python/viennals"] \ No newline at end of file +wheel.packages = ["python/viennals"] diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 5b17c4db..7e05fd33 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -98,6 +98,112 @@ if((VIENNALS_PACKAGE_PYTHON AND NOT APPLE) OR MSVC) # Other package builds still need this staged dependency folder. viennacore_setup_vtk_env(${MOD} ${VIENNALS_LIB_FOLDER}) + if(MSVC) + # /openmp:llvm links against the LLVM OpenMP runtime, which is usually + # available on GitHub runners but not on end-user Windows machines. + set(CMAKE_INSTALL_OPENMP_LIBRARIES TRUE) + set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) + include(InstallRequiredSystemLibraries) + + get_filename_component(_VIENNALS_COMPILER_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY) + set(_VIENNALS_OPENMP_DLL_CANDIDATES) + + foreach(_VIENNALS_SYSTEM_RUNTIME IN LISTS CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS) + get_filename_component(_VIENNALS_SYSTEM_RUNTIME_NAME "${_VIENNALS_SYSTEM_RUNTIME}" NAME) + if(_VIENNALS_SYSTEM_RUNTIME_NAME MATCHES "^libomp.*\\.dll$") + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES "${_VIENNALS_SYSTEM_RUNTIME}") + endif() + endforeach() + + if(DEFINED ENV{VCINSTALLDIR}) + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES + "$ENV{VCINSTALLDIR}/Tools/Llvm/bin/libomp140.x86_64.dll" + "$ENV{VCINSTALLDIR}/Tools/Llvm/x64/bin/libomp140.x86_64.dll") + endif() + + if(DEFINED ENV{VCToolsInstallDir}) + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES + "$ENV{VCToolsInstallDir}/../../Llvm/bin/libomp140.x86_64.dll" + "$ENV{VCToolsInstallDir}/../../Llvm/x64/bin/libomp140.x86_64.dll") + endif() + + if(DEFINED ENV{llvmX64}) + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES "$ENV{llvmX64}/libomp140.x86_64.dll") + endif() + + if(DEFINED ENV{VCToolsRedistDir}) + file(GLOB_RECURSE _VIENNALS_OPENMP_REDIST_DLLS "$ENV{VCToolsRedistDir}/*libomp140.x86_64.dll") + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES ${_VIENNALS_OPENMP_REDIST_DLLS}) + endif() + + if(DEFINED ENV{VCINSTALLDIR}) + file(GLOB_RECURSE _VIENNALS_OPENMP_VC_REDIST_DLLS + "$ENV{VCINSTALLDIR}/Redist/MSVC/*libomp140.x86_64.dll") + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES ${_VIENNALS_OPENMP_VC_REDIST_DLLS}) + endif() + + find_file( + _VIENNALS_OPENMP_DLL_FROM_PATH + NAMES libomp140.x86_64.dll + PATHS ENV PATH + NO_DEFAULT_PATH) + if(_VIENNALS_OPENMP_DLL_FROM_PATH) + list(APPEND _VIENNALS_OPENMP_DLL_CANDIDATES "${_VIENNALS_OPENMP_DLL_FROM_PATH}") + endif() + + list( + APPEND + _VIENNALS_OPENMP_DLL_CANDIDATES + "${_VIENNALS_COMPILER_BIN_DIR}/libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../../bin/libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../../../../../Llvm/bin/libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../../../../../Llvm/x64/bin/libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../../../../../../Tools/Llvm/bin/libomp140.x86_64.dll" + "${_VIENNALS_COMPILER_BIN_DIR}/../../../../../../Tools/Llvm/x64/bin/libomp140.x86_64.dll") + + list(REMOVE_DUPLICATES _VIENNALS_OPENMP_DLL_CANDIDATES) + + unset(_VIENNALS_OPENMP_DLL) + set(_VIENNALS_OPENMP_DLL_SEARCH_LOG) + foreach(_VIENNALS_OPENMP_DLL_CANDIDATE IN LISTS _VIENNALS_OPENMP_DLL_CANDIDATES) + get_filename_component(_VIENNALS_OPENMP_DLL_CANDIDATE_REAL + "${_VIENNALS_OPENMP_DLL_CANDIDATE}" ABSOLUTE) + list(APPEND _VIENNALS_OPENMP_DLL_SEARCH_LOG "${_VIENNALS_OPENMP_DLL_CANDIDATE_REAL}") + if(EXISTS "${_VIENNALS_OPENMP_DLL_CANDIDATE_REAL}") + set(_VIENNALS_OPENMP_DLL "${_VIENNALS_OPENMP_DLL_CANDIDATE_REAL}") + break() + endif() + endforeach() + + if(_VIENNALS_OPENMP_DLL) + message(STATUS "[ViennaLS] Bundling LLVM OpenMP runtime: ${_VIENNALS_OPENMP_DLL}") + add_custom_command( + TARGET ${MOD} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory "${VIENNALS_LIB_FOLDER}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_VIENNALS_OPENMP_DLL}" + "${VIENNALS_LIB_FOLDER}/" + VERBATIM) + + if(VIENNALS_PACKAGE_PYTHON) + install(PROGRAMS "${_VIENNALS_OPENMP_DLL}" DESTINATION ${PKG}.libs) + endif() + elseif(VIENNALS_PACKAGE_PYTHON) + string(REPLACE ";" "\n " _VIENNALS_OPENMP_DLL_SEARCH_LOG_TEXT + "${_VIENNALS_OPENMP_DLL_SEARCH_LOG}") + message( + FATAL_ERROR + "[ViennaLS] Could not locate libomp140.x86_64.dll for a Windows wheel build. " + "The produced wheel would fail to import on machines without the LLVM OpenMP runtime. " + "Searched:\n ${_VIENNALS_OPENMP_DLL_SEARCH_LOG_TEXT}") + else() + message( + WARNING "[ViennaLS] Could not locate libomp140.x86_64.dll. Windows wheels built " + "with /openmp:llvm may fail to import on machines without the LLVM OpenMP runtime.") + endif() + endif() + if(VIENNALS_USE_GPU) foreach(_CUDA_TARGET CUDA::cudart CUDA::cusparse) if(TARGET ${_CUDA_TARGET}) @@ -111,7 +217,21 @@ if((VIENNALS_PACKAGE_PYTHON AND NOT APPLE) OR MSVC) endif() endif() -install( - DIRECTORY ${VIENNALS_LIB_FOLDER} - DESTINATION . - OPTIONAL) +if(NOT VIENNALS_PACKAGE_PYTHON) + # Dev/test builds: install the whole lib folder (VTK DLLs from vcpkg on + # Windows, VTK .so on Linux, etc.) so the module can be imported in-place. + install( + DIRECTORY ${VIENNALS_LIB_FOLDER} + DESTINATION . + OPTIONAL) +elseif(MSVC) + # Package build on Windows: static VTK is already compiled into _core.pyd. + # Only DLLs are needed at runtime (libomp140.x86_64.dll etc.). + # Installing the full VIENNALS_LIB_FOLDER would add hundreds of MBs of .lib + # files; restrict to *.dll only. + install( + DIRECTORY ${VIENNALS_LIB_FOLDER} + DESTINATION . + OPTIONAL FILES_MATCHING + PATTERN "*.dll") +endif() diff --git a/python/__init__.py b/python/__init__.py index 82f33b80..4e4f7fe5 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -3,7 +3,7 @@ ======== ViennaLS is a level set library developed for high performance -topography simulations. The main design goals are simplicity and efficiency, +topography and semiconductor process simulations. The main design goals are simplicity and efficiency, tailored towards scientific simulations. ViennaLS can also be used for visualisation applications, although this is not the main design target. """ diff --git a/python/pyWrap.cpp b/python/pyWrap.cpp index d920729b..33b7f2cd 100644 --- a/python/pyWrap.cpp +++ b/python/pyWrap.cpp @@ -39,10 +39,10 @@ class PylsVelocityField : public VelocityField { PYBIND11_MODULE(VIENNALS_MODULE_NAME, module) { module.doc() = "ViennaLS is a header-only C++ level set library developed for high " - "performance topography simulations. The main design goals are " - "simplicity and efficiency, tailored towards scientific simulations. " - "ViennaLS can also be used for visualization applications, although this " - "is not the main design target."; + "performance topography and semiconductor process simulations. The main " + "design goals are simplicity and efficiency, tailored towards scientific " + "simulations. ViennaLS can also be used for visualization applications, " + "although this is not the main design target."; // set version string of python module module.attr("__version__") = versionString(); diff --git a/python/viennals/__init__.pyi b/python/viennals/__init__.pyi index 779995fd..0ae66518 100644 --- a/python/viennals/__init__.pyi +++ b/python/viennals/__init__.pyi @@ -4,7 +4,7 @@ ViennaLS ======== ViennaLS is a level set library developed for high performance -topography simulations. The main design goals are simplicity and efficiency, +topography and semiconductor process simulations. The main design goals are simplicity and efficiency, tailored towards scientific simulations. ViennaLS can also be used for visualisation applications, although this is not the main design target. """ diff --git a/python/viennals/_core.pyi b/python/viennals/_core.pyi index 663384a8..d085fb85 100644 --- a/python/viennals/_core.pyi +++ b/python/viennals/_core.pyi @@ -1,5 +1,5 @@ """ -ViennaLS is a header-only C++ level set library developed for high performance topography simulations. The main design goals are simplicity and efficiency, tailored towards scientific simulations. ViennaLS can also be used for visualization applications, although this is not the main design target. +ViennaLS is a header-only C++ level set library developed for high performance topography and semiconductor process simulations. The main design goals are simplicity and efficiency, tailored towards scientific simulations. ViennaLS can also be used for visualization applications, although this is not the main design target. """ from __future__ import annotations import collections.abc