# # Complexer smoke simulation with wavlet turbulence plus # - obstacle handling # - and uv coordinates # from manta import * import os, shutil, math, sys # how much to upres the XL sim? # set to zero to disable the second one completely upres = 4 # overall wavelet noise strength wltStrength = 0.3 # how many grids of uv coordinates to use (more than 2 usually dont pay off here) uvs = 1 # and how many octaves of wavelet turbulence? could be set manually, but ideally given by upres factor (round) octaves = 0 if(upres>0): octaves = int( math.log(upres)/ math.log(2.0) + 0.5 ) # simulation resolution dim = 2 res = 80 gs = vec3(res,int(1.5*res),res) if (dim==2): gs.z = 1 # setup low-res sim sm = Solver(name='main', gridSize = gs, dim=dim) sm.timestep = 1.5 # to do larger timesteps, without adaptive time steps, we need to set the length of a frame (1 is default) sm.frameLength = sm.timestep timings = Timings() # note - world space velocity, convert to grid space later velInflow = vec3(0.015, 0, 0) # inflow noise field noise = NoiseField( parent=sm, fixedSeed=265, loadFromFile=True) noise.posScale = vec3(20) # note, this is normalized to the grid size... noise.clamp = True noise.clampNeg = 0 noise.clampPos = 2 noise.valScale = 1 noise.valOffset = 0.075 noise.timeAnim = 0.3 # helper objects: inflow region, and obstacle source = Cylinder( parent=sm, center=gs*vec3(0.3,0.2,0.5), radius=res*0.081, z=gs*vec3(0.081, 0, 0)) sourceVel = Cylinder( parent=sm, center=gs*vec3(0.3,0.2,0.5), radius=res*0.15 , z=gs*vec3(0.15 , 0, 0)) obs = Sphere( parent=sm, center=gs*vec3(0.5,0.5,0.5), radius=res*0.15) # larger solver, recompute sizes... if(upres>0): xl_gs = vec3(upres*gs.x,upres*gs.y,upres*gs.z) if (dim==2): xl_gs.z = 1 # 2D xl = Solver(name='larger', gridSize = xl_gs, dim=dim) xl.timestep = sm.timestep xl.frameLength = xl.timestep xl_flags = xl.create(FlagGrid) xl_vel = xl.create(MACGrid) xl_density = xl.create(RealGrid) xl_flags.initDomain() xl_flags.fillGrid() xl_source = Cylinder( parent=xl, center=xl_gs*vec3(0.3,0.2,0.5), radius=xl_gs.x*0.081, z=xl_gs*vec3(0.081, 0, 0)) xl_obs = Sphere( parent=xl, center=xl_gs*vec3(0.5,0.5,0.5), radius=xl_gs.x*0.15) xl_obs.applyToGrid(grid=xl_flags, value=FlagObstacle) xl_noise = NoiseField( parent=xl, fixedSeed=265, loadFromFile=True) xl_noise.posScale = noise.posScale xl_noise.clamp = noise.clamp xl_noise.clampNeg = noise.clampNeg xl_noise.clampPos = noise.clampPos xl_noise.valScale = noise.valScale xl_noise.valOffset = noise.valOffset xl_noise.timeAnim = noise.timeAnim * upres # init lower res solver & grids bWidth=1 flags = sm.create(FlagGrid) flags.initDomain(boundaryWidth=bWidth) flags.fillGrid() setOpenBound(flags, bWidth,'yY', FlagOutflow|FlagEmpty) obs.applyToGrid(grid=flags, value=FlagObstacle) # create the array of uv grids uv = [] for i in range(uvs): uvGrid = sm.create(VecGrid) uv.append(uvGrid) resetUvGrid( uv[i] ) vel = sm.create(MACGrid) density = sm.create(RealGrid) pressure = sm.create(RealGrid) energy = sm.create(RealGrid) tempFlag = sm.create(FlagGrid) # wavelet turbulence noise field xl_wltnoise = NoiseField( parent=xl, loadFromFile=True) # scale according to lowres sim , smaller numbers mean larger vortices # note - this noise is parented to xl solver, thus will automatically rescale xl_wltnoise.posScale = vec3( int(1.0*gs.x) ) * 0.5 xl_wltnoise.timeAnim = 0.1 # setup user interface if (GUI): gui = Gui() sliderStr = gui.addControl(Slider, text='turb. strength', val=wltStrength, min=0, max=2) gui.show() #gui.pause() #printBuildInfo() # main loop for t in range(200): mantaMsg('\nFrame %i, simulation time %f' % (sm.frame, sm.timeTotal)) if (GUI): wltStrength = sliderStr.get() advectSemiLagrange(flags=flags, vel=vel, grid=density, order=2) advectSemiLagrange(flags=flags, vel=vel, grid=vel, order=2, openBounds=True, boundaryWidth=bWidth ) for i in range(uvs): advectSemiLagrange(flags=flags, vel=vel, grid=uv[i], order=2) # now we have to update the weights of the different uv channels # note: we have a timestep of 1.5 in this setup! so the value of 16.5 means reset every 11 steps updateUvWeight( resetTime=16.5 , index=i, numUvs=uvs, uv=uv[i] ); # also note, we have to update the weight after the advection # as it is stored at uv[i](0,0,0) , the advection overwrites this... applyInflow=False if (sm.timeTotal>=0 and sm.timeTotal<50.): densityInflow( flags=flags, density=density, noise=noise, shape=source, scale=1, sigma=0.5 ) sourceVel.applyToGrid( grid=vel , value=(velInflow*float(res)) ) applyInflow=True setWallBcs(flags=flags, vel=vel) addBuoyancy(density=density, vel=vel, gravity=vec3(0,-1e-3,0), flags=flags) vorticityConfinement( vel=vel, flags=flags, strength=0.4 ) solvePressure(flags=flags, vel=vel, pressure=pressure , cgMaxIterFac=1.0, cgAccuracy=0.01 ) setWallBcs(flags=flags, vel=vel) # determine weighting computeEnergy(flags=flags, vel=vel, energy=energy) # mark outer obstacle region by extrapolating flags for 2 layers tempFlag.copyFrom(flags) extrapolateSimpleFlags( flags=flags, val=tempFlag, distance=2, flagFrom=FlagObstacle, flagTo=FlagFluid ); # now extrapolate energy weights into obstacles to fix boundary layer extrapolateSimpleFlags( flags=tempFlag, val=energy, distance=6, flagFrom=FlagFluid, flagTo=FlagObstacle ); computeWaveletCoeffs(energy) #density.save('densitySm_%04d.vol' % t) sm.step() # xl ... if(upres>0): interpolateMACGrid( source=vel, target=xl_vel ) # add all necessary octaves sStr = 1.0 * wltStrength sPos = 2.0 for o in range(octaves): # add wavelet noise for each grid of uv coordinates #xl_vel.clear() # debug , show only noise eval for i in range(uvs): uvWeight = getUvWeight(uv[i]) applyNoiseVec3( flags=xl_flags, target=xl_vel, noise=xl_wltnoise, scale=sStr * uvWeight, scaleSpatial=sPos , weight=energy, uv=uv[i] ) #mantaMsg( "Octave "+str(o)+", ss="+str(sStr)+" sp="+str(sPos)+" uvs="+str(uvs) ) # debug output # update octave parameters for next iteration sStr *= 0.06 # magic kolmogorov factor sPos *= 2.0 # now advect for substep in range(upres): advectSemiLagrange(flags=xl_flags, vel=xl_vel, grid=xl_density, order=2) # manually recreate inflow if (applyInflow): densityInflow( flags=xl_flags, density=xl_density, noise=xl_noise, shape=xl_source, scale=1, sigma=0.5 ) #xl_density.save('densityXl_%04d.vol' % t) xl.step() #timings.display() # small and xl grid update done #gui.screenshot( 'wltObs_%04d.png' % t );