Skip to content

Commit 9c9141a

Browse files
authored
Merge pull request #88 from dav-og/feature/moordyn-coupling
Add MoorDyn mooring coupling with real-time tension visualization
2 parents fbdbe7e + aa388b7 commit 9c9141a

77 files changed

Lines changed: 483799 additions & 969 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ doc/user/_verification/demos/sphere_decay/*.png
4242
doc/user/_verification/demos/sphere_decay/*.hires.png
4343
_build/
4444

45+
# MoorDyn workaround (copied at configure time, see CMakeLists.txt)
46+
/cmake/MoorDynConfig.cmake
47+
/LICENSE.txt
48+
4549
# General
4650
build/
4751
demos/F3OF/f3of/

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "extern/MoorDyn"]
2+
path = extern/MoorDyn
3+
url = https://github.com/FloatingArrayDesign/MoorDyn.git

CMakeLists.txt

Lines changed: 168 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ configure_file(${CMAKE_SOURCE_DIR}/cmake/version.h.in
5252
# Add the cmake folder so the FindSphinx module is found
5353
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
5454

55-
# Deterministic dependency discovery (avoid ambient PATH/registry drift)
55+
# Prefer config-mode package discovery (finds vcpkg *-config.cmake files
56+
# instead of CMake's built-in Find*.cmake modules which may not understand vcpkg).
5657
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON)
5758
set(CMAKE_FIND_USE_PACKAGE_REGISTRY OFF)
58-
set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
5959

6060
# Force Release by default so CI builds are optimized and users get good performance
6161
if(NOT DEFINED HYDROCHRONO_DEFAULT_BUILD_TYPE)
@@ -79,6 +79,7 @@ option(HYDROCHRONO_ENABLE_IRRLICHT "Enable Irrlicht visualization library" OFF)
7979
option(HYDROCHRONO_ENABLE_VSG "Enable VSG visualization library" OFF)
8080
option(HYDROCHRONO_ENABLE_DEMOS "Enable demo executables" OFF)
8181
option(HYDROCHRONO_ENABLE_YAML_RUNNER "Enable YAML-based CLI runner" OFF)
82+
option(HYDROCHRONO_ENABLE_MOORDYN "Enable MoorDyn mooring coupling" OFF)
8283

8384
# ===============================================================================
8485
# ------ Build setup ------------------------------------------------------------
@@ -167,10 +168,36 @@ if(NOT Chrono_DIR)
167168
return()
168169
endif()
169170

171+
# Pre-load HDF5 targets so the shims below are only created when genuinely
172+
# needed (i.e., when HDF5 config-mode doesn't provide hdf5_tools).
173+
# Without this, installers that define all HDF5 targets as a batch
174+
# (e.g., HDF Group installer) error out on the partial pre-definition.
175+
find_package(HDF5 CONFIG QUIET)
176+
177+
# Chrono's export file may reference hdf5::hdf5_tools-{shared,static} which is
178+
# absent from some HDF5 installations (e.g., vcpkg). Provide empty shims so
179+
# ChronoTargets.cmake loads without errors. Must be created BEFORE find_package(Chrono).
180+
if(NOT TARGET hdf5::hdf5_tools-shared)
181+
add_library(hdf5::hdf5_tools-shared INTERFACE IMPORTED)
182+
endif()
183+
if(NOT TARGET hdf5::hdf5_tools-static)
184+
add_library(hdf5::hdf5_tools-static INTERFACE IMPORTED)
185+
endif()
186+
170187
find_package(Chrono
171188
CONFIG REQUIRED
172189
COMPONENTS Parsers)
173190

191+
# If the early HDF5 find failed but Chrono has now set HDF5_DIR, retry.
192+
# The early find may have cached HDF5_DIR-NOTFOUND, blocking Chrono's
193+
# non-FORCE cache set. Clear it and retry with the Chrono-provided path.
194+
if(NOT HDF5_FOUND AND DEFINED CACHE{HDF5_DIR})
195+
if("${HDF5_DIR}" MATCHES "NOTFOUND")
196+
unset(HDF5_DIR CACHE)
197+
find_package(HDF5 CONFIG QUIET)
198+
endif()
199+
endif()
200+
174201
message(STATUS "----\n")
175202

176203
message(STATUS "Chrono targets: ${CHRONO_TARGETS}")
@@ -203,6 +230,20 @@ else()
203230
message(STATUS "Set C++ standard to: ${HC_CXX_STANDARD}")
204231
endif()
205232

233+
# Detect whether Chrono has the newer ChLoadHydrodynamics API.
234+
# If not, fall back to HydroChrono's legacy added-mass implementation.
235+
set(CHRONO_HAS_LOAD_HYDRODYNAMICS FALSE)
236+
get_target_property(_chrono_inc_dirs Chrono::Chrono_core INTERFACE_INCLUDE_DIRECTORIES)
237+
foreach(_dir ${_chrono_inc_dirs})
238+
if(EXISTS "${_dir}/chrono/physics/ChLoadHydrodynamics.h")
239+
set(CHRONO_HAS_LOAD_HYDRODYNAMICS TRUE)
240+
break()
241+
endif()
242+
endforeach()
243+
if(NOT CHRONO_HAS_LOAD_HYDRODYNAMICS)
244+
message(STATUS "ChLoadHydrodynamics not found in Chrono -- using legacy added-mass implementation")
245+
endif()
246+
206247
# Enable Irrlicht support if requested and available
207248
if(HYDROCHRONO_ENABLE_IRRLICHT AND NOT TARGET Chrono::Chrono_irrlicht)
208249
message(FATAL_ERROR "HYDROCHRONO_ENABLE_IRRLICHT is ON but Chrono::Chrono_irrlicht target not found. Ensure Chrono was built with Irrlicht support.")
@@ -239,7 +280,49 @@ endif()
239280
find_package(OpenMP REQUIRED COMPONENTS CXX)
240281

241282
# -- HDF5 Integration --
242-
find_package(HDF5 REQUIRED COMPONENTS CXX)
283+
# Config-mode discovery runs early (before Chrono) to avoid target conflicts
284+
# with the hdf5_tools shims. Fall back to module-mode here if it wasn't found.
285+
if(NOT HDF5_FOUND)
286+
find_package(HDF5 REQUIRED COMPONENTS CXX)
287+
endif()
288+
289+
# -- MoorDyn mooring library (optional) --
290+
if(HYDROCHRONO_ENABLE_MOORDYN)
291+
message(STATUS "\n---- MoorDyn mooring library")
292+
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/extern/MoorDyn/CMakeLists.txt")
293+
message(FATAL_ERROR
294+
"MoorDyn submodule not found at extern/MoorDyn.\n"
295+
"Run: git submodule update --init extern/MoorDyn")
296+
endif()
297+
# MoorDyn's CMakeLists.txt uses CMAKE_SOURCE_DIR (which resolves to
298+
# HydroChrono's root) for its config template and CPack license.
299+
# Copy the files it expects so the configure step succeeds.
300+
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/MoorDynConfig.cmake")
301+
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/extern/MoorDyn/cmake/MoorDynConfig.cmake"
302+
DESTINATION "${CMAKE_SOURCE_DIR}/cmake/")
303+
endif()
304+
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/LICENSE.txt")
305+
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/extern/MoorDyn/LICENSE.txt"
306+
DESTINATION "${CMAKE_SOURCE_DIR}/")
307+
endif()
308+
309+
set(EXTERNAL_EIGEN OFF CACHE BOOL "Use MoorDyn bundled Eigen" FORCE)
310+
set(PYTHON_WRAPPER OFF CACHE BOOL "Disable MoorDyn Python wrapper" FORCE)
311+
set(FORTRAN_WRAPPER OFF CACHE BOOL "Disable MoorDyn Fortran wrapper" FORCE)
312+
set(MATLAB_WRAPPER OFF CACHE BOOL "Disable MoorDyn MATLAB wrapper" FORCE)
313+
set(RUST_WRAPPER OFF CACHE BOOL "Disable MoorDyn Rust wrapper" FORCE)
314+
set(BUILD_TESTING_SAVED ${BUILD_TESTING})
315+
set(BUILD_TESTING OFF)
316+
add_subdirectory(extern/MoorDyn)
317+
set(BUILD_TESTING ${BUILD_TESTING_SAVED})
318+
# MoorDyn is a shared library (DLL) -- its internal Eigen usage must not
319+
# leak into HydroChrono's include paths (HydroChrono uses Chrono's Eigen).
320+
set_target_properties(moordyn PROPERTIES
321+
INTERFACE_INCLUDE_DIRECTORIES ""
322+
)
323+
message(STATUS "MoorDyn mooring coupling: ENABLED")
324+
message(STATUS "----\n")
325+
endif()
243326

244327
# ===============================================================================
245328
# ----- Create HydroChrono configuration header ---------------------------------
@@ -261,6 +344,12 @@ else()
261344
set(HC_HAS_VSG "#undef HYDROCHRONO_HAVE_VSG")
262345
endif()
263346

347+
if(HYDROCHRONO_ENABLE_MOORDYN)
348+
set(HC_HAS_MOORDYN "#define HYDROCHRONO_HAVE_MOORDYN")
349+
else()
350+
set(HC_HAS_MOORDYN "#undef HYDROCHRONO_HAVE_MOORDYN")
351+
endif()
352+
264353
# For BUILD tree
265354
set(HC_DATA_DIR "#define HC_DATA_DIR \"${HC_BUILD_DATA}\"")
266355
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.h.in
@@ -317,13 +406,41 @@ set(HC_SOURCES
317406
src/hydro/logging/logging.cpp
318407
)
319408

409+
# MoorDyn mooring sources (conditionally compiled).
410+
# The wrapper is built as a separate OBJECT library so that MoorDyn's
411+
# bundled Eigen headers stay isolated and don't conflict with the
412+
# system/Chrono Eigen used by HydroChrono.
413+
if(HYDROCHRONO_ENABLE_MOORDYN)
414+
add_library(moordyn_bridge OBJECT
415+
src/hydro/mooring/moordyn_wrapper.cpp
416+
)
417+
target_include_directories(moordyn_bridge PRIVATE
418+
"${CMAKE_CURRENT_SOURCE_DIR}/extern/MoorDyn/source"
419+
"${CMAKE_CURRENT_SOURCE_DIR}"
420+
"${CMAKE_CURRENT_SOURCE_DIR}/include"
421+
)
422+
target_link_libraries(moordyn_bridge PRIVATE moordyn)
423+
target_compile_features(moordyn_bridge PUBLIC cxx_std_17)
424+
# Pick up HydroChrono's public headers (system_state.h, hydro_types.h)
425+
target_include_directories(moordyn_bridge PUBLIC
426+
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
427+
)
428+
# Chrono headers needed for Eigen (used by hydro_types.h)
429+
target_link_libraries(moordyn_bridge PUBLIC Chrono::Chrono_core)
430+
431+
list(APPEND HC_SOURCES
432+
src/hydro/force_components/mooring_component.cpp
433+
)
434+
endif()
435+
320436
set(HC_HEADERS
321437
${PROJECT_BINARY_DIR}/hydroc/config.h
322438
${PROJECT_BINARY_DIR}/hydroc/version.h
323439
include/hydroc/hydro_system.h
324440
include/hydroc/helper.h
325441
include/hydroc/logging.h
326442
include/hydroc/config/hydro_config.h
443+
include/hydroc/config/moordyn_config.h
327444
include/hydroc/core/force_component.h
328445
include/hydroc/core/hydro_forces.h
329446
include/hydroc/core/hydro_types.h
@@ -361,19 +478,23 @@ if(TARGET hdf5::hdf5-static)
361478
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_cpp-static)
362479
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_hl-static)
363480
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_hl_cpp-static)
364-
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_tools-static)
481+
if(TARGET hdf5::hdf5_tools-static)
482+
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_tools-static)
483+
endif()
365484
elseif(TARGET hdf5::hdf5-shared)
366485
message(STATUS "Linking shared libraries hdf5::hdf5-shared")
367486
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5-shared)
368487
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_cpp-shared)
369488
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_hl-shared)
370489
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_hl_cpp-shared)
371-
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_tools-shared)
372490
list(APPEND HDF5_TARGETS hdf5::hdf5-shared)
373491
list(APPEND HDF5_TARGETS hdf5::hdf5_cpp-shared)
374492
list(APPEND HDF5_TARGETS hdf5::hdf5_hl-shared)
375493
list(APPEND HDF5_TARGETS hdf5::hdf5_hl_cpp-shared)
376-
list(APPEND HDF5_TARGETS hdf5::hdf5_tools-shared)
494+
if(TARGET hdf5::hdf5_tools-shared)
495+
target_link_libraries(HydroChrono PUBLIC hdf5::hdf5_tools-shared)
496+
list(APPEND HDF5_TARGETS hdf5::hdf5_tools-shared)
497+
endif()
377498
elseif(TARGET HDF5::HDF5)
378499
message(STATUS "Linking HDF5::HDF5")
379500
target_link_libraries(HydroChrono PUBLIC HDF5::HDF5)
@@ -391,11 +512,21 @@ target_include_directories(HydroChrono
391512
${CMAKE_CURRENT_SOURCE_DIR}/include
392513
)
393514

515+
# Add MoorDyn (optional mooring coupling)
516+
if(HYDROCHRONO_ENABLE_MOORDYN AND TARGET moordyn)
517+
# Link moordyn import lib for DLL binding; moordyn_bridge provides
518+
# the compiled wrapper objects with MoorDyn headers isolated from HC.
519+
target_link_libraries(HydroChrono PRIVATE $<TARGET_LINKER_FILE:moordyn>)
520+
target_sources(HydroChrono PRIVATE $<TARGET_OBJECTS:moordyn_bridge>)
521+
target_compile_definitions(HydroChrono PUBLIC HYDROCHRONO_HAVE_MOORDYN)
522+
endif()
523+
394524
# Library-specific compile definitions
395525
target_compile_definitions(HydroChrono
396526
PUBLIC
397527
"HYDROCHRONO_VERSION=\"${PROJECT_VERSION}\""
398528
"HYDROCHRONO_BUILD_TYPE=\"$<IF:$<CONFIG:Debug>,Debug,$<IF:$<CONFIG:Release>,Release,$<IF:$<CONFIG:RelWithDebInfo>,RelWithDebInfo,$<IF:$<CONFIG:MinSizeRel>,MinSizeRel,Unknown>>>>\""
529+
$<$<NOT:$<BOOL:${CHRONO_HAS_LOAD_HYDRODYNAMICS}>>:HYDROCHRONO_USE_LEGACY_ADDED_MASS>
399530
PRIVATE
400531
$<$<BOOL:${HYDROCHRONO_ENABLE_LOGGING}>:HYDROCHRONO_ENABLE_LOGGING="1">
401532
)
@@ -425,6 +556,7 @@ if(HYDROCHRONO_ENABLE_VSG)
425556
src/gui/vsg_gui_component.cpp
426557
src/gui/vsg_lighting.cpp
427558
src/gui/vsg_materials.cpp
559+
src/gui/vsg_mooring_lines.cpp
428560
src/gui/vsg_water_surface.cpp
429561
src/gui/vsg_radiation_surface.cpp
430562
)
@@ -433,6 +565,7 @@ if(HYDROCHRONO_ENABLE_VSG)
433565
src/gui/vsg_gui_component.h
434566
src/gui/vsg_lighting.h
435567
src/gui/vsg_materials.h
568+
src/gui/vsg_mooring_lines.h
436569
src/gui/vsg_water_surface.h
437570
src/gui/vsg_radiation_surface.h
438571
)
@@ -515,6 +648,12 @@ function(configure_test_environment)
515648
list(APPEND DLL_DIRS ${VSG_DLL_DIR})
516649
endif()
517650

651+
# MoorDyn DLL path
652+
if(HYDROCHRONO_ENABLE_MOORDYN AND TARGET moordyn)
653+
list(APPEND DLL_DIRS "$<TARGET_FILE_DIR:moordyn>")
654+
message(STATUS " MoorDyn DLL directory: (via target)")
655+
endif()
656+
518657
set(TEST_ENVIRONMENT "PATH=${DLL_DIRS};$ENV{PATH}" PARENT_SCOPE)
519658
endfunction()
520659

@@ -547,6 +686,7 @@ if(HYDROCHRONO_ENABLE_TESTS)
547686
endif()
548687

549688
add_subdirectory(tests/regression)
689+
add_subdirectory(tests/verification)
550690
add_subdirectory(tests/unit)
551691
endif()
552692

@@ -715,6 +855,21 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
715855
endforeach()
716856
endif()
717857

858+
# MoorDyn DLL
859+
if(HYDROCHRONO_ENABLE_MOORDYN AND TARGET moordyn)
860+
install(TARGETS moordyn
861+
CONFIGURATIONS Release
862+
RUNTIME DESTINATION bin COMPONENT runtime)
863+
message(STATUS " MoorDyn DLL (via target)")
864+
endif()
865+
866+
endif()
867+
868+
# MoorDyn license bundling
869+
if(HYDROCHRONO_ENABLE_MOORDYN)
870+
install(FILES "${CMAKE_SOURCE_DIR}/extern/MoorDyn/LICENSE.txt"
871+
RENAME MoorDyn_LICENSE.txt
872+
DESTINATION licenses COMPONENT runtime)
718873
endif()
719874

720875
# Public, user-facing regression test suite only
@@ -750,6 +905,13 @@ if(MSVC AND OpenMP_CXX_FOUND)
750905
endif()
751906
endif()
752907
include(InstallRequiredSystemLibraries)
908+
909+
# Third-party license notices
910+
if(EXISTS "${CMAKE_SOURCE_DIR}/THIRD_PARTY_NOTICES.txt")
911+
install(FILES "${CMAKE_SOURCE_DIR}/THIRD_PARTY_NOTICES.txt"
912+
DESTINATION . COMPONENT runtime)
913+
endif()
914+
753915
set(CPACK_GENERATOR "ZIP")
754916
set(CPACK_PACKAGE_NAME "HydroChrono")
755917
set(CPACK_COMPONENTS_ALL runtime python-tests dev-demos)

THIRD_PARTY_NOTICES.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
2+
3+
HydroChrono incorporates third-party components under the licenses listed below.
4+
The original license files are included in the licenses/ directory of this
5+
distribution.
6+
7+
================================================================================
8+
MoorDyn v2.6.1
9+
https://github.com/FloatingArrayDesign/MoorDyn
10+
License: BSD 3-Clause
11+
License file: licenses/MoorDyn_LICENSE.txt
12+
13+
Copyright (c) 2022, Matt Hall
14+
All rights reserved.
15+
16+
Redistribution and use in source and binary forms, with or without modification,
17+
are permitted provided that the following conditions are met:
18+
19+
1. Redistributions of source code must retain the above copyright notice, this
20+
list of conditions and the following disclaimer.
21+
22+
2. Redistributions in binary form must reproduce the above copyright notice,
23+
this list of conditions and the following disclaimer in the documentation
24+
and/or other materials provided with the distribution.
25+
26+
3. Neither the name of the copyright holder nor the names of its contributors
27+
may be used to endorse or promote products derived from this software without
28+
specific prior written permission.
29+
30+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
31+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
33+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
34+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
35+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
36+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
37+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
38+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
39+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40+
================================================================================

0 commit comments

Comments
 (0)