VirtualFluids 0.2.0
Parallel CFD LBM Solver
Loading...
Searching...
No Matches
ActuatorFarm.cu
Go to the documentation of this file.
1//=======================================================================================
2// ____ ____ __ ______ __________ __ __ __ __
3// \ \ | | | | | _ \ |___ ___| | | | | / \ | |
4// \ \ | | | | | |_) | | | | | | | / \ | |
5// \ \ | | | | | _ / | | | | | | / /\ \ | |
6// \ \ | | | | | | \ \ | | | \__/ | / ____ \ | |____
7// \ \ | | |__| |__| \__\ |__| \________/ /__/ \__\ |_______|
8// \ \ | | ________________________________________________________________
9// \ \ | | | ______________________________________________________________|
10// \ \| | | | __ __ __ __ ______ _______
11// \ | | |_____ | | | | | | | | | _ \ / _____)
12// \ | | _____| | | | | | | | | | | \ \ \_______
13// \ | | | | |_____ | \_/ | | | | |_/ / _____ |
14// \ _____| |__| |________| \_______/ |__| |______/ (_______/
15//
16// This file is part of VirtualFluids. VirtualFluids is free software: you can
17// redistribute it and/or modify it under the terms of the GNU General Public
18// License as published by the Free Software Foundation, either version 3 of
19// the License, or (at your option) any later version.
20//
21// VirtualFluids is distributed in the hope that it will be useful, but WITHOUT
22// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24// for more details.
25//
26// SPDX-License-Identifier: GPL-3.0-or-later
27// SPDX-FileCopyrightText: Copyright © VirtualFluids Project contributors, see AUTHORS.md in root folder
28//
29//! \addtogroup gpu_PreCollisionInteractor PreCollisionInteractor
30//! \ingroup gpu_core core
31//! \{
32//! \author Henrik Asmuth, Henry Korb
33//======================================================================================
34#include "ActuatorFarm.h"
35#include "ActuatorFarmInlines.h"
36
37#include <algorithm>
38#include <cmath>
39#include <cuda.h>
40#include <cuda_runtime.h>
41#include <helper_cuda.h>
42#include <limits>
43
44#include <basics/constants/NumericConstants.h>
45#include <basics/writer/WbWriterVtkXmlBinary.h>
46#include <cuda_helper/CudaGrid.h>
47#include <logger/Logger.h>
48
49#include "Cuda/CudaMemoryManager.h"
50#include "Cuda/CudaStreamManager.h"
51#include "DataStructureInitializer/GridProvider.h"
52#include "Parameter/Parameter.h"
53#include "Utilities/GeometryUtils.h"
54#include "Utilities/KernelUtilities.h"
55#include "cuda_helper/CudaIndexCalculation.h"
56
57using namespace vf::basics::constant;
58namespace vf::gpu
59{
60
61struct GridData
62{
63 const uint* indices;
64 const uint nIndices;
65 const real *coordsX, *coordsY, *coordsZ;
66 const uint *neighborsX, *neighborsY, *neighborsZ, *neighborsWSB;
67 const real *vx, *vy, *vz;
68 real *fx, *fy, *fz;
69 const real deltaX, velocityRatio, forceRatio;
70};
71
72struct TurbineData
73{
74 const real *posX, *posY, *posZ;
75 const uint numberOfTurbines;
76 const real smearingWidth;
77 const real floatingAmplitude;
78};
79
80struct ComponentData
81{
82 const real referenceLength;
83 const real rotorBoundingMargin;
84 const uint numberOfBladePointsPerTurbine;
85 const uint totalNumberOfPoints;
86 const real *coordsX, *coordsY, *coordsZ;
87 real *velocitiesX, *velocitiesY, *velocitiesZ;
88 const real *forcesX, *forcesY, *forcesZ;
89 uint* gridIndices;
90 const uint numberOfHubPointsPerTurbine;
91 const real hubRadius, hubLength, hubPositionOffset;
92 const uint numberOfTowerPointsPerTurbine;
93 const real towerRadius, towerOffset, maxTowerHeight;
94 const bool useVAWTVolume;
95 const real vawtRotorHeight;
96 const bool flagLocalSmearingWidth;
97 const real* localSmearingWidth;
98};
99
100__global__ void interpolateVelocities(const GridData gridData, ComponentData componentData)
101{
102 const uint pointIndex = vf::cuda::get1DIndexFrom2DBlock();
103
104 if (pointIndex >= componentData.totalNumberOfPoints)
105 return;
106
107 const real coordX = componentData.coordsX[pointIndex];
108 const real coordY = componentData.coordsY[pointIndex];
109 const real coordZ = componentData.coordsZ[pointIndex];
110
111 const uint kMMM = findNearestCellBSW(componentData.gridIndices[pointIndex], gridData.coordsX, gridData.coordsY,
112 gridData.coordsZ, coordX, coordY, coordZ, gridData.neighborsX, gridData.neighborsY,
113 gridData.neighborsZ, gridData.neighborsWSB);
114
115 componentData.gridIndices[pointIndex] = kMMM;
116
117 uint kPMM, kMPM, kMMP, kPPM, kPMP, kMPP, kPPP;
118 getNeighborIndicesOfBSW(kMMM, kPMM, kMPM, kMMP, kPPM, kPMP, kMPP, kPPP, gridData.neighborsX, gridData.neighborsY,
119 gridData.neighborsZ);
120
121 const real distX = (coordX - gridData.coordsX[kMMM]) / gridData.deltaX;
122 const real distY = (coordY - gridData.coordsY[kMMM]) / gridData.deltaX;
123 const real distZ = (coordZ - gridData.coordsZ[kMMM]) / gridData.deltaX;
124
125 componentData.velocitiesX[pointIndex] =
126 trilinearInterpolation(distX, distY, distZ, kMMM, kPMM, kMPM, kMMP, kPPM, kPMP, kMPP, kPPP, gridData.vx) *
127 gridData.velocityRatio;
128 componentData.velocitiesY[pointIndex] =
129 trilinearInterpolation(distX, distY, distZ, kMMM, kPMM, kMPM, kMMP, kPPM, kPMP, kMPP, kPPP, gridData.vy) *
130 gridData.velocityRatio;
131 componentData.velocitiesZ[pointIndex] =
132 trilinearInterpolation(distX, distY, distZ, kMMM, kPMM, kMPM, kMMP, kPPM, kPMP, kMPP, kPPP, gridData.vz) *
133 gridData.velocityRatio;
134}
135
136template <bool UseLocalSmearingWidth>
137__device__ void accumulateSmearingForces(const ComponentData& componentData, real gridCoordX, real gridCoordY,
138 real gridCoordZ, uint startIndex, uint count, real smearingWidth, real& gridForceX,
139 real& gridForceY, real& gridForceZ)
140{
141 for (uint i = 0; i < count; i++) {
142 const uint node = startIndex + i;
143 const real distX = componentData.coordsX[node] - gridCoordX;
144 const real distY = componentData.coordsY[node] - gridCoordY;
145 const real distZ = componentData.coordsZ[node] - gridCoordZ;
146
147 if constexpr (UseLocalSmearingWidth)
148 smearingWidth = componentData.localSmearingWidth[node];
149
150 const real eta = gaussianSmearing(distX, distY, distZ, smearingWidth);
151 gridForceX += componentData.forcesX[node] * eta;
152 gridForceY += componentData.forcesY[node] * eta;
153 gridForceZ += componentData.forcesZ[node] * eta;
154 }
155}
156
157template <bool UseVAWT, bool HasHub, bool HasTower, bool UseLocalSmearingWidth>
158__global__ void applyBodyForces(GridData gridData, const TurbineData turbineData, const ComponentData componentData)
159{
160 const uint index = vf::cuda::get1DIndexFrom2DBlock();
161
162 if (index >= gridData.nIndices)
163 return;
164
165 const uint gridIndex = gridData.indices[index];
166 const real gridCoordX = gridData.coordsX[gridIndex];
167 const real gridCoordY = gridData.coordsY[gridIndex];
168 const real gridCoordZ = gridData.coordsZ[gridIndex];
169
170 real gridForceX = c0o1;
171 real gridForceY = c0o1;
172 real gridForceZ = c0o1;
173
174 const uint bladePointsTotal = componentData.numberOfBladePointsPerTurbine * turbineData.numberOfTurbines;
175
176 for (uint turbine = 0; turbine < turbineData.numberOfTurbines; turbine++) {
177
178 const real turbinePosX = turbineData.posX[turbine];
179 const real turbinePosY = turbineData.posY[turbine];
180 const real turbinePosZ = turbineData.posZ[turbine];
181
182 // --- Rotor bounding-volume check ---------------------
183 bool isInRotorVolume;
184 if constexpr (UseVAWT) {
185 isInRotorVolume = inCylinderVolume<z, true>(
186 gridCoordX, gridCoordY, gridCoordZ, turbinePosX, turbinePosY, turbinePosZ, componentData.vawtRotorHeight,
187 c1o2 * componentData.referenceLength, componentData.rotorBoundingMargin);
188 } else {
189 const real distToHubX = gridCoordX - turbinePosX;
190 const real distToHubY = gridCoordY - turbinePosY;
191 const real distToHubZ = gridCoordZ - turbinePosZ;
192 isInRotorVolume =
193 inSphereVolume(distToHubX, distToHubY, distToHubZ, componentData.referenceLength, turbineData.smearingWidth);
194 }
195
196 // --- Blade forces ---------------------------------
197 if (isInRotorVolume)
198 accumulateSmearingForces<UseLocalSmearingWidth>(
199 componentData, gridCoordX, gridCoordY, gridCoordZ, turbine * componentData.numberOfBladePointsPerTurbine,
200 componentData.numberOfBladePointsPerTurbine, turbineData.smearingWidth, gridForceX, gridForceY, gridForceZ);
201
202 if constexpr (HasHub) {
203 const real centerX = turbinePosX - componentData.hubPositionOffset;
204 const real centerY = turbinePosY;
205 const real centerZ = turbinePosZ;
206
207 if (inCylinderVolume<x, false>(gridCoordX, gridCoordY, gridCoordZ, centerX + c1o2 * componentData.hubLength,
208 centerY, centerZ, componentData.hubLength, componentData.hubRadius,
209 (c3o2 * turbineData.smearingWidth) + turbineData.floatingAmplitude))
210 accumulateSmearingForces<UseLocalSmearingWidth>(
211 componentData, gridCoordX, gridCoordY, gridCoordZ,
212 bladePointsTotal + turbine * componentData.numberOfHubPointsPerTurbine,
213 componentData.numberOfHubPointsPerTurbine, turbineData.smearingWidth, gridForceX, gridForceY,
214 gridForceZ);
215 }
216
217 if constexpr (HasTower) {
218 const real towerCenterX = turbinePosX + componentData.towerOffset;
219 const real towerCenterY = turbinePosY;
220 const real towerTopZ = turbinePosZ - componentData.hubRadius;
221 const uint hubPointsTotal = componentData.numberOfHubPointsPerTurbine * turbineData.numberOfTurbines;
222
223 if (inCylinderVolume<z, false>(gridCoordX, gridCoordY, gridCoordZ, towerCenterX, towerCenterY,
224 towerTopZ - c1o2 * componentData.maxTowerHeight, componentData.maxTowerHeight,
225 componentData.towerRadius, (c3o2 * turbineData.smearingWidth) + turbineData.floatingAmplitude))
226 accumulateSmearingForces<UseLocalSmearingWidth>(
227 componentData, gridCoordX, gridCoordY, gridCoordZ,
228 bladePointsTotal + hubPointsTotal + turbine * componentData.numberOfTowerPointsPerTurbine,
229 componentData.numberOfTowerPointsPerTurbine, turbineData.smearingWidth, gridForceX, gridForceY,
230 gridForceZ);
231 }
232 }
233
234 gridData.fx[gridIndex] += gridForceX / gridData.forceRatio;
235 gridData.fy[gridIndex] += gridForceY / gridData.forceRatio;
236 gridData.fz[gridIndex] += gridForceZ / gridData.forceRatio;
237}
238
239static void launchApplyBodyForces(bool useVAWT, bool hasHub, bool hasTower, bool UseLocalSmearingWidth,
240 const vf::cuda::CudaGrid& sphereGrid, cudaStream_t stream, const GridData& gridData,
241 const TurbineData& turbineData, const ComponentData& componentData)
242{
243
244 if (useVAWT) {
245 if (UseLocalSmearingWidth)
246 applyBodyForces<true, false, false, true><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
247 else
248 applyBodyForces<true, false, false, false><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
249 } else {
250 if (hasHub && hasTower)
251 applyBodyForces<false, true, true, false><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
252 else if (hasHub)
253 applyBodyForces<false, true, false, false><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
254 else if (hasTower)
255 applyBodyForces<false, false, true, false><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
256 else
257 applyBodyForces<false, false, false, false><<<sphereGrid.grid, sphereGrid.threads, 0, stream>>>(gridData, turbineData, componentData);
258 }
259}
260
261void ActuatorFarm::init()
262{
263 if (!para->getIsBodyForce())
264 throw std::runtime_error("try to allocate ActuatorFarm but BodyForce is not set in Parameter.");
265 if (para->getDensityRatio() == c0o1)
266 throw std::runtime_error("Parameter::densityRatio is zero. Non-zero density ratio needed for actuator farm.");
267 if (!this->hasVAWTRotorVolume() && this->flagLocalSmearingWidth)
268 throw std::runtime_error("ActuatorFarm::flagLocalSmearingWidth is only allowed for VAWTs.");
269 if (this->hasVAWTRotorVolume() && this->numberOfHubPointsPerTurbine)
270 throw std::runtime_error("ActuatorFarm::numberOfHubPointsPerTurbine is not defined for VAWTs.");
271 if (this->hasVAWTRotorVolume() && this->numberOfTowerPointsPerTurbine)
272 throw std::runtime_error("ActuatorFarm::numberOfTowerPointsPerTurbine is not defined for VAWTs.");
273 this->initTurbineGeometries();
274 this->initCoords();
275 this->initIndices();
276 this->initVelocities();
277 this->initForces();
278 this->initBoundingVolumes();
279
280 this->streamIndex = para->getStreamManager()->registerAndLaunchStream(CudaStreamIndex::ActuatorFarm);
281}
282
283void ActuatorFarm::interact(int level, uint t)
284{
285 if (level != this->level)
286 return;
287 cudaStream_t stream = para->getStreamManager()->getStream(CudaStreamIndex::ActuatorFarm, this->streamIndex);
288
289 if (useHostArrays)
290 cudaMemoryManager->cudaCopyCoordsHtoD(this);
291
292 if (this->writeOutput && ((t - this->tStartOut) % this->tOut == 0) && t >= this->tStartOut) {
293 if (!useHostArrays) {
294 cudaMemoryManager->cudaCopyCoordsDtoH(this);
295 cudaMemoryManager->cudaCopyVelocitiesDtoH(this);
296 cudaMemoryManager->cudaCopyForcesDtoH(this);
297 }
298 this->write(this->getFilename(t));
299 }
300
301 const GridData gridData { this->boundingVolumeIndicesD,
302 this->numberOfIndices,
303 para->getParD(this->level)->coordinateX,
304 para->getParD(this->level)->coordinateY,
305 para->getParD(this->level)->coordinateZ,
306 para->getParD(this->level)->neighborX,
307 para->getParD(this->level)->neighborY,
308 para->getParD(this->level)->neighborZ,
309 para->getParD(this->level)->neighborInverse,
310 para->getParD(this->level)->velocityX,
311 para->getParD(this->level)->velocityY,
312 para->getParD(this->level)->velocityZ,
313 para->getParD(this->level)->forceX_SP,
314 para->getParD(this->level)->forceY_SP,
315 para->getParD(this->level)->forceZ_SP,
316 para->getScaledLengthRatio(level),
317 para->getScaledVelocityRatio(level),
318 para->getScaledForceRatio(level) };
319
320 const TurbineData turbineData { this->turbinePosXD, this->turbinePosYD, this->turbinePosZD, this->numberOfTurbines,
321 this->smearingWidth, maxFloatingAmplitude };
322
323 const ComponentData componentData {
324 this->diameter + (c2o1 * maxFloatingAmplitude),
325 this->getVAWTRotorBoundingMargin(),
326 this->numberOfBladePointsPerTurbine,
327 this->getTotalNumberOfPoints(),
328 this->coordsXDCurrentTimestep,
329 this->coordsYDCurrentTimestep,
330 this->coordsZDCurrentTimestep,
331 this->velocitiesXDCurrentTimestep,
332 this->velocitiesYDCurrentTimestep,
333 this->velocitiesZDCurrentTimestep,
334 this->forcesXDCurrentTimestep,
335 this->forcesYDCurrentTimestep,
336 this->forcesZDCurrentTimestep,
337 this->indicesD,
338 this->numberOfHubPointsPerTurbine,
339 this->hubRadius,
340 this->hubLength,
341 this->hubPositionOffset,
342 this->numberOfTowerPointsPerTurbine,
343 this->towerRadius,
344 this->towerOffset,
345 this->maxTowerHeight,
346 this->hasVAWTRotorVolume(),
347 this->vawtRotorHeight,
348 this->flagLocalSmearingWidth,
349 this->getAllBladeLocalSmearingWidthDevice(),
350 };
351 vf::cuda::CudaGrid grid(para->getParH(level)->numberofthreads, this->getTotalNumberOfPoints());
352 interpolateVelocities<<<grid.grid, grid.threads, 0, stream>>>(gridData, componentData);
353 cudaStreamSynchronize(stream);
354
355 if (useHostArrays)
356 cudaMemoryManager->cudaCopyVelocitiesDtoH(this);
357
358 const uint subIterationTimestep = para->getTimeStep(level, t, false);
359 const real deltaT = para->getScaledTimeRatio(level);
360 const real time = subIterationTimestep * deltaT;
361 this->updateForcesAndCoordinates(time, deltaT);
362 this->swapDeviceArrays();
363
364 if (useHostArrays) {
365 cudaMemoryManager->cudaCopyForcesHtoD(this);
366 }
367 vf::cuda::CudaGrid sphereGrid = vf::cuda::CudaGrid(para->getParH(level)->numberofthreads, this->numberOfIndices);
368 launchApplyBodyForces(this->hasVAWTRotorVolume(), this->numberOfHubPointsPerTurbine > 0,
369 this->numberOfTowerPointsPerTurbine > 0, this->flagLocalSmearingWidth, sphereGrid, stream,
370 gridData, turbineData, componentData);
371 cudaStreamSynchronize(stream);
372}
373
374ActuatorFarm::~ActuatorFarm()
375{
376 cudaMemoryManager->cudaFreeBladeGeometries(this);
377 cudaMemoryManager->cudaFreeCoords(this);
378 cudaMemoryManager->cudaFreeVelocities(this);
379 cudaMemoryManager->cudaFreeForces(this);
380 cudaMemoryManager->cudaFreeIndices(this);
381 cudaMemoryManager->cudaFreeBoundingVolumeIndices(this);
382}
383
384void ActuatorFarm::getTaggedFluidNodes(GridProvider* gridProvider)
385{
386 std::vector<uint> indicesInBoundingVolumes(this->boundingVolumeIndicesH,
387 this->boundingVolumeIndicesH + this->numberOfIndices);
388 gridProvider->tagFluidNodeIndices(indicesInBoundingVolumes, CollisionTemplate::AllFeatures, this->level);
389}
390
391void ActuatorFarm::initTurbineGeometries()
392{
393 cudaMemoryManager->cudaAllocBladeGeometries(this);
394
395 std::copy(initialTurbinePositionsX.begin(), initialTurbinePositionsX.end(), turbinePosXH);
396 std::copy(initialTurbinePositionsY.begin(), initialTurbinePositionsY.end(), turbinePosYH);
397 std::copy(initialTurbinePositionsZ.begin(), initialTurbinePositionsZ.end(), turbinePosZH);
398
399 cudaMemoryManager->cudaCopyBladeGeometriesHtoD(this);
400}
401
402void ActuatorFarm::initCoords()
403{
404 cudaMemoryManager->cudaAllocCoords(this);
405
406 for (uint turbine = 0; turbine < this->numberOfTurbines; turbine++) {
407 for (uint blade = 0; blade < this->numberOfBlades; blade++) {
408 const real localAzimuth = this->azimuths[turbine] + blade * c2Pi / static_cast<real>(this->numberOfBlades);
409
410 for (uint bladePoint = 0; bladePoint < this->numberOfPointsPerBlade; bladePoint++) {
411 const uint node = calcPointIndexInBladeArrays({ turbine, blade, bladePoint }, this->numberOfPointsPerBlade,
412 this->numberOfBlades);
413
414 real x, y, z;
415 rotateFromBladeToGlobal(c0o1, c0o1, this->bladeRadii[bladePoint], x, y, z, localAzimuth);
416 getAllBladeCoordsX()[node] = x + this->turbinePosXH[turbine];
417 getAllBladeCoordsY()[node] = y + this->turbinePosYH[turbine];
418 getAllBladeCoordsZ()[node] = z + this->turbinePosZH[turbine];
419 }
420 }
421 }
422
423 if (numberOfHubPoints > 0) {
424 uint pointIndex = 0;
425 for (uint turbine = 0; turbine < numberOfTurbines; turbine++)
426 generateHubAxisPoints(turbine, pointIndex);
427 }
428
429 if (numberOfTowerPoints > 0) {
430 uint pointIndex = 0;
431 for (uint turbine = 0; turbine < numberOfTurbines; turbine++)
432 generateTowerAxisPoints(turbine, pointIndex);
433 }
434
435 cudaMemoryManager->cudaCopyCoordsHtoD(this);
436 std::swap(this->coordsXDCurrentTimestep, this->coordsXDPreviousTimestep);
437 std::swap(this->coordsYDCurrentTimestep, this->coordsYDPreviousTimestep);
438 std::swap(this->coordsZDCurrentTimestep, this->coordsZDPreviousTimestep);
439 cudaMemoryManager->cudaCopyCoordsHtoD(this);
440}
441
442void ActuatorFarm::initVelocities()
443{
444 cudaMemoryManager->cudaAllocVelocities(this);
445
446 const uint totalPoints = getTotalNumberOfPoints();
447 std::fill_n(velocitiesXH, totalPoints, c0o1);
448 std::fill_n(velocitiesYH, totalPoints, c0o1);
449 std::fill_n(velocitiesZH, totalPoints, c0o1);
450
451 cudaMemoryManager->cudaCopyVelocitiesHtoD(this);
452 std::swap(this->velocitiesXDCurrentTimestep, this->velocitiesXDPreviousTimestep);
453 std::swap(this->velocitiesYDCurrentTimestep, this->velocitiesYDPreviousTimestep);
454 std::swap(this->velocitiesZDCurrentTimestep, this->velocitiesZDPreviousTimestep);
455 cudaMemoryManager->cudaCopyVelocitiesHtoD(this);
456}
457
458void ActuatorFarm::initForces()
459{
460 cudaMemoryManager->cudaAllocForces(this);
461
462 const uint totalPoints = getTotalNumberOfPoints();
463 const bool requiresLocalSmearingWidth = this->requiresLocalSmearingWidth();
464 std::fill_n(forcesXH, totalPoints, c0o1);
465 std::fill_n(forcesYH, totalPoints, c0o1);
466 std::fill_n(forcesZH, totalPoints, c0o1);
467
468 if (requiresLocalSmearingWidth)
469 std::fill_n(localSmearingWidthH, totalPoints, this->smearingWidth);
470
471 cudaMemoryManager->cudaCopyForcesHtoD(this);
472 std::swap(this->forcesXDCurrentTimestep, this->forcesXDPreviousTimestep);
473 std::swap(this->forcesYDCurrentTimestep, this->forcesYDPreviousTimestep);
474 std::swap(this->forcesZDCurrentTimestep, this->forcesZDPreviousTimestep);
475 if (requiresLocalSmearingWidth)
476 std::swap(this->localSmearingWidthDCurrentTimestep, this->localSmearingWidthDPreviousTimestep);
477 cudaMemoryManager->cudaCopyForcesHtoD(this);
478}
479
480void ActuatorFarm::initIndices()
481{
482 cudaMemoryManager->cudaAllocIndices(this);
483
484 std::fill_n(indicesH, getTotalNumberOfPoints(), 1);
485
486 cudaMemoryManager->cudaCopyIndicesHtoD(this);
487}
488
489void ActuatorFarm::initBoundingVolumes()
490{
491 std::vector<uint> nodesInBoundingVolumes;
492
493 // --- Rotor Bounding Volume ---
494 const bool useVAWTVolume = this->hasVAWTRotorVolume();
495 const real rotorBoundingSmearingWidth = this->getRotorBoundingSmearingWidth();
496 const real rotorBoundingMargin = this->getVAWTRotorBoundingMargin();
497 const real rotorBoundingVolumeRadius =
498 (useVAWTVolume ? (c1o2 * this->diameter) + rotorBoundingMargin
499 : getRotorBoundingVolumeRadius(this->diameter, rotorBoundingSmearingWidth, false) + maxFloatingAmplitude);
500 const real rotorBoundingVolumeHeight = this->vawtRotorHeight + c2o1 * rotorBoundingMargin;
501 const real deltaX = para->getScaledLengthRatio(level);
502 const real effectiveRotorRadius = std::max(c0o1, rotorBoundingVolumeRadius - deltaX);
503 const real rotorBoundingVolumeInnerRadius = std::max(c0o1, c1o2 * this->diameter - rotorBoundingMargin);
504 const real effectiveRotorInnerRadius =
505 rotorBoundingVolumeInnerRadius > c0o1 ? std::max(c0o1, rotorBoundingVolumeInnerRadius + deltaX) : c0o1;
506 const real effectiveRotorHeight = std::max(c0o1, rotorBoundingVolumeHeight - c2o1 * deltaX);
507 const uint minimumNumberOfNodesPerRotorBoundingVolume =
508 useVAWTVolume ? std::max(uint(1), uint(cPi *
509 std::max(c0o1, std::pow(effectiveRotorRadius, c2o1) -
510 std::pow(effectiveRotorInnerRadius, c2o1)) *
511 effectiveRotorHeight / std::pow(deltaX, c3o1)))
512 : uint(c4o3 * cPi * std::pow(effectiveRotorRadius, c3o1) / std::pow(deltaX, c3o1));
513
514 for (uint turbine = 0; turbine < this->numberOfTurbines; turbine++) {
515
516 const real turbinePosX = this->turbinePosXH[turbine];
517 const real turbinePosY = this->turbinePosYH[turbine];
518 const real turbinePosZ = this->turbinePosZH[turbine];
519
520 uint nodesInThisTurbineBoundingVolume = 0;
521
522 for (size_t pos = 1; pos <= para->getParH(this->level)->numberOfNodes; pos++) {
523 const real nodeX = para->getParH(this->level)->coordinateX[pos];
524 const real nodeY = para->getParH(this->level)->coordinateY[pos];
525 const real nodeZ = para->getParH(this->level)->coordinateZ[pos];
526
527 bool inRotorVolume = false;
528 if (useVAWTVolume) {
529 inRotorVolume = inCylinderVolume<z, true>(nodeX, nodeY, nodeZ, turbinePosX, turbinePosY, turbinePosZ,
530 this->vawtRotorHeight, c1o2 * this->diameter,
531 rotorBoundingMargin);
532 } else {
533 const real distX = nodeX - turbinePosX;
534 const real distY = nodeY - turbinePosY;
535 const real distZ = nodeZ - turbinePosZ;
536 inRotorVolume = inSphereVolume(distX, distY, distZ, this->diameter,
537 rotorBoundingSmearingWidth + maxFloatingAmplitude);
538 }
539
540 if (inRotorVolume) {
541 nodesInBoundingVolumes.push_back(uint(pos));
542 nodesInThisTurbineBoundingVolume++;
543 }
544 }
545
546 if (nodesInThisTurbineBoundingVolume < minimumNumberOfNodesPerRotorBoundingVolume) {
547 VF_LOG_CRITICAL("Found only {} nodes in rotor bounding volume of turbine no. {}, expected at least {}!",
548 nodesInThisTurbineBoundingVolume, turbine, minimumNumberOfNodesPerRotorBoundingVolume);
549 throw std::runtime_error(
550 "ActuatorFarm::initBoundingVolumes: Turbine rotor bounding volume partially out of domain.");
551 }
552 }
553
554 // --- Hub Bounding Volume ---
555 if (numberOfHubPoints > 0) {
556 for (uint turbine = 0; turbine < numberOfTurbines; turbine++) {
557 const real centerX = turbinePosXH[turbine] - hubPositionOffset;
558 const real centerY = turbinePosYH[turbine];
559 const real centerZ = turbinePosZH[turbine];
560
561 for (size_t pos = 1; pos <= para->getParH(level)->numberOfNodes; pos++) {
562 const real nodeX = para->getParH(level)->coordinateX[pos];
563 const real nodeY = para->getParH(level)->coordinateY[pos];
564 const real nodeZ = para->getParH(level)->coordinateZ[pos];
565
566 if (inCylinderVolume<x, false>(nodeX, nodeY, nodeZ, centerX + (c1o2 * hubLength), centerY, centerZ, hubLength,
567 hubRadius, c3o2 * this->smearingWidth + maxFloatingAmplitude)) {
568 nodesInBoundingVolumes.push_back((uint)pos);
569 }
570 }
571 }
572 }
573
574 // --- Tower Bounding Volume ---
575 if (numberOfTowerPoints > 0) {
576 maxTowerHeight = *std::max_element(towerHeights.begin(), towerHeights.end());
577 for (uint turbine = 0; turbine < numberOfTurbines; turbine++) {
578 const real towerCenterX = turbinePosXH[turbine] + towerOffset;
579 const real towerCenterY = turbinePosYH[turbine];
580 const real towerTopZ = turbinePosZH[turbine] - hubRadius;
581 const real towerBottomZ = towerTopZ - towerHeights[turbine];
582
583 real localMinZ = std::numeric_limits<real>::max();
584 for (size_t pos = 1; pos <= para->getParH(level)->numberOfNodes; pos++) {
585 const real nodeX = para->getParH(level)->coordinateX[pos];
586 const real nodeY = para->getParH(level)->coordinateY[pos];
587 const real nodeZ = para->getParH(level)->coordinateZ[pos];
588
589 if (std::abs(nodeX - towerCenterX) < deltaX && std::abs(nodeY - towerCenterY) < deltaX)
590 localMinZ = std::min(localMinZ, nodeZ);
591
592 if (inCylinderVolume<z, false>(nodeX, nodeY, nodeZ, towerCenterX, towerCenterY,
593 towerTopZ - c1o2 * towerHeights[turbine], towerHeights[turbine], towerRadius,
594 c3o2 * smearingWidth + maxFloatingAmplitude)) {
595 nodesInBoundingVolumes.push_back((uint)pos);
596 }
597 }
598
599 if (towerBottomZ < localMinZ)
600 throw std::runtime_error("ActuatorFarm::initBoundingVolumes: Tower of turbine " + std::to_string(turbine) +
601 " extends below the domain (tower bottom z = " + std::to_string(towerBottomZ) +
602 ", domain min z = " + std::to_string(localMinZ) +
603 "). Reduce the tower height so that it does not extend below the domain.");
604 }
605 }
606
607 std::sort(nodesInBoundingVolumes.begin(), nodesInBoundingVolumes.end());
608 nodesInBoundingVolumes.erase(std::unique(nodesInBoundingVolumes.begin(), nodesInBoundingVolumes.end()),
609 nodesInBoundingVolumes.end());
610
611 this->numberOfIndices = uint(nodesInBoundingVolumes.size());
612
613 cudaMemoryManager->cudaAllocBoundingVolumeIndices(this);
614 std::copy(nodesInBoundingVolumes.begin(), nodesInBoundingVolumes.end(), this->boundingVolumeIndicesH);
615 cudaMemoryManager->cudaCopyBoundingVolumeIndicesHtoD(this);
616}
617
618void ActuatorFarm::setAllBladeCoords(const real* bladeCoordsX, const real* bladeCoordsY, const real* bladeCoordsZ) const
619{
620 std::copy_n(bladeCoordsX, this->numberOfBladePoints, this->getAllBladeCoordsX());
621 std::copy_n(bladeCoordsY, this->numberOfBladePoints, this->getAllBladeCoordsY());
622 std::copy_n(bladeCoordsZ, this->numberOfBladePoints, this->getAllBladeCoordsZ());
623}
624
625void ActuatorFarm::setAllBladeVelocities(const real* bladeVelocitiesX, const real* bladeVelocitiesY,
626 const real* bladeVelocitiesZ) const
627{
628 std::copy_n(bladeVelocitiesX, this->numberOfBladePoints, this->getAllBladeVelocitiesX());
629 std::copy_n(bladeVelocitiesY, this->numberOfBladePoints, this->getAllBladeVelocitiesY());
630 std::copy_n(bladeVelocitiesZ, this->numberOfBladePoints, this->getAllBladeVelocitiesZ());
631}
632
633void ActuatorFarm::setAllBladeForces(const real* bladeForcesX, const real* bladeForcesY, const real* bladeForcesZ) const
634{
635 std::copy_n(bladeForcesX, this->numberOfBladePoints, this->getAllBladeForcesX());
636 std::copy_n(bladeForcesY, this->numberOfBladePoints, this->getAllBladeForcesY());
637 std::copy_n(bladeForcesZ, this->numberOfBladePoints, this->getAllBladeForcesZ());
638}
639
640void ActuatorFarm::setTurbineBladeCoords(size_t turbine, const real* bladeCoordsX, const real* bladeCoordsY,
641 const real* bladeCoordsZ) const
642{
643 std::copy_n(bladeCoordsX, this->numberOfBladePointsPerTurbine,
644 &this->getAllBladeCoordsX()[turbine * this->numberOfBladePointsPerTurbine]);
645 std::copy_n(bladeCoordsY, this->numberOfBladePointsPerTurbine,
646 &this->getAllBladeCoordsY()[turbine * this->numberOfBladePointsPerTurbine]);
647 std::copy_n(bladeCoordsZ, this->numberOfBladePointsPerTurbine,
648 &this->getAllBladeCoordsZ()[turbine * this->numberOfBladePointsPerTurbine]);
649}
650
651void ActuatorFarm::setTurbineBladeVelocities(size_t turbine, const real* bladeVelocitiesX, const real* bladeVelocitiesY,
652 const real* bladeVelocitiesZ) const
653{
654 std::copy_n(bladeVelocitiesX, this->numberOfBladePointsPerTurbine,
655 &this->getAllBladeVelocitiesX()[turbine * this->numberOfBladePointsPerTurbine]);
656 std::copy_n(bladeVelocitiesY, this->numberOfBladePointsPerTurbine,
657 &this->getAllBladeVelocitiesY()[turbine * this->numberOfBladePointsPerTurbine]);
658 std::copy_n(bladeVelocitiesZ, this->numberOfBladePointsPerTurbine,
659 &this->getAllBladeVelocitiesZ()[turbine * this->numberOfBladePointsPerTurbine]);
660}
661
662void ActuatorFarm::setTurbineBladeForces(size_t turbine, const real* bladeForcesX, const real* bladeForcesY,
663 const real* bladeForcesZ) const
664{
665 std::copy_n(bladeForcesX, this->numberOfBladePointsPerTurbine,
666 &this->getAllBladeForcesX()[turbine * this->numberOfBladePointsPerTurbine]);
667 std::copy_n(bladeForcesY, this->numberOfBladePointsPerTurbine,
668 &this->getAllBladeForcesY()[turbine * this->numberOfBladePointsPerTurbine]);
669 std::copy_n(bladeForcesZ, this->numberOfBladePointsPerTurbine,
670 &this->getAllBladeForcesZ()[turbine * this->numberOfBladePointsPerTurbine]);
671}
672
673void ActuatorFarm::swapDeviceArrays()
674{
675 std::swap(this->coordsXDPreviousTimestep, this->coordsXDCurrentTimestep);
676 std::swap(this->coordsYDPreviousTimestep, this->coordsYDCurrentTimestep);
677 std::swap(this->coordsZDPreviousTimestep, this->coordsZDCurrentTimestep);
678
679 std::swap(this->velocitiesXDPreviousTimestep, this->velocitiesXDCurrentTimestep);
680 std::swap(this->velocitiesYDPreviousTimestep, this->velocitiesYDCurrentTimestep);
681 std::swap(this->velocitiesZDPreviousTimestep, this->velocitiesZDCurrentTimestep);
682
683 std::swap(this->forcesXDPreviousTimestep, this->forcesXDCurrentTimestep);
684 std::swap(this->forcesYDPreviousTimestep, this->forcesYDCurrentTimestep);
685 std::swap(this->forcesZDPreviousTimestep, this->forcesZDCurrentTimestep);
686 if (this->requiresLocalSmearingWidth())
687 std::swap(this->localSmearingWidthDPreviousTimestep, this->localSmearingWidthDCurrentTimestep);
688}
689
690void ActuatorFarm::generateHubAxisPoints(uint turbineIndex, uint& pointIndex)
691{
692 const real centerX = turbinePosXH[turbineIndex] - hubPositionOffset;
693 const real centerY = turbinePosYH[turbineIndex];
694 const real centerZ = turbinePosZH[turbineIndex];
695
696 const real segmentLength = hubLength / static_cast<real>(numberOfHubPointsPerTurbine);
697
698 for (uint i = 0; i < numberOfHubPointsPerTurbine; i++) {
699 getAllHubCoordsX()[pointIndex] = centerX + static_cast<real>(i) * segmentLength;
700 getAllHubCoordsY()[pointIndex] = centerY;
701 getAllHubCoordsZ()[pointIndex] = centerZ;
702 pointIndex++;
703 }
704}
705
706void ActuatorFarm::generateTowerAxisPoints(uint turbineIndex, uint& pointIndex)
707{
708 const real centerX = turbinePosXH[turbineIndex] + towerOffset;
709 const real centerY = turbinePosYH[turbineIndex];
710 const real towerTop = turbinePosZH[turbineIndex] - hubRadius;
711 const real towerHeight = this->towerHeights[turbineIndex];
712
713 const real segmentHeight = towerHeight / static_cast<real>(numberOfTowerPointsPerTurbine);
714
715 for (uint i = 0; i < numberOfTowerPointsPerTurbine; i++) {
716 getAllTowerCoordsX()[pointIndex] = centerX;
717 getAllTowerCoordsY()[pointIndex] = centerY;
718 getAllTowerCoordsZ()[pointIndex] = towerTop - (static_cast<real>(i) + c1o2) * segmentHeight;
719 pointIndex++;
720 }
721}
722
723std::string ActuatorFarm::getFilename(uint t) const
724{
725 return para->getOutputPath() + this->outputName + "_ID_" + std::to_string(para->getMyProcessID()) + "_t_" +
726 std::to_string(t);
727}
728
729void ActuatorFarm::write(const std::string& filename) const
730{
731 const uint totalPoints = getTotalNumberOfPoints();
732
733 std::vector<std::string> dataNames = { "VelocitiesX", "VelocitiesY", "VelocitiesZ", "ForcesX", "ForcesY",
734 "ForcesZ", "Blade", "Hub", "Tower" };
735 std::vector<UbTupleFloat3> nodes(totalPoints);
736 std::vector<std::vector<double>> nodeData(dataNames.size());
737 for (auto& data : nodeData)
738 data.resize(totalPoints, 0.0);
739
740 for (uint i = 0; i < totalPoints; i++) {
741 nodes[i] = UbTupleFloat3(this->coordsXH[i], this->coordsYH[i], this->coordsZH[i]);
742 nodeData[0][i] = this->velocitiesXH[i];
743 nodeData[1][i] = this->velocitiesYH[i];
744 nodeData[2][i] = this->velocitiesZH[i];
745 nodeData[3][i] = this->forcesXH[i];
746 nodeData[4][i] = this->forcesYH[i];
747 nodeData[5][i] = this->forcesZH[i];
748 }
749
750 // Boolean flags for Paraview Threshold filtering:
751 // Select scalar "Blade"/"Hub"/"Tower" and threshold [1, 1]
752 for (uint i = 0; i < numberOfBladePoints; i++)
753 nodeData[6][i] = 1.0;
754 for (uint i = 0; i < numberOfHubPoints; i++)
755 nodeData[7][numberOfBladePoints + i] = 1.0;
756 for (uint i = 0; i < numberOfTowerPoints; i++)
757 nodeData[8][numberOfBladePoints + numberOfHubPoints + i] = 1.0;
758
759 this->appendOutputData(dataNames, nodeData);
760
761 WbWriterVtkXmlBinary::getInstance()->writeNodesWithNodeData(filename, nodes, dataNames, nodeData);
762}
763
764} // namespace vf::gpu
765
766//! \}