/* * Copyright (c) 2017-2020 Nils Kohl. * * This file is part of HyTeG * (see https://i10git.cs.fau.de/hyteg/hyteg). * * This program 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. * * This program 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "core/DataTypes.h" #include "core/config/Config.h" #include "core/mpi/MPIManager.h" #include "hyteg/MeshQuality.hpp" #include "hyteg/composites/StrongFreeSlipWrapper.hpp" #include "hyteg/composites/UnsteadyDiffusion.hpp" #include "hyteg/dataexport/SQL.hpp" #include "hyteg/dataexport/TimingOutput.hpp" #include "hyteg/dataexport/VTKOutput.hpp" #include "hyteg/elementwiseoperators/P2P1ElementwiseBlendingStokesOperator.hpp" #include "hyteg/functions/FunctionProperties.hpp" #include "hyteg/geometry/AnnulusMap.hpp" #include "hyteg/geometry/IcosahedralShellMap.hpp" #include "hyteg/geometry/SphereTools.hpp" #include "hyteg/gridtransferoperators/P2P1StokesToP2P1StokesProlongation.hpp" #include "hyteg/gridtransferoperators/P2P1StokesToP2P1StokesRestriction.hpp" #include "hyteg/memory/MemoryAllocation.hpp" #include "hyteg/mesh/MeshInfo.hpp" #include "hyteg/numerictools/CFDHelpers.hpp" #include "hyteg/numerictools/SphericalHarmonicsTool.hpp" #include "hyteg/p1functionspace/P1ConstantOperator.hpp" #include "hyteg/p2functionspace/P2ConstantOperator.hpp" #include "hyteg/p2functionspace/P2Function.hpp" #include "hyteg/p2functionspace/P2ProjectNormalOperator.hpp" #include "hyteg/petsc/PETScBlockPreconditionedStokesSolver.hpp" #include "hyteg/petsc/PETScLUSolver.hpp" #include "hyteg/petsc/PETScManager.hpp" #include "hyteg/petsc/PETScMinResSolver.hpp" #include "hyteg/primitivestorage/PrimitiveStorage.hpp" #include "hyteg/primitivestorage/SetupPrimitiveStorage.hpp" #include "hyteg/primitivestorage/Visualization.hpp" #include "hyteg/primitivestorage/loadbalancing/SimpleBalancer.hpp" #include "hyteg/solvers/CGSolver.hpp" #include "hyteg/solvers/FullMultigridSolver.hpp" #include "hyteg/solvers/GaussSeidelSmoother.hpp" #include "hyteg/solvers/GeometricMultigridSolver.hpp" #include "hyteg/solvers/MinresSolver.hpp" #include "hyteg/solvers/UzawaSmoother.hpp" #include "hyteg/solvers/WeightedJacobiSmoother.hpp" #include "hyteg/solvers/controlflow/SolverLoop.hpp" #include "hyteg/solvers/preconditioners/stokes/StokesBlockDiagonalPreconditioner.hpp" #include "hyteg/solvers/preconditioners/stokes/StokesPressureBlockPreconditioner.hpp" #include "hyteg/solvers/preconditioners/stokes/StokesVelocityBlockBlockDiagonalPreconditioner.hpp" #include "hyteg/solvers/solvertemplates/StokesSolverTemplates.hpp" #include "coupling_hyteg_convection_particles/MMOCTransport.hpp" #include "sqlite/SQLite.h" namespace hyteg { using walberla::int_c; using walberla::real_t; using walberla::math::pi; #if 0 static std::string getDateTimeID() { std::vector< char > cTimeString( 64 ); WALBERLA_ROOT_SECTION() { std::time_t t; std::time( &t ); std::strftime( cTimeString.data(), 64, "%F_%H-%M-%S", std::localtime( &t ) ); } walberla::mpi::broadcastObject( cTimeString ); std::string timeString( cTimeString.data() ); return timeString; } #endif enum class StokesSolverType : int { PETSC_MUMPS = 0, PETSC_MINRES_JACOBI = 1, PETSC_MINRES_BOOMER = 2, HYTEG_MINRES = 3, HYTEG_MINRES_GMG = 4, HYTEG_UZAWA_V = 5, HYTEG_UZAWA_FMG = 6 }; enum class DiffusionSolverType : int { PETSC_MINRES = 0, HYTEG_CG = 1, HYTEG_GMG = 2 }; struct DomainInfo { bool threeDim = false; real_t rMin = 0; real_t rMax = 0; uint_t nTan = 0; uint_t nRad = 0; real_t domainVolume() const { if ( !threeDim ) { return walberla::math::pi * rMax * rMax - walberla::math::pi * rMin * rMin; } else { return ( 4. / 3. ) * walberla::math::pi * rMax * rMax * rMax - ( 4. / 3. ) * walberla::math::pi * rMin * rMin * rMin; } } }; struct SolverInfo { StokesSolverType stokesSolverType = StokesSolverType::PETSC_MUMPS; uint_t stokesMaxNumIterations = 10; real_t stokesAbsoluteResidualUTolerance = 0; real_t stokesRelativeResidualUTolerance = 0; uint_t uzawaInnerIterations = 10; uint_t uzawaPreSmooth = 6; uint_t uzawaPostSmooth = 6; DiffusionSolverType diffusionSolverType = DiffusionSolverType::PETSC_MINRES; uint_t diffusionMaxNumIterations = 10000; real_t diffusionAbsoluteResidualUTolerance = 10000; real_t uzawaOmega = 0.3; }; struct OutputInfo { bool vtk; bool vtkOutputVelocity; bool sphericalTemperatureSlice; int sphericalTemperatureSliceNumMeridians; int sphericalTemperatureSliceNumParallels; int sphericalTemperatureSliceIcoRefinements; uint_t printInterval; uint_t vtkInterval; uint_t sphericalTemperatureSliceInterval; bool vtkOutputVertexDoFs; std::string outputDirectory; std::string outputBaseName; }; /// Calculates and returns /// /// ||u||_L2 = sqrt( u^T M u ) /// template < typename FunctionType, typename MassOperator > real_t normL2( const FunctionType& u, const FunctionType& tmp, const MassOperator& M, const uint_t& level, const DoFType& flag ) { tmp.interpolate( 0, level ); M.apply( u, tmp, level, flag ); return std::sqrt( u.dotGlobal( tmp, level, flag ) ); } template < typename FunctionType, typename MassOperator > real_t normL2Squared( const FunctionType& u, const FunctionType& tmp, const MassOperator& M, const uint_t& level, const DoFType& flag ) { tmp.interpolate( 0, level ); M.apply( u, tmp, level, flag ); return u.dotGlobal( tmp, level, flag ); } template < typename StokesFunction, typename StokesOperator, typename MassOperatorVelocity, typename MassOperatorPressure > void calculateStokesResiduals( const StokesOperator& A, const MassOperatorVelocity& Mu, const MassOperatorPressure& Mp, const StokesFunction& x, const StokesFunction& b, uint_t level, StokesFunction& r, StokesFunction& tmp, real_t& residual ) { tmp.interpolate( 0, level, All ); r.interpolate( 0, level, All ); A.apply( x, tmp, level, Inner | NeumannBoundary | FreeslipBoundary ); r.assign( { 1.0, -1.0 }, { b, tmp }, level, Inner | NeumannBoundary | FreeslipBoundary ); residual = normL2Squared( r.uvw[0], tmp.uvw[0], Mu, level, Inner | NeumannBoundary | FreeslipBoundary ); residual += normL2Squared( r.uvw[1], tmp.uvw[1], Mu, level, Inner | NeumannBoundary | FreeslipBoundary ); if ( x.getStorage()->hasGlobalCells() ) { residual += normL2Squared( r.uvw[2], tmp.uvw[2], Mu, level, Inner | NeumannBoundary | FreeslipBoundary ); } residual += normL2Squared( r.p, tmp.p, Mp, level, Inner | NeumannBoundary | FreeslipBoundary ); residual = std::sqrt( residual ); } template < typename UnsteadyDiffusion, typename UnsteadyDiffusionOperator, typename LaplaceOperator, typename MassOperatorVelocity, typename ScalarFuncionType > void calculateDiffusionResidual( UnsteadyDiffusion& unsteadyDiffusion, const UnsteadyDiffusionOperator& unsteadyDiffusionOperator, const LaplaceOperator& laplacian, const MassOperatorVelocity& Mu, const ScalarFuncionType& c, const ScalarFuncionType& cOld, const ScalarFuncionType& f, ScalarFuncionType& r, ScalarFuncionType& tmp, uint_t level, real_t& residual ) { unsteadyDiffusion.calculateResidual( unsteadyDiffusionOperator, laplacian, Mu, c, cOld, f, f, r, level, Inner | NeumannBoundary | FreeslipBoundary ); residual = normL2( r, tmp, Mu, level, Inner | NeumannBoundary | FreeslipBoundary ); } template < typename StokesFunction, typename VelocityMass > real_t velocityRMS( const StokesFunction& u, const StokesFunction& tmp, const VelocityMass& M, real_t domainVolume, uint_t level ) { auto norm = std::pow( normL2( u.uvw[0], tmp.uvw[0], M, level, All ), 2.0 ) + std::pow( normL2( u.uvw[1], tmp.uvw[1], M, level, All ), 2.0 ); if ( u.getStorage()->hasGlobalCells() ) { norm += std::pow( normL2( u.uvw[2], tmp.uvw[2], M, level, All ), 2.0 ); } return std::sqrt( norm / domainVolume ); } std::shared_ptr< SetupPrimitiveStorage > createSetupStorage( DomainInfo domainInfo ) { MeshInfo meshInfo = MeshInfo::emptyMeshInfo(); if ( domainInfo.threeDim ) { meshInfo = MeshInfo::meshSphericalShell( domainInfo.nTan, domainInfo.nRad, domainInfo.rMin, domainInfo.rMax ); } else { meshInfo = MeshInfo::meshAnnulus( domainInfo.rMin, domainInfo.rMax, MeshInfo::CRISS, domainInfo.nTan, domainInfo.nRad ); } auto setupStorage = std::make_shared< SetupPrimitiveStorage >( meshInfo, walberla::uint_c( walberla::mpi::MPIManager::instance()->numProcesses() ) ); loadbalancing::roundRobinVolume( *setupStorage ); setupStorage->setMeshBoundaryFlagsOnBoundary( 1, 0, true ); if ( domainInfo.threeDim ) { IcosahedralShellMap::setMap( *setupStorage ); } else { AnnulusMap::setMap( *setupStorage ); } return setupStorage; } void runBenchmark( real_t cflMax, real_t rayleighNumber, bool fixedTimeStep, real_t dtConstant, uint_t maxNumTimeSteps, uint_t minLevel, uint_t level, SolverInfo solverInfo, DomainInfo domainInfo, bool predictorCorrector, real_t simulationTime, OutputInfo outputInfo, bool verbose ) { walberla::WcTimer localTimer; const bool outputTimingJSON = true; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Building setup storage ..." ); } auto setupStorage = createSetupStorage( domainInfo ); if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Building distributed storage ..." ); } auto storage = std::make_shared< PrimitiveStorage >( *setupStorage, 1 ); if ( outputInfo.vtk ) { if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Writing domain partitioning VTK ..." ); } writeDomainPartitioningVTK( storage, outputInfo.outputDirectory, outputInfo.outputBaseName + "_domain" ); } auto timer = storage->getTimingTree(); timer->start( "Setup" ); if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Gathering domain information ..." ); } const uint_t unknownsTemperature = numberOfGlobalDoFs< P2FunctionTag >( *storage, level ); const uint_t unknownsStokes = numberOfGlobalDoFs< P2P1TaylorHoodFunctionTag >( *storage, level ); const real_t hMin = MeshQuality::getMinimalEdgeLength( storage, level ); const real_t hMax = MeshQuality::getMaximalEdgeLength( storage, level ); const real_t diffusivity = 1.0; const real_t internalHeating = 0.0; const real_t sliceEvaluationRadius = domainInfo.rMin + 0.5 * ( domainInfo.rMax - domainInfo.rMin ); WALBERLA_LOG_INFO_ON_ROOT( "" ) WALBERLA_LOG_INFO_ON_ROOT( "Benchmark name: " << outputInfo.outputBaseName ) WALBERLA_LOG_INFO_ON_ROOT( " - time discretization: " ) WALBERLA_LOG_INFO_ON_ROOT( " + fixed time step: " << fixedTimeStep ) if ( fixedTimeStep ) { WALBERLA_LOG_INFO_ON_ROOT( " + dt (constant): " << dtConstant ) } else { WALBERLA_LOG_INFO_ON_ROOT( " + max CFL: " << cflMax ) } WALBERLA_LOG_INFO_ON_ROOT( " + simulation time: " << simulationTime ) WALBERLA_LOG_INFO_ON_ROOT( " - space discretization: " ) WALBERLA_LOG_INFO_ON_ROOT( " + rMin: " << domainInfo.rMin ) WALBERLA_LOG_INFO_ON_ROOT( " + rMax: " << domainInfo.rMax ) WALBERLA_LOG_INFO_ON_ROOT( " + nTan: " << domainInfo.nTan ) WALBERLA_LOG_INFO_ON_ROOT( " + nRad: " << domainInfo.nRad ) WALBERLA_LOG_INFO_ON_ROOT( " + dimensions: " << ( storage->hasGlobalCells() ? "3" : "2" ) ) WALBERLA_LOG_INFO_ON_ROOT( " + level: " << level ) WALBERLA_LOG_INFO_ON_ROOT( " + unknowns temperature, including boundary: " << unknownsTemperature ) for ( uint_t l = minLevel; l <= level; l++ ) { const uint_t unknownsStokesLevel = numberOfGlobalDoFs< P2P1TaylorHoodFunctionTag >( *storage, l ); WALBERLA_LOG_INFO_ON_ROOT( " + unknowns Stokes, including boundary, level " << l << ": " << unknownsStokesLevel ) } WALBERLA_LOG_INFO_ON_ROOT( " + h_min: " << hMin ) WALBERLA_LOG_INFO_ON_ROOT( " + h_max: " << hMax ) WALBERLA_LOG_INFO_ON_ROOT( " - benchmark settings: " ) WALBERLA_LOG_INFO_ON_ROOT( " + Rayleigh number: " << rayleighNumber ) WALBERLA_LOG_INFO_ON_ROOT( " + internal heating: " << internalHeating ) WALBERLA_LOG_INFO_ON_ROOT( " - app settings: " ) WALBERLA_LOG_INFO_ON_ROOT( " + Stokes solver: " << int_c( solverInfo.stokesSolverType ) ) WALBERLA_LOG_INFO_ON_ROOT( " + Uzawa omega: " << solverInfo.uzawaOmega ) WALBERLA_LOG_INFO_ON_ROOT( " + diffusion solver: " << int_c( solverInfo.diffusionSolverType ) ) if ( outputInfo.vtk ) { WALBERLA_LOG_INFO_ON_ROOT( " + VTK interval: " << outputInfo.vtkInterval ) } if ( outputInfo.sphericalTemperatureSlice ) { WALBERLA_LOG_INFO_ON_ROOT( " + spherical slice output interval: " << outputInfo.sphericalTemperatureSliceInterval ) } WALBERLA_LOG_INFO_ON_ROOT( " + print interval: " << outputInfo.printInterval ) WALBERLA_LOG_INFO_ON_ROOT( " + output directory: " << outputInfo.outputDirectory ) WALBERLA_LOG_INFO_ON_ROOT( " + output base name: " << outputInfo.outputBaseName ) WALBERLA_LOG_INFO_ON_ROOT( "" ) auto storageInfo = storage->getGlobalInfo(); WALBERLA_LOG_INFO_ON_ROOT( storageInfo ); WALBERLA_LOG_INFO_ON_ROOT( "" ); FixedSizeSQLDB db( outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + ".db" ); db.setConstantEntry( "ra", rayleighNumber ); db.setConstantEntry( "ntan", domainInfo.nTan ); db.setConstantEntry( "nrad", domainInfo.nRad ); db.setConstantEntry( "rmin", domainInfo.rMin ); db.setConstantEntry( "rmax", domainInfo.rMax ); db.setConstantEntry( "fixed_time_step", fixedTimeStep ); db.setConstantEntry( "cfl_max", cflMax ); db.setConstantEntry( "dt_constant", dtConstant ); db.setConstantEntry( "simulation_time", simulationTime ); db.setConstantEntry( "level", uint_c( level ) ); db.setConstantEntry( "unknowns_temperature", uint_c( unknownsTemperature ) ); db.setConstantEntry( "unknowns_stokes", uint_c( unknownsStokes ) ); db.setConstantEntry( "h_min", hMin ); db.setConstantEntry( "h_max", hMax ); db.setConstantEntry( "num_macro_cells", uint_c( setupStorage->getNumberOfCells() ) ); db.setConstantEntry( "num_macro_faces", uint_c( setupStorage->getNumberOfFaces() ) ); db.setConstantEntry( "num_macro_edges", uint_c( setupStorage->getNumberOfEdges() ) ); db.setConstantEntry( "num_macro_vertices", uint_c( setupStorage->getNumberOfVertices() ) ); db.setConstantEntry( "num_macro_primitives", uint_c( setupStorage->getNumberOfPrimitives() ) ); db.setConstantEntry( "diffusivity", diffusivity ); db.setConstantEntry( "stokes_solver_type", int64_c( solverInfo.stokesSolverType ) ); db.setConstantEntry( "uzawa_omega", solverInfo.uzawaOmega ); db.setConstantEntry( "uzawa_inner_smooth", solverInfo.uzawaInnerIterations ); db.setConstantEntry( "uzawa_pre_smooth", solverInfo.uzawaPreSmooth ); db.setConstantEntry( "uzawa_post_smooth", solverInfo.uzawaPostSmooth ); db.setConstantEntry( "stokes_absolute_residual_threshold", solverInfo.stokesAbsoluteResidualUTolerance ); db.setConstantEntry( "stokes_relative_residual_threshold", solverInfo.stokesRelativeResidualUTolerance ); typedef P2P1TaylorHoodFunction< real_t > StokesFunction; typedef P2Function< real_t > ScalarFunction; typedef P2P1ElementwiseBlendingStokesOperator StokesOperator; typedef P2ElementwiseBlendingLaplaceOperator LaplaceOperator; typedef P2ElementwiseBlendingMassOperator MassOperatorVelocity; typedef P1ConstantMassOperator MassOperatorPressure; typedef P2ElementwiseUnsteadyDiffusionOperator UnsteadyDiffusionOperator; BoundaryCondition bcVelocity; BoundaryCondition bcTemperature; bcVelocity.createDirichletBC( "outerBoundaryVelocity", 1 ); bcVelocity.createDirichletBC( "innerBoundaryVelocity", 1 ); bcTemperature.createDirichletBC( "outerBoundaryTemperature", 1 ); bcTemperature.createDirichletBC( "innerBoundaryTemperature", 1 ); if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Allocating functions ..." ); } ScalarFunction c( "c", storage, minLevel, level, bcTemperature ); ScalarFunction cPr( "cPr", storage, minLevel, level, bcTemperature ); ScalarFunction cOld( "cOld", storage, minLevel, level, bcTemperature ); ScalarFunction cTmp( "cTmp", storage, minLevel, level, bcTemperature ); ScalarFunction cTmp2( "cTmp2", storage, minLevel, level, bcTemperature ); ScalarFunction q( "q", storage, minLevel, level, bcTemperature ); StokesFunction u( "u", storage, minLevel, level, bcVelocity ); StokesFunction uLast( "uLast", storage, minLevel, level, bcVelocity ); StokesFunction f( "f", storage, minLevel, level, bcVelocity ); StokesFunction outwardNormal( "outwardNormal", storage, minLevel, level, bcVelocity ); StokesFunction stokesTmp( "stokesTmp", storage, minLevel, level, bcVelocity ); StokesFunction stokesResidual( "stokesResidual", storage, minLevel, level, bcVelocity ); ScalarFunction uMagnitudeSquared( "uMagnitudeSquared", storage, minLevel, level, bcVelocity ); ScalarFunction uTmp( "uTmp", storage, minLevel, level, bcVelocity ); ScalarFunction uTmp2( "uTmp2", storage, minLevel, level, bcVelocity ); SphericalHarmonicsTool sphTool( 6 ); std::function< real_t( const Point3D& ) > initialTemperature = [&]( const Point3D& x ) { auto radius = std::sqrt( x[0] * x[0] + x[1] * x[1] + x[2] * x[2] ); const auto linearSlope = std::max( real_c( 0 ), 1 - ( ( radius - domainInfo.rMin ) / ( domainInfo.rMax - domainInfo.rMin ) ) ); const auto xn = x / x.norm(); // sph ~ in (-2, 2) const auto powerPertubation = sphTool.shconvert_eval( 4, 3, xn[0], xn[1], xn[2] ); const auto powerSlope = std::pow( linearSlope, 4 + powerPertubation ); return powerSlope; }; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Interpolating initial temperature and internal heating ..." ); } for ( uint_t l = minLevel; l <= level; l++ ) { c.interpolate( initialTemperature, l, All ); q.interpolate( internalHeating, l, All ); } std::function< real_t( const Point3D& ) > normalX = []( const Point3D& x ) { return x[0] / x.norm(); }; std::function< real_t( const Point3D& ) > normalY = []( const Point3D& x ) { return x[1] / x.norm(); }; std::function< real_t( const Point3D& ) > normalZ = []( const Point3D& x ) { return x[2] / x.norm(); }; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Interpolating normals ..." ); } for ( uint_t l = minLevel; l <= level; l++ ) { outwardNormal.uvw.interpolate( { normalX, normalY, normalZ }, l ); } if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Preparing operators ..." ); } UnsteadyDiffusionOperator diffusionOperator( storage, minLevel, level, 1.0, diffusivity, DiffusionTimeIntegrator::ImplicitEuler ); auto A = std::make_shared< StokesOperator >( storage, minLevel, level ); LaplaceOperator L( storage, minLevel, level ); MassOperatorVelocity MVelocity( storage, minLevel, level ); MassOperatorPressure MPressure( storage, minLevel, level ); MMOCTransport< ScalarFunction > transport( storage, minLevel, level, TimeSteppingScheme::RK4 ); if ( storage->hasGlobalCells() ) { A->computeAndStoreLocalElementMatrices(); L.computeAndStoreLocalElementMatrices(); MVelocity.computeAndStoreLocalElementMatrices(); } if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Preparing solvers ..." ); } std::shared_ptr< Solver< StokesOperator > > stokesSolver; std::shared_ptr< Solver< StokesOperator > > stokesSolverBlockPrecMinRes; real_t initialResiudalU = 0; real_t vCycleResidualULast = 0; real_t vCycleResidualCLast = 0; uint_t numVCycles = 0; real_t averageResidualReductionU = 0; if ( solverInfo.stokesSolverType == StokesSolverType::PETSC_MUMPS ) { stokesSolver = std::make_shared< PETScLUSolver< StokesOperator > >( storage, level ); } else if ( solverInfo.stokesSolverType == StokesSolverType::PETSC_MINRES_JACOBI || solverInfo.stokesSolverType == StokesSolverType::PETSC_MINRES_BOOMER ) { auto velocityPreconditionerType = 1; if ( solverInfo.stokesSolverType == StokesSolverType::PETSC_MINRES_BOOMER ) { velocityPreconditionerType = 3; } auto stokesSolverTmp = std::make_shared< PETScBlockPreconditionedStokesSolver< StokesOperator > >( storage, level, solverInfo.stokesAbsoluteResidualUTolerance, solverInfo.stokesMaxNumIterations, velocityPreconditionerType, 1 ); stokesSolverTmp->reassembleMatrix( false ); stokesSolverTmp->setVerbose( verbose ); stokesSolver = stokesSolverTmp; } else if ( solverInfo.stokesSolverType == StokesSolverType::HYTEG_MINRES ) { stokesSolver = solvertemplates::stokesMinResSolver< StokesOperator >( storage, level, solverInfo.stokesAbsoluteResidualUTolerance, solverInfo.stokesMaxNumIterations, verbose ); } else if ( solverInfo.stokesSolverType == StokesSolverType::HYTEG_MINRES_GMG ) { typedef CGSolver< P2ElementwiseBlendingLaplaceOperator > CoarseGridSolver_T; typedef GeometricMultigridSolver< P2ElementwiseBlendingLaplaceOperator > GMGSolver_T; auto coarseGridSolver = std::make_shared< CoarseGridSolver_T >( storage, minLevel, level ); auto smoother = std::make_shared< WeightedJacobiSmoother< P2ElementwiseBlendingLaplaceOperator > >( storage, minLevel, level, 0.66 ); auto prolongationOperator = std::make_shared< P2toP2QuadraticProlongation >(); auto restrictionOperator = std::make_shared< P2toP2QuadraticRestriction >(); auto gmgSolver = std::make_shared< GMGSolver_T >( storage, smoother, coarseGridSolver, restrictionOperator, prolongationOperator, minLevel, level, 3, 3 ); auto lumpedInvPressureMass = std::make_shared< P1LumpedInvMassOperator >( storage, minLevel, level ); typedef StokesBlockDiagonalPreconditioner< StokesOperator, P1LumpedInvMassOperator > Preconditioner_T; auto preconditioner = std::make_shared< Preconditioner_T >( storage, minLevel, level, 3, gmgSolver ); auto stokesSolverTmp = std::make_shared< MinResSolver< StokesOperator > >( storage, minLevel, level, solverInfo.stokesMaxNumIterations, 1e-30, preconditioner ); stokesSolverTmp->setPrintInfo( verbose ); stokesSolverTmp->setAbsoluteTolerance( solverInfo.stokesAbsoluteResidualUTolerance ); if ( solverInfo.stokesSolverType == StokesSolverType::HYTEG_MINRES_GMG ) { stokesSolver = stokesSolverTmp; } else { stokesSolverBlockPrecMinRes = stokesSolverTmp; } } else if ( solverInfo.stokesSolverType == StokesSolverType::HYTEG_UZAWA_V || solverInfo.stokesSolverType == StokesSolverType::HYTEG_UZAWA_FMG ) { auto prolongationOperator = std::make_shared< P2P1StokesToP2P1StokesProlongation >(); auto restrictionOperator = std::make_shared< P2P1StokesToP2P1StokesRestriction >( true ); std::shared_ptr< Solver< typename StokesOperator::VelocityOperator_T > > smoother = std::make_shared< WeightedJacobiSmoother< typename StokesOperator::VelocityOperator_T > >( storage, minLevel, level, 0.66 ); auto uzawaVelocityPreconditioner = std::make_shared< StokesVelocityBlockBlockDiagonalPreconditioner< StokesOperator > >( storage, smoother ); auto uzawaSmoother = std::make_shared< UzawaSmoother< StokesOperator > >( storage, uzawaVelocityPreconditioner, minLevel, level, solverInfo.uzawaOmega, Inner | NeumannBoundary, solverInfo.uzawaInnerIterations ); std::shared_ptr< Solver< StokesOperator > > coarseGridSolverInternal; auto petscSolverInternalTmp = std::make_shared< PETScBlockPreconditionedStokesSolver< StokesOperator > >( storage, minLevel, solverInfo.stokesAbsoluteResidualUTolerance, 1000, 1 ); petscSolverInternalTmp->setVerbose( verbose ); auto coarseGridSolver = petscSolverInternalTmp; auto multigridSolver = std::make_shared< GeometricMultigridSolver< StokesOperator > >( storage, uzawaSmoother, coarseGridSolver, restrictionOperator, prolongationOperator, minLevel, level, solverInfo.uzawaPreSmooth, solverInfo.uzawaPostSmooth, 2, CycleType::VCYCLE ); if ( solverInfo.stokesSolverType == StokesSolverType::HYTEG_UZAWA_V ) { auto stopIterationCallback = [&]( const StokesOperator& _A, const StokesFunction& _u, const StokesFunction& _b, const uint_t _level ) { real_t r_u; calculateStokesResiduals( _A, MVelocity, MPressure, _u, _b, _level, stokesResidual, stokesTmp, r_u ); if ( numVCycles == 0 ) { WALBERLA_LOG_INFO_ON_ROOT( walberla::format( "[Uzawa] iter %3d | residual: %10.5e | initial ", 0, vCycleResidualULast ) ); } auto reductionRateU = r_u / vCycleResidualULast; vCycleResidualULast = r_u; numVCycles++; averageResidualReductionU += reductionRateU; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( walberla::format( "[Uzawa] iter %3d | residual: %10.5e | reduction: %10.5e ", numVCycles, r_u, reductionRateU ) ); } if ( r_u / initialResiudalU < solverInfo.stokesRelativeResidualUTolerance ) { WALBERLA_LOG_INFO_ON_ROOT( "[Uzawa] reached relative residual threshold" ) return true; } if ( r_u < solverInfo.stokesAbsoluteResidualUTolerance ) { WALBERLA_LOG_INFO_ON_ROOT( "[Uzawa] reached absolute residual threshold" ) return true; } if ( reductionRateU > 0.8 ) { WALBERLA_LOG_INFO_ON_ROOT( "[Uzawa] reached convergence rate threshold" ) return true; } return false; }; stokesSolver = std::make_shared< SolverLoop< StokesOperator > >( multigridSolver, solverInfo.stokesMaxNumIterations, stopIterationCallback ); } else { auto fmgSolver = std::make_shared< FullMultigridSolver< StokesOperator > >( storage, multigridSolver, prolongationOperator, minLevel, level, 3 ); stokesSolver = fmgSolver; } } else { WALBERLA_ABORT( "Invalid solver type." ); } std::shared_ptr< Solver< UnsteadyDiffusionOperator > > diffusionLinearSolver; UnsteadyDiffusion< ScalarFunction, UnsteadyDiffusionOperator, LaplaceOperator, MassOperatorVelocity > diffusionSolver( storage, minLevel, level, diffusionLinearSolver ); if ( solverInfo.diffusionSolverType == DiffusionSolverType::PETSC_MINRES ) { auto internalDiffusionSolver = std::make_shared< PETScMinResSolver< UnsteadyDiffusionOperator > >( storage, level, 1e-30, solverInfo.diffusionAbsoluteResidualUTolerance, solverInfo.diffusionMaxNumIterations ); internalDiffusionSolver->reassembleMatrix( true ); diffusionLinearSolver = internalDiffusionSolver; } else if ( solverInfo.diffusionSolverType == DiffusionSolverType::HYTEG_CG ) { auto internalDiffusionSolver = std::make_shared< CGSolver< UnsteadyDiffusionOperator > >( storage, minLevel, level, solverInfo.diffusionMaxNumIterations, solverInfo.diffusionAbsoluteResidualUTolerance ); internalDiffusionSolver->setPrintInfo( verbose ); diffusionLinearSolver = internalDiffusionSolver; } else if ( solverInfo.diffusionSolverType == DiffusionSolverType::HYTEG_GMG ) { WALBERLA_ABORT( "Somethings not working here for 3D" ); typedef CGSolver< UnsteadyDiffusionOperator > CoarseGridSolver_T; typedef GeometricMultigridSolver< UnsteadyDiffusionOperator > GMGSolver_T; auto coarseGridSolver = std::make_shared< CoarseGridSolver_T >( storage, minLevel, level ); auto smoother = std::make_shared< WeightedJacobiSmoother< UnsteadyDiffusionOperator > >( storage, minLevel, level, 0.66 ); auto prolongationOperator = std::make_shared< P2toP2QuadraticProlongation >(); auto restrictionOperator = std::make_shared< P2toP2QuadraticRestriction >(); auto gmgSolver = std::make_shared< GMGSolver_T >( storage, smoother, coarseGridSolver, restrictionOperator, prolongationOperator, minLevel, level, 3, 3 ); auto stopIterationCallback = [&]( const UnsteadyDiffusionOperator&, const ScalarFunction& _u, const ScalarFunction& _b, const uint_t ) { real_t r_c; calculateDiffusionResidual( diffusionSolver, diffusionOperator, L, MVelocity, _u, cOld, _b, cTmp, cTmp2, level, r_c ); auto reductionRateC = r_c / vCycleResidualCLast; vCycleResidualCLast = r_c; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( walberla::format( "[Diffusion GMG] residual: %10.5e | reduction: %10.5e", r_c, reductionRateC ) ); } if ( r_c < solverInfo.diffusionAbsoluteResidualUTolerance ) { return true; } if ( reductionRateC > 0.8 ) { return true; } return false; }; diffusionLinearSolver = std::make_shared< SolverLoop< UnsteadyDiffusionOperator > >( gmgSolver, 20, stopIterationCallback ); } diffusionSolver.setSolver( diffusionLinearSolver ); real_t timeTotal = 0; real_t vMax = 0; real_t vRms = 0; real_t residualU = 0; real_t timeStepTotal = 0; real_t timeStokes = 0; real_t timeMMOC = 0; real_t timeDiffusion = 0; real_t timeVTK = 0; hyteg::VTKOutput vtkOutput( outputInfo.outputDirectory, outputInfo.outputBaseName, storage, outputInfo.vtkInterval ); vtkOutput.setVTKDataFormat( vtk::DataFormat::BINARY ); if ( outputInfo.vtkOutputVertexDoFs ) { vtkOutput.add( c.getVertexDoFFunction() ); } else { vtkOutput.add( c ); } if ( outputInfo.vtkOutputVelocity ) { if ( outputInfo.vtkOutputVertexDoFs ) { vtkOutput.add( u.uvw[0].getVertexDoFFunction() ); vtkOutput.add( u.uvw[1].getVertexDoFFunction() ); vtkOutput.add( u.uvw[2].getVertexDoFFunction() ); } else { vtkOutput.add( u ); } } else { if ( outputInfo.vtkOutputVertexDoFs ) { vtkOutput.add( uMagnitudeSquared.getVertexDoFFunction() ); } else { vtkOutput.add( uMagnitudeSquared ); } } WALBERLA_LOG_INFO_ON_ROOT( "" ); printFunctionAllocationInfo( *storage, 1 ); WALBERLA_LOG_INFO_ON_ROOT( "" ); printCurrentMemoryUsage(); WALBERLA_LOG_INFO_ON_ROOT( "" ); timer->stop( "Setup" ); timer->start( "Simulation" ); uint_t timeStep = 0; walberla::WcTimer timeStepTimer; if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Initial Stokes solve ..." ); } timeStepTimer.start(); for ( uint_t l = minLevel; l <= level; l++ ) { MVelocity.apply( c, f.uvw[0], l, All ); MVelocity.apply( c, f.uvw[1], l, All ); if ( storage->hasGlobalCells() ) { MVelocity.apply( c, f.uvw[2], l, All ); } f.uvw.multElementwise( { f.uvw, outwardNormal.uvw }, l ); f.uvw.assign( { rayleighNumber }, { f.uvw }, l, All ); } calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); initialResiudalU = residualU; vCycleResidualULast = residualU; localTimer.start(); stokesSolver->solve( *A, u, f, level ); localTimer.end(); timeStokes = localTimer.last(); calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); if ( storage->hasGlobalCells() ) { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], u.uvw[2], uTmp, uMagnitudeSquared, level, All ); } else { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], uTmp, uMagnitudeSquared, level, All ); } localTimer.start(); if ( outputInfo.vtk ) { vtkOutput.write( level ); } localTimer.end(); timeVTK = localTimer.last(); if ( outputInfo.sphericalTemperatureSlice && timeStep % outputInfo.sphericalTemperatureSliceInterval == 0 ) { if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Evaluating spherical slices ..." ); } evaluateSphericalSliceUV( sliceEvaluationRadius, outputInfo.sphericalTemperatureSliceNumMeridians, outputInfo.sphericalTemperatureSliceNumParallels, c, level, outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + "_temp_slice_uv_" + std::to_string( timeStep ) + ".csv" ); evaluateSphericalSliceIco( sliceEvaluationRadius, outputInfo.sphericalTemperatureSliceIcoRefinements, c, level, outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + "_temp_slice_ico_" + std::to_string( timeStep ) + ".csv" ); if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Done." ); } } timeStepTimer.end(); timeStepTotal = timeStepTimer.last(); WALBERLA_LOG_INFO_ON_ROOT( " timestep | dt | time total | velocity RMS | velocity max magnitude | residual u | total | Stokes | MMOC | diff | VTK |" ) WALBERLA_LOG_INFO_ON_ROOT( "----------+--------------+--------------+--------------+------------------------+--------------+--------+--------+--------+--------+--------+" ) WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " %8s | %12s | %12.8f | %12.4f | %22.4f | %12.5e | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f |", "initial", "-", timeTotal, vRms, vMax, residualU, timeStepTotal, timeStokes, timeMMOC, timeDiffusion, timeVTK ) ) db.setVariableEntry( "ts", uint_c( timeStep ) ); db.setVariableEntry( "sim_time", timeTotal ); db.setVariableEntry( "run_time_advection", timeMMOC ); db.setVariableEntry( "run_time_diffusion", timeDiffusion ); db.setVariableEntry( "run_time_stokes", timeStokes ); db.setVariableEntry( "run_time_time_step", timeStepTotal ); db.setVariableEntry( "run_time_vtk", timeVTK ); db.setVariableEntry( "v_max", vMax ); db.setVariableEntry( "v_rms", vRms ); db.setVariableEntry( "dt", real_c( 0 ) ); db.setVariableEntry( "initial_residual_u_predictor", initialResiudalU ); db.setVariableEntry( "num_v_cycles_predictor", numVCycles ); db.setVariableEntry( "avg_residual_reduction_u_predictor", averageResidualReductionU / real_c( numVCycles ) ); db.setVariableEntry( "final_residual_u_predictor", vCycleResidualULast ); db.setVariableEntry( "initial_residual_u_corrector", real_c( 0 ) ); db.setVariableEntry( "num_v_cycles_corrector", real_c( 0 ) ); db.setVariableEntry( "avg_residual_reduction_u_corrector", real_c( 0 ) ); db.setVariableEntry( "final_residual_u_corrector", real_c( 0 ) ); db.writeRowOnRoot(); timer->stop( "Simulation" ); while ( timeTotal < simulationTime && timeStep < maxNumTimeSteps ) { timer->start( "Simulation" ); timeStepTimer.start(); timeStep++; // new time step size if ( storage->hasGlobalCells() ) { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], u.uvw[2], uTmp, uTmp2, level, All ); } else { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], uTmp, uTmp2, level, All ); } real_t dt; if ( fixedTimeStep ) { dt = dtConstant; } else { dt = ( cflMax / vMax ) * hMin; } // energy // advection // start value for predictor cPr.assign( { 1.0 }, { c }, level, All ); // let's just use the current velocity for the prediction uLast.assign( { 1.0 }, { u }, level, All ); // diffusion cPr.interpolate( initialTemperature, level, DirichletBoundary ); cOld.assign( { 1.0 }, { cPr }, level, All ); diffusionOperator.setDt( 0.5 * dt ); if ( storage->hasGlobalCells() ) { diffusionOperator.getOperator().computeAndStoreLocalElementMatrices(); } calculateDiffusionResidual( diffusionSolver, diffusionOperator, L, MVelocity, cPr, cOld, q, cTmp, cTmp2, level, vCycleResidualCLast ); localTimer.start(); diffusionSolver.step( diffusionOperator, L, MVelocity, cPr, cOld, q, q, level, Inner | NeumannBoundary | FreeslipBoundary ); localTimer.end(); timeDiffusion = localTimer.last(); localTimer.start(); transport.step( cPr, u.uvw, uLast.uvw, level, All, dt, 1, true ); localTimer.end(); timeMMOC = localTimer.last(); // diffusion cPr.interpolate( initialTemperature, level, DirichletBoundary ); cOld.assign( { 1.0 }, { cPr }, level, All ); calculateDiffusionResidual( diffusionSolver, diffusionOperator, L, MVelocity, cPr, cOld, q, cTmp, cTmp2, level, vCycleResidualCLast ); localTimer.start(); diffusionSolver.step( diffusionOperator, L, MVelocity, cPr, cOld, q, q, level, Inner | NeumannBoundary | FreeslipBoundary ); localTimer.end(); timeDiffusion += localTimer.last(); // Stokes for ( uint_t l = minLevel; l <= level; l++ ) { MVelocity.apply( cPr, f.uvw[0], l, All ); MVelocity.apply( cPr, f.uvw[1], l, All ); if ( storage->hasGlobalCells() ) { MVelocity.apply( cPr, f.uvw[2], l, All ); } f.uvw.multElementwise( { f.uvw, outwardNormal.uvw }, l ); f.uvw.assign( { rayleighNumber }, { f.uvw }, l, All ); } calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); vCycleResidualULast = residualU; initialResiudalU = residualU; averageResidualReductionU = real_c( 0 ); numVCycles = 0; localTimer.start(); stokesSolver->solve( *A, u, f, level ); localTimer.end(); timeStokes = localTimer.last(); calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); db.setVariableEntry( "initial_residual_u_predictor", initialResiudalU ); db.setVariableEntry( "num_v_cycles_predictor", numVCycles ); db.setVariableEntry( "avg_residual_reduction_u_predictor", averageResidualReductionU / real_c( numVCycles ) ); db.setVariableEntry( "final_residual_u_predictor", vCycleResidualULast ); if ( predictorCorrector ) { // energy // diffusion c.interpolate( initialTemperature, level, DirichletBoundary ); cOld.assign( { 1.0 }, { c }, level, All ); calculateDiffusionResidual( diffusionSolver, diffusionOperator, L, MVelocity, c, cOld, q, cTmp, cTmp2, level, vCycleResidualCLast ); localTimer.start(); diffusionSolver.step( diffusionOperator, L, MVelocity, c, cOld, q, q, level, Inner | NeumannBoundary | FreeslipBoundary ); localTimer.end(); timeDiffusion += localTimer.last(); // advection localTimer.start(); transport.step( c, u.uvw, uLast.uvw, level, All, dt, 1, true ); localTimer.end(); timeMMOC += localTimer.last(); // diffusion c.interpolate( initialTemperature, level, DirichletBoundary ); cOld.assign( { 1.0 }, { c }, level, All ); calculateDiffusionResidual( diffusionSolver, diffusionOperator, L, MVelocity, c, cOld, q, cTmp, cTmp2, level, vCycleResidualCLast ); localTimer.start(); diffusionSolver.step( diffusionOperator, L, MVelocity, c, cOld, q, q, level, Inner | NeumannBoundary | FreeslipBoundary ); localTimer.end(); timeDiffusion += localTimer.last(); // Stokes for ( uint_t l = minLevel; l <= level; l++ ) { MVelocity.apply( c, f.uvw[0], l, All ); MVelocity.apply( c, f.uvw[1], l, All ); if ( storage->hasGlobalCells() ) { MVelocity.apply( c, f.uvw[2], l, All ); } f.uvw.multElementwise( { f.uvw, outwardNormal.uvw }, l ); f.uvw.assign( { rayleighNumber }, { f.uvw }, l, All ); } calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); vCycleResidualULast = residualU; initialResiudalU = residualU; averageResidualReductionU = real_c( 0 ); numVCycles = 0; localTimer.start(); stokesSolver->solve( *A, u, f, level ); localTimer.end(); timeStokes += localTimer.last(); calculateStokesResiduals( *A, MVelocity, MPressure, u, f, level, stokesResidual, stokesTmp, residualU ); db.setVariableEntry( "initial_residual_u_corrector", initialResiudalU ); db.setVariableEntry( "num_v_cycles_corrector", numVCycles ); db.setVariableEntry( "avg_residual_reduction_u_corrector", averageResidualReductionU / real_c( numVCycles ) ); db.setVariableEntry( "final_residual_u_corrector", vCycleResidualULast ); } else { // use predicted value c.assign( { 1.0 }, { cPr }, level, All ); db.setVariableEntry( "initial_residual_u_corrector", real_c( 0 ) ); db.setVariableEntry( "num_v_cycles_corrector", real_c( 0 ) ); db.setVariableEntry( "avg_residual_reduction_u_corrector", real_c( 0 ) ); db.setVariableEntry( "final_residual_u_corrector", real_c( 0 ) ); } timeTotal += dt; vRms = velocityRMS( u, stokesTmp, MVelocity, domainInfo.domainVolume(), level ); if ( storage->hasGlobalCells() ) { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], u.uvw[2], uTmp, uMagnitudeSquared, level, All ); } else { vMax = velocityMaxMagnitude( u.uvw[0], u.uvw[1], uTmp, uMagnitudeSquared, level, All ); } localTimer.start(); if ( outputInfo.vtk ) { vtkOutput.write( level, timeStep ); } localTimer.end(); timeVTK = localTimer.last(); if ( outputInfo.sphericalTemperatureSlice && timeStep % outputInfo.sphericalTemperatureSliceInterval == 0 ) { if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Evaluating spherical slices ..." ); } evaluateSphericalSliceUV( sliceEvaluationRadius, outputInfo.sphericalTemperatureSliceNumMeridians, outputInfo.sphericalTemperatureSliceNumParallels, c, level, outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + "_temp_slice_uv_" + std::to_string( timeStep ) + ".csv" ); evaluateSphericalSliceIco( sliceEvaluationRadius, outputInfo.sphericalTemperatureSliceIcoRefinements, c, level, outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + "_temp_slice_ico_" + std::to_string( timeStep ) + ".csv" ); if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Done." ); } } timeStepTimer.end(); timeStepTotal = timeStepTimer.last(); db.setVariableEntry( "ts", uint_c( timeStep ) ); db.setVariableEntry( "sim_time", timeTotal ); db.setVariableEntry( "run_time_advection", timeMMOC ); db.setVariableEntry( "run_time_diffusion", timeDiffusion ); db.setVariableEntry( "run_time_stokes", timeStokes ); db.setVariableEntry( "run_time_time_step", timeStepTotal ); db.setVariableEntry( "run_time_vtk", timeVTK ); db.setVariableEntry( "v_max", vMax ); db.setVariableEntry( "v_rms", vRms ); db.setVariableEntry( "dt", dt ); db.writeRowOnRoot(); if ( outputInfo.printInterval > 0 && timeStep % outputInfo.printInterval == 0 ) { WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " %8d | %12.5e | %12.8f | %12.4f | %22.4f | %12.5e | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f |", timeStep, dt, timeTotal, vRms, vMax, residualU, timeStepTotal, timeStokes, timeMMOC, timeDiffusion, timeVTK ) ) } timer->stop( "Simulation" ); if ( outputTimingJSON ) { if ( verbose ) { WALBERLA_LOG_INFO_ON_ROOT( "Writing timing tree to .json ..." ); } writeTimingTreeJSON( *timer, outputInfo.outputDirectory + "/" + outputInfo.outputBaseName + "_up_to_ts_" + std::to_string( timeStep ) + "_timing.json" ); } } } } // namespace hyteg int main( int argc, char** argv ) { walberla::Environment env( argc, argv ); walberla::MPIManager::instance()->useWorldComm(); #ifdef HYTEG_BUILD_WITH_PETSC hyteg::PETScManager manager( &argc, &argv ); #endif auto cfg = std::make_shared< walberla::config::Config >(); if ( env.config() == nullptr ) { auto defaultFile = "./MantleConvection.prm"; WALBERLA_LOG_INFO_ON_ROOT( "No Parameter file given loading default parameter file: " << defaultFile ); cfg->readParameterFile( defaultFile ); } else { cfg = env.config(); } const walberla::Config::BlockHandle mainConf = cfg->getBlock( "Parameters" ); hyteg::DomainInfo domainInfo; hyteg::SolverInfo solverInfo; hyteg::OutputInfo outputInfo; domainInfo.threeDim = mainConf.getParameter< bool >( "threeDim" ); domainInfo.rMin = mainConf.getParameter< hyteg::real_t >( "rMin" ); domainInfo.rMax = mainConf.getParameter< hyteg::real_t >( "rMax" ); domainInfo.nTan = mainConf.getParameter< hyteg::uint_t >( "nTan" ); domainInfo.nRad = mainConf.getParameter< hyteg::uint_t >( "nRad" ); const hyteg::real_t cflMax = mainConf.getParameter< hyteg::real_t >( "cflMax" ); const hyteg::real_t rayleighNumber = mainConf.getParameter< hyteg::real_t >( "rayleighNumber" ); const bool fixedTimeStep = mainConf.getParameter< bool >( "fixedTimeStep" ); const hyteg::real_t dtConstant = mainConf.getParameter< hyteg::real_t >( "dtConstant" ); const uint_t minLevel = mainConf.getParameter< uint_t >( "minLevel" ); const uint_t level = mainConf.getParameter< uint_t >( "level" ); const hyteg::real_t simulationTime = mainConf.getParameter< hyteg::real_t >( "simulationTime" ); const hyteg::uint_t maxNumTimeSteps = mainConf.getParameter< hyteg::uint_t >( "maxNumTimeSteps" ); const int stokesSolverTypeInt = mainConf.getParameter< int >( "stokesSolverType" ); const int diffusionSolverTypeInt = mainConf.getParameter< int >( "diffusionSolverType" ); solverInfo.stokesSolverType = static_cast< hyteg::StokesSolverType >( stokesSolverTypeInt ); solverInfo.stokesMaxNumIterations = mainConf.getParameter< uint_t >( "stokesMaxNumIterations" ); solverInfo.stokesAbsoluteResidualUTolerance = mainConf.getParameter< real_t >( "stokesAbsoluteResidualUTolerance" ); solverInfo.stokesRelativeResidualUTolerance = mainConf.getParameter< real_t >( "stokesRelativeResidualUTolerance" ); solverInfo.uzawaOmega = mainConf.getParameter< real_t >( "uzawaOmega" ); solverInfo.uzawaInnerIterations = mainConf.getParameter< uint_t >( "uzawaInnerIterations" ); solverInfo.uzawaPreSmooth = mainConf.getParameter< uint_t >( "uzawaPreSmooth" ); solverInfo.uzawaPostSmooth = mainConf.getParameter< uint_t >( "uzawaPostSmooth" ); solverInfo.diffusionSolverType = static_cast< hyteg::DiffusionSolverType >( diffusionSolverTypeInt ); solverInfo.diffusionMaxNumIterations = mainConf.getParameter< uint_t >( "diffusionMaxNumIterations" ); solverInfo.diffusionAbsoluteResidualUTolerance = mainConf.getParameter< real_t >( "diffusionAbsoluteResidualUTolerance" ); outputInfo.outputDirectory = mainConf.getParameter< std::string >( "outputDirectory" ); outputInfo.outputBaseName = mainConf.getParameter< std::string >( "outputBaseName" ); outputInfo.vtk = mainConf.getParameter< bool >( "vtk" ); outputInfo.vtkOutputVelocity = mainConf.getParameter< bool >( "vtkOutputVelocity" ); outputInfo.vtkInterval = mainConf.getParameter< uint_t >( "vtkOutputInterval" ); outputInfo.vtkOutputVertexDoFs = mainConf.getParameter< bool >( "vtkOutputVertexDoFs" ); outputInfo.printInterval = 1; outputInfo.sphericalTemperatureSlice = mainConf.getParameter< bool >( "sphericalTemperatureSlice" ); outputInfo.sphericalTemperatureSliceInterval = mainConf.getParameter< uint_t >( "sphericalTemperatureSliceInterval" ); outputInfo.sphericalTemperatureSliceNumMeridians = mainConf.getParameter< int >( "sphericalTemperatureSliceNumMeridians" ); outputInfo.sphericalTemperatureSliceNumParallels = mainConf.getParameter< int >( "sphericalTemperatureSliceNumParallels" ); outputInfo.sphericalTemperatureSliceIcoRefinements = mainConf.getParameter< int >( "sphericalTemperatureSliceIcoRefinements" ); const bool verbose = mainConf.getParameter< bool >( "verbose" ); hyteg::runBenchmark( cflMax, rayleighNumber, fixedTimeStep, dtConstant, maxNumTimeSteps, minLevel, level, solverInfo, domainInfo, true, simulationTime, outputInfo, verbose ); }