Every Ceres solve in the project selects a dense linear solver explicitly -- DENSE_QR in PostRefine, RingOptimizer and StillsPartialityRefine, DENSE_NORMAL_CHOLESKY in XtalOptimizer, DENSE_SCHUR in GeometryRefiner -- and none of the seven Solver::Options sites sets dense_linear_algebra_library_type, which defaults to EIGEN. ceres::Covariance, the other SuiteSparse consumer, is never instantiated. So no sparse factorization and no LAPACK call is reachable. Measured rather than argued: an LD_PRELOAD shim exporting dgeqrf_, dpotrf_, dtrtrs_, spotrf_ and METIS_NodeND, each aborting on entry, let a full mx run (1800 frames, index -> refine -> integrate -> scale -> merge) finish with every counter at zero. The change is a no-op on results. Baseline and modified binaries produce byte-identical run.mtz, run.hkl and run.cif on a rotation dataset at -N 1, and two baseline runs give that same signature, so the comparison is meaningful. Only the DATE= stamp in the report differs. What it buys is six fewer shared libraries: libopenblas and libmetis, plus libgfortran, libquadmath, libgomp and libz behind them. rugnux links its own zlib statically -- zero undefined zlib symbols before and after -- so the dynamic libz only ever arrived through OpenBLAS. It also makes the build reproducible across hosts. SUITESPARSE defaults ON and self-disables only when its probe fails, so a machine where SuiteSparse_DIR is not found already compiled CERES_NO_SUITESPARSE while one carrying the SuiteSparse CMake config would not have. Pinning it removes that divergence, and shortens what a cross-compiled aarch64 build has to supply. EIGENSPARSE stays ON, so a sparse solver remains available if one is ever selected, without an external library. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SQjneRUssfhi1k9rq8Ts3h
92 lines
4.9 KiB
CMake
92 lines
4.9 KiB
CMake
# Eigen has to be resolved BEFORE Ceres is added below. Whichever find_package(Eigen3) runs first
|
|
# creates the Eigen3::Eigen imported target, and every later one leaves an existing target alone --
|
|
# so the first one wins for the whole build, Ceres included. See the guard after Ceres for why that
|
|
# matters here.
|
|
FIND_PACKAGE(Eigen3 3.4 REQUIRED NO_MODULE) # provides Eigen3::Eigen
|
|
|
|
SET(PROVIDE_UNINSTALL_TARGET OFF)
|
|
# Force Ceres to build WITHOUT its own CUDA support: Jungfraujoch never uses Ceres' GPU solvers, so
|
|
# this just drops unused code and build time (Ceres would otherwise enable CUDA whenever a toolkit is
|
|
# found). See the Eigen note in the top-level CMakeLists for the related Windows/cmake history.
|
|
SET(USE_CUDA OFF CACHE BOOL "Force Ceres to build without CUDA (Jungfraujoch does not use Ceres' GPU solvers)" FORCE)
|
|
|
|
# Ceres' sparse and LAPACK back-ends are dead weight here: every Ceres solve in this project picks a
|
|
# DENSE solver explicitly (DENSE_QR / DENSE_NORMAL_CHOLESKY / DENSE_SCHUR) and none sets
|
|
# dense_linear_algebra_library_type, which defaults to EIGEN -- so no sparse factorization and no
|
|
# LAPACK call is ever reachable. Left at their defaults they cost rugnux six shared libraries
|
|
# (libopenblas, libmetis, and libgfortran/libquadmath/libgomp/libz behind them), and SuiteSparse
|
|
# turns itself on or off depending on what happens to sit on the build host, so two machines build
|
|
# different Ceres from the same source. Pinning all three off leaves the results bit-identical.
|
|
# EIGENSPARSE stays ON: it keeps a sparse implementation available without an external library.
|
|
SET(SUITESPARSE OFF CACHE BOOL "Ceres: no sparse solver is ever selected" FORCE)
|
|
SET(LAPACK OFF CACHE BOOL "Ceres: dense solves go through Eigen, not LAPACK" FORCE)
|
|
SET(EIGENMETIS OFF CACHE BOOL "Ceres: METIS ordering only applies to sparse factorization" FORCE)
|
|
|
|
# Prevent MKL from being found (guarantees no MKL)
|
|
SET(CMAKE_DISABLE_FIND_PACKAGE_MKL TRUE)
|
|
SET(CERES_THREADING_MODEL "CXX_THREADS")
|
|
|
|
FetchContent_Declare(
|
|
ceres
|
|
GIT_REPOSITORY https://github.com/ceres-solver/ceres-solver
|
|
GIT_TAG 0c70ed3
|
|
EXCLUDE_FROM_ALL
|
|
)
|
|
|
|
FetchContent_MakeAvailable(ceres)
|
|
|
|
# Ceres asks for Eigen with a version RANGE -- find_package(Eigen3 3.3.4...5 NO_MODULE) -- and
|
|
# Eigen's own Eigen3ConfigVersion.cmake rejects any range whose two endpoints differ in major
|
|
# version. A 3.4 Eigen therefore reports itself INCOMPATIBLE with Ceres' query and the search falls
|
|
# through to whatever older Eigen comes next on the prefix path (a distro eigen3-devel, say). Having
|
|
# resolved Eigen above, Ceres inherits our Eigen3::Eigen instead, because a generated targets file
|
|
# returns early when its targets already exist. If that ever stops holding, Ceres compiles against
|
|
# one Eigen and the rest of the project against another; the two versions' identically mangled
|
|
# template instantiations are then merged at link time -- an ODR violation whose symptom is a crash
|
|
# inside Eigen. Refuse to build it.
|
|
GET_PROPERTY(CERES_IMPORTED_TARGETS DIRECTORY ${ceres_SOURCE_DIR} PROPERTY IMPORTED_TARGETS)
|
|
IF ("Eigen3::Eigen" IN_LIST CERES_IMPORTED_TARGETS)
|
|
MESSAGE(FATAL_ERROR "Ceres created an Eigen3::Eigen of its own instead of using the Eigen this "
|
|
"project resolved. Two Eigen versions in one binary is undefined behaviour. Leave a "
|
|
"single Eigen on the find_package path, or point CMAKE_PREFIX_PATH / Eigen3_DIR at the "
|
|
"one to use.")
|
|
ENDIF()
|
|
|
|
ADD_LIBRARY(JFJochImageAnalysis STATIC
|
|
MXAnalysisWithoutFPGA.cpp
|
|
MXAnalysisWithoutFPGA.h
|
|
MXAnalysisAfterFPGA.h
|
|
MXAnalysisAfterFPGA.cpp
|
|
IndexAndRefine.cpp
|
|
IndexAndRefine.h
|
|
dark_mask_analysis/DarkMaskAnalysis.cpp
|
|
dark_mask_analysis/DarkMaskAnalysis.h
|
|
beam_stop/ShadowFinder.cpp
|
|
beam_stop/ShadowFinder.h
|
|
$<$<BOOL:${JFJOCH_USE_CUDA}>:beam_stop/ShadowAccumulatorGPU.cu>
|
|
beam_stop/ShadowAccumulatorGPU.h
|
|
rotation_indexer/RotationIndexer.cpp
|
|
rotation_indexer/RotationIndexer.h
|
|
WriteReflections.cpp
|
|
WriteReflections.h
|
|
LoadFCalcFromMtz.cpp
|
|
LoadFCalcFromMtz.h
|
|
UpdateReflectionResolution.cpp
|
|
UpdateReflectionResolution.h
|
|
IntegrationOutcome.h
|
|
rotation_indexer/RotationIndexerCounter.cpp
|
|
rotation_indexer/RotationIndexerCounter.h)
|
|
|
|
ADD_SUBDIRECTORY(spot_finding)
|
|
ADD_SUBDIRECTORY(bragg_integration)
|
|
ADD_SUBDIRECTORY(bragg_prediction)
|
|
ADD_SUBDIRECTORY(indexing)
|
|
ADD_SUBDIRECTORY(geom_refinement)
|
|
ADD_SUBDIRECTORY(lattice_search)
|
|
ADD_SUBDIRECTORY(scale_merge)
|
|
ADD_SUBDIRECTORY(image_preprocessing)
|
|
ADD_SUBDIRECTORY(azint)
|
|
ADD_SUBDIRECTORY(roi)
|
|
|
|
TARGET_LINK_LIBRARIES(JFJochImageAnalysis JFJochAzIntEngine JFJochROIIntegration JFJochImagePreprocessing JFJochBraggPrediction JFJochBraggIntegration JFJochLatticeSearch JFJochIndexing JFJochSpotFinding JFJochCommon JFJochGeomRefinement JFJochScaleMerge gemmi)
|