Commit 8bbf638f authored by wagnandr's avatar wagnandr
Browse files

Merge branch 'master' into wagnandr/stokes-eg

parents 8886f91f adba0cff
Pipeline #39555 failed with stages
in 10 minutes and 27 seconds
......@@ -79,9 +79,9 @@ variables:
intel_19_serial:
intel_20_serial:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
WALBERLA_BUILD_WITH_MPI: "OFF"
WALBERLA_BUILD_WITH_OPENMP: "OFF"
......@@ -93,9 +93,9 @@ intel_19_serial:
- docker
- intel
intel_19_mpionly:
intel_20_mpionly:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
WALBERLA_BUILD_WITH_OPENMP: "OFF"
only:
......@@ -105,9 +105,9 @@ intel_19_mpionly:
- docker
- intel
intel_19_serial_dbg:
intel_20_serial_dbg:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
WALBERLA_BUILD_WITH_MPI: "OFF"
WALBERLA_BUILD_WITH_OPENMP: "OFF"
......@@ -117,9 +117,9 @@ intel_19_serial_dbg:
- docker
- intel
intel_19_mpionly_dbg_eigen_petsc-complex_trilinos:
intel_20_mpionly_dbg_eigen_petsc-complex_trilinos:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
CMAKE_BUILD_TYPE: "DebugOptimized"
WALBERLA_BUILD_WITH_OPENMP: "OFF"
......@@ -131,9 +131,9 @@ intel_19_mpionly_dbg_eigen_petsc-complex_trilinos:
- docker
- intel
intel_19_mpionly_dbg_sp:
intel_20_mpionly_dbg_sp:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
CMAKE_BUILD_TYPE: "DebugOptimized"
WALBERLA_BUILD_WITH_OPENMP: "OFF"
......@@ -144,9 +144,9 @@ intel_19_mpionly_dbg_sp:
- docker
- intel
intel_19_mpionly_eigen_petsc_trilinos:
intel_20_mpionly_eigen_petsc_trilinos:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
variables:
WALBERLA_BUILD_WITH_OPENMP: "OFF"
HYTEG_BUILD_WITH_PETSC: "ON"
......@@ -157,9 +157,9 @@ intel_19_mpionly_eigen_petsc_trilinos:
- docker
- intel
intel_19_mpionly_eigen_petsc_trilinos_no_werror:
intel_20_mpionly_eigen_petsc_trilinos_no_werror:
extends: .build_template
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
stage: no_werror
variables:
WALBERLA_BUILD_WITH_OPENMP: "OFF"
......@@ -1552,7 +1552,7 @@ benchmark_build_time:
- cd $CI_PROJECT_DIR/
- cat BuildTiming.txt
- python3 $CI_PROJECT_DIR/data/scripts/upload.py
image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:9
image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:11
tags:
- docker-benchmark
variables:
......@@ -1560,11 +1560,6 @@ benchmark_build_time:
benchmark_ClangBuildAnalyzer:
script:
- apt-get update --fix-missing
- apt-get -y install apt-transport-https ca-certificates gnupg software-properties-common wget
- wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | apt-key add -
- apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main'
- apt-get -y install cmake ninja-build
- cmake --version
- ccache --version
- mpirun --version
......@@ -1586,7 +1581,7 @@ benchmark_ClangBuildAnalyzer:
- ninja hyteg
- ClangBuildAnalyzer --stop src CBA
- ClangBuildAnalyzer --analyze CBA
image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:9.0
image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:13.0
tags:
- docker-benchmark
variables:
......@@ -1661,14 +1656,14 @@ benchmark_ClangBuildAnalyzer:
needs: [ ]
stage: benchmark
benchmark_intel19:
benchmark_intel20:
<<: *benchmark_definition
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:20
benchmark_gcc9:
benchmark_gcc11:
<<: *benchmark_definition
image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:9
image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:11
benchmark_clang8:
benchmark_clang13:
<<: *benchmark_definition
image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:8.0
\ No newline at end of file
image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:13.0
\ No newline at end of file
......@@ -21,6 +21,7 @@
#include <core/Environment.h>
#include <core/Format.hpp>
#include <core/config/Create.h>
#include <core/math/Constants.h>
#include <core/mpi/Broadcast.h>
#include <core/timing/Timer.h>
......@@ -28,16 +29,21 @@
#include "hyteg/dataexport/VTKOutput.hpp"
#include "hyteg/geometry/AnnulusMap.hpp"
#include "hyteg/geometry/IcosahedralShellMap.hpp"
#include "hyteg/gridtransferoperators/P1toP1LinearProlongation.hpp"
#include "hyteg/gridtransferoperators/P1toP1LinearRestriction.hpp"
#include "hyteg/memory/MemoryAllocation.hpp"
#include "hyteg/p1functionspace/P1VariableOperator.hpp"
#include "hyteg/primitivestorage/PrimitiveStorage.hpp"
#include "hyteg/primitivestorage/Visualization.hpp"
#include "hyteg/primitivestorage/loadbalancing/SimpleBalancer.hpp"
#include "hyteg/solvers/CGSolver.hpp"
#include "hyteg/solvers/GaussSeidelSmoother.hpp"
#include "hyteg/solvers/GeometricMultigridSolver.hpp"
using namespace hyteg;
using walberla::real_t;
using walberla::uint_t;
#define PI 3.14159265359
#define R_min 1.0
#define R_max 2.0
......@@ -54,26 +60,40 @@ PDE_data functions( uint_t dim, uint_t shape, real_t alpha, real_t beta )
{
PDE_data pde;
pde.u_anal = [=]( const hyteg::Point3D& x ) {
auto x0 = tanh( alpha * ( x.norm() - 1.5 ) );
return 0.5 * ( exp( 2 * beta ) * std::expint( -beta * ( x0 + 1 ) ) - std::expint( -beta * ( x0 - 1 ) ) ) * exp( -beta ) /
alpha;
};
if ( shape == 0 )
{
pde.u_anal = [=]( const hyteg::Point3D& x ) { return 1.0 - tanh( alpha * x.norm() ); };
pde.k = [=]( const hyteg::Point3D& x ) {
auto x0 = x.norm();
auto x1 = ( dim == 2 ) ? x0 : x0 * x0;
return exp( beta * tanh( alpha * ( x0 - 1.5 ) ) ) / x1;
};
pde.k = [=]( const hyteg::Point3D& ) { return 1.0; };
pde.f = [=]( const hyteg::Point3D& ) { return 0; };
pde.f = [=]( const hyteg::Point3D& x ) {
auto x0 = x.norm();
if (x0 < 1e-100)
return x0;
auto x1 = alpha * x0;
auto x12 = cosh( x1 );
auto x2 = 1.0 / ( x12 * x12 );
return -2.0 * ( alpha * alpha ) * x2 * tanh( x1 ) + real_t( dim - 1 ) * alpha * x2 / x0;
};
if ( shape == 0 ) // no blending
{
pde.u_D = pde.u_anal;
}
else // blending
else
{
pde.u_anal = [=]( const hyteg::Point3D& x ) {
auto x0 = tanh( alpha * ( x.norm() - 1.5 ) );
return 0.5 * ( exp( 2 * beta ) * std::expint( -beta * ( x0 + 1 ) ) - std::expint( -beta * ( x0 - 1 ) ) ) * exp( -beta ) /
alpha;
};
pde.k = [=]( const hyteg::Point3D& x ) {
auto x0 = x.norm();
auto x1 = ( dim == 2 ) ? x0 : x0 * x0;
return exp( beta * tanh( alpha * ( x0 - 1.5 ) ) ) / x1;
};
pde.f = [=]( const hyteg::Point3D& ) { return 0; };
auto R_mid = ( R_min + R_max ) / 2.0;
auto u_inner = pde.u_anal( Point3D( { R_min, 0, 0 } ) );
auto u_outer = pde.u_anal( Point3D( { R_max, 0, 0 } ) );
......@@ -88,29 +108,35 @@ SetupPrimitiveStorage domain( uint_t dim, uint_t shape, uint_t N1, uint_t N2, ui
{
MeshInfo meshInfo = MeshInfo::emptyMeshInfo();
if ( dim == 3 && shape == 0 )
if ( dim != 2 && dim != 3 )
{
Point3D n( { 1, 1, 1 } );
n /= n.norm();
meshInfo = MeshInfo::meshCuboid( R_min * n, R_max * n, N1, N2, N3 );
}
else if ( dim == 3 && shape == 1 )
{
meshInfo = MeshInfo::meshSphericalShell( N1, N2, R_min, R_max );
}
else if ( dim == 2 && shape == 0 )
{
Point2D n( { 1, 1 } );
n /= n.norm();
meshInfo = MeshInfo::meshRectangle( R_min * n, R_max * n, MeshInfo::CRISS, N1, N2 );
WALBERLA_ABORT( "Dimension must be either 2 or 3, shape must be either 0 or 1!" );
}
else if ( dim == 2 && shape == 1 )
if ( shape == 0 )
{
meshInfo = MeshInfo::meshAnnulus( R_min, R_max, MeshInfo::CRISS, N1, N2 );
if ( dim == 3 )
{
Point3D n( { 1, 1, 1 } );
meshInfo = MeshInfo::meshCuboid( -n, n, N1, N2, N3 );
}
else
{
Point2D n( { 1, 1 } );
meshInfo = MeshInfo::meshRectangle( -n, n, MeshInfo::CRISS, N1, N2 );
}
}
else
{
WALBERLA_ABORT( "Dimension must be either 2 or 3, shape must be either 0 or 1!" );
if ( dim == 3 )
{
meshInfo = MeshInfo::meshSphericalShell( N1, N2, R_min, R_max );
}
else
{
meshInfo = MeshInfo::meshAnnulus( R_min, R_max, MeshInfo::CRISS, N1, N2 );
}
}
SetupPrimitiveStorage setupStorage( meshInfo, uint_c( walberla::mpi::MPIManager::instance()->numProcesses() ) );
......@@ -133,29 +159,39 @@ SetupPrimitiveStorage domain( uint_t dim, uint_t shape, uint_t N1, uint_t N2, ui
return setupStorage;
}
// solve problem with current refinement and return sorted list of elementwise squared errors
std::vector< std::pair< real_t, hyteg::PrimitiveID > >
solve( std::shared_ptr< PrimitiveStorage > storage, const PDE_data& pde, uint_t lvl, uint_t iter, real_t tol, int vtk )
// solve problem with current refinement and return list of elementwise squared errors of local elements
adaptiveRefinement::ErrorVector solve( std::shared_ptr< PrimitiveStorage > storage,
const PDE_data& pde,
uint_t l_min,
uint_t l_max,
uint_t max_iter,
real_t tol,
std::string vtkname,
uint_t refinement_step,
bool l2_error_each_iteration = true )
{
uint_t l_min = lvl;
uint_t l_max = lvl;
uint_t dim = storage->hasGlobalCells() ? 3 : 2;
// operators
using M_t = P1BlendingMassOperator;
using A_t = P1BlendingDivkGradOperator;
using P_t = P1toP1LinearProlongation;
using R_t = P1toP1LinearRestriction;
forms::p1_div_k_grad_blending_q3 form( pde.k, pde.k );
M_t M( storage, l_min, l_max );
A_t A( storage, l_min, l_max, form );
auto M = std::make_shared< M_t >( storage, l_min, l_max + 1 );
auto A = std::make_shared< A_t >( storage, l_min, l_max + 1, form );
auto P = std::make_shared< P_t >();
auto R = std::make_shared< R_t >();
// FE functions
P1Function< real_t > b( "b", storage, l_min, l_max );
P1Function< real_t > u( "u", storage, l_min, l_max );
P1Function< real_t > u_exact( "u_exact", storage, l_min, l_max );
P1Function< real_t > err( "err", storage, l_min, l_max );
P1Function< real_t > tmp( "tmp", storage, l_min, l_max );
P1Function< real_t > u( "u", storage, l_min, l_max + 1 );
P1Function< real_t > r( "r", storage, l_max, l_max );
P1Function< real_t > u_anal( "u_anal", storage, l_max, l_max + 1 );
P1Function< real_t > err( "err", storage, l_max, l_max + 1 );
P1Function< real_t > tmp( "tmp", storage, l_min, l_max + 1 );
// global DoF
tmp.interpolate( []( const hyteg::Point3D& ) { return 1.0; }, l_max, hyteg::Inner );
......@@ -164,32 +200,80 @@ std::vector< std::pair< real_t, hyteg::PrimitiveID > >
// rhs
tmp.interpolate( pde.f, l_max );
M.apply( tmp, b, l_max, hyteg::All );
// exact solution
u_exact.interpolate( pde.u_anal, l_max );
M->apply( tmp, b, l_max, hyteg::All );
// analytical solution
u_anal.interpolate( pde.u_anal, l_max + 1 );
// initialize u
u.setToZero( l_max );
u.setToZero( l_max + 1 );
u.interpolate( pde.u_D, l_max, hyteg::DirichletBoundary );
u.interpolate( []( const hyteg::Point3D& ) { return 0.0; }, l_max, hyteg::Inner );
u.interpolate( pde.u_D, l_max + 1, hyteg::DirichletBoundary );
// solver
auto cg = std::make_shared< CGSolver< A_t > >( storage, l_min, l_max, std::max( max_iter, n_dof ), tol * 1e-1 );
auto smoother = std::make_shared< GaussSeidelSmoother< A_t > >();
GeometricMultigridSolver< A_t > gmg( storage, smoother, cg, R, P, l_min, l_max, 3, 3 );
// computation of residual and L2 error
err.setToZero( l_max );
err.setToZero( l_max + 1 );
tmp.setToZero( l_max );
tmp.setToZero( l_max + 1 );
auto compute_residual = [&]() -> real_t {
A->apply( u, tmp, l_max, hyteg::Inner, Replace );
r.assign( { 1.0, -1.0 }, { b, tmp }, l_max, hyteg::Inner );
return std::sqrt( r.dotGlobal( r, l_max, hyteg::Inner ) );
};
auto compute_L2error = [&]() -> real_t {
P->prolongate( u, l_max, hyteg::Inner );
err.assign( { 1.0, -1.0 }, { u, u_anal }, l_max + 1, hyteg::Inner );
M->apply( err, tmp, l_max + 1, hyteg::All, Replace );
return std::sqrt( err.dotGlobal( tmp, l_max + 1 ) );
};
// solve
hyteg::CGSolver< A_t > solver( storage, l_min, l_min, iter, tol );
solver.solve( A, u, b, l_max );
WALBERLA_LOG_INFO_ON_ROOT( " -> run multigrid solver" );
WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " -> %6s |%18s |%13s", "k", "||r_k||/||r_0||", "L2 error" ) );
real_t norm_r0 = compute_residual();
real_t norm_r = 0;
real_t l2err = 0;
uint_t iter = 0;
do
{
++iter;
// compute total error
err.assign( { 1.0, -1.0 }, { u, u_exact }, l_max );
M.apply( err, tmp, l_max, hyteg::All, Replace );
real_t l2err = std::sqrt( err.dotGlobal( tmp, l_max ) );
gmg.solve( *A, u, b, l_max );
WALBERLA_LOG_INFO_ON_ROOT( "L2-error = " << l2err );
norm_r = compute_residual();
if ( l2_error_each_iteration )
{
l2err = compute_L2error();
WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " -> %6d |%18.3e |%13.3e", iter, norm_r / norm_r0, l2err ) );
}
else
{
WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " -> %6d |%18.3e |", iter, norm_r / norm_r0 ) );
}
} while ( norm_r / norm_r0 > tol && iter < max_iter );
if ( !l2_error_each_iteration )
{
l2err = compute_L2error();
WALBERLA_LOG_INFO_ON_ROOT( walberla::format( " -> %6s %18s |%13.3e", "", "", l2err ) );
}
// compute elementwise error
std::vector< std::pair< real_t, hyteg::PrimitiveID > > err_2_elwise_loc;
adaptiveRefinement::ErrorVector err_2_elwise_loc;
if ( dim == 3 )
{
for ( auto& [id, cell] : storage->getCells() )
{
real_t err_2_cell = vertexdof::macrocell::dot< real_t >( l_max, *cell, err.getCellDataID(), err.getCellDataID(), 0 );
real_t err_2_cell = vertexdof::macrocell::dot< real_t >( l_max + 1, *cell, err.getCellDataID(), err.getCellDataID(), 0 );
// scale squared error by cell-volume
std::array< Point3D, 3 + 1 > vertices;
......@@ -208,7 +292,7 @@ std::vector< std::pair< real_t, hyteg::PrimitiveID > >
{
for ( auto& [id, face] : storage->getFaces() )
{
real_t err_2_face = vertexdof::macroface::dot< real_t >( l_max, *face, err.getFaceDataID(), err.getFaceDataID(), 0 );
real_t err_2_face = vertexdof::macroface::dot< real_t >( l_max + 1, *face, err.getFaceDataID(), err.getFaceDataID(), 0 );
// scale squared error by face-volume
std::array< Point3D, 2 + 1 > vertices;
......@@ -224,102 +308,120 @@ std::vector< std::pair< real_t, hyteg::PrimitiveID > >
}
}
// communication
std::vector< std::pair< real_t, hyteg::PrimitiveID > > err_2_elwise;
std::vector< std::pair< real_t, hyteg::PrimitiveID > > err_2_elwise_other;
walberla::mpi::SendBuffer send;
walberla::mpi::RecvBuffer recv;
send << err_2_elwise_loc;
walberla::mpi::allGathervBuffer( send, recv );
for ( int rnk = 0; rnk < walberla::mpi::MPIManager::instance()->numProcesses(); ++rnk )
{
recv >> err_2_elwise_other;
err_2_elwise.insert( err_2_elwise.end(), err_2_elwise_other.begin(), err_2_elwise_other.end() );
}
// sort by errors
std::sort( err_2_elwise.begin(), err_2_elwise.end() );
// export to vtk
if ( vtk >= 0 )
if ( vtkname != "" )
{
// coefficient
P1Function< real_t > k( "k", storage, l_min, l_max );
k.interpolate( pde.k, l_max );
// boundary flag
P1Function< real_t > boundary( "boundary", storage, l_min, l_max );
boundary.interpolate( []( const hyteg::Point3D& ) { return 0.0; }, l_max, hyteg::All );
boundary.interpolate( []( const hyteg::Point3D& ) { return 1.0; }, l_max, hyteg::DirichletBoundary );
// analytic solution
u_anal.interpolate( pde.u_anal, l_max );
// error
R->restrict( err, l_max + 1, hyteg::Inner );
// squared error
P1Function< real_t > err_2( "err^2", storage, l_min, l_max );
err_2.multElementwise( { err, err }, l_max, hyteg::All );
VTKOutput vtkOutput( "output", "adaptive_" + std::to_string( dim ) + "d", storage );
// write vtkfile
VTKOutput vtkOutput( "output", vtkname, storage );
vtkOutput.setVTKDataFormat( vtk::DataFormat::BINARY );
vtkOutput.add( u );
vtkOutput.add( k );
vtkOutput.add( err );
vtkOutput.add( err_2 );
vtkOutput.add( u_exact );
vtkOutput.add( u_anal );
vtkOutput.add( b );
vtkOutput.add( boundary );
vtkOutput.write( l_max, uint_t( vtk ) );
vtkOutput.write( l_max, refinement_step );
}
return err_2_elwise;
return err_2_elwise_loc;
}
void solve_for_each_refinement( const SetupPrimitiveStorage& initialStorage,
const PDE_data& pde,
uint_t n,
real_t p,
uint_t lvl,
uint_t iter,
real_t tol,
bool vtk )
void solve_for_each_refinement( const SetupPrimitiveStorage& setupStorage,
const PDE_data& pde,
uint_t n_ref,
uint_t n_el_max,
real_t p_ref,
uint_t l_min,
uint_t l_max,
uint_t max_iter,
real_t tol,
std::string vtkname,
adaptiveRefinement::Loadbalancing loadbalancing,
bool writePartitioning )
{
// construct adaptive mesh
adaptiveRefinement::Mesh mesh( initialStorage );
SetupPrimitiveStorage& setupStorage = mesh.setupStorage();
std::vector< std::pair< real_t, hyteg::PrimitiveID > > local_errors;
adaptiveRefinement::Mesh mesh( setupStorage );
// PrimitiveStorage
std::shared_ptr< PrimitiveStorage > storage = nullptr;
// loop over refinement levels
for ( uint_t refinement = 0; refinement <= n; ++refinement )
uint_t refinement = 0;
while ( 1 )
{
// apply refinement
if ( refinement > 0 )
{
WALBERLA_LOG_INFO_ON_ROOT( "* refinement " << refinement );
WALBERLA_LOG_INFO_ON_ROOT( "* apply load balancing and create PrimitiveStorage ..." );
storage = mesh.make_storage( loadbalancing );
printCurrentMemoryUsage();
uint_t N_tot = local_errors.size();
uint_t N_ref = uint_t( std::ceil( real_t( N_tot ) * p ) );
WALBERLA_LOG_INFO_ON_ROOT( "* solve system ..." );
auto local_errors = solve( storage, pde, l_min, l_max, max_iter, tol, vtkname, refinement );
if ( refinement >= n_ref )
{
WALBERLA_LOG_INFO_ON_ROOT( "* maximum number of refinements!" );
break;
}
WALBERLA_LOG_INFO_ON_ROOT( " -> " << N_ref << " of " << N_tot << " elements are being refined ..." );
auto n_el_old = mesh.n_elements();
++refinement;
WALBERLA_LOG_INFO_ON_ROOT( "* apply refinement " << refinement );
WALBERLA_LOG_INFO_ON_ROOT( " -> n_el_old = " << n_el_old );
// collect elements to refine
std::vector< PrimitiveID > R( N_ref );
for ( uint_t i = 0; i < N_ref; ++i )
// refinement strategy
const auto p_idx = std::min( uint_t( std::ceil( real_t( n_el_old ) * p_ref ) ), n_el_old - 1 );
auto criterion = [&]( const adaptiveRefinement::ErrorVector& err_global, uint_t i ) -> bool {
if ( i == 0 )
{
R[i] = local_errors[N_tot - 1 - i].second;
WALBERLA_LOG_INFO_ON_ROOT( " -> min_i err_i = " << err_global.back().first );
WALBERLA_LOG_INFO_ON_ROOT( " -> max_i err_i = " << err_global.front().first );
WALBERLA_LOG_INFO_ON_ROOT( " -> refining all elements i where err_i >= " << 0.5 * err_global[p_idx].first );
}
return err_global[i].first >= 0.5 * err_global[p_idx].first;
};
// apply refinement and update setupStorage
setupStorage = mesh.refineRG( R );
// apply refinement
auto ratio = mesh.refineRG( local_errors, criterion, n_el_max );
WALBERLA_LOG_INFO_ON_ROOT( " -> n_el_new = " << mesh.n_elements() );
// compute mesh quality
auto v_mean = mesh.volume() / real_t( mesh.n_elements() );
auto v_minmax = mesh.min_max_volume();
auto a_minmax = mesh.min_max_angle();
auto a_meanminmax = mesh.mean_min_max_angle();
WALBERLA_LOG_INFO_ON_ROOT( " -> el volume (min, max, mean): " << v_minmax.first << ", " << v_minmax.second << ", "
<< v_mean );
WALBERLA_LOG_INFO_ON_ROOT( " -> angle (min, max) over all elements: " << a_minmax.first << ", " << a_minmax.second );
WALBERLA_LOG_INFO_ON_ROOT( " -> angle (min, max) mean over all elements: " << a_meanminmax.first << ", "
<< a_meanminmax.second );
if ( ratio < 0.95 )
{
WALBERLA_LOG_INFO_ON_ROOT(
"* refinement can't be applied to all required elements\n without breaking the max number of elements!" );
break;
}
}
WALBERLA_LOG_INFO_ON_ROOT( "* solving system with " << mesh.n_elements() << " macro elements ..." );
// std::stringstream ss;
// setupStorage.toStream( ss, true );
// WALBERLA_LOG_INFO_ON_ROOT( ss.str() );
// construct PrimitiveStorage from setupStorage corresponding to current refinement
auto storage = std::make_shared< PrimitiveStorage >( setupStorage );
int vtkname = ( vtk ) ? int( refinement ) : -1;
local_errors = solve(