Add GPU-aware functionObjects: FunctionObjectIO, Forces, ForceCoeffs#265
Add GPU-aware functionObjects: FunctionObjectIO, Forces, ForceCoeffs#265HendriceH wants to merge 13 commits into
Conversation
|
Deployed test documentation to https://exasim-project.com/NeoFOAM/Build_PR_265 |
1c6eb92 to
76360f4
Compare
greole
left a comment
There was a problem hiding this comment.
Some quick, comments, please make sure that the clang-tidy check also passes. And use scalar instead of double in case we need to run on SP hardware.
| NeoN::atomic_add(&accView[0], fp[0]); | ||
| NeoN::atomic_add(&accView[1], fp[1]); | ||
| NeoN::atomic_add(&accView[2], fp[2]); | ||
| NeoN::atomic_add(&accView[3], mp[0]); | ||
| NeoN::atomic_add(&accView[4], mp[1]); | ||
| NeoN::atomic_add(&accView[5], mp[2]); |
There was a problem hiding this comment.
This might be not ideal to have a bunch of atomics here. If this is a bottleneck, which i don't know, it migth make sense to split this loop into a map and a parallel reduce.
There was a problem hiding this comment.
I've run in for the 6.3M cells WindsorBody case and didn't see any performance impact. Should test for bigger cases.
| << "Available patches: " << pbm.names() | ||
| << Foam::abort(Foam::FatalError); | ||
| } | ||
| patchIndices_.push_back(static_cast<int>(idx)); |
There was a problem hiding this comment.
Is this loop only part of initializiation or called multiple times?
There was a problem hiding this comment.
The loop should not executed one meshAdapter_ is set. So only in initialisation.
There was a problem hiding this comment.
Pull request overview
This PR adds the first NeoFOAM GPU-aware OpenFOAM functionObject implementations (neoForces, neoForceCoeffs) plus shared I/O utilities and a DatabaseWrapper bridge so function objects can access the NeoN database via Foam::Time’s object registry.
Changes:
- Introduce
FunctionObjectIObase class for OpenFOAM-compatible postProcessing output management and formatting. - Add GPU pressure force/moment integration (
neoForces) and coefficient projection/normalisation (neoForceCoeffs) with RT selection table registration. - Add
DatabaseWrapperregistration increateAdapterRunTimeand new tests wired into the test suite.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
include/NeoFOAM/functionObjects/functionObjectIO.hpp |
Declares base class for persistent output streams and OpenFOAM-style formatting helpers. |
src/functionObjects/functionObjectIO.cpp |
Implements output directory selection, file creation/header-once, and formatting helpers. |
include/NeoFOAM/functionObjects/forces.hpp |
Declares neoForces API and documents intended behavior/output. |
src/functionObjects/forces.cpp |
Implements RTST registration, mesh adapter lookup, GPU patch reduction, and writes force.dat/moment.dat. |
include/NeoFOAM/functionObjects/forceCoeffs.hpp |
Declares neoForceCoeffs (extends Forces) and documents coefficient output. |
src/functionObjects/forceCoeffs.cpp |
Implements direction handling, coefficient computation, and writes coefficient.dat. |
include/NeoFOAM/datastructures/databaseWrapper.hpp |
Declares DatabaseWrapper for exposing NeoN::Database through Foam::Time registry. |
src/datastructures/databaseWrapper.cpp |
Implements wrapper registration key and constructor wiring. |
include/NeoFOAM/datastructures/runTime.hpp |
Extends RunTime to own the DatabaseWrapper for safe lifetime/registry ordering. |
src/auxiliary/setup.cpp |
Registers DatabaseWrapper during createAdapterRunTime and adjusts mesh registry handling. |
src/CMakeLists.txt |
Adds new functionObject and database wrapper sources to the library build. |
test/test_forceCoeffs.cpp |
Adds integration tests comparing NeoN GPU forces to OpenFOAM reference arithmetic. |
test/CMakeLists.txt |
Registers the new forceCoeffs unit test target. |
CHANGELOG.md |
Notes the addition of forceCoeffs/functionObject infrastructure in the unreleased changelog. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Foam::vector dragFoam = dict.getOrDefault<Foam::vector>("dragDir", Foam::vector(1, 0, 0)); | ||
| Foam::vector liftFoam = dict.getOrDefault<Foam::vector>("liftDir", Foam::vector(0, 0, 1)); | ||
| dragDir_ = NeoN::Vec3(dragFoam[0], dragFoam[1], dragFoam[2]); | ||
| liftDir_ = NeoN::Vec3(liftFoam[0], liftFoam[1], liftFoam[2]); | ||
|
|
There was a problem hiding this comment.
dragDir_/liftDir_ are treated as direction vectors but are not normalised. This makes coefficient magnitudes depend on the input vector lengths, and can also produce a non-unit sideDir_ from the cross product below. Consider normalising/validating these vectors (non-zero, non-parallel) and normalising sideDir_.
Introduces a base I/O class and two concrete GPU-accelerated function objects that integrate with OpenFOAM's controlDict RTST pattern: - FunctionObjectIO: abstract base inheriting Foam::functionObject, providing persistent postProcessing/<name>/<startTime>/ file I/O - Forces (neoForces): GPU kernel via parallelFor + atomic_add into a 6-element device buffer; only 96 bytes transferred to host per patch - ForceCoeffs (neoForceCoeffs): extends Forces with dynamic pressure normalisation (Cd = F / (0.5*rho*U²*Aref)) Also fixes createAdapterRunTime to check out any pre-registered plain fvMesh before creating the MeshAdapter, ensuring the adapter is findable via lookupObject in tests and in production solvers. Integration test (test_forceCoeffs) verifies GPU results match OpenFOAM reference for uniform pressure, random pressure, multiple patches, and coefficient normalisation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0a1d5c1 to
5f8d74b
Compare
Add GPU-aware
neoForcesandneoForceCoeffsfunction objectsSummary
This PR introduces the first NeoFOAM function objects:
neoForcesandneoForceCoeffs. Both are registered in OpenFOAM's runtime selection table and can be dropped into anycontrolDictfunctionsblock alongside standard OpenFOAM function objects. Their output files are format-compatible with OpenFOAM's built-inforcesandforceCoeffs, so existing post-processing scripts work unmodified.Architecture
FunctionObjectIO— base class for all NeoFOAM function objectsFunctionObjectIO(include/NeoFOAM/functionObjects/functionObjectIO.hpp) inheritsFoam::functionObjectand provides:postProcessing/<name>/<startTime>/, created on first write.getOrCreateFile(filename, headerCallback)opens the file once, calls a user-suppliedstd::function<void(std::ostream&)>to write the multi-line header on creation, and returns the persistentstd::ofstream&for all subsequent appends. No header-written flags needed by callers.Foam::functionObjects::writeFile:writeCurrentTime,writeHeader,writeHeaderValue,writeCommented,writeTabbed,writeVec3,fmtScalar,fmtVec3writePrecision = 6,charWidth = 14— scientific notation, fixed field width throughout.Pattern for future function objects: subclass
FunctionObjectIO, implementexecute()for GPU work andwrite()for I/O. CallgetOrCreateFilewith a lambda that writes the header once; use the formatting helpers for consistent output.DatabaseWrapper— bridgingNeoN::DatabaseandFoam::TimeNeoN::Databaselives insideNeoFOAM::RunTimeand is not directly reachable from within aFoam::functionObject, which only has access toconst Foam::Time&.DatabaseWrapper(include/NeoFOAM/datastructures/databaseWrapper.hpp) solves this by subclassingFoam::regIOobject, registering itself inFoam::Time's object registry atRunTimeconstruction, and storing a non-owning pointer toNeoN::Database. Function objects look it up viatime_.lookupObject<DatabaseWrapper>(DatabaseWrapper::registryName).Lifetime is safe:
RunTimeownsDatabaseWrapperviastd::unique_ptr; LIFO destruction ensuresDatabaseWrapperunregisters from theFoam::Timeregistry beforeFoam::Timeitself is destroyed.Pattern for future function objects: any function object that needs to reach the NeoN database follows the same const-safe lookup —
time_.foundObject<DatabaseWrapper>(...)+time_.lookupObject<DatabaseWrapper>(...).db().neoForcesRegistered as
type neoForcesincontrolDict.execute()— GPU pressure force/moment integral:NeoN::DatabaseviaDatabaseWrapperin theFoam::Timeregistry.fvcc::VectorCollection— the field registered there by the solver (e.g.neoIcoFoamviaconstructAndRegister). No field copy from OpenFOAM occurs here.NeoN::parallelForkernel over boundary patch faces, accumulatingF = ρ(p − p_ref)·Sfand the corresponding moment into a 6-element device buffer viaatomic_add.write()— two files matching OpenFOAM format exactly:force.dat:moment.dat— identical structure for moment components.neoForceCoeffsRegistered as
type neoForceCoeffsincontrolDict. ExtendsneoForces.New dict entries:
dragDir(default(1 0 0)) andliftDir(default(0 0 1)).sideDiris derived asliftDir × dragDir(right-hand system), mirroring OpenFOAM's coordinate system convention.execute()— after callingForces::execute(), projects the global force/moment vectors onto the three direction axes via dot product and normalises:Front/rear axle split (matches OpenFOAM exactly):
write()— writescoefficient.datonly (force/moment files are the responsibility ofneoForcesalone):12 scalar columns in alphabetical order, matching OpenFOAM's sorted coefficient map.
Tests
test/test_forceCoeffs.cppverifiesneoForcesagainst a pure OpenFOAM reference implementation (no NeoN) for:The NeoN GPU result must match the OpenFOAM scalar reduction to within
1e-10.Files changed
include/NeoFOAM/functionObjects/functionObjectIO.hpp/.cppinclude/NeoFOAM/functionObjects/forces.hpp/.cppforce.dat+moment.datinclude/NeoFOAM/functionObjects/forceCoeffs.hpp/.cppcoefficient.datinclude/NeoFOAM/datastructures/databaseWrapper.hpp/.cppFoam::regIOobjectwrapper exposingNeoN::Databaseto function objectsinclude/NeoFOAM/datastructures/runTime.hppstd::unique_ptr<DatabaseWrapper> dbWrappersrc/auxiliary/setup.cppDatabaseWrapperincreateAdapterRunTimetest/test_forceCoeffs.cpp