AD: fix OLAF treecode near-core regularization floor and TwrInfl OpenMP data race - #3430
Conversation
…MP data race FVW treecode: the wake path used Tree%DistanceDirect = 2*mean(RegParam), averaged over an over-allocated array whose sentinel tail drove it negative during wake buildup, disabling the near-core direct-evaluation fallback. Replace it with a per-node maxRegParam (max eps over the cell's particles); each branch now uses distDirect = BranchFactor*radius + 2*maxRegParam so control points inside a regularization core fall back to direct evaluation. Applied to both the particle and segment trees. TwrInflArray: make FirstWarn_TowerStrike firstprivate and ErrStat2/ErrMsg2 private in the OpenMP loop to avoid a data race. Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus <noreply@anthropic.com>
In ui_part_nograd_11 the idRegExp mollifier (1-exp(-(r/rc)^3))/r^3 differs from 1/r^3 by <exp(-8)~3.4e-4 once r>2*rc, which is exactly the accuracy the far-field multipole already accepts at its BranchFactor*radius+2*maxRegParam floor. Treat the mollifier as 1 beyond that boundary (new PART_REG_NRAD/PART_REG_CUT3 params) so the near-field direct kernel and the far-field multipole share the same 2*rc cutoff. Since most near-field tree pairs have r>>rc, this skips exp() for the majority of evaluations, giving ~25-32 percent serial speedup on the treecode path with the output unchanged to ~1.8e-5 relative. Also caches r^2/r^3/rc^3 to drop redundant ** intrinsics; the compact-support branch is refactored identically (bit-for-bit). FVW_Subs: scope the DEV_VERSION NaN/sentinel checks in SegmentsToPartWrap to the active particles (1:nPart); the preallocated tail intentionally keeps its sentinel and must not trip the check. Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adjusts OLAF/FVW induced-velocity computation to make near-core/direct-evaluation behavior robust during wake buildup, removes an OpenMP data race in tower-influence calculations, and optimizes the particle exponential-regularization kernel to reduce expensive exp() calls while keeping results within the stated tolerance.
Changes:
- Replace the global tree “direct-eval distance” floor (based on a mean regularization parameter) with a per-node
maxRegParamfloor used during traversal for both particle and segment trees. - Fix an OpenMP data race in
TwrInflArrayby making loop-local state (FirstWarn_TowerStrike,ErrStat2/ErrMsg2) thread-safe. - Optimize
ui_part_nograd_11by reusingr^2/r^3/rc^3and skipping the exponential mollifier computation forr > 2*rc.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| modules/aerodyn/src/FVW_VortexTools.f90 | Adds per-node maxRegParam and applies it in tree traversal to enforce a near-core direct-evaluation floor. |
| modules/aerodyn/src/FVW_Subs.f90 | Restricts DEV NaN/sentinel checks to active particle ranges to avoid false negatives from preallocated tails. |
| modules/aerodyn/src/FVW_BiotSavart.f90 | Speeds up particle exponential regularization by sharing the same 2*rc cutoff as the far-field floor and caching powers of r/rc. |
| modules/aerodyn/src/AeroDyn.f90 | Removes an OpenMP data race in TwrInflArray via firstprivate/private scoping. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
To better understand the implications of this modification, I took the OLAF model from OC7 Phase III WP 3.1 and ran it with the original code and the proposed code. The proposed modifications do not change the rotor loading: However, there is a significant impact on the wake behavior. The OLAF model tested uses 3,400 near-wake panels (all of them free). No far-wake is included in the model. Before the fix, the Before the fix ( Affter the fix ( As can be observed, the behavior is very different. Before the proposed changes, we can observe highly unstable flow in the wake (despite using This can also be observed in the vertical plane at different downstream distances. The wake deficit shown below corresponds to an averaged timeframe of 10 s (from 20 to 30 s). It's also important to note that after the proposed fix, the computational time increases for higher Finally, this modification does not improve the issue reported here: #3429 |
|
@RBergua Thanks for sharing the results! Regarding the increased computing time, I'm looking at implementing an alternative particle regularization function with true compact support. Right now, for particle/particle tree, OLAF either uses no regularization or exponential regularization. The latter is quite expensive. There is already a particle regularization function called |
|
A good check is to compare the wake shapes between VelocityMethod =2 and 3 (particles only). The "best reference should be VelocityMethod=1 (segment N^2), but there is no one-to-one equivalence between the regularization of segments and particles. A rough overview of the wake shape for those three cases would be informative. |
|
I agree with the changes, the mean(eps) previously introduced was indeed a temporary hack introduced as a compromise. As a quick workaround, we should probably introduce a maximum reg param (either as part of the core model, or part of the tree model). |
|
Hi @ebranlard, thanks for reviewing the changes. Yes, we can remove Regarding the increased computing time, it looks like the compact particle regularization (PR #3457) is helping somewhat. The same run that took >5 days is on track to complete in just over 3 days (won't get exact time until it's done). Max regularization param is a good idea. I think it should be part of the core model. Enforcing that in the tree code could cause discontinuous velocity field, and we are back to the same problem. Also, it would be better to have a dynamic bound based on minimum core overlap ratio to ensure wake smoothness. |
…d dispatch to select case Remove always-zero T_Tree%DistanceDirect field and its assignments; the two wake-path reads now pass 0.0_ReKi literally (regularization floor is applied per node via node%maxRegParam). Convert the if/elseif velocity-method dispatch in FVW_InitRegularization, InducedVelocitiesAll_Init/Calc/End and LiftingLineInducedVelocities to select case, adding case default fatal for unhandled methods. Blade-path DistanceDirect (MaxWingLength*2.2) is unchanged. Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Claude Opus <noreply@anthropic.com>





Feature or improvement description
FVW treecode: the wake path used
Tree%DistanceDirect = 2*mean(RegParam), averaged over an over-allocated array whose sentinel tail drove it negative during wake buildup, disabling the near-core direct-evaluation fallback. Replace it with a per-nodemaxRegParam(max eps over the cell's particles); each branch now usesdistDirect = BranchFactor*radius + 2*maxRegParamso control points inside a regularization core fall back to direct evaluation. Applied to both the particle and segment trees.TwrInflArray: makeFirstWarn_TowerStrikefirstprivateandErrStat2/ErrMsg2private in the OpenMP loop to avoid a data race.In
ui_part_nograd_11, theidRegExpmollifier(1-exp(-(r/rc)^3))/r^3differs from1/r^3by<exp(-8)~3.4e-4oncer>2*rc, which is exactly the accuracy the far-field multipole already accepts at itsBranchFactor*radius+2*maxRegParamfloor. Treat the mollifier as 1 beyond that boundary (newPART_REG_NRAD/PART_REG_CUT3params) so the near-field direct kernel and the far-field multipole share the same2*rccutoff. Since most near-field tree pairs haver>>rc, this skipsexp()for the majority of evaluations, giving ~25-32 percent serial speedup on the treecode path with the output unchanged to~1.8e-5relative. Also cachesr^2/r^3/rc^3to drop redundant calculations; the compact-support branch is refactored identically (bit-for-bit).Potential impact
This bug fix can potentially generate better behaved rotor wake. Below are two snapshots of OLAF wake generated from the same AeroDyn driver/OLAF case provided by @RBergua. The simulation uses steady wind and all free near-wake panels. There are no frozen wake or far-wake panels. The two images show the same time instant. The only difference is the regularization fix.
Steady wind; without regularization fix

Steady wind; with regularization fix

Notice that the wake is significantly more ordered with the regularization fix. This is expected with the large
CoreSpreadEddyVisc=1000and age-based core spreading. At the same time, the simulation with regularization fix is considerably slower; however, this is expected because the large core radii, especially with the older wake elements further downstream, force the code to resort to the costly direct and exact evaluation of induction velocity more often to properly account for core regularization. Previously, the incorrect regularization floor effectively disables near-core direct-evaluation fallback in many cases, resulting in faster simulation but highly disordered wake. To speed up the simulation with the regularization fix, we can reduceCoreSpreadEddyViscas appropriate.Also, note that the two simulations provide very similar rotor loads. However, the core-regularization fix can be important if the far wake is of interest of if simulating multiple tandem rotors.
Two snapshots of the wake with turbulent wind are shown below. The turbulent inflow makes the wake far less organized. Nevertheless, the wake with regularization fix shows more structure than the one without.
Turbulent wind; without regularization fix

Turbulent wind; with regularization fix

The instantaneous wake streamwise velocity computed with the direct particle method and the particle tree method is compared below. The agreement is fairly good.

Related issue, if one exists
There are several reported issues with OLAF. Unclear if this fix addresses any of them.
Impacted areas of the software
OLAF
Generative AI usage
Co-authored-by: Microsoft Copilot copilot@microsoft.com
Co-authored-by: Anthropic Claude claude@anthropic.com
Test results, if applicable
One r-test needs rebasing:
ad_BAR_OLAF. Only two output channels did not pass the threshold, but the changes are small, barely exceeding the threshold.