Skip to content

Commit fcb9865

Browse files
committed
Merge branch 'main' into feature/radiation-state-space
2 parents e87d652 + 9ed4224 commit fcb9865

115 files changed

Lines changed: 482729 additions & 50133 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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ build-config.json
6060
/tests/regression/run_hydrochrono/iea_sphere/decay/outputs
6161
/tests/regression/run_hydrochrono/oswec/decay/outputs
6262
/tests/regression/run_hydrochrono/rm3/decay/outputs
63+
/data/demos/run_hydrochrono/**/outputs

CHANGELOG.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [0.5.0] — 2026-02-16
9+
10+
### Added
11+
12+
- **VSG free-surface visualization** — animated water-surface mesh driven by the wave model, with per-vertex normals and configurable grid resolution (`vsg_water_surface`)
13+
- **Radiated wave overlay** — visualization of body-generated wave patterns layered on the free surface (`vsg_radiation_surface`)
14+
- **PBR ocean materials, scene lighting, and GUI stats component** — new modules `vsg_materials`, `vsg_lighting`, `vsg_gui_component`, `vsg_config`
15+
- **Wave model API extensions**`GetElevationGradientXY()` and `GetElevationForVisualization()` added to `WaveBase`, `RegularWave`, and `IrregularWave`
16+
- **Added-mass determinism unit test**`test_added_mass_determinism` verifies bit-identical added-mass assembly across independent trials and sweeps all Chrono solver types
17+
- **Regression tests produce HDF5 output** validated by external Python comparison scripts; tests now run fully headless (all GUI/visualization code stripped from test builds)
18+
- **CPack packaging** — project DLLs (`HydroChrono`, `HydroChronoGUI`) now included in the installer ZIP
19+
- **OpenMP runtime** (`vcomp140.dll`) explicitly installed for MSVC packages
20+
- **Chrono DLL collection** changed to glob all DLLs from Chrono's bin directory, capturing transitive VSG/yaml-cpp/draco dependencies
21+
22+
### Changed
23+
24+
- **Switched added-mass implementation to Chrono's built-in `ChLoadHydrodynamics`** — replaces HydroChrono's legacy `ChLoadAddedMass` (`ChLoadCustomMultiple`-based) as the default. The legacy implementation is retained in the codebase and can be re-activated by uncommenting `#define HYDROCHRONO_USE_LEGACY_ADDED_MASS` in `hydro_system.h`.
25+
- Default solver changed from GMRES to SPARSE_QR for most regression tests (faster, deterministic)
26+
- Irregular wave surface evaluation performance improved (parallelism and caching)
27+
- Regression test report generator now correctly parses CTest logs for PASS/FAIL status instead of assuming PASS when plots exist
28+
29+
### Fixed
30+
31+
- **YAML runner: `LoadSolverData` never called** — YAML structure mismatch prevented solver data from being loaded
32+
- **YAML runner: mesh file paths broken**`m_script_directory` was empty, breaking relative `model_file:` paths
33+
- **Regression report image paths** — report generator now computes correct relative paths to plot images instead of using hardcoded paths
34+
- OSWEC solver switched from SPARSE_QR to GMRES to prevent divergence (see Known Issues)
35+
- RM3 decay test fixes and cleanup
36+
- Sphere irregular wave test default arguments corrected
37+
- **Regular wave excitation phase indexing for multi-body systems**`RegularWave::GetForceAtTime()` used `excitation_force_phase_[rowEx]` instead of `excitation_force_phase_[body_offset + rowEx]`, causing body 2+ to use body 1's excitation phase. This affected the RM3 plate (and any second+ body) heave response in regular waves. Single-body models (e.g., sphere) were unaffected.
38+
39+
### Known Issues
40+
41+
- **OSWEC tests use GMRES solver.** OSWEC demos and tests use GMRES rather than SPARSE_QR. SPARSE_QR may work for OSWEC but has not yet been validated.
42+
- **PSOR solver cannot handle added-mass assembly.** The added-mass determinism unit test solver sweep reports that PSOR errors out because it cannot handle stiffness/damping matrices. All other swept solvers (SPARSE_QR, SPARSE_LU, MINRES, GMRES, BICGSTAB, BARZILAIBORWEIN, APGD) pass assembly.
43+
44+
## [0.4.0] — 2025
45+
46+
- YAML-driven CLI (`run_hydrochrono`) for running simulations from text-based configuration files
47+
- Cummins-equation time-domain solver with BEM hydrodynamic coefficients (BEMIO HDF5 format)
48+
- Multibody system support via Project Chrono (bodies, joints, actuators)
49+
- Irrlicht run-time visualization (optional)
50+
- HDF5 output for post-processing and validation
51+
- Regression test suite (IEA sphere, OSWEC, RM3, F3OF / DeepCWind)
52+
- Windows installer (ZIP) via CPack

