Skip to content
Snippets Groups Projects
AtmosphericBoundaryLayer.cpp 19.2 KiB
Newer Older
//=======================================================================================
// ____          ____    __    ______     __________   __      __       __        __
// \    \       |    |  |  |  |   _   \  |___    ___| |  |    |  |     /  \      |  |
//  \    \      |    |  |  |  |  |_)   |     |  |     |  |    |  |    /    \     |  |
//   \    \     |    |  |  |  |   _   /      |  |     |  |    |  |   /  /\  \    |  |
//    \    \    |    |  |  |  |  | \  \      |  |     |   \__/   |  /  ____  \   |  |____
//     \    \   |    |  |__|  |__|  \__\     |__|      \________/  /__/    \__\  |_______|
//      \    \  |    |   ________________________________________________________________
//       \    \ |    |  |  ______________________________________________________________|
//        \    \|    |  |  |         __          __     __     __     ______      _______
//         \         |  |  |_____   |  |        |  |   |  |   |  |   |   _  \    /  _____)
//          \        |  |   _____|  |  |        |  |   |  |   |  |   |  | \  \   \_______
//           \       |  |  |        |  |_____   |   \_/   |   |  |   |  |_/  /    _____  |
//            \ _____|  |__|        |________|   \_______/    |__|   |______/    (_______/
//
//  This file is part of VirtualFluids. VirtualFluids is free software: you can
//  redistribute it and/or modify it under the terms of the GNU General Public
//  License as published by the Free Software Foundation, either version 3 of
//  the License, or (at your option) any later version.
//
//  VirtualFluids is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
//  for more details.
//
//  SPDX-License-Identifier: GPL-3.0-or-later
//  SPDX-FileCopyrightText: Copyright © VirtualFluids Project contributors, see AUTHORS.md in root folder
//! \addtogroup AtmosphericBoundaryLayer
//! \ingroup gpu_apps
//! \{
//! \author Henry Korb, Henrik Asmuth
//=======================================================================================
#include <numeric>
#include <basics/DataTypes.h>
Henrik Asmuth's avatar
Henrik Asmuth committed
#include <basics/config/ConfigurationFile.h>
#include <basics/constants/NumericConstants.h>
Henrik Asmuth's avatar
Henrik Asmuth committed

#include <logger/Logger.h>

#include <parallel/MPICommunicator.h>
Henrik Asmuth's avatar
Henrik Asmuth committed

//////////////////////////////////////////////////////////////////////////

#include "GridGenerator/TransientBCSetter/TransientBCSetter.h"
#include "GridGenerator/geometries/BoundingBox/BoundingBox.h"
Hkorb's avatar
Hkorb committed
#include "GridGenerator/geometries/Cuboid/Cuboid.h"
#include "GridGenerator/grid/BoundaryConditions/BoundaryCondition.h"
#include "GridGenerator/grid/BoundaryConditions/Side.h"
Henrik Asmuth's avatar
Henrik Asmuth committed
#include "GridGenerator/grid/GridBuilder/LevelGridBuilder.h"
#include "GridGenerator/grid/GridBuilder/MultipleGridBuilder.h"
Hkorb's avatar
Hkorb committed
#include "GridGenerator/utilities/communication.h"
Henrik Asmuth's avatar
Henrik Asmuth committed

//////////////////////////////////////////////////////////////////////////

Hkorb's avatar
Hkorb committed
#include "gpu/core/BoundaryConditions/BoundaryConditionFactory.h"
#include "gpu/core/Calculation/Simulation.h"
#include "gpu/core/Cuda/CudaMemoryManager.h"
Hkorb's avatar
Hkorb committed
#include "gpu/core/GridScaling/GridScalingFactory.h"
#include "gpu/core/Kernel/KernelTypes.h"
#include "gpu/core/Parameter/Parameter.h"
#include "gpu/core/Samplers/PrecursorWriter.h"
#include "gpu/core/Samplers/PlanarAverageProbe.h"
Hkorb's avatar
Hkorb committed
#include "gpu/core/Samplers/Probe.h"
#include "gpu/core/Samplers/WallModelProbe.h"
#include "gpu/core/TurbulenceModels/TurbulenceModelFactory.h"
using namespace vf::basics::constant;
const std::string defaultConfigFile = "abl.cfg";

