#include #include #include #include #include #include #include "analysis/pmmc.h" #include "common/ScaLBL.h" #include "common/MPI_Helpers.h" #include "common/Communication.h" #include "IO/Mesh.h" #include "IO/Writer.h" #include "ProfilerApp.h" using namespace std; std::shared_ptr readInput( const std::string& file ) { if ( exists( file ) return std::make_shared( file ); auto db = std::make_shared(); db.putData( "Color", std::make_shared() ); db.putData( "Domain", std::make_shared() ); return db; } int main(int argc, char **argv) { // Initialize MPI int provided_thread_support = -1; MPI_Init_thread(&argc,&argv,MPI_THREAD_MULTIPLE,&provided_thread_support); MPI_Comm comm; MPI_Comm_dup(MPI_COMM_WORLD,&comm); int rank = comm_rank(comm); int nprocs = comm_size(comm); if ( rank==0 && provided_thread_supportgetDatabase( "Color" ); auto tau = color_db->getScalarWithDefault( "tau", 1.0 ); auto alpha = color_db->getScalarWithDefault( "alpha", 1e-3 ); auto beta = color_db->getScalarWithDefault( "beta", 0.95 ); auto phi_s = color_db->getScalarWithDefault( "phi_s", 0.0 ); auto Fx = color_db->getVectorWithDefault( "F", { 0, 0, 0 } )[0]; auto Fy = color_db->getScalarWithDefault( "F", { 0, 0, 0 } )[1]; auto Fz = color_db->getScalarWithDefault( "F", { 0, 0, 0 } )[2]; auto Restart = color_db->getScalarWithDefault( "Restart", 0 ); auto pBC = color_db->getScalarWithDefault( "pBC", 0 ); auto din = color_db->getScalarWithDefault( "din", 1.0 ); auto dout = color_db->getScalarWithDefault( "dout", 1.0 ); auto timestepMax = color_db->getScalarWithDefault( "timestepMax", 3 ); auto interval = color_db->getScalarWithDefault( "interval", 100 ); auto tol = color_db->getScalarWithDefault( "tol", 1e-6 ); auto das = color_db->getScalarWithDefault( "das", 0.1 ); auto dbs = color_db->getScalarWithDefault( "dab", 0.9 ); // Read variables from Domain auto domain_db = db->getDatabase( "Domain" ); auto n = domain_db->getVectorWithDefault( "n", { 50, 50, 50 } ); auto L = domain_db->getVectorWithDefault( "L", { 1.0, 1.0, 1.0 } ); auto nproc = domain_db->getVectorWithDefault( "nproc", { 1, 1, 1 } ); auto nspheres = domain_db->getScalarWithDefault( "nspheres", 1 ); int Nx = n[0]; int Ny = n[1]; int Nz = n[2]; int nprocx = nproc[0]; int nprocy = nproc[1]; int nprocz = nproc[2]; double Lx = L[0]; double Ly = L[1]; double Lz = L[2]; // Get the rank info const RankInfoStruct rank_info(rank,nprocx,nprocy,nprocz); int iproc = rank_info.ix; int jproc = rank_info.jy; int kproc = rank_info.kz; MPI_Barrier(comm); // ************************************************************** // ************************************************************** double Ps = -(das-dbs)/(das+dbs); double rlxA = 1.f/tau; double rlxB = 8.f*(2.f-rlxA)/(8.f-rlxA); double xIntPos; xIntPos = log((1.0+phi_s)/(1.0-phi_s))/(2.0*beta); if (nprocs != nprocx*nprocy*nprocz){ printf("Fatal error in processor number! \n"); printf(" nprocx = %i \n",nprocx); printf(" nprocy = %i \n",nprocy); printf(" nprocz = %i \n",nprocz); return 1; } if (rank==0){ printf("********************************************************\n"); printf("tau = %f \n", tau); printf("alpha = %f \n", alpha); printf("beta = %f \n", beta); printf("das = %f \n", das); printf("dbs = %f \n", dbs); printf("Value of phi at solid surface = %f \n", phi_s); printf("Distance to phi = 0.0: %f \n", xIntPos); printf("gamma_{wn} = %f \n", 5.796*alpha); // printf("cos theta_c = %f \n", 1.05332*Ps); printf("Force(x) = %f \n", Fx); printf("Force(y) = %f \n", Fy); printf("Force(z) = %f \n", Fz); printf("Sub-domain size = %i x %i x %i\n",Nz,Nz,Nz); printf("Parallel domain size = %i x %i x %i\n",nprocx,nprocy,nprocz); printf("********************************************************\n"); } // Full domain used for averaging (do not use mask for analysis) Domain Dm(Nx,Ny,Nz,rank,nprocx,nprocy,nprocz,Lx,Ly,Lz,pBC); Dm.CommInit(comm); // Mask that excludes the solid phase Domain Mask(Nx,Ny,Nz,rank,nprocx,nprocy,nprocz,Lx,Ly,Lz,pBC); MPI_Barrier(comm); Nx+=2; Ny+=2; Nz += 2; int N = Nx*Ny*Nz; int dist_mem_size = N*sizeof(double); int S = N/nthreads/nBlocks+1; // unsigned int nBlocks = N/nthreads + (N%nthreads == 0?0:1); // dim3 grid(nBlocks,1,1); if (rank==0) printf("Number of blocks = %i \n", nBlocks); if (rank==0) printf("Threads per block = %i \n", nthreads); if (rank==0) printf("Sweeps per thread = %i \n", S); if (rank==0) printf("Number of nodes per side = %i \n", Nx); if (rank==0) printf("Total Number of nodes = %i \n", N); if (rank==0) printf("********************************************************\n"); //....................................................................... if (rank == 0) printf("Read input media... \n"); //....................................................................... //....................................................................... // Filenames used char LocalRankString[8]; char LocalRankFilename[40]; char LocalRestartFile[40]; sprintf(LocalRankString,"%05d",rank); sprintf(LocalRankFilename,"%s%s","ID.",LocalRankString); sprintf(LocalRestartFile,"%s%s","Restart.",LocalRankString); // printf("Local File Name = %s \n",LocalRankFilename); // .......... READ THE INPUT FILE ....................................... // char value; char *id; id = new char[N]; int sum = 0; double iVol_global = 1.0/(1.0*Nx*Ny*Nz*nprocs); double porosity = 0; DoubleArray SDs(Nx,Ny,Nz); DoubleArray SDn(Nx,Ny,Nz); //....................................................................... double BubbleRadius = 15.5; // Radius of the capillary tube sum=0; for (k=0;k fluid_isovalue) int cube[8][3] = {{0,0,0},{1,0,0},{0,1,0},{1,1,0},{0,0,1},{1,0,1},{0,1,1},{1,1,1}}; // cube corners DoubleArray CubeValues(2,2,2); // int count_in=0,count_out=0; // int nodx,nody,nodz; // initialize lists for vertices for surfaces, common line DTMutableList nw_pts(20); DTMutableList ns_pts(20); DTMutableList ws_pts(20); DTMutableList nws_pts(20); // initialize triangle lists for surfaces IntArray nw_tris(3,20); IntArray ns_tris(3,20); IntArray ws_tris(3,20); // initialize list for line segments IntArray nws_seg(2,20); DTMutableList tmp(20); DoubleArray Values(20); DoubleArray ContactAngle(20); DoubleArray Curvature(20); DoubleArray InterfaceSpeed(20); DoubleArray NormalVector(60); // IntArray store; int n_nw_pts=0,n_ns_pts=0,n_ws_pts=0,n_nws_pts=0; int n_nw_tris=0, n_ns_tris=0, n_ws_tris=0, n_nws_seg=0; // double s,s1,s2,s3; // Triangle sides (lengths) Point A,B,C,P; // double area; // Initialize arrays for local solid surface DTMutableList local_sol_pts(20); int n_local_sol_pts = 0; IntArray local_sol_tris(3,18); int n_local_sol_tris; DoubleArray values(20); DTMutableList local_nws_pts(20); int n_local_nws_pts; //int n_nw_tris_beg, n_ns_tris_beg, n_ws_tris_beg; int c; //int newton_steps = 0; //........................................................................... int ncubes = (Nx-2)*(Ny-2)*(Nz-2); // Exclude the "upper" halo IntArray cubeList(3,ncubes); int nc=0; //........................................................................... // Set up the cube list (very regular in this case due to lack of blob-ID) for (k=0; k CPU //........................................................................... ScaLBL_DeviceBarrier(); ScaLBL_D3Q19_Pressure(ID,f_even,f_odd,Pressure,Nx,Ny,Nz); ScaLBL_CopyToHost(Phase.data(),Phi,N*sizeof(double)); ScaLBL_CopyToHost(Press.data(),Pressure,N*sizeof(double)); MPI_Barrier(comm); //........................................................................... int timestep = 0; //************ MAIN ITERATION LOOP ***************************************/ PROFILE_START("Time-loop"); while (timestep < timestepMax){ //************************************************************************* // Fused Color Gradient and Collision //************************************************************************* ScaLBL_D3Q19_ColorCollide( ID,f_even,f_odd,Phi,ColorGrad, Velocity,Nx,Ny,Nz,rlxA,rlxB,alpha,beta,Fx,Fy,Fz); //************************************************************************* ScaLBL_DeviceBarrier(); //************************************************************************* // Pack and send the D3Q19 distributions ScaLBL_Comm.SendD3Q19(f_even, f_odd); //************************************************************************* //************************************************************************* // Carry out the density streaming step for mass transport //************************************************************************* ScaLBL_D3Q7_ColorCollideMass(ID, A_even, A_odd, B_even, B_odd, Den, Phi, ColorGrad, Velocity, beta, N, pBC); //************************************************************************* ScaLBL_DeviceBarrier(); MPI_Barrier(comm); //************************************************************************* // Swap the distributions for momentum transport //************************************************************************* ScaLBL_D3Q19_Swap(ID, f_even, f_odd, Nx, Ny, Nz); //************************************************************************* ScaLBL_DeviceBarrier(); MPI_Barrier(comm); //************************************************************************* // Wait for communications to complete and unpack the distributions ScaLBL_Comm.RecvD3Q19(f_even, f_odd); //************************************************************************* ScaLBL_DeviceBarrier(); //************************************************************************* // Pack and send the D3Q7 distributions ScaLBL_Comm.BiSendD3Q7(A_even, A_odd, B_even, B_odd); //************************************************************************* ScaLBL_DeviceBarrier(); ScaLBL_D3Q7_Swap(ID, A_even, A_odd, Nx, Ny, Nz); ScaLBL_D3Q7_Swap(ID, B_even, B_odd, Nx, Ny, Nz); ScaLBL_DeviceBarrier(); MPI_Barrier(comm); //************************************************************************* // Wait for communication and unpack the D3Q7 distributions ScaLBL_Comm.BiRecvD3Q7(A_even, A_odd, B_even, B_odd); //************************************************************************* ScaLBL_DeviceBarrier(); //.................................................................................. ScaLBL_D3Q7_Density(ID, A_even, A_odd, &Den[0], Nx, Ny, Nz); ScaLBL_D3Q7_Density(ID, B_even, B_odd, &Den[N], Nx, Ny, Nz); //************************************************************************* // Compute the phase indicator field //************************************************************************* // ScaLBL_ComputePhaseField(ID, Phi, Copy, Den, N); ScaLBL_DeviceBarrier(); MPI_Barrier(comm); ScaLBL_ComputePhaseField(ID, Phi, Den, N); //************************************************************************* ScaLBL_Comm.SendHalo(Phi); ScaLBL_DeviceBarrier(); ScaLBL_Comm.RecvHalo(Phi); //************************************************************************* ScaLBL_DeviceBarrier(); // Pressure boundary conditions if (pBC && kproc == 0) { ScaLBL_D3Q19_Pressure_BC_z(f_even,f_odd,din,Nx,Ny,Nz); ScaLBL_D3Q19_Velocity(ID,f_even,f_odd,Velocity,Nx,Ny,Nz); ScaLBL_Color_BC_z(Phi,Den,Velocity,A_even,A_odd,B_even,B_odd,Nx,Ny,Nz); } if (pBC && kproc == nprocz-1){ ScaLBL_D3Q19_Pressure_BC_Z(f_even,f_odd,dout,Nx,Ny,Nz,Nx*Ny*(Nz-2)); ScaLBL_D3Q19_Velocity(ID,f_even,f_odd,Velocity,Nx,Ny,Nz); ScaLBL_Color_BC_Z(Phi,Den,Velocity,A_even,A_odd,B_even,B_odd,Nx,Ny,Nz); } //................................................................................... MPI_Barrier(comm); // Timestep completed! timestep++; if (timestep%RESTART_INTERVAL == 0){ // Copy the data to the CPU ScaLBL_CopyToHost(cDistEven,f_even,10*N*sizeof(double)); ScaLBL_CopyToHost(cDistOdd,f_odd,9*N*sizeof(double)); ScaLBL_CopyToHost(cDen,Den,2*N*sizeof(double)); // Read in the restart file to CPU buffers WriteCheckpoint(LocalRestartFile, cDen, cDistEven, cDistOdd, N); } } PROFILE_STOP("Time-loop"); // End the bubble loop //........................................................................... // Copy the phase indicator field for the later timestep ScaLBL_DeviceBarrier(); ScaLBL_D3Q19_Pressure(ID,f_even,f_odd,Pressure,Nx,Ny,Nz); ScaLBL_CopyToHost(Phase_tminus.data(),Phi,N*sizeof(double)); ScaLBL_CopyToHost(Phase_tplus.data(),Phi,N*sizeof(double)); ScaLBL_CopyToHost(Phase.data(),Phi,N*sizeof(double)); ScaLBL_CopyToHost(Press.data(),Pressure,N*sizeof(double)); double temp=0.5/beta; for (n=0; n 0 ){ // 1-D index for this cube corner n = i+cube[p][0] + (j+cube[p][1])*Nx + (k+cube[p][2])*Nx*Ny; // compute the norm of the gradient of the phase indicator field delphi = sqrt(Phase_x(n)*Phase_x(n)+Phase_y(n)*Phase_y(n)+Phase_z(n)*Phase_z(n)); // Compute the non-wetting phase volume contribution if ( Phase(i+cube[p][0],j+cube[p][1],k+cube[p][2]) > 0 ){ nwp_volume += 0.125; // volume the excludes the interfacial region if (delphi < 1e-4){ vol_n += 0.125; // pressure pan += 0.125*Press(n); } } else if (delphi < 1e-4){ // volume the excludes the interfacial region vol_w += 0.125; // pressure paw += 0.125*Press(n); } } } //........................................................................... // Construct the interfaces and common curve pmmc_ConstructLocalCube(SDs, SDn, solid_isovalue, fluid_isovalue, nw_pts, nw_tris, values, ns_pts, ns_tris, ws_pts, ws_tris, local_nws_pts, nws_pts, nws_seg, local_sol_pts, local_sol_tris, n_local_sol_tris, n_local_sol_pts, n_nw_pts, n_nw_tris, n_ws_pts, n_ws_tris, n_ns_tris, n_ns_pts, n_local_nws_pts, n_nws_pts, n_nws_seg, i, j, k, Nx, Ny, Nz); // Integrate the contact angle efawns += pmmc_CubeContactAngle(CubeValues,Values,Phase_x,Phase_y,Phase_z,SDs_x,SDs_y,SDs_z, local_nws_pts,i,j,k,n_local_nws_pts); // Integrate the mean curvature Jwn += pmmc_CubeSurfaceInterpValue(CubeValues,MeanCurvature,nw_pts,nw_tris,Values,i,j,k,n_nw_pts,n_nw_tris); pmmc_InterfaceSpeed(dPdt, Phase_x, Phase_y, Phase_z, CubeValues, nw_pts, nw_tris, NormalVector, InterfaceSpeed, vawn, i, j, k, n_nw_pts, n_nw_tris); //........................................................................... // Compute the Interfacial Areas, Common Line length /* awn += pmmc_CubeSurfaceArea(nw_pts,nw_tris,n_nw_tris); ans += pmmc_CubeSurfaceArea(ns_pts,ns_tris,n_ns_tris); aws += pmmc_CubeSurfaceArea(ws_pts,ws_tris,n_ws_tris); */ As += pmmc_CubeSurfaceArea(local_sol_pts,local_sol_tris,n_local_sol_tris); // Compute the surface orientation and the interfacial area awn += pmmc_CubeSurfaceOrientation(Gwn,nw_pts,nw_tris,n_nw_tris); ans += pmmc_CubeSurfaceOrientation(Gns,ns_pts,ns_tris,n_ns_tris); aws += pmmc_CubeSurfaceOrientation(Gws,ws_pts,ws_tris,n_ws_tris); lwns += pmmc_CubeCurveLength(local_nws_pts,n_local_nws_pts); //........................................................................... } //........................................................................... MPI_Barrier(comm); MPI_Allreduce(&nwp_volume,&nwp_volume_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&awn,&awn_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&ans,&ans_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&aws,&aws_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&lwns,&lwns_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&As,&As_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&Jwn,&Jwn_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&efawns,&efawns_global,1,MPI_DOUBLE,MPI_SUM,comm); // Phase averages MPI_Allreduce(&vol_w,&vol_w_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&vol_n,&vol_n_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&paw,&paw_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&pan,&pan_global,1,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&vaw(0),&vaw_global(0),3,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&van(0),&van_global(0),3,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&vawn(0),&vawn_global(0),3,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&Gwn(0),&Gwn_global(0),6,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&Gns(0),&Gns_global(0),6,MPI_DOUBLE,MPI_SUM,comm); MPI_Allreduce(&Gws(0),&Gws_global(0),6,MPI_DOUBLE,MPI_SUM,comm); MPI_Barrier(comm); //......................................................................... // Compute the change in the total surface energy based on the defined interval // See McClure, Prins and Miller (2013) //......................................................................... dAwn += awn_global; dAns += ans_global; dEs = 6.01603*alpha*(dAwn + 1.05332*Ps*dAns); dAwn = -awn_global; // Get ready for the next analysis interval dAns = -ans_global; // Normalize the phase averages // (density of both components = 1.0) paw_global = paw_global / vol_w_global; vaw_global(0) = vaw_global(0) / vol_w_global; vaw_global(1) = vaw_global(1) / vol_w_global; vaw_global(2) = vaw_global(2) / vol_w_global; pan_global = pan_global / vol_n_global; van_global(0) = van_global(0) / vol_n_global; van_global(1) = van_global(1) / vol_n_global; van_global(2) = van_global(2) / vol_n_global; // Normalize surface averages by the interfacial area Jwn_global /= awn_global; efawns_global /= lwns_global; if (awn_global > 0.0) for (i=0; i<3; i++) vawn_global(i) /= awn_global; if (awn_global > 0.0) for (i=0; i<6; i++) Gwn_global(i) /= awn_global; if (ans_global > 0.0) for (i=0; i<6; i++) Gns_global(i) /= ans_global; if (aws_global > 0.0) for (i=0; i<6; i++) Gws_global(i) /= aws_global; sat_w = 1.0 - nwp_volume_global*iVol_global/porosity; // Compute the specific interfacial areas and common line length (per unit volume) awn_global = awn_global*iVol_global; ans_global = ans_global*iVol_global; aws_global = aws_global*iVol_global; lwns_global = lwns_global*iVol_global; dEs = dEs*iVol_global; //......................................................................... if (rank==0){ printf("%.5g ",BubbleRadius); // bubble radius printf("%.5g %.5g ",paw_global,pan_global); // saturation and pressure printf("%.5g ",awn_global); // interfacial area printf("%.5g ",Jwn_global); // curvature of wn interface printf("%.5g %.5g %.5g %.5g %.5g %.5g \n", Gwn_global(0),Gwn_global(1),Gwn_global(2),Gwn_global(3),Gwn_global(4),Gwn_global(5)); // orientation of wn interface } shared_ptr mesh( new IO::TriList() ); mesh->A.reserve(8*ncubes); mesh->B.reserve(8*ncubes); mesh->C.reserve(8*ncubes); for (c=0;cA.push_back(A); mesh->B.push_back(B); mesh->C.push_back(C); } } std::vector meshData(1); meshData[0].meshName = "wn-tris"; meshData[0].mesh = mesh; for (size_t k=0; k dist( new IO::Variable() ); dist->name = "distance"; dist->dim = 1; dist->type = IO::VariableType::NodeVariable; dist->data.resize(3*mesh->A.size()); for (size_t i=0; iA.size(); i++) { const Point& a = mesh->A[i]; const Point& b = mesh->B[i]; const Point& c = mesh->C[i]; dist->data(3*i+0) = sqrt(a.x*a.x+a.y*a.y+a.z*a.z); dist->data(3*i+1) = sqrt(b.x*b.x+b.y*b.y+b.z*b.z); dist->data(3*i+2) = sqrt(c.x*c.x+c.y*c.y+c.z*c.z); } meshData[k].vars.push_back(dist); } IO::writeData( bubbleCount, meshData, comm ); PROFILE_STOP(bubbleCountName); } NULL_USE(sat_w); //************************************************************************/ ScaLBL_DeviceBarrier(); MPI_Barrier(comm); stoptime = MPI_Wtime(); if (rank==0) printf("-------------------------------------------------------------------\n"); // Compute the walltime per timestep cputime = (stoptime - starttime)/timestep; // Performance obtained from each node double MLUPS = double(Nx*Ny*Nz)/cputime/1000000; if (rank==0) printf("********************************************************\n"); if (rank==0) printf("CPU time = %f \n", cputime); if (rank==0) printf("Lattice update rate (per core)= %f MLUPS \n", MLUPS); MLUPS *= nprocs; if (rank==0) printf("Lattice update rate (total)= %f MLUPS \n", MLUPS); if (rank==0) printf("********************************************************\n"); //************************************************************************/ // Write out the phase indicator field //************************************************************************/ sprintf(LocalRankFilename,"%s%s","Phase.",LocalRankString); // printf("Local File Name = %s \n",LocalRankFilename); // ScaLBL_CopyToHost(Phase.data(),Phi,N*sizeof(double)); FILE *PHASE; PHASE = fopen(LocalRankFilename,"wb"); fwrite(Press.data(),8,N,PHASE); // fwrite(MeanCurvature.data(),8,N,PHASE); fclose(PHASE); /* double *DensityValues; DensityValues = new double [2*N]; ScaLBL_CopyToHost(DensityValues,Copy,2*N*sizeof(double)); FILE *PHASE; PHASE = fopen(LocalRankFilename,"wb"); fwrite(DensityValues,8,2*N,PHASE); fclose(PHASE); */ //************************************************************************/ PROFILE_SAVE("TestBubble"); // **************************************************** MPI_Barrier(comm); } // Limit scope so variables that contain communicators will free before MPI_Finialize MPI_Comm_free(&comm); MPI_Finalize(); return 0; }