/* This file is part of the OpenLB library * * Copyright (C) 2018 Julius Woerner, Albert Mink * E-mail contact: info@openlb.net * The most recent release of OpenLB can be downloaded at * * * 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 2 * 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, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /** \file * discretization of anisotrphic phasefunction * DOI 10.1016/j.ijheatmasstransfer.2011.11.009 * */ #include #include #include #include #include #include #include #include #ifdef FEATURE_OPENBLAS #include #endif #include "utilities/vectorHelpers.h" namespace olb { /** Function to compute Henyey Greenstein phase funtion * \param cosTheta cos(theta) of scattering event with net direction theta * \param g anisotropy factor */ double henyeyGreenstein(double cosTheta, double g) { return (1-g*g) / std::pow(1+g*g-2*g*cosTheta ,1.5); } // check for energy conservation template< int q, typename DESCRIPTOR> std::array testEnergyConservationColumn( const std::array,q>& phi ) { std::array discIntegral; for( int iVec = 0; iVec < q; iVec++ ) { discIntegral[iVec] = 0; for( int iSum = 0; iSum < q; iSum++ ) { discIntegral[iVec] += descriptors::t(iSum) * phi[iSum][iVec]; } } return discIntegral; } // check for energy conservation template std::array testEnergyConservationRow( const std::array,q>& phi ) { std::array discIntegral; for( int iVec = 0; iVec < q; iVec++ ) { discIntegral[iVec] = 0; for( int iSum = 0; iSum < q; iSum++ ) { discIntegral[iVec] += descriptors::t(iSum) * phi[iVec][iSum]; } } return discIntegral; } // check for anisotropy conservation template< int q> std::array testAnisotropyConservationColumn( const std::array,q>& phi, const double weights[q], std::array,q>& cosTheta) { std::array discIntegral; for( int iVec = 0; iVec < q; iVec++ ) { discIntegral[iVec] = 0; for( int iSum = 0; iSum < q; iSum++ ) { discIntegral[iVec] += weights[iSum] * cosTheta[iVec][iSum]* phi[iVec][iSum]; } } return discIntegral; } /** Computes vector [a, a+stepsize, a+2*stepsize, ..., b] * \param stepsize * \param a * \param b */ std::vector linespace( double const stepsize, double const a, double const b ) { std::vector linspace{}; // initalize to empty if( util::nearZero( a-b ) ) { return linspace; } int const h = (int) ( (b-a) / stepsize); for (int n = 0; n <= h; n++) { linspace.push_back( a +double(n)/h * b ); } return linspace; } /** * \tparam DESCRIPTOR a lattice stencil * \tparam q number of lattice stencils MINUS one, 0th direction not needed * \param stepsize choose precision * \param anisotropyFactor also known as g * \param solution for debugging * \param phi is anisotropy matrix(sym), element [i][j] probability of scattering from i into j (direction) * \param breakAfter to limit endless iteration for testing */ template void computeAnisotropyMatrix( double const stepsize, double const anisotropyFactor, double solution[(DESCRIPTOR::q-1)*((DESCRIPTOR::q-1)+1)/2], std::array, DESCRIPTOR::q-1>& phi, int const breakAfter = -1) { using namespace descriptors; OstreamManager clout( std::cout, "AnisotropyMatrix" ); clout << "Call AnistorpiyMatrix()" << std::endl; #ifdef FEATURE_OPENBLAS clout << "Compute anisotropy matrix ..." << std::endl; typedef DESCRIPTOR L; int const q = L::q-1; int const mm = 2*q; int const nn = (q+1)*q/2; // qxq matrix 0th row and 0th column are empty std::vector> angleProb; angleProb.resize(q); for ( int n = 0; n < q; n++ ) { angleProb[n].resize(q); } double dotProduct; double normI; double normJ; for (int iPop=0; iPop(iPop+1)*c(jPop+1); normI = std::sqrt( c(iPop+1)*c(iPop+1) ); normJ = std::sqrt( c(jPop+1)*c(jPop+1) ); angleProb[iPop][jPop] = dotProduct / (normI*normJ); } } for (int i=0; i anisoIterVector = linespace( stepsize, 0, anisotropyFactor ); // additional condition only for unit testing size_t index = 0; for ( ; index < anisoIterVector.size() && index != (std::size_t)(breakAfter); index++) { // wipe matrices and vectors for (int m = 0; m < mm; m++) { for (int n = 0; n < nn; n++) { U[m][n] = 0; } } // compute matrix U, iter handels symmetry iter = 0; for (int m=0; m(m+1)*phi[n][m]; U[n][iter] = t(n+1)*phi[m][n]; U[m+q][iter] = t(m+1)*phi[n][m]*angleProb[n][m]; U[n+q][iter] = t(n+1)*phi[m][n]*angleProb[m][n]; iter++; } } double sum1; double sum2; // compute vector b for (int n=0; n(k+1)*phi[k][n]; sum2 += t(k+1)*phi[k][n]*angleProb[k][n]; } solution[n] = 1 - sum1; solution[q+n] = anisoIterVector[index] - sum2; } // transform 2d array to 1d array, column major for ( int n = 0; n < nn; ++n) { for ( int m = 0; m < mm; ++m ) { U_array[n*mm +m] = U[m][n]; } } //util::print(b, nn, "b"); // solve Ax = QRx = R'Q'x = b // equiv x = Q*R'\x LAPACKE_dgels( LAPACK_COL_MAJOR, 'N', mm, nn, 1, U_array, mm, solution, nn); //util::print(b, nn, "b after"); iter = 0; for (int m=0; m(phi,L::t), "colum sum elementwise" ); //util::print( testEnergyConservationRow(phi,L::t), "row sum elementwise" ); //util::print( testEnergyConservationSec(phi,L::t,angleProb), "second Moment" ); } clout << "Terminate after " << index << " iterations" << std::endl; #endif } template void computeAnisotropyMatrixKimAndLee( double const anisotropyFactor, std::array,DESCRIPTOR::q>& phi ) { OstreamManager clout( std::cout, "AnisotropyMatrix_KimAndLee" ); clout << "Compute anisotropy matrix ..." << std::endl; typedef DESCRIPTOR L; int const q = L::q; // qxq matrix 0th row and 0th column are empty std::array, q> cosTheta; double dotProduct; double normI; double normJ; for (int iPop=0; iPop(iPop) * descriptors::c(jPop); normI = std::sqrt( util::normSqr(descriptors::c(iPop)) ); normJ = std::sqrt( util::normSqr(descriptors::c(jPop)) ); cosTheta[iPop][jPop] = dotProduct /(normI*normJ); if( util::normSqr(descriptors::c(iPop)) == 0 || util::normSqr(descriptors::c(jPop)) == 0){ cosTheta[iPop][jPop] = 0.0; } } } std::array, q> phaseFunction; for (int m=0; m(i); } phi[m][n] = phaseFunction[m][n] / dotProduct; } } //util::print( testEnergyConservationColumn(phi,L::t), "colum sum elementwise" ); //util::print( testEnergyConservationRow(phi,L::t), "row sum elementwise" ); //util::print( testAnisotropyConservationColumn(phi,L::t,cosTheta), "anisotropyConservation Moment" ); } } // namespace olb