Hkorb's avatar
Hkorb committed
void run(const vf::basics::ConfigurationFile& config)
Henrik Asmuth's avatar
Henrik Asmuth committed
{
Hkorb's avatar
Hkorb committed
    const std::string simulationName("AtmosphericBoundaryLayer");
Hkorb's avatar
Hkorb committed
    const uint samplingOffsetWallModel = 2;
    const uint averagingTimestepsPlaneProbes = 10;
    const uint maximumNumberOfTimestepsPerPrecursorFile = 1000;
    const real viscosity = 1.56e-5;
    const real machNumber = 0.1;
Hkorb's avatar
Hkorb committed
    const real boundaryLayerHeight = config.getValue("boundaryLayerHeight", 1000.0);
Hkorb's avatar
Hkorb committed

Hkorb's avatar
Hkorb committed
    const real lengthX = 6 * boundaryLayerHeight;
    const real lengthY = 4 * boundaryLayerHeight;
    const real lengthZ = boundaryLayerHeight;
Hkorb's avatar
Hkorb committed
    const real roughnessLength = config.getValue("z0", c1o10);
    const real frictionVelocity = config.getValue("u_star", c4o10);
    const real vonKarmanConstant = config.getValue("vonKarmanConstant", c4o10);
    const uint nodesPerBoundaryLyerHeight = config.getValue<uint>("NodesPerBoundaryLayerHeight", 64);
    const float periodicShift = config.getValue<float>("PeriodicShift", c0o1);
    const bool writePrecursor = config.getValue("WritePrecursor", false);
    const bool useDistributionsForPrecursor = config.getValue<bool>("UseDistributions", false);
    std::string precursorDirectory = config.getValue<std::string>("PrecursorDirectory", "precursor/");
    if (precursorDirectory.back() != '/')
Hkorb's avatar
Hkorb committed
        precursorDirectory += '/';
Hkorb's avatar
Hkorb committed
    const int timeStepsWritePrecursor = config.getValue<int>("nTimestepsWritePrecursor", 10);
    const real timeStartPrecursor = config.getValue<real>("tStartPrecursor", 36000.);
    const real positionXPrecursorSamplingPlane = config.getValue<real>("posXPrecursor", c1o2 * lengthX);

    const bool usePrecursorInflow = config.getValue("ReadPrecursor", false);
Hkorb's avatar
Hkorb committed
    const int timestepsBetweenReadsPrecursor = config.getValue<int>("nTimestepsReadPrecursor", 10);
    const bool useRefinement = config.getValue<bool>("Refinement", false);
Hkorb's avatar
Hkorb committed

Hkorb's avatar
Hkorb committed
    const float timeStartOut = config.getValue<real>("tStartOut");
    const float timeOut = config.getValue<real>("tOut");
    const float timeEnd = config.getValue<real>("tEnd");
Hkorb's avatar
Hkorb committed
    const float timeStartAveraging = config.getValue<real>("tStartAveraging");
    const float timeStartTemporalAveraging = config.getValue<real>("tStartTmpAveraging");
    const float timeAveraging = config.getValue<real>("tAveraging");
    const float timeStartOutProbe = config.getValue<real>("tStartOutProbe");
    const float timeOutProbe = config.getValue<real>("tOutProbe");
Hkorb's avatar
Hkorb committed
    //////////////////////////////////////////////////////////////////////////
    // compute parameters in lattice units
    //////////////////////////////////////////////////////////////////////////
    const auto velocityProfile = [&](real coordZ) {
        return frictionVelocity / vonKarmanConstant * log(coordZ / roughnessLength + c1o1);
    };
    const real velocity = c1o2 * velocityProfile(boundaryLayerHeight);
Hkorb's avatar
Hkorb committed
    const real deltaX = boundaryLayerHeight / real(nodesPerBoundaryLyerHeight);
Hkorb's avatar
Hkorb committed
    const real deltaT = c1oSqrt3 * deltaX * machNumber / velocity;
Hkorb's avatar
Hkorb committed
    const real velocityLB = velocity * deltaT / deltaX;
Hkorb's avatar
Hkorb committed
    const real viscosityLB = viscosity * deltaT / (deltaX * deltaX);
Hkorb's avatar
Hkorb committed
    const real pressureGradient = frictionVelocity * frictionVelocity / boundaryLayerHeight;
    const real pressureGradientLB = pressureGradient * (deltaT * deltaT) / deltaX;
Hkorb's avatar
Hkorb committed
    const uint timeStepStartAveraging = uint(timeStartAveraging / deltaT);
    const uint timeStepStartTemporalAveraging = uint(timeStartTemporalAveraging / deltaT);
    const uint timeStepAveraging = uint(timeAveraging / deltaT);
    const uint timeStepStartOutProbe = uint(timeStartOutProbe / deltaT);
    const uint timeStepOutProbe = uint(timeOutProbe / deltaT);
Hkorb's avatar
Hkorb committed
    const uint timeStepStartPrecursor = uint(timeStartPrecursor / deltaT);
Hkorb's avatar
Hkorb committed
    //////////////////////////////////////////////////////////////////////////
    // create grid
    //////////////////////////////////////////////////////////////////////////
Hkorb's avatar
Hkorb committed
    vf::parallel::Communicator& communicator = *vf::parallel::MPICommunicator::getInstance();
Hkorb's avatar
Hkorb committed
    const int numberOfProcesses = communicator.getNumberOfProcesses();
    const int processID = communicator.getProcessID();
    const bool isMultiGPU = numberOfProcesses > 1;
Hkorb's avatar
Hkorb committed
    const real lengthXPerProcess = lengthX / numberOfProcesses;
    const real overlap = 8.0 * deltaX;
Hkorb's avatar
Hkorb committed
    const real xMin = processID * lengthXPerProcess;
    const real xMax = (processID + 1) * lengthXPerProcess;
    real xGridMin = processID * lengthXPerProcess;
    real xGridMax = (processID + 1) * lengthXPerProcess;
Hkorb's avatar
Hkorb committed
    const real yMin = c0o1;
    const real yMax = lengthY;
    const real zMin = c0o1;
    const real zMax = lengthZ;
Hkorb's avatar
Hkorb committed
    const bool isFirstSubDomain = isMultiGPU && (processID == 0);
    const bool isLastSubDomain = isMultiGPU && (processID == numberOfProcesses - 1);
    const bool isMidSubDomain = isMultiGPU && !(isFirstSubDomain || isLastSubDomain);

    if (isFirstSubDomain) {
        xGridMax += overlap;
    if (isFirstSubDomain && !usePrecursorInflow) {
Hkorb's avatar
Hkorb committed
        xGridMin -= overlap;
    }

    if (isLastSubDomain) {
        xGridMin -= overlap;
    }
Hkorb's avatar
Hkorb committed

    if (isLastSubDomain && !usePrecursorInflow) {
Hkorb's avatar
Hkorb committed
        xGridMax += overlap;
    }

    if (isMidSubDomain) {
        xGridMax += overlap;
        xGridMin -= overlap;
Hkorb's avatar
Hkorb committed
    auto gridBuilder = std::make_shared<MultipleGridBuilder>();
    auto scalingFactory = GridScalingFactory();
Hkorb's avatar
Hkorb committed

Hkorb's avatar
Hkorb committed
    gridBuilder->addCoarseGrid(xGridMin, c0o1, c0o1, xGridMax, lengthY, lengthZ, deltaX);
Hkorb's avatar
Hkorb committed

Hkorb's avatar
Hkorb committed
    if (useRefinement) {
        gridBuilder->setNumberOfLayers(4, 0);
        real xMaxRefinement = xGridMax;
        if (usePrecursorInflow) {
            xMaxRefinement = xGridMax - boundaryLayerHeight;
        }
        gridBuilder->addGrid(std::make_shared<Cuboid>(xGridMin, c0o1, c0o1, xMaxRefinement, lengthY, c1o2 * lengthZ), 1);
        scalingFactory.setScalingFactory(GridScalingFactory::GridScaling::ScaleCompressible);
Hkorb's avatar
Hkorb committed
    if (numberOfProcesses > 1) {
        gridBuilder->setSubDomainBox(std::make_shared<BoundingBox>(xMin, xMax, yMin, yMax, zMin, zMax));
        gridBuilder->setPeriodicBoundaryCondition(false, true, false);
    } else {
        gridBuilder->setPeriodicBoundaryCondition(!usePrecursorInflow, true, false);
Hkorb's avatar
Hkorb committed
    if (!usePrecursorInflow) {
Hkorb's avatar
Hkorb committed
        gridBuilder->setPeriodicShiftOnXBoundaryInYDirection(periodicShift);
    }

    gridBuilder->buildGrids(true); // buildGrids() has to be called before setting the BCs!!!!
Hkorb's avatar
Hkorb committed
    //////////////////////////////////////////////////////////////////////////
    // set parameters
    //////////////////////////////////////////////////////////////////////////
    auto para = std::make_shared<Parameter>(numberOfProcesses, communicator.getProcessID(), &config);

    para->setOutputPrefix(simulationName);

    para->setPrintFiles(true);

    if (!usePrecursorInflow)
        para->setForcing(pressureGradientLB, 0, 0);
    para->setVelocityLB(velocityLB);
    para->setViscosityLB(viscosityLB);
    para->setVelocityRatio(deltaX / deltaT);
    para->setViscosityRatio(deltaX * deltaX / deltaT);
    para->setDensityRatio(c1o1);

    para->setUseStreams(numberOfProcesses > 1);
    para->configureMainKernel(vf::collisionKernel::compressible::K17CompressibleNavierStokes);

    para->setTimestepStartOut(uint(timeStartOut / deltaT));
    para->setTimestepOut(uint(timeOut / deltaT));
    para->setTimestepEnd(uint(timeEnd / deltaT));

    std::vector<uint> devices(10);
    std::iota(devices.begin(), devices.end(), 0);
    para->setDevices(devices);
    para->setMaxDev(numberOfProcesses);
    if (usePrecursorInflow) {
        para->setInitialCondition([&](real coordX, real coordY, real coordZ, real& rho, real& vx, real& vy, real& vz) {
            rho = c0o1;
            vx = velocityProfile(coordZ) * deltaT / deltaX;
            vy = c0o1;
            vz = c0o1;
        });
    } else {
        para->setInitialCondition([&](real coordX, real coordY, real coordZ, real& rho, real& vx, real& vy, real& vz) {
            const real relativeX = coordX / lengthX;
            const real relativeY = coordY / lengthY;
            const real relativeZ = coordZ / lengthZ;

            const real horizontalPerturbation = sin(cPi * c16o1 * relativeX);
            const real verticalPerturbation = sin(cPi * c8o1 * relativeZ) / (pow(relativeZ, c2o1) + c1o1);
            const real perturbation = c2o1 * horizontalPerturbation * verticalPerturbation;

            rho = c0o1;
            vx = (velocityProfile(coordZ) + perturbation) * (c1o1 - c1o10 * abs(relativeY - c1o2)) * deltaT / deltaX;
            vy = perturbation * deltaT / deltaX;
            vz = c8o1 * frictionVelocity / vonKarmanConstant *
                 (sin(cPi * c8o1 * coordY / boundaryLayerHeight) * sin(cPi * c8o1 * relativeZ) +
                  sin(cPi * c8o1 * relativeX)) /
                 (pow(c1o2 * lengthZ - coordZ, c2o1) + c1o1) * deltaT / deltaX;
        });
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    if (numberOfProcesses > 1) {
        if (isFirstSubDomain || isMidSubDomain) {
            gridBuilder->findCommunicationIndices(communication_directions::PX);
            gridBuilder->setCommunicationProcess(communication_directions::PX, processID + 1);
        }

        if (isLastSubDomain || isMidSubDomain) {
            gridBuilder->findCommunicationIndices(communication_directions::MX, true);
            gridBuilder->setCommunicationProcess(communication_directions::MX, processID - 1);
Hkorb's avatar
Hkorb committed
        if (isFirstSubDomain && !usePrecursorInflow) {
            gridBuilder->findCommunicationIndices(communication_directions::MX);
            gridBuilder->setCommunicationProcess(communication_directions::MX, numberOfProcesses - 1);
Hkorb's avatar
Hkorb committed
        if (isLastSubDomain && !usePrecursorInflow) {
            gridBuilder->findCommunicationIndices(communication_directions::PX);
            gridBuilder->setCommunicationProcess(communication_directions::PX, 0);
Hkorb's avatar
Hkorb committed

Hkorb's avatar
Hkorb committed
    if (usePrecursorInflow) {
        if (!isMultiGPU || isFirstSubDomain) {
Hkorb's avatar
Hkorb committed
            auto precursor = createFileCollection(precursorDirectory + "precursor", TransientBCFileType::VTK);
            gridBuilder->setPrecursorBoundaryCondition(SideType::MX, precursor, timestepsBetweenReadsPrecursor);
Hkorb's avatar
Hkorb committed
        if (!isMultiGPU || isLastSubDomain) {
            gridBuilder->setPressureBoundaryCondition(SideType::PX, c0o1);
        }
    }
Hkorb's avatar
Hkorb committed
    gridBuilder->setStressBoundaryCondition(SideType::MZ, c0o1, c0o1, c1o1, samplingOffsetWallModel, roughnessLength,
                                            deltaX);
Hkorb's avatar
Hkorb committed
    gridBuilder->setSlipBoundaryCondition(SideType::PZ, c0o1, c0o1, -c1o1);
Hkorb's avatar
Hkorb committed
    BoundaryConditionFactory bcFactory = BoundaryConditionFactory();
    bcFactory.setVelocityBoundaryCondition(BoundaryConditionFactory::VelocityBC::VelocityInterpolatedCompressible);
    bcFactory.setStressBoundaryCondition(BoundaryConditionFactory::StressBC::StressBounceBackPressureCompressible);
    bcFactory.setSlipBoundaryCondition(BoundaryConditionFactory::SlipBC::SlipTurbulentViscosityCompressible);
Hkorb's avatar
Hkorb committed
    bcFactory.setPressureBoundaryCondition(BoundaryConditionFactory::PressureBC::OutflowNonReflective);
Hkorb's avatar
Hkorb committed
    if (useDistributionsForPrecursor) {
        bcFactory.setPrecursorBoundaryCondition(BoundaryConditionFactory::PrecursorBC::PrecursorDistributions);
Hkorb's avatar
Hkorb committed
    } else {
        bcFactory.setPrecursorBoundaryCondition(BoundaryConditionFactory::PrecursorBC::PrecursorNonReflectiveCompressible);
Hkorb's avatar
Hkorb committed
    //////////////////////////////////////////////////////////////////////////
    // add probes
    //////////////////////////////////////////////////////////////////////////
    auto cudaMemoryManager = std::make_shared<CudaMemoryManager>(para);

Hkorb's avatar
Hkorb committed
    if (!usePrecursorInflow && (isFirstSubDomain || !isMultiGPU)) {
        const auto planarAverageProbe = std::make_shared<PlanarAverageProbe>(
            para, cudaMemoryManager, para->getOutputPath(), "planarAverageProbe", timeStepStartAveraging,
            timeStepStartTemporalAveraging, timeStepAveraging, timeStepStartOutProbe, timeStepOutProbe, PlanarAverageProbe::PlaneNormal::z, true);
Henrik Asmuth's avatar
Henrik Asmuth committed
        planarAverageProbe->addAllAvailableStatistics();
        planarAverageProbe->setFileNameToNOut();
        para->addSampler(planarAverageProbe);
Hkorb's avatar
Hkorb committed

        const auto wallModelProbe = std::make_shared<WallModelProbe>(
            para, cudaMemoryManager, para->getOutputPath(), "wallModelProbe", timeStepStartAveraging,
            timeStepStartTemporalAveraging, timeStepAveraging / 4, timeStepStartOutProbe, timeStepOutProbe, false, true, true, para->getIsBodyForce());
        para->addSampler(wallModelProbe);
Hkorb's avatar
Hkorb committed

        para->setHasWallModelMonitor(true);
Henrik Asmuth's avatar
Henrik Asmuth committed
    }
Hkorb's avatar
Hkorb committed
    for (int iPlane = 0; iPlane < 3; iPlane++) {
Hkorb's avatar
Hkorb committed
        const std::string name = "planeProbe" + std::to_string(iPlane);
Hkorb's avatar
Hkorb committed
        const auto horizontalProbe =
Hkorb's avatar
Hkorb committed
            std::make_shared<Probe>(para, cudaMemoryManager, para->getOutputPath(), name, timeStepStartAveraging,
Hkorb's avatar
Hkorb committed
                                         averagingTimestepsPlaneProbes, timeStepStartOutProbe, timeStepOutProbe, false, false);
Hkorb's avatar
Hkorb committed
        horizontalProbe->addProbePlane(c0o1, c0o1, iPlane * lengthZ / c4o1, lengthX, lengthY, deltaX);
Hkorb's avatar
Hkorb committed
        horizontalProbe->addAllAvailableStatistics();
        para->addSampler(horizontalProbe);
Hkorb's avatar
Hkorb committed
    }

Hkorb's avatar
Hkorb committed
    auto crossStreamPlane = std::make_shared<Probe>(para, cudaMemoryManager, para->getOutputPath(), "crossStreamPlane",
Hkorb's avatar
Hkorb committed
                                                         timeStepStartAveraging, averagingTimestepsPlaneProbes,
                                                         timeStepStartOutProbe, timeStepOutProbe, false, false);
Hkorb's avatar
Hkorb committed
    crossStreamPlane->addProbePlane(c1o2 * lengthX, c0o1, c0o1, deltaX, lengthY, lengthZ);
Hkorb's avatar
Hkorb committed
    crossStreamPlane->addAllAvailableStatistics();
    para->addSampler(crossStreamPlane);
Hkorb's avatar
Hkorb committed

    if (usePrecursorInflow) {
Hkorb's avatar
Hkorb committed
        auto streamwisePlane = std::make_shared<Probe>(
Hkorb's avatar
Hkorb committed
            para, cudaMemoryManager, para->getOutputPath(), "streamwisePlane", timeStepStartAveraging,
            averagingTimestepsPlaneProbes, timeStepStartOutProbe, timeStepOutProbe, false, false);
Hkorb's avatar
Hkorb committed
        streamwisePlane->addProbePlane(c0o1, c1o2 * lengthY, c0o1, lengthX, deltaX, lengthZ);
Hkorb's avatar
Hkorb committed
        streamwisePlane->addAllAvailableStatistics();
        para->addSampler(streamwisePlane);
Hkorb's avatar
Hkorb committed
    if (writePrecursor) {
        const std::string fullPrecursorDirectory = para->getOutputPath() + precursorDirectory;
        const auto outputVariable =
            useDistributionsForPrecursor ? OutputVariable::Distributions : OutputVariable::Velocities;
        auto precursorWriter = std::make_shared<PrecursorWriter>(
            para, cudaMemoryManager, fullPrecursorDirectory, "precursor", positionXPrecursorSamplingPlane, c0o1, lengthY,
            c0o1, lengthZ, timeStepStartPrecursor, timeStepsWritePrecursor, outputVariable,
            maximumNumberOfTimestepsPerPrecursorFile);
        para->addSampler(precursorWriter);
Hkorb's avatar
Hkorb committed

    auto tmFactory = std::make_shared<TurbulenceModelFactory>(para);
    tmFactory->readConfigFile(config);

    VF_LOG_INFO("process parameter:");
    VF_LOG_INFO("Number of Processes {} process ID {}", numberOfProcesses, processID);
    if (isFirstSubDomain)
        VF_LOG_INFO("Process ID {} is the first subdomain");
    if (isLastSubDomain)
        VF_LOG_INFO("Process ID {} is the last subdomain");
    if (isMidSubDomain)
        VF_LOG_INFO("Process ID {} is a mid subdomain");
    printf("\n");
    Simulation simulation(para, cudaMemoryManager, gridBuilder, &bcFactory, tmFactory, &scalingFactory);
Hkorb's avatar
Hkorb committed
int main(int argc, char* argv[])
Henrik Asmuth's avatar
Henrik Asmuth committed
{
Hkorb's avatar
Hkorb committed
    try {
        vf::logging::Logger::initializeLogger();
        auto config = vf::basics::loadConfig(argc, argv, defaultConfigFile);
Hkorb's avatar
Hkorb committed
        run(config);
    } catch (const std::exception& e) {
        VF_LOG_WARNING("{}", e.what());
        return 1;
Henrik Asmuth's avatar
Henrik Asmuth committed
    }
    return 0;
}