Commit 71cbbcea by Michael Kuron

### First attempt at allowing PE to run at double precision while LB runs at float

parent 111b212c
 ... ... @@ -61,6 +61,7 @@ include( CTest ) # Build options option ( WALBERLA_DOUBLE_ACCURACY "Floating point accuracy, defaults to double" ON ) option ( WALBERLA_PE_DOUBLE_ACCURACY "PE Floating point accuracy, defaults to double" ON ) option ( WALBERLA_ENABLE_GUI "Compile with GUI" ) option ( WALBERLA_BUILD_TESTS "Build Testcases" ) ... ...
 ... ... @@ -22,6 +22,7 @@ #pragma once #include "waLBerlaDefinitions.h" #include "core/math/MathTrait.h" #include #include ... ... @@ -171,6 +172,11 @@ typedef double real_t; #else typedef float real_t; #endif #ifdef WALBERLA_PE_DOUBLE_ACCURACY typedef double pe_real_t; #else typedef float pe_real_t; #endif template< typename T > inline real_t real_c ( T t ) { return numeric_cast< real_t >(t); } ///< cast to type real_t using "real_c(x)" template< typename T > inline double double_c( T t ) { return numeric_cast< double >(t); } ///< cast to type double ... ... @@ -214,20 +220,11 @@ inline bool realIsEqual( const real_t a, const real_t b, const real_t eps = real return std::fabs( a - b ) < eps; } inline bool floatIsEqual( long double lhs, long double rhs, const long double epsilon = real_comparison::Epsilon::value ) { return std::fabs( lhs - rhs ) < epsilon; } inline bool floatIsEqual( double lhs, double rhs, const double epsilon = real_comparison::Epsilon::value ) { return std::fabs( lhs - rhs ) < epsilon; } inline bool floatIsEqual( float lhs, float rhs, const float epsilon = real_comparison::Epsilon::value ) template< typename T, typename U > inline bool floatIsEqual( T lhs, U rhs, const typename math::MathTrait::LowType epsilon = real_comparison::Epsilon::LowType>::value ) { return std::fabs( lhs - rhs ) < epsilon; typedef typename math::MathTrait::LowType LT; return std::fabs( LT(lhs) - LT(rhs) ) < epsilon; } ... ...
 ... ... @@ -1745,16 +1745,16 @@ namespace walberla { namespace debug { namespace check_functions_detail { template< > inline bool check_float_equal( const math::Matrix3 & lhs, const math::Matrix3 & rhs ) template< typename T, typename U > inline bool check_float_equal( const math::Matrix3 & lhs, const math::Matrix3 & rhs ) { return floatIsEqual( lhs[0], rhs[0] ) && floatIsEqual( lhs[1], rhs[1] ) && floatIsEqual( lhs[2], rhs[2] ) && floatIsEqual( lhs[3], rhs[3] ) && floatIsEqual( lhs[4], rhs[4] ) && floatIsEqual( lhs[5], rhs[5] ) && floatIsEqual( lhs[6], rhs[6] ) && floatIsEqual( lhs[7], rhs[7] ) && floatIsEqual( lhs[8], rhs[8] ); } template< > inline bool check_float_equal_eps( const math::Matrix3 & lhs, const math::Matrix3 & rhs, const real_t epsilon ) template< typename T, typename U > inline bool check_float_equal_eps( const math::Matrix3 & lhs, const math::Matrix3 & rhs, const typename math::MathTrait::LowType epsilon ) { return floatIsEqual( lhs[0], rhs[0], epsilon ) && floatIsEqual( lhs[1], rhs[1], epsilon ) && floatIsEqual( lhs[2], rhs[2], epsilon ) && floatIsEqual( lhs[3], rhs[3], epsilon ) && floatIsEqual( lhs[4], rhs[4], epsilon ) && floatIsEqual( lhs[5], rhs[5], epsilon ) ... ...
 ... ... @@ -115,7 +115,7 @@ void FunctionParser::parse( const std::string & eq ) if (isConstant_) { const double value = evaluate(std::map()); isZero_ = floatIsEqual(value, 0); isZero_ = floatIsEqual(value, 0.0); } else { ... ...
 ... ... @@ -271,7 +271,7 @@ inline Quaternion::Quaternion( Vector3 axis, Type angle ) static_assert(boost::is_floating_point::value, "Axis has to be floating point!" ); auto axisLength = axis.length(); if( (floatIsEqual(axisLength, 0)) || (math::equal(std::fabs(angle), real_c(0))) ) { if( (floatIsEqual(axisLength, 0.0)) || (math::equal(std::fabs(angle), real_c(0))) ) { reset(); return; } ... ... @@ -1192,14 +1192,14 @@ namespace walberla { namespace debug { namespace check_functions_detail { template< > inline bool check_float_equal( const math::Quaternion & lhs, const math::Quaternion & rhs ) template< typename T, typename U > inline bool check_float_equal( const math::Quaternion & lhs, const math::Quaternion & rhs ) { return floatIsEqual( lhs[0], rhs[0] ) && floatIsEqual( lhs[1], rhs[1] ) && floatIsEqual( lhs[2], rhs[2] ) && floatIsEqual( lhs[3], rhs[3] ); } template< > inline bool check_float_equal_eps( const math::Quaternion & lhs, const math::Quaternion & rhs, const real_t epsilon ) template< typename T, typename U > inline bool check_float_equal_eps( const math::Quaternion & lhs, const math::Quaternion & rhs, const typename math::MathTrait::LowType epsilon ) { return floatIsEqual( lhs[0], rhs[0], epsilon ) && floatIsEqual( lhs[1], rhs[1], epsilon ) && floatIsEqual( lhs[2], rhs[2], epsilon ) && floatIsEqual( lhs[3], rhs[3], epsilon ); } ... ...
 ... ... @@ -1944,14 +1944,14 @@ namespace walberla { namespace debug { namespace check_functions_detail { template< > inline bool check_float_equal( const math::Vector3 & lhs, const math::Vector3 & rhs ) template< typename T, typename U > inline bool check_float_equal( const math::Vector3 & lhs, const math::Vector3 & rhs ) { return floatIsEqual( lhs[0], rhs[0] ) && floatIsEqual( lhs[1], rhs[1] ) && floatIsEqual( lhs[2], rhs[2] ); } template< > inline bool check_float_equal_eps( const math::Vector3 & lhs, const math::Vector3 & rhs, const real_t epsilon ) template< typename T, typename U > inline bool check_float_equal_eps( const math::Vector3 & lhs, const math::Vector3 & rhs, const typename math::MathTrait::LowType epsilon ) { return floatIsEqual( lhs[0], rhs[0], epsilon ) && floatIsEqual( lhs[1], rhs[1], epsilon ) && floatIsEqual( lhs[2], rhs[2], epsilon ); } ... ...
 ... ... @@ -61,32 +61,33 @@ namespace geometry { * \image html lineBox.png * \image latex lineBox.eps "Calculation of the closest points between a line and a box" width=455pt */ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret ) template void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret ) { using namespace walberla::math; //----- Note: All computations will be performed in the box-relative coordinate-system ----- // Calculating the global and relative direction of the line p1--p2 const Vector3 l( p2 - p1 ); Vector3 tmp( R * l ); const Vector3 l( p2 - p1 ); Vector3 tmp( R * l ); // Saving the sign of the direction p1--p2 const real_t sign[] = { math::sign(tmp[0]), math::sign(tmp[1]), math::sign(tmp[2]) }; const T sign[] = { math::sign(tmp[0]), math::sign(tmp[1]), math::sign(tmp[2]) }; // Calculating the absolute values of the direction direction const real_t v [] = { sign[0]*tmp[0], sign[1]*tmp[1], sign[2]*tmp[2] }; const real_t v2[] = { sq( v[0] ) , sq( v[1] ) , sq( v[2] ) }; const T v [] = { sign[0]*tmp[0], sign[1]*tmp[1], sign[2]*tmp[2] }; const T v2[] = { sq( v[0] ) , sq( v[1] ) , sq( v[2] ) }; // Calculating the starting point of the line p1--p2 in box-relative coordinates tmp = p1 - c; const real_t s[] = { sign[0]*( R[0]*tmp[0] + R[3]*tmp[1] + R[6]*tmp[2] ), const T s[] = { sign[0]*( R[0]*tmp[0] + R[3]*tmp[1] + R[6]*tmp[2] ), sign[1]*( R[1]*tmp[0] + R[4]*tmp[1] + R[7]*tmp[2] ), sign[2]*( R[2]*tmp[0] + R[5]*tmp[1] + R[8]*tmp[2] ) }; // Calculating the half lengths of the box const real_t h[] = { real_t(0.5)*side[0], real_t(0.5)*side[1], real_t(0.5)*side[2] }; const T h[] = { T(0.5)*side[0], T(0.5)*side[1], T(0.5)*side[2] }; // Estimating the region of the starting point depending on which side of the ... ... @@ -95,10 +96,10 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& // are no more. Additionally, d|d|^2/dt is computed for t=0. If it is >= 0 // then p1 is the closest point since the line points away from the box. int region [] = { 0, 0, 0 }; real_t tanchor[] = { 2, 2, 2 }; // Invalid t values; t cannot be greater than 1 real_t dd2dt( 0 ); T tanchor[] = { 2, 2, 2 }; // Invalid t values; t cannot be greater than 1 T dd2dt( 0 ); if( v[0] > Limits::epsilon() ) if( v[0] > Limits::epsilon() ) { if( s[0] < -h[0] ) { region[0] = -1; ... ... @@ -115,7 +116,7 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& } } if( v[1] > Limits::epsilon() ) if( v[1] > Limits::epsilon() ) { if( s[1] < -h[1] ) { region[1] = -1; ... ... @@ -132,7 +133,7 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& } } if( v[2] > Limits::epsilon() ) if( v[2] > Limits::epsilon() ) { if( s[2] < -h[2] ) { region[2] = -1; ... ... @@ -151,26 +152,26 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& // Calculating the value t for the closest point on the line real_t t( 0 ); T t( 0 ); if( dd2dt < -Limits::accuracy() ) if( dd2dt < -Limits::accuracy() ) { do { // Finding the t value for the next clip plane/line intersection real_t next_t( 1 ); T next_t( 1 ); if( ( tanchor[0] > t ) && ( tanchor[0] < real_t(1) ) && ( tanchor[0] < next_t ) ) { if( ( tanchor[0] > t ) && ( tanchor[0] < T(1) ) && ( tanchor[0] < next_t ) ) { next_t = tanchor[0]; } if( ( tanchor[1] > t ) && ( tanchor[1] < real_t(1) ) && ( tanchor[1] < next_t ) ) { if( ( tanchor[1] > t ) && ( tanchor[1] < T(1) ) && ( tanchor[1] < next_t ) ) { next_t = tanchor[1]; } if( ( tanchor[2] > t ) && ( tanchor[2] < real_t(1) ) && ( tanchor[2] < next_t ) ) { if( ( tanchor[2] > t ) && ( tanchor[2] < T(1) ) && ( tanchor[2] < next_t ) ) { next_t = tanchor[2]; } // Computing d|d|^2/dt for the next t real_t next_dd2dt( 0 ); T next_dd2dt( 0 ); if( region[0] != 0 ) { next_dd2dt += v2[0] * ( next_t - tanchor[0] ); ... ... @@ -205,11 +206,11 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& t = next_t; dd2dt = next_dd2dt; } while( t < real_t(1) ); while( t < T(1) ); } WALBERLA_ASSERT_GREATER_EQUAL( t, real_t(0), "Invalid line point" ); WALBERLA_ASSERT_LESS_EQUAL( t, real_t(1), "Invalid line point" ); WALBERLA_ASSERT_GREATER_EQUAL( t, T(0), "Invalid line point" ); WALBERLA_ASSERT_LESS_EQUAL( t, T(1), "Invalid line point" ); // Computing the closest point on the line lret = p1 + t * l; ... ... @@ -229,6 +230,12 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& bret = ( R * tmp ) + c; } template void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret ); template void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret ); //************************************************************************************************* ... ... @@ -248,45 +255,46 @@ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& * involving the endpoint of at least one line will be returned. This will also work correctly * for zero length lines, e.g. if \a a1 = \a a2 and/or \a b1 = \a b2. */ void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ) template void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ) { using namespace walberla::math; //----- Checking the vertex-vertex features ----- const Vector3 a1a2( a2 - a1 ); const Vector3 b1b2( b2 - b1 ); const Vector3 a1b1( b1 - a1 ); const real_t da1 ( a1a2 * a1b1 ); const real_t db1 ( b1b2 * a1b1 ); if( da1 <= +Limits::fpuAccuracy() && db1 >= -Limits::fpuAccuracy() ) { const Vector3 a1a2( a2 - a1 ); const Vector3 b1b2( b2 - b1 ); const Vector3 a1b1( b1 - a1 ); const T da1 ( a1a2 * a1b1 ); const T db1 ( b1b2 * a1b1 ); if( da1 <= +Limits::fpuAccuracy() && db1 >= -Limits::fpuAccuracy() ) { cp1 = a1; cp2 = b1; return; } const Vector3 a1b2( b2 - a1 ); const real_t da2 ( a1a2 * a1b2 ); const real_t db2 ( b1b2 * a1b2 ); if( da2 <= +Limits::fpuAccuracy() && db2 <= +Limits::fpuAccuracy() ) { const Vector3 a1b2( b2 - a1 ); const T da2 ( a1a2 * a1b2 ); const T db2 ( b1b2 * a1b2 ); if( da2 <= +Limits::fpuAccuracy() && db2 <= +Limits::fpuAccuracy() ) { cp1 = a1; cp2 = b2; return; } const Vector3 a2b1( b1 - a2 ); const real_t da3 ( a1a2 * a2b1 ); const real_t db3 ( b1b2 * a2b1 ); if( da3 >= -Limits::fpuAccuracy() && db3 >= -Limits::fpuAccuracy() ) { const Vector3 a2b1( b1 - a2 ); const T da3 ( a1a2 * a2b1 ); const T db3 ( b1b2 * a2b1 ); if( da3 >= -Limits::fpuAccuracy() && db3 >= -Limits::fpuAccuracy() ) { cp1 = a2; cp2 = b1; return; } const Vector3 a2b2( b2 - a2 ); const real_t da4 ( a1a2 * a2b2 ); const real_t db4 ( b1b2 * a2b2 ); if( da4 >= -Limits::fpuAccuracy() && db4 <= +Limits::fpuAccuracy() ) { const Vector3 a2b2( b2 - a2 ); const T da4 ( a1a2 * a2b2 ); const T db4 ( b1b2 * a2b2 ); if( da4 >= -Limits::fpuAccuracy() && db4 <= +Limits::fpuAccuracy() ) { cp1 = a2; cp2 = b2; return; ... ... @@ -297,44 +305,44 @@ void getClosestLineSegmentPoints( const Vector3& a1, const Vector3 n, k; Vector3 n, k; const real_t la( a1a2 * a1a2 ); if( da1 >= -Limits::fpuAccuracy() && da3 <= +Limits::fpuAccuracy() ) { const T la( a1a2 * a1a2 ); if( da1 >= -Limits::fpuAccuracy() && da3 <= +Limits::fpuAccuracy() ) { k = (da1 / la) * a1a2; n = a1b1 - k; if( b1b2 * n >= -Limits::fpuAccuracy() ) { if( b1b2 * n >= -Limits::fpuAccuracy() ) { cp1 = a1 + k; cp2 = b1; return; } } if( da2 >= -Limits::fpuAccuracy() && da4 <= +Limits::fpuAccuracy() ) { if( da2 >= -Limits::fpuAccuracy() && da4 <= +Limits::fpuAccuracy() ) { k = (da2 / la) * a1a2; n = a1b2 - k; if( b1b2 * n <= +Limits::fpuAccuracy() ) { if( b1b2 * n <= +Limits::fpuAccuracy() ) { cp1 = a1 + k; cp2 = b2; return; } } const real_t lb( b1b2 * b1b2 ); if( db1 <= +Limits::fpuAccuracy() && db2 >= -Limits::fpuAccuracy() ) { const T lb( b1b2 * b1b2 ); if( db1 <= +Limits::fpuAccuracy() && db2 >= -Limits::fpuAccuracy() ) { k = (-db1 / lb) * b1b2; n = -a1b1 - k; if( a1a2 * n >= -Limits::fpuAccuracy() ) { if( a1a2 * n >= -Limits::fpuAccuracy() ) { cp1 = a1; cp2= b1 + k; return; } } if( db3 <= +Limits::fpuAccuracy() && db4 >= -Limits::fpuAccuracy() ) { if( db3 <= +Limits::fpuAccuracy() && db4 >= -Limits::fpuAccuracy() ) { k = (-db3 / lb) * b1b2; n = -a2b1 - k; if( a1a2 * n <= +Limits::fpuAccuracy() ) { if( a1a2 * n <= +Limits::fpuAccuracy() ) { cp1 = a2; cp2 = b1 + k; return; ... ... @@ -344,18 +352,24 @@ void getClosestLineSegmentPoints( const Vector3& a1, const Vector3( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ); template void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ); //************************************************************************************************* ... ... @@ -377,30 +391,37 @@ void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, real_t& s, real_t& t ) template void intersectLines( const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, T& s, T& t ) { using namespace walberla::math; const real_t sqrlen( ( d1 % d2 ).sqrLength() ); const T sqrlen( ( d1 % d2 ).sqrLength() ); if( floatIsEqual(sqrlen, 0) ) if( floatIsEqual(sqrlen, 0.0) ) { s = real_t(0), t = real_t(0); s = T(0), t = T(0); } else { const real_t isqrlen( real_t(1) / sqrlen ); const Vector3 p( o2 - o1 ); const real_t dot( d1 * d2 ); const real_t a ( d1 * p ); const real_t b ( -d2 * p ); const T isqrlen( T(1) / sqrlen ); const Vector3 p( o2 - o1 ); const T dot( d1 * d2 ); const T a ( d1 * p ); const T b ( -d2 * p ); s = ( a * ( d2 * d2 ) + dot*b ) * isqrlen; t = ( b * ( d1 * d1 ) + dot*a ) * isqrlen; } } template void intersectLines( const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, float& s, float& t ); template void intersectLines( const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, double& s, double& t ); //************************************************************************************************* } // namespace math ... ...
 ... ... @@ -47,14 +47,17 @@ namespace geometry { //************************************************************************************************* /*!\name Geometry functions */ //@{ void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret); template void getClosestLineBoxPoints( const Vector3& p1, const Vector3& p2, const Vector3& c, const Matrix3& R, const Vector3& side, Vector3& lret, Vector3& bret); void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ); template void getClosestLineSegmentPoints( const Vector3& a1, const Vector3& a2, const Vector3& b1, const Vector3& b2, Vector3& cp1, Vector3& cp2 ); void intersectLines( const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, real_t& s, real_t& t ); template void intersectLines( const Vector3& o1, const Vector3& d1, const Vector3& o2, const Vector3& d2, T& s, T& t ); inline bool originInTetrahedron( const Vector3& A, const Vector3& B, const Vector3& C, const Vector3& D ); inline bool pointInTetrahedron ( const Vector3& A, const Vector3& B, const Vector3& C, const Vector3& D, ... ...
 ... ... @@ -47,10 +47,11 @@ bool hasNeighborOwner(const BlockT& block, const Owner& owner) * Returns -1 if no valid block is found otherwise the process rank of the containing block is returned. */ template Owner findContainingProcess(const BlockT& block, math::Vector3 pt) Owner findContainingProcess(const BlockT& block, math::Vector3 pt) { if (block.getAABB().contains(pt)) return Owner(int_c(block.getProcess()), block.getId().getID()); if (!block.getBlockStorage().getDomain().contains(pt)) block.getBlockStorage().mapToPeriodicDomain(pt); Vector3 pr(real_c(pt[0]), real_c(pt[1]), real_c(pt[2])); if (!block.getBlockStorage().getDomain().contains(pr)) block.getBlockStorage().mapToPeriodicDomain(pr); for( uint_t i = uint_t(0); i != block.getNeighborhoodSize(); ++i ) { if (block.getNeighborAABB(i).contains(pt)) return Owner(int_c(block.getNeighborProcess(i)), block.getNeighborId(i).getID()); ... ...
 ... ... @@ -5,7 +5,11 @@ # ################################################################################################### if(WALBERLA_DOUBLE_ACCURACY) if (WALBERLA_DOUBLE_ACCURACY AND NOT WALBERLA_PE_DOUBLE_ACCURACY) message(FATAL_ERROR "PE cannot use double precision if waLBerla uses single precision") endif() if(WALBERLA_PE_DOUBLE_ACCURACY) set(CCD_DOUBLE ON) set(CCD_SINGLE OFF) else() ... ...
 ... ... @@ -44,7 +44,7 @@ namespace pe { * For more details about the calculation of the motion of a rigid body, see the description of * the RigidBody::calcMotion() function. */ const real_t sleepThreshold = real_c( 0 ); const pe_real_t sleepThreshold = real_c( 0 ); //************************************************************************************************* ... ... @@ -57,7 +57,7 @@ const real_t sleepThreshold = real_c( 0 ); * recency-weighted average equal to the current motion of the rigid body, a bias of one * ignores the current motion altogether. */ const real_t sleepBias = real_c( 0.5 ); const pe_real_t sleepBias = real_c( 0.5 ); //************************************************************************************************* } // namespace pe ... ...
 ... ... @@ -72,34 +72,34 @@ bool Material::activateMaterials() // Initializing the coefficients of restitution // | Iron | Copper | Granite | Oak | Fir | const real_t cor[5][5] = { { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Iron { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Copper { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Granite { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Oak { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) } // Fir const pe_real_t cor[5][5] = { { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Iron { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Copper { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast(0.25) }, // Granite { static_cast(0.25), static_cast(0.25), static_cast(0.25), static_cast<