5d8a73e6ab8fe720221b2027e88c4611f895c3cc
[blender.git] / intern / elbeem / intern / simulation_object.cpp
1 /******************************************************************************
2  *
3  * El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
4  * Copyright 2003,2004 Nils Thuerey
5  *
6  * Basic interface for all simulation modules
7  *
8  *****************************************************************************/
9
10 #include "simulation_object.h"
11 #include "solver_interface.h"
12 #include "ntl_bsptree.h"
13 #include "ntl_ray.h"
14 #include "ntl_world.h"
15 #include "solver_interface.h"
16 #include "particletracer.h"
17 #include "elbeem.h"
18
19 #ifdef _WIN32
20 #else
21 #include <sys/time.h>
22 #endif
23
24
25 //! lbm factory functions
26 LbmSolverInterface* createSolver();
27
28
29 /******************************************************************************
30  * Constructor
31  *****************************************************************************/
32 SimulationObject::SimulationObject() :
33         ntlGeometryShader(),
34         mGeoStart(-100.0), mGeoEnd(100.0),
35         mpGiTree(NULL), mpGiObjects(NULL),
36         mpGlob(NULL),
37         mPanic( false ),
38         mDebugType( 1 /* =FLUIDDISPNothing*/ ),
39         mpLbm(NULL), mpParam( NULL ),
40         mShowSurface(true), mShowParticles(false),
41         mSelectedCid( NULL ),
42         mpElbeemSettings( NULL )
43
44 {
45         mpParam = new Parametrizer();
46         //for(int i=0; i<MAX_DEBDISPSET; i++) { mDebDispSet[i].type  = (i); mDebDispSet[i].on    = false; mDebDispSet[i].scale = 1.0; }
47
48         // reset time
49         mTime                                           = 0.0;
50 }
51
52
53 /******************************************************************************
54  * Destructor
55  *****************************************************************************/
56 SimulationObject::~SimulationObject()
57 {
58         if(mpGiTree)         delete mpGiTree;
59         if(mpElbeemSettings) delete mpElbeemSettings;
60         if(mpLbm)            delete mpLbm;
61   if(mpParam)          delete mpParam;
62         debMsgStd("SimulationObject",DM_MSG,"El'Beem Done!\n",10);
63 }
64
65
66
67 /*****************************************************************************/
68 /*! init tree for certain geometry init */
69 /*****************************************************************************/
70 void SimulationObject::initGeoTree() {
71         if(mpGlob == NULL) { 
72                 errFatal("SimulationObject::initGeoTree error","Requires globals!", SIMWORLD_INITERROR); 
73                 return;
74         }
75         ntlScene *scene = mpGlob->getSimScene();
76         mpGiObjects = scene->getObjects();
77
78         if(mpGiTree != NULL) delete mpGiTree;
79         char treeFlag = (1<<(mGeoInitId+4));
80         mpGiTree = new ntlTree( 20, 4, // warning - fixed values for depth & maxtriangles here...
81                                                                                                 scene, treeFlag );
82 }
83
84 /*****************************************************************************/
85 /*! destroy tree etc. when geometry init done */
86 /*****************************************************************************/
87 void SimulationObject::freeGeoTree() {
88         if(mpGiTree != NULL) delete mpGiTree;
89 }
90
91
92
93 // copy & remember settings for later use
94 void SimulationObject::copyElbeemSettings(elbeemSimulationSettings *settings) {
95         mpElbeemSettings = new elbeemSimulationSettings;
96         *mpElbeemSettings = *settings;
97
98         mGeoInitId = settings->domainId+1;
99 }
100
101 /******************************************************************************
102  * simluation interface: initialize simulation using the given configuration file 
103  *****************************************************************************/
104 int SimulationObject::initializeLbmSimulation(ntlRenderGlobals *glob)
105 {
106         if(! isSimworldOk() ) return 1;
107         
108         // already inited?
109         if(mpLbm) return 0;
110         
111         mpGlob = glob;
112         if(!getVisible()) {
113                 mpAttrs->setAllUsed();
114                 return 0;
115         }
116
117
118         this->mGeoInitId = mpAttrs->readInt("geoinitid", this->mGeoInitId,"LbmSolverInterface", "mGeoInitId", false);
119         //mDimension, mSolverType are deprecated
120         string mSolverType(""); 
121         mSolverType = mpAttrs->readString("solver", mSolverType, "SimulationObject","mSolverType", false ); 
122
123         mpLbm = createSolver(); 
124   /* check lbm pointer */
125         if(mpLbm == NULL) {
126                 errFatal("SimulationObject::initializeLbmSimulation","Unable to init LBM solver! ", SIMWORLD_INITERROR);
127                 return 2;
128         }
129         debMsgStd("SimulationObject::initialized",DM_MSG,"IdStr:"<<mpLbm->getIdString() <<" LBM solver! ", 2);
130
131         // for non-param simulations
132         mpLbm->setParametrizer( mpParam );
133         mpParam->setAttrList( getAttributeList() );
134         // not needed.. done in solver_init: mpParam->setSize ... in solver_interface
135         mpParam->parseAttrList();
136
137         mpLbm->setAttrList( getAttributeList() );
138         mpLbm->parseAttrList();
139         mpParts = new ParticleTracer();
140         mpParts->parseAttrList( getAttributeList() );
141
142         if(! isSimworldOk() ) return 3;
143         mpParts->setName( getName() + "_part" );
144         mpParts->initialize( glob );
145         if(! isSimworldOk() ) return 4;
146         
147         // init material settings
148         string matMc("default");
149         matMc = mpAttrs->readString("material_surf", matMc, "SimulationObject","matMc", false );
150         mShowSurface   = mpAttrs->readInt("showsurface", mShowSurface, "SimulationObject","mShowSurface", false ); 
151         mShowParticles = mpAttrs->readInt("showparticles", mShowParticles, "SimulationObject","mShowParticles", false ); 
152
153         checkBoundingBox( mGeoStart, mGeoEnd, "SimulationObject::initializeSimulation" );
154         mpLbm->setLbmInitId( mGeoInitId );
155         mpLbm->setGeoStart( mGeoStart );
156         mpLbm->setGeoEnd( mGeoEnd );
157         mpLbm->setRenderGlobals( mpGlob );
158         mpLbm->setName( getName() + "_lbm" );
159         mpLbm->setParticleTracer( mpParts );
160         if(mpElbeemSettings) {
161                 // set further settings from API struct init
162                 if(mpElbeemSettings->outputPath) this->mOutFilename = string(mpElbeemSettings->outputPath);
163                 mpLbm->initDomainTrafo( mpElbeemSettings->surfaceTrafo );
164                 mpLbm->setSmoothing(1.0 * mpElbeemSettings->surfaceSmoothing, 1.0 * mpElbeemSettings->surfaceSmoothing);
165                 mpLbm->setSizeX(mpElbeemSettings->resolutionxyz);
166                 mpLbm->setSizeY(mpElbeemSettings->resolutionxyz);
167                 mpLbm->setSizeZ(mpElbeemSettings->resolutionxyz);
168                 mpLbm->setPreviewSize(mpElbeemSettings->previewresxyz);
169                 mpLbm->setRefinementDesired(mpElbeemSettings->maxRefine);
170                 mpLbm->setGenerateParticles(mpElbeemSettings->generateParticles);
171                 // set initial particles
172                 mpParts->setNumInitialParticles(mpElbeemSettings->numTracerParticles);
173
174                 string dinitType = string("no");
175                 if     (mpElbeemSettings->obstacleType==FLUIDSIM_OBSTACLE_PARTSLIP) dinitType = string("part"); 
176                 else if(mpElbeemSettings->obstacleType==FLUIDSIM_OBSTACLE_FREESLIP) dinitType = string("free"); 
177                 else /*if(mpElbeemSettings->obstacleType==FLUIDSIM_OBSTACLE_NOSLIP)*/ dinitType = string("no"); 
178                 mpLbm->setDomainBound(dinitType);
179                 mpLbm->setDomainPartSlip(mpElbeemSettings->obstaclePartslip);
180                 mpLbm->setDumpVelocities(mpElbeemSettings->generateVertexVectors);
181                 mpLbm->setFarFieldSize(mpElbeemSettings->farFieldSize);
182                 debMsgStd("SimulationObject::initialize",DM_MSG,"Added domain bound: "<<dinitType<<" ps="<<mpElbeemSettings->obstaclePartslip<<" vv"<<mpElbeemSettings->generateVertexVectors<<","<<mpLbm->getDumpVelocities(), 9 );
183
184                 debMsgStd("SimulationObject::initialize",DM_MSG,"Set ElbeemSettings values "<<mpLbm->getGenerateParticles(),10);
185         }
186
187         if(! mpLbm->initializeSolverMemory()   )         { errMsg("SimulationObject::initialize","initializeSolverMemory failed"); mPanic=true; return 10; }
188         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverMemory status"); mPanic=true; return 11; } 
189         if(! mpLbm->initializeSolverGrids()    )         { errMsg("SimulationObject::initialize","initializeSolverGrids  failed"); mPanic=true; return 12; }
190         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverGrids  status"); mPanic=true; return 13; } 
191         if(! mpLbm->initializeSolverPostinit() )         { errMsg("SimulationObject::initialize","initializeSolverPostin failed"); mPanic=true; return 14; }
192         if(checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0)) { errMsg("SimulationObject::initialize","initializeSolverPostin status"); mPanic=true; return 15; } 
193
194         // print cell type stats
195         const int jmax = sizeof(CellFlagType)*8;
196         int totalCells = 0;
197         int flagCount[jmax];
198         for(int j=0; j<jmax ; j++) flagCount[j] = 0;
199         int diffInits = 0;
200         LbmSolverInterface::CellIdentifier cid = mpLbm->getFirstCell();
201         for(; mpLbm->noEndCell( cid );
202               mpLbm->advanceCell( cid ) ) {
203                 int flag = mpLbm->getCellFlag(cid,0);
204                 int flag2 = mpLbm->getCellFlag(cid,1);
205                 if(flag != flag2) {
206                         diffInits++;
207                 }
208                 for(int j=0; j<jmax ; j++) {
209                         if( flag&(1<<j) ) flagCount[j]++;
210                 }
211                 totalCells++;
212         }
213         mpLbm->deleteCellIterator( &cid );
214
215         char charNl = '\n';
216         debugOutNnl("SimulationObject::initializeLbmSimulation celltype stats: " <<charNl, 5);
217         debugOutNnl("no. of cells = "<<totalCells<<", "<<charNl ,5);
218         for(int j=0; j<jmax ; j++) {
219                 std::ostringstream out;
220                 if(flagCount[j]>0) {
221                         out<<"\t" << flagCount[j] <<" x "<< convertCellFlagType2String( (CellFlagType)(1<<j) ) <<", " << charNl;
222                         debugOutNnl(out.str(), 5);
223                 }
224         }
225         // compute dist. of empty/bnd - fluid - if
226         // cfEmpty   = (1<<0), cfBnd  = (1<< 2), cfFluid   = (1<<10), cfInter   = (1<<11),
227         if(1){
228                 std::ostringstream out;
229                 out.precision(2); out.width(4);
230                 int totNum = flagCount[1]+flagCount[2]+flagCount[7]+flagCount[8];
231                 double ebFrac = (double)(flagCount[1]+flagCount[2]) / totNum;
232                 double flFrac = (double)(flagCount[7]) / totNum;
233                 double ifFrac = (double)(flagCount[8]) / totNum;
234                 //???
235                 out<<"\tFractions: [empty/bnd - fluid - interface - ext. if]  =  [" << ebFrac<<" - " << flFrac<<" - " << ifFrac<<"] "<< charNl;
236
237                 if(diffInits > 0) {
238                         debMsgStd("SimulationObject::initializeLbmSimulation",DM_MSG,"celltype Warning: Diffinits="<<diffInits<<"!" , 5);
239                 }
240                 debugOutNnl(out.str(), 5);
241         }
242
243         // might be modified by mpLbm
244         mpParts->setStart( mGeoStart );
245         mpParts->setEnd( mGeoEnd );
246         mpParts->setCastShadows( false );
247         mpParts->setReceiveShadows( false );
248         mpParts->searchMaterial( glob->getMaterials() );
249
250         // this has to be inited here - before, the values might be unknown
251         ntlGeometryObject *surf = mpLbm->getSurfaceGeoObj();
252         if(surf) {
253                 surf->setName( "final" ); // final surface mesh 
254                 // warning - this might cause overwriting effects for multiple sims and geom dump...
255                 surf->setCastShadows( true );
256                 surf->setReceiveShadows( false );
257                 surf->searchMaterial( glob->getMaterials() );
258                 if(mShowSurface) mObjects.push_back( surf );
259         }
260         
261 #ifdef ELBEEM_PLUGIN
262         mShowParticles=1;
263 #endif // ELBEEM_PLUGIN
264         if((mpLbm->getGenerateParticles()>0.0)||(mpParts->getNumInitialParticles()>0)) {
265                 mShowParticles=1;
266                 mpParts->setDumpParts(true);
267         }
268                 //debMsgStd("SimulationObject::init",DM_NOTIFY,"Using envvar ELBEEM_DUMPPARTICLE to set mShowParticles, DEBUG!",1);
269         //}  // DEBUG ENABLE!!!!!!!!!!
270         if(mShowParticles) {
271                 mObjects.push_back(mpParts);
272         }
273
274         // add objects to display for debugging (e.g. levelset particles)
275         vector<ntlGeometryObject *> debugObjs = mpLbm->getDebugObjects();
276         for(size_t i=0;i<debugObjs.size(); i++) {
277                 debugObjs[i]->setCastShadows( false );
278                 debugObjs[i]->setReceiveShadows( false );
279                 debugObjs[i]->searchMaterial( glob->getMaterials() );
280                 mObjects.push_back( debugObjs[i] );
281         }
282         return 0;
283 }
284
285 /*! set current frame */
286 void SimulationObject::setFrameNum(int num) {
287         // advance parametrizer
288         mpParam->setFrameNum(num);
289 }
290
291 /******************************************************************************
292  * simluation interface: advance simulation another step (whatever delta time that might be) 
293  *****************************************************************************/
294 void SimulationObject::step( void )
295 {
296         if(mpParam->getCurrentAniFrameTime()>0.0) {
297                 // dont advance for stopped time
298                 mpLbm->step();
299                 mTime += mpParam->getTimestep();
300         }
301         if(mpLbm->getPanic()) mPanic = true;
302
303         checkCallerStatus(FLUIDSIM_CBSTATUS_STEP, 0);
304         //if((mpElbeemSettings)&&(mpElbeemSettings->runsimCallback)) {
305                 //int ret = (mpElbeemSettings->runsimCallback)(mpElbeemSettings->runsimUserData, FLUIDSIM_CBSTATUS_STEP, 0);
306                 //errMsg("runSimulationCallback cbtest1"," "<<this->getName()<<" ret="<<ret);
307         //}
308   //debMsgStd("SimulationObject::step",DM_MSG," Sim '"<<mName<<"' stepped to "<<mTime<<" (stept="<<(mpParam->getTimestep())<<", framet="<<getFrameTime()<<") ", 10);
309 }
310 /*! prepare visualization of simulation for e.g. raytracing */
311 void SimulationObject::prepareVisualization( void ) {
312         if(mPanic) return;
313         mpLbm->prepareVisualization();
314 }
315
316
317 /******************************************************************************/
318 /* get current start simulation time */
319 double SimulationObject::getStartTime( void ) {
320         //return mpParam->calculateAniStart();
321         return mpParam->getAniStart();
322 }
323 /* get time for a single animation frame */
324 double SimulationObject::getFrameTime( int frame ) {
325         return mpParam->getAniFrameTime(frame);
326 }
327 /* get time for a single time step  */
328 double SimulationObject::getTimestep( void ) {
329         return mpParam->getTimestep();
330 }
331
332
333 /******************************************************************************
334  * return a pointer to the geometry object of this simulation 
335  *****************************************************************************/
336 //ntlGeometryObject *SimulationObject::getGeometry() { return mpMC; }
337 vector<ntlGeometryObject *>::iterator 
338 SimulationObject::getObjectsBegin()
339 {
340         return mObjects.begin();
341 }
342 vector<ntlGeometryObject *>::iterator 
343 SimulationObject::getObjectsEnd()
344 {
345         return mObjects.end();
346 }
347
348
349
350
351
352 /******************************************************************************
353  * GUI - display debug info 
354  *****************************************************************************/
355
356 void SimulationObject::drawDebugDisplay() {
357 #ifndef NOGUI
358         if(!getVisible()) return;
359
360         //if( mDebugType > (MAX_DEBDISPSET-1) ){ errFatal("SimulationObject::drawDebugDisplay","Invalid debug type!", SIMWORLD_GENERICERROR); return; }
361         //mDebDispSet[ mDebugType ].on = true;
362         //errorOut( mDebugType <<"//"<< mDebDispSet[mDebugType].type );
363         mpLbm->debugDisplay( mDebugType );
364
365         //::lbmMarkedCellDisplay<>( mpLbm );
366         mpLbm->lbmMarkedCellDisplay();
367 #endif
368 }
369
370 /* GUI - display interactive info  */
371 void SimulationObject::drawInteractiveDisplay()
372 {
373 #ifndef NOGUI
374         if(!getVisible()) return;
375         if(mSelectedCid) {
376                 // in debugDisplayNode if dispset is on is ignored...
377                 mpLbm->debugDisplayNode( FLUIDDISPGrid, mSelectedCid );
378         }
379 #endif
380 }
381
382
383 /*******************************************************************************/
384 // GUI - handle mouse movement for selection 
385 /*******************************************************************************/
386 void SimulationObject::setMousePos(int x,int y, ntlVec3Gfx org, ntlVec3Gfx dir)
387 {
388         normalize( dir );
389         // assume 2D sim is in XY plane...
390         
391         double zplane = (mGeoEnd[2]-mGeoStart[2])*0.5;
392         double zt = (zplane-org[2]) / dir[2];
393         ntlVec3Gfx pos(
394                         org[0]+ dir[0] * zt,
395                         org[1]+ dir[1] * zt, 0.0);
396
397         mSelectedCid = mpLbm->getCellAt( pos );
398         //errMsg("SMP ", mName<< x<<" "<<y<<" - "<<dir );
399         x = y = 0; // remove warning
400 }
401                         
402
403 void SimulationObject::setMouseClick()
404 {
405         if(mSelectedCid) {
406                 //::debugPrintNodeInfo<>( mpLbm, mSelectedCid, mpLbm->getNodeInfoString() );
407                 mpLbm->debugPrintNodeInfo( mSelectedCid );
408         }
409 }
410
411 /*! notify object that dump is in progress (e.g. for field dump) */
412 void SimulationObject::notifyShaderOfDump(int dumptype, int frameNr,char *frameNrStr,string outfilename) {
413         if(!mpLbm) return;
414
415         mpLbm->notifySolverOfDump(dumptype, frameNr,frameNrStr,outfilename);
416         checkCallerStatus(FLUIDSIM_CBSTATUS_NEWFRAME, frameNr);
417 }
418
419 /*! check status (e.g. stop/abort) from calling program, returns !=0 if sth. happened... */
420 int SimulationObject::checkCallerStatus(int status, int frame) {
421         //return 0; // DEBUG
422         int ret = 0;
423         if((mpElbeemSettings)&&(mpElbeemSettings->runsimCallback)) {
424                 ret = (mpElbeemSettings->runsimCallback)(mpElbeemSettings->runsimUserData, status,frame);
425                 if(ret!=FLUIDSIM_CBRET_CONTINUE) {
426                         if(ret==FLUIDSIM_CBRET_STOP) {
427                                 debMsgStd("SimulationObject::notifySolverOfDump",DM_NOTIFY,"Got stop signal from caller",1);
428                                 setElbeemState( SIMWORLD_STOP );
429                         }
430                         else if(ret==FLUIDSIM_CBRET_ABORT) {
431                                 errFatal("SimulationObject::notifySolverOfDump","Got abort signal from caller, aborting...", SIMWORLD_GENERICERROR );
432                                 mPanic = 1;
433                         }
434                         else {
435                                 errMsg("SimulationObject::notifySolverOfDump","Invalid callback return value: "<<ret<<", ignoring... ");
436                         }
437                 }
438         }
439
440         errMsg("SimulationObject::checkCallerStatus","s="<<status<<",f="<<frame<<" "<<this->getName()<<" ret="<<ret);
441         if(isSimworldOk()) return 0;
442         return 1;
443 }
444