CMakeLists.txt

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ endif()
2020
# ===============================================================================
2121

2222
project(HydroChrono
23-
VERSION 0.4.0
23+
VERSION 0.5.0
2424
DESCRIPTION "Hydrodynamics for Project Chrono."
2525
LANGUAGES CXX
2626
)
@@ -169,8 +169,7 @@ endif()
169169

170170
find_package(Chrono
171171
CONFIG REQUIRED
172-
COMPONENTS Parsers
173-
OPTIONAL_COMPONENTS Irrlicht VSG)
172+
COMPONENTS Parsers)
174173

175174
message(STATUS "----\n")
176175

@@ -306,6 +305,7 @@ set(HC_SOURCES
306305
src/hydro/radiation/radiation_ss_fitter.cpp
307306
src/hydro/core/hydro_forces.cpp
308307
# Chrono coupling layer (bridges Chrono physics with Chrono-free core)
308+
src/hydro/chrono/added_mass.cpp
309309
src/hydro/chrono/chrono_state_utils.cpp
310310
src/hydro/chrono/chrono_hydro_coupler.cpp
311311
src/hydro/force_components/hydrostatics_component.cpp
@@ -329,6 +329,7 @@ set(HC_HEADERS
329329
include/hydroc/core/hydro_forces.h
330330
include/hydroc/core/hydro_types.h
331331
include/hydroc/core/system_state.h
332+
include/hydroc/coupling/added_mass.h
332333
include/hydroc/coupling/chrono_coupler.h
333334
include/hydroc/io/h5_reader.h
334335
include/hydroc/io/h5_writer.h
@@ -422,6 +423,19 @@ endif()
422423
if(HYDROCHRONO_ENABLE_VSG)
423424
set(HCGUI_SOURCES ${HCGUI_SOURCES}
424425
src/gui/guihelperVSG.cpp
426+
src/gui/vsg_gui_component.cpp
427+
src/gui/vsg_lighting.cpp
428+
src/gui/vsg_materials.cpp
429+
src/gui/vsg_water_surface.cpp
430+
src/gui/vsg_radiation_surface.cpp
431+
)
432+
set(HCGUI_HEADERS ${HCGUI_HEADERS}
433+
src/gui/vsg_config.h
434+
src/gui/vsg_gui_component.h
435+
src/gui/vsg_lighting.h
436+
src/gui/vsg_materials.h
437+
src/gui/vsg_water_surface.h
438+
src/gui/vsg_radiation_surface.h
425439
)
426440
endif()
427441

@@ -534,6 +548,7 @@ if(HYDROCHRONO_ENABLE_TESTS)
534548
endif()
535549

536550
add_subdirectory(tests/regression)
551+
add_subdirectory(tests/unit)
537552
endif()
538553

539554
# ===============================================================================
@@ -609,27 +624,33 @@ endif()
609624
# Flat install tree for public distribution
610625
option(HC_INSTALL_DEV_DEMOS "Install developer demo executables" OFF)
611626

612-
# Install main CLI only (if built)
627+
# Install main CLI and project shared libraries (if built)
613628
if(TARGET run_hydrochrono)
614629
install(TARGETS run_hydrochrono CONFIGURATIONS Release DESTINATION bin COMPONENT runtime)
615630
endif()
631+
if(TARGET HydroChrono)
632+
install(TARGETS HydroChrono CONFIGURATIONS Release RUNTIME DESTINATION bin COMPONENT runtime)
633+
endif()
634+
if(TARGET HydroChronoGUI)
635+
install(TARGETS HydroChronoGUI CONFIGURATIONS Release RUNTIME DESTINATION bin COMPONENT runtime)
636+
endif()
616637

617638
# On Windows, install DLLs
618639
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
619640
message(STATUS "Set DLLs for installation")
620641

621-
# Chrono DLLs
622-
foreach(tgt ${CHRONO_TARGETS})
623-
get_target_property(tgt_DLL ${tgt} IMPORTED_LOCATION_RELEASE)
624-
get_target_property(tgt_DLL_d ${tgt} IMPORTED_LOCATION_DEBUG)
625-
if(EXISTS ${tgt_DLL})
626-
message(STATUS " Chrono DLL ${tgt_DLL}")
627-
install(FILES ${tgt_DLL} DESTINATION bin COMPONENT runtime)
628-
endif()
629-
if(EXISTS ${tgt_DLL_d})
630-
install(FILES ${tgt_DLL_d} DESTINATION bin COMPONENT runtime)
631-
endif()
632-
endforeach()
642+
# Chrono DLLs — glob ALL DLLs from the Chrono bin directory to catch
643+
# transitive dependencies (e.g., Chrono_fsitdpf_vsg, yaml-cpp) that may
644+
# not be listed in CHRONO_TARGETS.
645+
get_target_property(_chrono_core_dll Chrono::Chrono_core IMPORTED_LOCATION_RELEASE)
646+
if(_chrono_core_dll)
647+
get_filename_component(_chrono_dll_dir "${_chrono_core_dll}" DIRECTORY)
648+
file(GLOB _chrono_dlls "${_chrono_dll_dir}/*.dll")
649+
foreach(_dll ${_chrono_dlls})
650+
message(STATUS " Chrono DLL ${_dll}")
651+
install(FILES "${_dll}" DESTINATION bin COMPONENT runtime)
652+
endforeach()
653+
endif()
633654

634655
# HDF5 DLLs
635656
foreach(tgt ${HDF5_TARGETS})
@@ -658,24 +679,42 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
658679
endif()
659680
endif()
660681

661-
# Python DLLs, if Chrono::Parsers depends on it
662-
if(CHRONO_PARSERS_PYTHON)
663-
find_package(Python3 QUIET COMPONENTS Interpreter Development)
664-
if(Python3_Interpreter_FOUND AND Python3_Development_FOUND)
665-
#message(STATUS "Found Python and dependencies")
666-
#message(STATUS " Python3_Interpreter_FOUND: ${Python3_Interpreter_FOUND}")
667-
#message(STATUS " Python3_Development_FOUND: ${Python3_Development_FOUND}")
668-
get_target_property(tgt_DLL Python3::Python IMPORTED_LOCATION_RELEASE)
669-
get_target_property(tgt_DLL_d Python3::Python IMPORTED_LOCATION_DEBUG)
670-
if(EXISTS ${tgt_DLL})
671-
message(STATUS " Python DLL ${tgt_DLL}")
672-
install(FILES ${tgt_DLL} DESTINATION bin COMPONENT runtime)
682+
# VSG DLLs (vsg, vsgImGui, vsgXchange, draco, etc.)
683+
if(HYDROCHRONO_ENABLE_VSG)
684+
# VSG_DLL_DIR is set by Chrono's find_package when VSG is found
685+
# Fall back to deriving from vsg_DIR if VSG_DLL_DIR is not set
686+
if(NOT DEFINED VSG_DLL_DIR OR VSG_DLL_DIR STREQUAL "")
687+
if(DEFINED vsg_DIR)
688+
# vsg_DIR points to lib/cmake/vsg, go up 3 levels to root then into bin
689+
get_filename_component(_vsg_root "${vsg_DIR}" DIRECTORY) # lib/cmake
690+
get_filename_component(_vsg_root "${_vsg_root}" DIRECTORY) # lib
691+
get_filename_component(_vsg_root "${_vsg_root}" DIRECTORY) # root
692+
set(VSG_DLL_DIR "${_vsg_root}/bin")
673693
endif()
674-
if(EXISTS ${tgt_DLL_d})
675-
install(FILES ${tgt_DLL_d} DESTINATION bin COMPONENT runtime)
694+
endif()
695+
696+
if(DEFINED VSG_DLL_DIR AND EXISTS "${VSG_DLL_DIR}")
697+
file(GLOB _vsg_dlls "${VSG_DLL_DIR}/*.dll")
698+
if(_vsg_dlls)
699+
message(STATUS "Installing VSG DLLs from: ${VSG_DLL_DIR}")
700+
install(FILES ${_vsg_dlls} DESTINATION bin COMPONENT runtime)
676701
endif()
702+
else()
703+
message(WARNING "HYDROCHRONO_ENABLE_VSG is ON but VSG_DLL_DIR not found. VSG DLLs will not be packaged.")
677704
endif()
678705
endif()
706+
707+
# Python DLLs, if Chrono::Parsers depends on it.
708+
# Derive the DLL path from the already-found interpreter rather than calling
709+
# find_package(Python3 ... Development), which can stall on Windows/conda.
710+
if(CHRONO_PARSERS_PYTHON AND Python3_EXECUTABLE)
711+
get_filename_component(_py_bin_dir "${Python3_EXECUTABLE}" DIRECTORY)
712+
file(GLOB _py_dlls "${_py_bin_dir}/python3*.dll")
713+
foreach(_py_dll ${_py_dlls})
714+
message(STATUS " Python DLL ${_py_dll}")
715+
install(FILES "${_py_dll}" DESTINATION bin COMPONENT runtime)
716+
endforeach()
717+
endif()
679718

680719
endif()
681720

@@ -697,6 +736,20 @@ install(PROGRAMS ${PROJECT_SOURCE_DIR}/scripts/RUN-TESTS.ps1
697736
DESTINATION tests COMPONENT python-tests)
698737

699738
# MSVC runtime DLLs and ZIP packaging via CPack
739+
# InstallRequiredSystemLibraries handles msvcp/vcruntime/ucrt, but not vcomp (OpenMP).
740+
# Append the OpenMP runtime so it's included in the package.
741+
if(MSVC AND OpenMP_CXX_FOUND)
742+
get_filename_component(_msvc_dir "${CMAKE_CXX_COMPILER}" DIRECTORY)
743+
find_file(_vcomp_dll "vcomp140.dll"
744+
PATHS "${_msvc_dir}" "${_msvc_dir}/../../../redist/x64" "C:/Windows/System32"
745+
NO_DEFAULT_PATH)
746+
if(_vcomp_dll)
747+
list(APPEND CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS "${_vcomp_dll}")
748+
message(STATUS " OpenMP DLL ${_vcomp_dll}")
749+
else()
750+
message(WARNING "vcomp140.dll not found — OpenMP runtime will be missing from package")
751+
endif()
752+
endif()
700753
include(InstallRequiredSystemLibraries)
701754
set(CPACK_GENERATOR "ZIP")
702755
set(CPACK_PACKAGE_NAME "HydroChrono")

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
<p align="center">
33
<img src="docs/assets/img/hydrochrono_banner.png" />
44
<br/>
5-
<a href="https://github.com/NREL/HydroChrono/releases"><img src="https://img.shields.io/badge/version-v0.4-blue.svg" /></a>
5+
<a href="https://github.com/NREL/HydroChrono/releases"><img src="https://img.shields.io/badge/version-v0.5-blue.svg" /></a>
66
<a href="#"><img src="https://img.shields.io/badge/status-Prototype-orange.svg" /></a>
77
</p>
88

9-
> ⚠️ HydroChrono is under active development (`v0.4` prototype). This early release focuses on a YAML‑driven CLI and portable HDF5 outputs so you can try the code and share feedback. Expect rapid iteration over the coming year (inc. more advanced PTO, control, mooring & hydrodynamics) — please get in touch if you have any issues or feature requests.
9+
> ⚠️ HydroChrono is under active development (`v0.5` prototype). This early release focuses on a YAML‑driven CLI, real-time VSG visualization with animated free-surface rendering, and portable HDF5 outputs so you can try the code and share feedback. Expect rapid iteration over the coming year (inc. more advanced PTO, control, mooring & hydrodynamics) — please get in touch if you have any issues or feature requests.
1010
1111

1212
HydroChrono (Hydrodynamics for Project Chrono) is a hydrodynamics simulation toolkit built on [Project Chrono](https://projectchrono.org/). It is designed for simulating wave energy converters (WECs) and other complex ocean systems, and is **100% free and open‑source** end‑to‑end — no proprietary dependencies required. This repo ships a prototype, YAML‑driven CLI app for running simulations and exporting portable results.
@@ -20,6 +20,7 @@ HydroChrono (Hydrodynamics for Project Chrono) is a hydrodynamics simulation too
2020
- Uses Boundary Element Method (BEM) hydrodynamic coefficients (e.g., from [Capytaine](https://github.com/capytaine/capytaine)) to describe added mass, radiation damping, and wave excitation. HydroChrono reads these coefficients from BEMIO‑format HDF5 (.h5) files, an approach familiar to [WEC‑Sim](https://github.com/WEC-Sim/WEC-Sim) users.
2121
- Runs time‑domain simulations via the Cummins equation (impulse‑response/convolution form for radiation effects).
2222
- Builds complex, nonlinear multibody systems using Project Chrono; hydrodynamic loads are currently first‑order, with expanded models planned.
23+
- **Real-time VSG visualization** with animated free-surface rendering driven by the wave model, radiated wave overlays, PBR ocean materials, and interactive GUI stats (new in v0.5).
2324
- Supports a YAML-driven CLI, with logging, GUI and exports portable results to HDF5 for post‑processing and validation.
2425

2526
## Download
@@ -61,7 +62,7 @@ Tips: use `--quiet` to reduce logs, `--debug`/`--trace` for deeper diagnostics.
6162

6263
### GUI Example
6364

64-
Use the GUI to visually inspect the assembled multibody system (bodies, joints, actuators) and verify that YAML inputs are wired correctly. Use the `--nogui` option to disable visualization straight from CLI, or change the settings in the `*.simulation.yaml` file.
65+
Use the GUI to visually inspect the assembled multibody system (bodies, joints, actuators) and verify that YAML inputs are wired correctly. With VSG enabled, the visualization includes an animated free-surface mesh and radiated wave overlays driven by the live wave model. Use the `--nogui` option to disable visualization straight from CLI, or change the settings in the `*.simulation.yaml` file.
6566

6667
<p align="center"><img src="docs/assets/img/gui_example.png" width="40%" /></p>
6768

build.ps1

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<#
1+
<#
22
HydroChrono Build Script
33
44
Configures and builds HydroChrono. Dependency paths (Eigen, HDF5, Irrlicht)
@@ -71,6 +71,10 @@ if ($Help) {
7171
Write-Host " .\build.ps1 -Clean -Verbose # Clean build with details"
7272
Write-Host " .\build.ps1 -Package # Build and create ZIP`n"
7373

74+
Write-Host "LONG TESTS:" -ForegroundColor Yellow
75+
Write-Host " Set `$env:HYDROCHRONO_LONG_TESTS='1' before running ctest to use extended"
76+
Write-Host " simulation durations for publication-quality regression reports.`n"
77+
7478
Write-Host "CONFIG FILE:" -ForegroundColor Yellow
7579
Write-Host ' { "ChronoDir": "C:/path/to/chrono/build/cmake" }' -ForegroundColor Gray
7680
Write-Host ""
@@ -250,7 +254,7 @@ if ($LASTEXITCODE -ne 0) {
250254
Write-OK "Built in $buildSecs seconds"
251255

252256
# =============================================================================
253-
# Copy Third-Party DLLs (Irrlicht - not handled by Chrono's DLL copy command)
257+
# Copy Third-Party DLLs (not handled by Chrono's DLL copy command)
254258
# =============================================================================
255259

256260
$binPath = ".\build\bin\$BuildType"
@@ -269,6 +273,43 @@ if ($useIrrlicht) {
269273
}
270274
}
271275

276+
if ($useVSG) {
277+
# Get VSG DLL directory from Chrono config (set by find_package(vsg))
278+
# VSG_DLL_DIR is set by Chrono when it finds VSG
279+
$vsgDllDir = $null
280+
281+
if ($chronoContent -match 'VSG_DLL_DIR\s+([^\s\)]+)') {
282+
$vsgDllDir = $Matches[1].Trim('"')
283+
}
284+
285+
# Fallback: derive from vsg_DIR (lib/cmake/vsg -> bin)
286+
if (-not $vsgDllDir -or -not (Test-Path $vsgDllDir)) {
287+
if ($chronoContent -match 'vsg_DIR\s+"([^"]+)"') {
288+
$vsgCmakeDir = $Matches[1]
289+
# Go up from lib/cmake/vsg to root, then into bin
290+
$vsgRoot = Split-Path (Split-Path (Split-Path $vsgCmakeDir))
291+
$vsgDllDir = Join-Path $vsgRoot "bin"
292+
}
293+
}
294+
295+
if ($vsgDllDir -and (Test-Path $vsgDllDir)) {
296+
$vsgDlls = Get-ChildItem -Path $vsgDllDir -Filter "*.dll" -ErrorAction SilentlyContinue
297+
$copiedCount = 0
298+
foreach ($dll in $vsgDlls) {
299+
$destDll = Join-Path $binPath $dll.Name
300+
if (-not (Test-Path $destDll)) {
301+
Copy-Item $dll.FullName $destDll -Force
302+
$copiedCount++
303+
}
304+
}
305+
if ($copiedCount -gt 0) {
306+
Write-OK "Copied $copiedCount VSG DLLs from $vsgDllDir"
307+
}
308+
} else {
309+
Write-Warn "VSG enabled but could not find VSG DLL directory"
310+
}
311+
}
312+
272313
# =============================================================================
273314
# Verify
274315
# =============================================================================
@@ -324,5 +365,16 @@ Write-Host "BUILD SUCCESSFUL" -ForegroundColor Green
324365
Write-Host "========================================`n" -ForegroundColor Green
325366

326367
Write-Host "Output: $binPath" -ForegroundColor Cyan
368+
Write-Host ""
327369
Write-Host "Tests: ctest -C $BuildType -L regression --test-dir build" -ForegroundColor Gray
370+
Write-Host " ctest -C $BuildType -L unit --test-dir build" -ForegroundColor Gray
371+
Write-Host " Add -V for verbose output, --output-on-failure for failures only" -ForegroundColor DarkGray
372+
Write-Host ""
373+
Write-Host "Long: `$env:HYDROCHRONO_LONG_TESTS='1'" -ForegroundColor Gray
374+
Write-Host " ctest -C $BuildType -L regression --test-dir build" -ForegroundColor Gray
375+
Write-Host " Runs with extended simulation durations" -ForegroundColor DarkGray
376+
Write-Host ""
377+
Write-Host "Report: python tests/regression/utilities/generate_report.py --build-dir build --pdf" -ForegroundColor Gray
378+
Write-Host " Generates regression test report (markdown + PDF) in build/bin/report/" -ForegroundColor DarkGray
379+
Write-Host " Requires: pip install pypandoc (or pandoc on PATH)" -ForegroundColor DarkGray
328380
Write-Host ""
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-22.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)