/* Lattice Boltzmann sample, written in C++, using the OpenLB * library * * Copyright (C) 2018 Robin Trunk * 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. */ /* youngLaplace3d.cpp * In this example a Young-Laplace test is performed. A spherical domain * of fluid 2 is immersed in fluid 1. A diffusive interface forms and the * surface tension can be calculated using the Laplace pressure relation. * The pressure difference is calculated between a point in the middle of * the circular domain and a point furthest away from it in the * computational domain (here left bottom corner). * * This example shows the simplest case for the free-energy model with two * fluid components. */ #include "olb3D.h" #include "olb3D.hh" // use only generic version! #include #include #include using namespace olb; using namespace olb::descriptors; using namespace olb::graphics; using namespace std; typedef double T; #define DESCRIPTOR D3Q19 // Parameters for the simulation setup const int N = 100; const T nx = 100.; const T radius = 0.25 * nx; const T alpha = 1.5; // Interfacial width [lattice units] const T kappa1 = 0.0075; // For surface tensions [lattice units] const T kappa2 = 0.005; // For surface tensions [lattice units] const T gama = 1.; // For mobility of interface [lattice units] const int maxIter = 60000; const int vtkIter = 200; const int statIter = 200; void prepareGeometry( SuperGeometry3D& superGeometry ) { OstreamManager clout( std::cout,"prepareGeometry" ); clout << "Prepare Geometry ..." << std::endl; superGeometry.rename( 0,1 ); superGeometry.innerClean(); superGeometry.checkForErrors(); superGeometry.print(); clout << "Prepare Geometry ... OK" << std::endl; } void prepareLattice( SuperLattice3D& sLattice1, SuperLattice3D& sLattice2, Dynamics& bulkDynamics1, Dynamics& bulkDynamics2, UnitConverter& converter, SuperGeometry3D& superGeometry ) { OstreamManager clout( std::cout,"prepareLattice" ); clout << "Prepare Lattice ..." << std::endl; // define lattice Dynamics sLattice1.defineDynamics( superGeometry, 0, &instances::getNoDynamics() ); sLattice2.defineDynamics( superGeometry, 0, &instances::getNoDynamics() ); sLattice1.defineDynamics( superGeometry, 1, &bulkDynamics1 ); sLattice2.defineDynamics( superGeometry, 1, &bulkDynamics2 ); // bulk initial conditions // define spherical domain for fluid 2 std::vector v( 3,T() ); AnalyticalConst3D zeroVelocity( v ); AnalyticalConst3D one ( 1. ); SmoothIndicatorSphere3D sphere( {nx/2., nx/2., nx/2.}, radius, 10.*alpha ); AnalyticalIdentity3D rho( one ); AnalyticalIdentity3D phi( one - sphere - sphere ); sLattice1.iniEquilibrium( superGeometry, 1, rho, zeroVelocity ); sLattice2.iniEquilibrium( superGeometry, 1, phi, zeroVelocity ); sLattice1.initialize(); sLattice2.initialize(); clout << "Prepare Lattice ... OK" << std::endl; } void prepareCoupling(SuperLattice3D& sLattice1, SuperLattice3D& sLattice2) { OstreamManager clout( std::cout,"prepareCoupling" ); clout << "Add lattice coupling" << endl; // Add the lattice couplings // The chemical potential coupling must come before the force coupling FreeEnergyChemicalPotentialGenerator3D coupling1( alpha, kappa1, kappa2); FreeEnergyForceGenerator3D coupling2; sLattice1.addLatticeCoupling( coupling1, sLattice2 ); sLattice2.addLatticeCoupling( coupling2, sLattice1 ); clout << "Add lattice coupling ... OK!" << endl; } void getResults( SuperLattice3D& sLattice2, SuperLattice3D& sLattice1, int iT, SuperGeometry3D& superGeometry, Timer& timer, UnitConverter converter) { OstreamManager clout( std::cout,"getResults" ); SuperVTMwriter3D vtmWriter( "youngLaplace3d" ); if ( iT==0 ) { // Writes the geometry, cuboid no. and rank no. as vti file for visualization SuperLatticeGeometry3D geometry( sLattice1, superGeometry ); SuperLatticeCuboid3D cuboid( sLattice1 ); SuperLatticeRank3D rank( sLattice1 ); vtmWriter.write( geometry ); vtmWriter.write( cuboid ); vtmWriter.write( rank ); vtmWriter.createMasterFile(); } // Get statistics if ( iT%statIter==0 ) { // Timer console output timer.update( iT ); timer.printStep(); sLattice1.getStatistics().print( iT, converter.getPhysTime(iT) ); sLattice2.getStatistics().print( iT, converter.getPhysTime(iT) ); } // Writes the VTK files if ( iT%vtkIter==0 ) { AnalyticalConst3D half_( 0.5 ); SuperLatticeFfromAnalyticalF3D half(half_, sLattice1); SuperLatticeDensity3D density1( sLattice1 ); density1.getName() = "rho"; SuperLatticeDensity3D density2( sLattice2 ); density2.getName() = "phi"; SuperIdentity3D c1 (half*(density1+density2)); c1.getName() = "density-fluid-1"; SuperIdentity3D c2 (half*(density1-density2)); c2.getName() = "density-fluid-2"; vtmWriter.addFunctor( density1 ); vtmWriter.addFunctor( density2 ); vtmWriter.addFunctor( c1 ); vtmWriter.addFunctor( c2 ); vtmWriter.write( iT ); // calculate bulk pressure, pressure difference and surface tension if(iT%statIter==0) { AnalyticalConst3D two_( 2. ); AnalyticalConst3D onefive_( 1.5 ); AnalyticalConst3D k1_( kappa1 ); AnalyticalConst3D k2_( kappa2 ); AnalyticalConst3D cs2_( 1./descriptors::invCs2() ); SuperLatticeFfromAnalyticalF3D two(two_, sLattice1); SuperLatticeFfromAnalyticalF3D onefive(onefive_, sLattice1); SuperLatticeFfromAnalyticalF3D k1(k1_, sLattice1); SuperLatticeFfromAnalyticalF3D k2(k2_, sLattice1); SuperLatticeFfromAnalyticalF3D cs2(cs2_, sLattice1); // Calculation of bulk pressure: // c_1 = density of fluid 1; c_2 = density of fluid 2 // p_bulk = rho*c_s^2 + kappa1 * (3/2*c_1^4 - 2*c_1^3 + 0.5*c_1^2) // + kappa2 * (3/2*c_2^4 - 2*c_2^3 + 0.5*c_2^2) SuperIdentity3D bulkPressure ( density1*cs2 + k1*( onefive*c1*c1*c1*c1 - two*c1*c1*c1 + half*c1*c1 ) + k2*( onefive*c2*c2*c2*c2 - two*c2*c2*c2 + half*c2*c2 ) ); AnalyticalFfromSuperF3D interpolPressure( bulkPressure, true, 1); double position[3] = { 0.5*nx, 0.5*nx, 0.5*nx }; double pressureIn = 0.; double pressureOut = 0.; interpolPressure(&pressureIn, position); position[0] = ((double)N/100.)*converter.getPhysDeltaX(); position[1] = ((double)N/100.)*converter.getPhysDeltaX(); position[2] = ((double)N/100.)*converter.getPhysDeltaX(); interpolPressure(&pressureOut, position); clout << "Pressure Difference: " << pressureIn-pressureOut << " ; "; clout << "Surface Tension: " << radius*(pressureIn-pressureOut)/2 << std::endl; clout << "Analytical Pressure Difference: " << alpha/(3.*radius) * (kappa1 + kappa2) << " ; "; clout << "Analytical Surface Tension: " << alpha/6. * (kappa1 + kappa2) << std::endl; } } } int main( int argc, char *argv[] ) { // === 1st Step: Initialization === olbInit( &argc, &argv ); singleton::directories().setOutputDir( "./tmp/" ); OstreamManager clout( std::cout,"main" ); UnitConverterFromResolutionAndRelaxationTime converter( (T) N, // resolution (T) 1., // lattice relaxation time (tau) (T) nx, // charPhysLength: reference length of simulation geometry (T) 1.e-6, // charPhysVelocity: maximal/highest expected velocity during simulation in __m / s__ (T) 0.1, // physViscosity: physical kinematic viscosity in __m^2 / s__ (T) 1. // physDensity: physical density in __kg / m^3__ ); // Prints the converter log as console output converter.print(); // === 2nd Step: Prepare Geometry === std::vector extend = { nx, nx, nx }; std::vector origin = { 0, 0, 0 }; IndicatorCuboid3D cuboid(extend,origin); #ifdef PARALLEL_MODE_MPI CuboidGeometry3D cGeometry( cuboid, converter.getPhysDeltaX(), singleton::mpi().getSize() ); #else CuboidGeometry3D cGeometry( cuboid, converter.getPhysDeltaX() ); #endif // set periodic boundaries to the domain cGeometry.setPeriodicity( true, true, true ); // Instantiation of loadbalancer HeuristicLoadBalancer loadBalancer( cGeometry ); loadBalancer.print(); // Instantiation of superGeometry SuperGeometry3D superGeometry( cGeometry,loadBalancer ); prepareGeometry( superGeometry ); // === 3rd Step: Prepare Lattice === SuperLattice3D sLattice1( superGeometry ); SuperLattice3D sLattice2( superGeometry ); ForcedBGKdynamics bulkDynamics1 ( converter.getLatticeRelaxationFrequency(), instances::getBulkMomenta() ); FreeEnergyBGKdynamics bulkDynamics2 ( converter.getLatticeRelaxationFrequency(), gama, instances::getBulkMomenta() ); prepareLattice( sLattice1, sLattice2, bulkDynamics1, bulkDynamics2, converter, superGeometry ); prepareCoupling( sLattice1, sLattice2); SuperExternal3D sExternal1 (superGeometry, sLattice1, sLattice1.getOverlap() ); SuperExternal3D sExternal2 (superGeometry, sLattice2, sLattice2.getOverlap() ); // === 4th Step: Main Loop with Timer === int iT = 0; clout << "starting simulation..." << endl; Timer timer( maxIter, superGeometry.getStatistics().getNvoxel() ); timer.start(); for ( iT=0; iT<=maxIter; ++iT ) { // Computation and output of the results getResults( sLattice2, sLattice1, iT, superGeometry, timer, converter ); // Collide and stream execution sLattice1.collideAndStream(); sLattice2.collideAndStream(); // MPI communication for lattice data sLattice1.communicate(); sLattice2.communicate(); // Execute coupling between the two lattices sLattice1.executeCoupling(); sExternal1.communicate(); sExternal2.communicate(); sLattice2.executeCoupling(); } timer.stop(); timer.printSummary(); }