Grid2D.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002 *       SOFA, Simulation Open-Framework Architecture, version 1.0 beta 3      *
00003 *                (c) 2006-2008 MGH, INRIA, USTL, UJF, CNRS                    *
00004 *                                                                             *
00005 * This library is free software; you can redistribute it and/or modify it     *
00006 * under the terms of the GNU Lesser General Public License as published by    *
00007 * the Free Software Foundation; either version 2.1 of the License, or (at     *
00008 * your option) any later version.                                             *
00009 *                                                                             *
00010 * This library is distributed in the hope that it will be useful, but WITHOUT *
00011 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
00012 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
00013 * for more details.                                                           *
00014 *                                                                             *
00015 * You should have received a copy of the GNU Lesser General Public License    *
00016 * along with this library; if not, write to the Free Software Foundation,     *
00017 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.          *
00018 *******************************************************************************
00019 *                               SOFA :: Modules                               *
00020 *                                                                             *
00021 * Authors: The SOFA Team and external contributors (see Authors.txt)          *
00022 *                                                                             *
00023 * Contact information: contact@sofa-framework.org                             *
00024 ******************************************************************************/
00025 #include <iostream>
00026 #include <sofa/component/behaviormodel/eulerianfluid/Grid2D.h>
00027 #include <cstring>
00028 
00029 namespace sofa
00030 {
00031 
00032 namespace component
00033 {
00034 
00035 namespace behaviormodel
00036 {
00037 
00038 namespace eulerianfluid
00039 {
00040 
00041 const unsigned long* Grid2D::obstacles = NULL;
00042 
00043 // For loop macros
00044 
00045 #define FOR_ALL_CELLS(grid,cmd)               \
00046 {                                             \
00047   int ind = index(0,0);                       \
00048     for (int y=0;y<ny;y++)                    \
00049       for (int x=0;x<nx;x++,ind+=index(1,0))  \
00050       {                                       \
00051     cmd;                                  \
00052       }                                       \
00053 }
00054 
00055 #define FOR_READ_INNER_CELLS(cmd)             \
00056 {                                             \
00057   int ind = index(1,1);                       \
00058     for (int y=1;y<ny-1;y++,ind+=index(2,0))  \
00059       for (int x=1;x<nx-1;x++,ind+=index(1,0))\
00060       {                                       \
00061     cmd;                                  \
00062       }                                       \
00063 }
00064 
00065 #define FOR_INNER_CELLS(grid,cmd)             \
00066 {                                             \
00067   int ind = index(1,1);                       \
00068     for (int y=1;y<ny-1;y++,ind+=index(2,0))  \
00069       for (int x=1;x<nx-1;x++,ind+=index(1,0))\
00070       {                                       \
00071     cmd;                                  \
00072       }                                       \
00073 }
00074 
00075 // Surface cells  are inner  cells and borders  between a  fluid inner
00076 // cell and an empty out cell (right or bottom side)
00077 
00078 #define FOR_SURFACE_CELLS(grid,cmd)           \
00079 {                                             \
00080   int ind = index(1,1);                       \
00081     for (int y=1;y<ny-1;y++,ind+=index(2,0))  \
00082       for (int x=1;x<nx-1;x++,ind+=index(1,0))\
00083       {                                       \
00084     cmd;                                  \
00085       }                                       \
00086 }
00087 
00088 Grid2D::Grid2D()
00089 : nx(0), ny(0), ncell(0),
00090   fdata(NULL), pressure(NULL), levelset(NULL),
00091   t(0), tend(60),
00092   max_pressure(0.0),
00093   fmm_status(NULL),
00094   fmm_heap(NULL),
00095   fmm_heap_size(0)
00096 {
00097   bcell.u.clear();
00098   bcell.type=-1;
00099 }
00100 
00101 Grid2D::~Grid2D()
00102 {
00103   if (fdata!=NULL) delete[] fdata;
00104   if (pressure!=NULL) delete[] pressure;
00105   if (levelset!=NULL) delete[] levelset;
00106   if (fmm_status!=NULL) delete[] fmm_status;
00107   if (fmm_heap!=NULL) delete[] fmm_heap;
00108 }
00109 
00110 void Grid2D::clear(int _nx, int _ny)
00111 {
00112     t = 0;
00113     tend = 40;
00114     
00115     int old_ncell=ncell;
00116     nx=_nx; ny=_ny;
00117     ncell = nx*ny;
00118     
00119     if (ncell != old_ncell)
00120     {
00121         if (fdata!=NULL) delete[] fdata;
00122         if (ncell>0)  fdata = new Cell[ncell];
00123         else          fdata = NULL;
00124         if (pressure!=NULL) delete[] pressure;
00125         if (ncell>0)  pressure = new real[ncell];
00126         else          pressure = NULL;
00127         if (levelset!=NULL) delete[] levelset;
00128         if (ncell>0)  levelset = new real[ncell];
00129         else          levelset = NULL;
00130         if (fmm_status!=NULL) delete[] fmm_status;
00131         if (ncell>0)  fmm_status = new int[ncell];
00132         else          fmm_status = NULL;
00133         if (fmm_heap!=NULL) delete[] fmm_heap;
00134         if (ncell>0)  fmm_heap = new int[ncell];
00135         else          fmm_heap = NULL;
00136     }
00137     if (ncell>0)
00138         memset(fdata,0,ncell*sizeof(Cell));
00139     if (ncell>0)
00140         memset(pressure,0,ncell*sizeof(real));
00141     if (ncell>0)
00142         memset(levelset,0,ncell*sizeof(real));
00143 }
00144 
00145 void Grid2D::seed(real height)
00146 {
00147     //seed(vec2(0,0), vec2(nx,height));
00148     FOR_ALL_CELLS(fdata,
00149     {
00150         real d = y - height;
00151         levelset[ind] = d;
00152     });
00153 }
00154 
00155 void Grid2D::seed(real height, vec2 normal)
00156 {
00157     normal.normalize();
00158     FOR_ALL_CELLS(fdata,
00159     {
00160         real d = vec2((real)x,(real)y)*normal - height;
00161         levelset[ind] = d;
00162     });
00163 }
00164 
00165 void Grid2D::seed(vec2 p0, vec2 p1, vec2 velocity)
00166 {
00167     if (p0[0]<0.5f) p0[0]=0.5f;
00168     if (p0[1]<0.5f) p0[1]=0.5f;
00169     if (p1[0]>nx-1.5f) p1[0]=nx-1.5f;
00170     if (p1[1]>ny-1.5f) p1[1]=ny-1.5f;
00171     if (p0[0]>=p1[0]) return;
00172     if (p0[1]>=p1[1]) return;
00173     std::cout << "p0="<<p0<<" p1="<<p1<<std::endl;
00174     vec2 center = (p0+p1)*0.5f;
00175     vec2 dim = (p1-p0)*0.5f;
00176     FOR_ALL_CELLS(fdata,
00177     {
00178         vec2 v ((real)x,(real)y);
00179         v -= center;
00180         v[0] = rabs(v[0]) - dim[0];
00181         v[1] = rabs(v[1]) - dim[1];
00182         real d;
00183         if (v[0] <= 0 && v[1] <= 0)
00184         {
00185             d = rmax(v[0],v[1]);
00186             fdata[ind].u = velocity;
00187         }
00188         else
00189         {
00190             if (v[0] < 0) v[0] = 0;
00191             if (v[1] < 0) v[1] = 0;
00192             d = v.norm();
00193         }
00194         levelset[ind] = d;
00195     });
00196 }
00197 
00198 void Grid2D::step(Grid2D* prev, Grid2D* temp, real dt, real diff)
00199 {
00200     t = prev->t+dt;
00201     tend = prev->tend;
00202     //std::cout << "STEP\n";
00203     
00204     //clear(prev->nx, prev->ny);
00205     //temp->clear(prev->nx, prev->ny);
00206     
00207     step_init(prev, temp, dt, diff);      // init fluid obstacles
00208     //step_particles(prev, temp, dt, diff); // init particles
00209     step_levelset(prev, temp, dt, diff); // advance levelset
00210     step_forces(prev, temp, dt, diff);    // init fluid u with prev u, particles and gravity
00211     step_surface(prev, temp, dt, diff);   // calc fluid u at air/fluid surfaces
00212     step_advect(prev, temp, dt, diff);    // advance fluid u to temp u
00213     step_diffuse(prev, temp, dt, diff);   // calc diffusion of temp u to fluid u
00214     step_project(prev, temp, dt, diff);   // calc pressure and project fluid u to divergent free field. use temp as temporary scalar fields
00215     
00216     // And that should be it!
00217     
00218     //std::cout << "STEP: Done!\n";
00219 }
00220 
00223 
00224 void Grid2D::step_init(const Grid2D* /*prev*/, Grid2D* /*temp*/, real /*dt*/, real /*diff*/)
00225 {
00226     //std::cout << "STEP: Obstacles\n";
00227     // Currently: only borders are obstacle
00228 
00229     int bsize = 1;
00230     //  int x0 = nx/2-2;
00231     //  int x1 = x0+4;
00232     //  int y0 = ny/2-2;
00233     //  int y1 = y0+4;
00234     
00235     const unsigned char* obs = (const unsigned char*)obstacles;
00236     int lnsize = (nx+7)/8;
00237     
00238     //memset(fdata,0,ncell*sizeof(Cell));
00239     
00240     FOR_ALL_CELLS(fdata,
00241     {
00242         if (x<bsize || y<bsize || x>=nx-bsize || y>=ny-bsize
00243         //    || (x>=x0 && x<x1 && y>=y0 && y<y1)
00244             || (obs!=NULL && ((obs[(y)*lnsize+((x)>>3)])&(1<<((x)&7))))
00245             )
00246         {
00247             fdata[ind].type = PART_WALL;
00248         }
00249         else
00250         {
00251             fdata[ind].type = PART_EMPTY;
00252         }
00253         levelset[ind] = 5;
00254     });
00255 }
00256 
00259 
00260 #define RMAX 1e10
00261 
00262 void Grid2D::step_levelset(Grid2D* prev, Grid2D* temp, real dt, real /*diff*/)
00263 {
00264 //    std::cout << "STEP: levelset\n";
00265 
00266     // advect levelset into temp
00267     
00268     // Modified Eulerian / Midpoint method
00269     // Carlson Thesis page 22
00270 
00271     FOR_INNER_CELLS(levelset,
00272     {
00273         //if (prev->fdata[ind].type != PART_WALL && rabs(prev->levelset[ind]) < 5)
00274         if (rabs(prev->levelset[ind]) < 5)
00275         {
00276             vec2 xn ( (real)x, (real)y );
00277             vec2 un = prev->interp(xn);
00278             vec2 xn1 = xn - un*dt; // xn1_2 at this time
00279 
00280             // TODO: Check if xn1_2 is not in empty cell
00281             un = (un + prev->interp(xn1))*0.5;
00282             xn1 = xn - un*dt;
00283             
00284             temp->levelset[ind] = prev->getlevelset(xn1);
00285         }
00286         else
00287             temp->levelset[ind] = prev->levelset[ind];
00288     });
00289 
00290     // fill border levelset using neighbors
00291 
00292     FOR_ALL_CELLS(fmm_status,
00293     {
00294         fmm_status[ind] = FMM_FAR;
00295         if (fdata[ind].type == PART_WALL)
00296         {
00297             // find a neighbor cell
00298             real phi = 0;
00299             int n = 0;
00300                     for (int dy=-1;dy<=1;dy++)
00301                         if ((unsigned)(y+dy)<(unsigned)ny)
00302                             for (int dx=-1;dx<=1;dx++)
00303                                 if ((unsigned)(x+dx)<(unsigned)nx)
00304                                 {
00305                                     int ind2 = ind+index(dx,dy);
00306                                     if (fdata[ind2].type != PART_WALL)
00307                                     {
00308                                         phi += temp->levelset[ind2];
00309                                         ++n;
00310                                     }
00311                                 }
00312             temp->levelset[ind] = phi/n;
00313         }
00314     });
00315     
00316     // Levelset Reinitilization using Fast Marching Method
00317     //TODO do not reinit at every steps
00318     
00319     fmm_heap_size = 0;
00320     const int cmax[2] = { nx-1, ny-1 };
00321     const int dind[2] = { 1, nx };
00322 
00323     // Compute all known points
00324     FOR_ALL_CELLS(fdata,
00325     {
00326         int c[2]; c[0] = x; c[1] = y;
00327         bool known = false;
00328         real phi0 = temp->levelset[ind];
00329         real inv_dist2 = 0;
00330         for (int dim = 0; dim < 2; dim++)
00331         {
00332             real dist = RMAX;
00333             bool border = false;
00334             if (c[dim]>0)
00335             {
00336                 real phi1 = temp->levelset[ind-dind[dim]];
00337                 if (phi1*phi0 < 0)
00338                 {
00339                     dist = phi0/(phi0-phi1);
00340                     border = true;
00341                 }
00342             }
00343             if (c[dim]<cmax[dim])
00344             {
00345                 real phi1 = temp->levelset[ind+dind[dim]];
00346                 if (phi1*phi0 < 0)
00347                 {
00348                     real d = phi0/(phi0-phi1);
00349                     if (d < dist) dist = d;
00350                     border = true;
00351                 }
00352             }
00353             if (border)
00354             {
00355                 known = true;
00356                 inv_dist2 += 1 / (dist*dist);
00357             }
00358         }
00359         if (known)
00360         {
00361             //real phi = 1 / sqrt(inv_dist2);
00362             real phi = rabs(phi0);
00363             levelset[ind] = phi;
00364             fmm_status[ind] = FMM_KNOWN;
00365         }
00366         else
00367         {
00368             levelset[ind] = 5; //RMAX;
00369             fmm_status[ind] = FMM_FAR;
00370         }
00371     });
00372 
00373     // Update known points neighbors
00374     FOR_ALL_CELLS(fdata,
00375     {
00376         if (fmm_status[ind] == FMM_KNOWN)
00377         {
00378             int c[2]; c[0] = x; c[1] = y;
00379             real phi1 = levelset[ind];
00380             phi1+=1;
00381             for (int dim = 0; dim < 2; dim++)
00382             {
00383                 if (c[dim]>0)
00384                 {
00385                     int ind2 = ind-dind[dim];
00386                     if (fmm_status[ind2] >= FMM_FAR)
00387                     {
00388                         real phi2 = levelset[ind2];
00389                         if ((phi1) < (phi2))
00390                         {
00391                             levelset[ind2] = phi1;
00392                             fmm_push(ind2); // create or update the corresponding entry in the heap
00393                         }
00394                     }
00395                 }
00396                 if (c[dim]<cmax[dim])
00397                 {
00398                     int ind2 = ind+dind[dim];
00399                     if (fmm_status[ind2] >= FMM_FAR)
00400                     {
00401                         real phi2 = levelset[ind2];
00402                         if ((phi1) < (phi2))
00403                         {
00404                             levelset[ind2] = phi1;
00405                             fmm_push(ind2); // create or update the corresponding entry in the heap
00406                         }
00407                     }
00408                 }
00409             }
00410         }
00411     });
00412     
00413     while (fmm_heap_size > 0)
00414     {
00415         int ind = fmm_pop();
00416         real phi1 = levelset[ind]+1;
00417         if ((phi1) >= 5) break;
00418         int c[2] = { ind % nx, (ind / nx) };
00419         for (int dim = 0; dim < 2; dim++)
00420         {
00421             if (c[dim]>0)
00422             {
00423                 int ind2 = ind-dind[dim];
00424                 if (fmm_status[ind2] >= FMM_FAR)
00425                 {
00426                     real phi2 = levelset[ind2];
00427                     if ((phi1) < (phi2))
00428                     {
00429                         levelset[ind2] = phi1;
00430                         fmm_push(ind2); // create or update the corresponding entry in the heap
00431                     }
00432                 }
00433             }
00434             if (c[dim]<cmax[dim])
00435             {
00436                 int ind2 = ind+dind[dim];
00437                 if (fmm_status[ind2] >= FMM_FAR)
00438                 {
00439                     real phi2 = levelset[ind2];
00440                     if ((phi1) < (phi2))
00441                     {
00442                         levelset[ind2] = phi1;
00443                         fmm_push(ind2); // create or update the corresponding entry in the heap
00444                     }
00445                 }
00446             }
00447         }
00448     }
00449 
00450     FOR_ALL_CELLS(levelset,
00451     {
00452         if(temp->levelset[ind] < 0)
00453         {
00454             levelset[ind] = -levelset[ind];
00455             if (fdata[ind].type == PART_EMPTY)
00456                 fdata[ind].type = PART_FULL;
00457         }
00458     });
00459 }
00460 
00461 inline void Grid2D::fmm_swap(int entry1, int entry2)
00462 {
00463     int ind1 = fmm_heap[entry1];
00464     int ind2 = fmm_heap[entry2];
00465     fmm_heap[entry1] = ind2;
00466     fmm_heap[entry2] = ind1;
00467     fmm_status[ind1] = entry2 + FMM_FRONT0;
00468     fmm_status[ind2] = entry1 + FMM_FRONT0;
00469 }
00470 
00471 int Grid2D::fmm_pop()
00472 {
00473     int res = fmm_heap[0];
00474 #ifdef FMM_VERBOSE
00475     std::cout << "fmm_pop -> <"<<(res%nx)<<','<<((res/nx)%ny)<<','<<(res/nxny)<<">="<<levelset[res]<<std::endl;
00476 #endif
00477     --fmm_heap_size;
00478     if (fmm_heap_size>0)
00479     {
00480         fmm_swap(0, fmm_heap_size);
00481         int i=0;
00482         real phi = (levelset[fmm_heap[i]]);
00483         while (i*2+1 < fmm_heap_size)
00484         {
00485             real phi1 = (levelset[fmm_heap[i*2+1]]);
00486             if (i*2+2 < fmm_heap_size)
00487             {
00488                 real phi2 = (levelset[fmm_heap[i*2+2]]);
00489                 if (phi1 < phi)
00490                 {
00491                     if (phi1 < phi2)
00492                     {
00493                         fmm_swap(i, i*2+1);
00494                         i = i*2+1;
00495                     }
00496                     else
00497                     {
00498                         fmm_swap(i, i*2+2);
00499                         i = i*2+2;
00500                     }
00501                 }
00502                 else if (phi2 < phi)
00503                 {
00504                     fmm_swap(i, i*2+2);
00505                     i = i*2+2;
00506                 }
00507                 else break;
00508             }
00509             else if (phi1 < phi)
00510             {
00511                 fmm_swap(i, i*2+1);
00512                 i = i*2+1;
00513             }
00514             else break;
00515         }
00516     }
00517 #ifdef FMM_VERBOSE
00518     std::cout << "fmm_heap = [";
00519     for (int i=0;i<fmm_heap_size;i++)
00520         std::cout << " <"<<(fmm_heap[i]%nx)<<','<<((fmm_heap[i]/nx)%ny)<<','<<(fmm_heap[i]/nxny)<<">="<<levelset[fmm_heap[i]];
00521     std::cout << std::endl;
00522 #endif
00523     fmm_status[res] = FMM_KNOWN;
00524     return res;
00525 }
00526 
00527 void Grid2D::fmm_push(int index)
00528 {
00529     real phi = (levelset[index]);
00530     int i;
00531     if (fmm_status[index] >= FMM_FRONT0)
00532     {
00533         i = fmm_status[index] - FMM_FRONT0;
00534 #ifdef FMM_VERBOSE
00535         std::cout << "fmm update <"<<(index%nx)<<','<<((index/nx)%ny)<<','<<(index/nxny)<<">="<<levelset[index]<<" from entry "<<i<<std::endl;
00536 #endif
00537         while (i>0 && phi < (levelset[fmm_heap[(i-1)/2]]))
00538         {
00539             fmm_swap(i,(i-1)/2);
00540             i = (i-1)/2;
00541         }
00542         while (i*2+1 < fmm_heap_size)
00543         {
00544             real phi1 = (levelset[fmm_heap[i*2+1]]);
00545             if (i*2+2 < fmm_heap_size)
00546             {
00547                 real phi2 = (levelset[fmm_heap[i*2+2]]);
00548                 if (phi1 < phi)
00549                 {
00550                     if (phi1 < phi2)
00551                     {
00552                         fmm_swap(i, i*2+1);
00553                         i = i*2+1;
00554                     }
00555                     else
00556                     {
00557                         fmm_swap(i, i*2+2);
00558                         i = i*2+2;
00559                     }
00560                 }
00561                 else if (phi2 < phi)
00562                 {
00563                     fmm_swap(i, i*2+2);
00564                     i = i*2+2;
00565                 }
00566                 else break;
00567             }
00568             else if (phi1 < phi)
00569             {
00570                 fmm_swap(i, i*2+1);
00571                 i = i*2+1;
00572             }
00573             else break;
00574         }
00575     }
00576     else
00577     {
00578 #ifdef FMM_VERBOSE
00579         std::cout << "fmm push <"<<(index%nx)<<','<<((index/nx)%ny)<<','<<(index/nxny)<<">="<<levelset[index]<<std::endl;
00580 #endif
00581         i = fmm_heap_size;
00582         ++fmm_heap_size;
00583         fmm_heap[i] = index;
00584         fmm_status[index] = i;
00585         while (i>0 && phi < (levelset[fmm_heap[(i-1)/2]]))
00586         {
00587             fmm_swap(i,(i-1)/2);
00588             i = (i-1)/2;
00589         }
00590     }
00591 #ifdef FMM_VERBOSE
00592     std::cout << "fmm_heap = [";
00593     for (int i=0;i<fmm_heap_size;i++)
00594         std::cout << " <"<<(fmm_heap[i]%nx)<<','<<((fmm_heap[i]/nx)%ny)<<','<<(fmm_heap[i]/nxny)<<">="<<levelset[fmm_heap[i]];
00595     std::cout << std::endl;
00596 #endif
00597 }
00598 
00603 
00604 void Grid2D::step_forces(const Grid2D* prev, Grid2D* /*temp*/, real dt, real /*diff*/, real /*scale*/)
00605 {
00606     //  std::cout << "STEP: Boundary and Forces\n";
00607     // Solid immovable obstacles set all velocity inside and touching them to 0
00608     // (free-slip condition)
00609     // Carlson Thesis page 23
00610     
00611     //vec2 f(0,-9.81*dt/scale);
00612     vec2 f(0,-5*dt);
00613     
00614     FOR_INNER_CELLS(fdata,
00615     {
00616         vec2 u = f;
00617         int p0 = fdata[ind].type;
00618         if (p0 < 0)
00619             u.clear();
00620         else
00621         { // not an obstacle
00622             int p1;
00623             // X Axis
00624             p1 = fdata[ind+index(-1,0)].type;
00625             if (p1>=0 && p0+p1>0) // this face is now in the fluid
00626             {
00627                 u[0] += prev->fdata[ind].u[0]; // was in the fluid in previous step
00628             }
00629             else //if (p1<0)
00630                 u[0] = 0; // Obstacle
00631             // Y Axis
00632             p1 = fdata[ind+index(0,-1)].type;
00633             if (p1>=0 && p0+p1>0) // this face is now in the fluid
00634             {
00635                 u[1] += prev->fdata[ind].u[1]; // was in the fluid in previous step
00636             }
00637             else //if (p1<0)
00638                 u[1] = 0; // Obstacle
00639         }
00640         fdata[ind].u = u;
00641     });
00642     
00643     //std::cout << "t="<<t<<" tend="<<tend<<std::endl;
00644     
00645     if (t > 0 && t < tend)
00646     {
00647         float f = 2*t;
00648         srand(*(const unsigned int*)(const void*)&f);
00649         vec2 v(4,0);
00650         int cx = 1*ny/4;
00651         int cy = 3*ny/4;
00652         real r = ny/8.0f;
00653         if (t < 1) r *= t;
00654         else if (t>tend-1) r*= tend-t;
00655         int ir = rceil(r)+5;
00656         //real r2=r*r;
00657     
00658         //real cr = (t+0)/3; cr = rabs(cr-rnear(cr))*2;
00659         //real cg = (t+1)/3; cg = rabs(cg-rnear(cg))*2;
00660         //real cb = (t+2)/3; cb = rabs(cb-rnear(cb))*2;
00661         //real cr = (t+2.5)/3; cr = rmax(0.0f,rabs(cr-rnear(cr))*3-0.5f);
00662         //real cg = (t    )/3; cg = rmin(1.0f,rabs(cg-rnear(cg))*3);
00663         //real cb = 1;
00664     
00665                 for (int y=cy-ir;y<=cy+ir;y++)
00666                     if ((unsigned)y<(unsigned)ny)
00667                         for (int x=cx-ir;x<=cx+ir;x++)
00668                         {
00669                             real d = sqrtf((real)((y-cy)*(y-cy)+(x-cx)*(x-cx))) - r;
00670                             int ind = index(x,y);
00671                             if (d < 0)
00672                             {
00673                                 fdata[ind].u = v;
00674                                 if (fdata[ind].type == PART_EMPTY)
00675                                     fdata[ind].type = PART_FULL;
00676                             }
00677                             if (d < levelset[ind])
00678                                 levelset[ind] = d;
00679                         }
00680     }
00681 }
00682 
00685 
00686 void Grid2D::step_surface(const Grid2D* /*prev*/, Grid2D* /*temp*/, real /*dt*/, real /*diff*/)
00687 {
00688     //  std::cout << "STEP: Surfaces\n";
00689     // Boundary conditions are not trivial...
00690     // Carlson Thesis page 24-28
00691     
00692     enum {
00693         FACE_X0=1<<0,
00694         FACE_X1=1<<1,
00695         FACE_Y0=1<<2,
00696         FACE_Y1=1<<3,
00697     };
00698     
00699     FOR_SURFACE_CELLS(fdata,
00700     {
00701         if (fdata[ind].type>0)
00702         {
00703             // get face air bitmask
00704             int mask = 0;
00705             if (fdata[ind+index(-1,0)].type==0) mask|=FACE_X0;
00706             if (fdata[ind+index( 1,0)].type==0) mask|=FACE_X1;
00707             if (fdata[ind+index(0,-1)].type==0) mask|=FACE_Y0;
00708             if (fdata[ind+index(0, 1)].type==0) mask|=FACE_Y1;
00709             if (mask!=0)
00710             {
00711                 real& x0 = fdata[ind].u[0];
00712                 real& y0 = fdata[ind].u[1];
00713                 real& x1 = fdata[ind+index( 1,0)].u[0];
00714                 real& y1 = fdata[ind+index(0, 1)].u[1];
00715                 // Continuity condition: x1-x0+y1-y0 = 0
00716                 switch (mask)
00717                 { // 16 cases...
00718                     
00719                     // 1 face: add the other values
00720                     case FACE_X0: x0 = x1   +y1-y0; break;
00721                     case FACE_X1: x1 = x0   +y0-y1; break;
00722                     case FACE_Y0: y0 = x1-x0+y1   ; break;
00723                     case FACE_Y1: y1 = x0-x1+y0   ; break;
00724             
00725                     // 2 faces: weaker condition x1=x0 y1=y0
00726                     // opposite faces -> do nothing
00727                     case FACE_X0|FACE_X1: break;
00728                     case FACE_Y0|FACE_Y1: break;
00729                     // other cases: copy opposite value and add half the difference of the remaining two faces
00730                     case FACE_X0|FACE_Y0: x0 = x1;  y0 = y1; break;
00731                     case FACE_X0|FACE_Y1: x0 = x1;  y1 = y0; break;
00732                     case FACE_X1|FACE_Y0: x1 = x0;  y0 = y1; break;
00733                     case FACE_X1|FACE_Y1: x1 = x0;  y1 = y0; break;
00734                     
00735                     // 3 faces with one opposite pair: ignore the pair and resolve the remaining one
00736                     case FACE_X0|FACE_Y0|FACE_Y1: x0 = x1   +y1-y0; break;
00737                     case FACE_X1|FACE_Y0|FACE_Y1: x1 = x0   +y0-y1; break;
00738                     case FACE_Y0|FACE_X0|FACE_X1: y0 = x1-x0+y1   ; break;
00739                     case FACE_Y1|FACE_X0|FACE_X1: y1 = x0-x1+y0   ; break;
00740                     
00741                     // 4 faces: can't do anything
00742                     case FACE_X0|FACE_X1|FACE_Y0|FACE_Y1: break;
00743                     
00744                     // Done!
00745                 }
00746             }
00747         }
00748     });
00749 }
00750 
00753 
00754   // This is the real fluid simulation...
00755 
00756 void Grid2D::step_advect(const Grid2D* /*prev*/, Grid2D* temp, real dt, real /*diff*/)
00757 {
00758     //  std::cout << "STEP: Fluid Advection\n";
00759     
00760     // Calculate advection using a semi-lagrangian technique
00761     // Stam
00762     
00763     // not much optimized for now...
00764     
00765     memset(temp->fdata,0,temp->ncell*sizeof(Cell));
00766     
00767     FOR_INNER_CELLS(temp->fdata,
00768     {
00769         // X Axis
00770         vec2 px( x-0.5f - dt*(fdata[ind].u[0]),
00771                  y      - dt*0.25f*(fdata[ind].u[1]+fdata[ind+index(-1,0)].u[1]+fdata[ind+index(0,1)].u[1]+fdata[ind+index(-1,1)].u[1]));
00772         temp->fdata[ind].u[0] = interp<0>(px);
00773         // Y Axis
00774         vec2 py( x      - dt*0.25f*(fdata[ind].u[0]+fdata[ind+index(0,-1)].u[0]+fdata[ind+index(1,0)].u[0]+fdata[ind+index(1,-1)].u[0]),
00775                  y-0.5f - dt*(fdata[ind].u[1]));
00776         temp->fdata[ind].u[1] = interp<1>(py);
00777     });
00778     // Result is now is temp
00779 }
00780 
00781 
00782 void Grid2D::step_diffuse(const Grid2D* /*prev*/, Grid2D* temp, real /*dt*/, real diff)
00783 {
00784     //  std::cout << "STEP: Fluid Diffusion\n";
00785     // Calculate diffusion back to here
00786     // TODO: Check boundary conditions
00787     
00788     if (diff==0.0f)
00789     {
00790         for (int ind=0;ind<ncell;ind++)
00791             fdata[ind].u = temp->fdata[ind].u;
00792         return;
00793     }
00794     
00795     real a = diff;
00796     real inv_c = 1.0f / (1.0001f + 4*a);
00797     
00798     FOR_INNER_CELLS(fdata,
00799     {
00800         fdata[ind].u = (temp->fdata[ind].u +
00801                        (temp->fdata[ind+index(-1,0)].u+temp->fdata[ind+index(1,0)].u+
00802                         temp->fdata[ind+index(0,-1)].u+temp->fdata[ind+index(0,1)].u
00803                        )*a)*inv_c;
00804     });
00805 }
00806 
00807 
00808 void Grid2D::step_project(const Grid2D* prev, Grid2D* temp, real dt, real /*diff*/)
00809 {
00810     //  std::cout << "STEP: Fluid Projection\n";
00811     
00812     // Finally calculate projection to divergence free velocity
00813     
00814     // u_new = u~ - dt/P Dp where P = fluid density and p = pressure, Dp = (dp/dx,dp/dy)
00815     // D.u_new = 0  =>  -dx²D²p = -P/dt dx²D.u~
00816     //   where  -dx²D²p = 6p(i,j,k)-p(i-1,j,k)-p(i,j-1,k)-p(i,j,k-1)-p(i+1,j,k)-p(i,j+1,k)-p(i,j,k+1)
00817     //     and  -P/dt dx²D.u~ = -P/dt dx ( u~(i+1,j,k) - u~(i,j,k) + v~(i,j+1,k) - v~(i,j,k) + w~(i,j,k+1) - w~(i,j,k) )
00818     // Ap = b where A is a diagonal matrix plus neighbour coefficients at -1
00819     
00820     memset(temp->fdata,0,temp->ncell*sizeof(Cell));
00821     memset(temp->pressure,0,temp->ncell*sizeof(real));
00822     
00823     real* diag = (real*)temp->fdata;
00824     real* b = diag+ncell;
00825     real* r = b+ncell;
00826     real* g = r+ncell;
00827     real* q = temp->pressure;
00828     
00829     real a = -1.0f/dt;
00830     
00831     real b_norm2 = 0.0;
00832     
00833     //  int nbdiag[7]={0,0,0,0,0,0,0};
00834     
00835     FOR_INNER_CELLS(diag,
00836     {
00837         if (fdata[ind].type>0)
00838         {
00839             real d = 4; // count air/fluid neighbours
00840             d -= ((unsigned int)fdata[ind+index(-1,0)].type)>>31;
00841             d -= ((unsigned int)fdata[ind+index(0,-1)].type)>>31;
00842             d -= ((unsigned int)fdata[ind+index( 1,0)].type)>>31;
00843             d -= ((unsigned int)fdata[ind+index(0, 1)].type)>>31;
00844             //  nbdiag[(int)d]++;
00845             diag[ind] = d;
00846         }
00847     });
00848     
00849     FOR_INNER_CELLS(b,
00850     {
00851         if (fdata[ind].type>0)
00852         {
00853             real bi = a*(fdata[ind+index(1,0)].u[0]-fdata[ind].u[0] + fdata[ind+index(0,1)].u[1]-fdata[ind].u[1]);
00854             b[ind] = bi;
00855             b_norm2 += bi*bi;
00856         }
00857     });
00858     
00859     //  std::cout << "Proc"<<Grank<<" local b2="<<b_norm2<<std::endl;
00860     //reduceAll(1,&b_norm2);
00861     //  std::cout << "Proc"<<Grank<<" global b2="<<b_norm2<<std::endl;
00862     
00863     FOR_ALL_CELLS(pressure,
00864     {
00865         if (fdata[ind].type>0)
00866             pressure[ind] = prev->pressure[ind]; // use previous pressure as initial estimate
00867         else pressure[ind] = 0;
00868     });
00869     
00870     //  std::cout << "STEP: Pressure diag";
00871     //  for (int i=0;i<7;i++) std::cout << ' ' << nbdiag[i];
00872     //  std::cout << '\n';
00873     
00874     real err = 0.0;
00875     
00876     // r = b - Ax
00877     FOR_INNER_CELLS(r,
00878     {
00879         if (diag[ind] != 0)
00880         {
00881             r[ind] = b[ind] - (diag[ind]*pressure[ind]
00882                               -pressure[ind+index(-1,0)]-pressure[ind+index(0,-1)]
00883                               -pressure[ind+index( 1,0)]-pressure[ind+index(0, 1)]);
00884         }
00885     });
00886     
00887     FOR_ALL_CELLS(g,
00888     {
00889         g[ind] = r[ind]; // first direction is r
00890     });
00891     
00892     real min_err = 0.0001f*b_norm2;
00893     
00894     int step;
00895     for (step=0;step<100;step++)
00896     {
00897         real err_old = err;
00898         err = 0.0f;
00899         FOR_READ_INNER_CELLS(
00900         {
00901         err += r[ind]*r[ind];
00902         });
00903         
00904         if (err<=min_err) break;
00905         if (step>0)
00906         {
00907             real beta = err/err_old;
00908             // g = g*beta + r
00909             FOR_ALL_CELLS(g,
00910             {
00911                 g[ind] = g[ind]*beta + r[ind];
00912             });
00913         }
00914         real g_q = 0.0;
00915         // q = Ag
00916         FOR_INNER_CELLS(q,
00917         {
00918             if (diag[ind] != 0)
00919             {
00920                 real Ag = (diag[ind]*g[ind]
00921                           -g[ind+index(-1,0)]-g[ind+index(0,-1)]
00922                           -g[ind+index( 1,0)]-g[ind+index(0, 1)]); 
00923                 q[ind] = Ag;
00924                 g_q += g[ind]*Ag;
00925             }
00926         });
00927         
00928         real alpha = err/g_q;
00929         
00930         FOR_ALL_CELLS(pressure,
00931         {
00932             pressure[ind] += alpha*g[ind];
00933             r[ind] -= alpha*q[ind];
00934         });
00935     }
00936 
00937     //  std::cout << "STEP: CG iteration "<<step<<" error "<<sqrt(err/b_norm2)<<"\n";
00938     
00939     // Now apply pressure back to velocity
00940     a = dt;
00941     
00942     real max_speed = 0.5f/dt;
00943     
00944     //max_pressure = 0.0f;
00945     max_pressure = prev->max_pressure;
00946     
00947     FOR_INNER_CELLS(fdata,
00948     {
00949         if (fdata[ind].type>=PART_EMPTY)
00950         {
00951             if (fdata[ind+index(-1,0)].type>=PART_EMPTY)
00952             {
00953                 fdata[ind].u[0] -= a*(pressure[ind] - pressure[ind+index(-1,0)]);
00954                 // safety check
00955                 if (fdata[ind].u[0] >  max_speed) fdata[ind].u[0] =  max_speed;
00956                 else if (fdata[ind].u[0] < -max_speed) fdata[ind].u[0] = -max_speed;
00957             }
00958             if (fdata[ind+index(0,-1)].type>=PART_EMPTY)
00959             {
00960                 fdata[ind].u[1] -= a*(pressure[ind] - pressure[ind+index(0,-1)]);
00961                 if (fdata[ind].u[1] >  max_speed) fdata[ind].u[1] =  max_speed;
00962                 else if (fdata[ind].u[1] < -max_speed) fdata[ind].u[1] = -max_speed;
00963             }
00964         }
00965     });
00966     
00967     //  std::cout << "STEP: max pressure "<<max_pressure<<'\n';
00968 }
00969 
00970 } // namespace eulerianfluid
00971 
00972 } // namespace behaviormodel
00973 
00974 } // namespace component
00975 
00976 } // namespace sofa
00977 

Generated on Sun Nov 23 02:29:31 2008 for Sofa by  doxygen 1.5.7.1