Blender  V2.93
fluid_script.h
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2016 Blender Foundation.
17  * All rights reserved.
18  */
19 
24 #include <string>
25 
27 // LIBRARIES
29 
30 const std::string manta_import =
31  "\
32 from manta import *\n\
33 import os.path, shutil, math, sys, gc, multiprocessing, platform, time\n\
34 \n\
35 withMPBake = False # Bake files asynchronously\n\
36 withMPSave = False # Save files asynchronously\n\
37 isWindows = platform.system() != 'Darwin' and platform.system() != 'Linux'\n\
38 # TODO(sebbas): Use this to simulate Windows multiprocessing (has default mode spawn)\n\
39 #try:\n\
40 # multiprocessing.set_start_method('spawn')\n\
41 #except:\n\
42 # pass\n";
43 
45 // DEBUG
47 
48 const std::string manta_debuglevel =
49  "\n\
50 def set_manta_debuglevel(level):\n\
51  setDebugLevel(level=level)\n # level 0 = mute all output from manta\n";
52 
54 // SOLVERS
56 
57 const std::string fluid_solver =
58  "\n\
59 mantaMsg('Solver base')\n\
60 s$ID$ = Solver(name='solver_base$ID$', gridSize=gs_s$ID$, dim=dim_s$ID$)\n";
61 
62 const std::string fluid_solver_noise =
63  "\n\
64 mantaMsg('Solver noise')\n\
65 sn$ID$ = Solver(name='solver_noise$ID$', gridSize=gs_sn$ID$)\n";
66 
67 const std::string fluid_solver_mesh =
68  "\n\
69 mantaMsg('Solver mesh')\n\
70 sm$ID$ = Solver(name='solver_mesh$ID$', gridSize=gs_sm$ID$)\n";
71 
72 const std::string fluid_solver_particles =
73  "\n\
74 mantaMsg('Solver particles')\n\
75 sp$ID$ = Solver(name='solver_particles$ID$', gridSize=gs_sp$ID$)\n";
76 
77 const std::string fluid_solver_guiding =
78  "\n\
79 mantaMsg('Solver guiding')\n\
80 sg$ID$ = Solver(name='solver_guiding$ID$', gridSize=gs_sg$ID$)\n";
81 
82 const std::string fluid_solver_viscosity =
83  "\n\
84 mantaMsg('Solver viscosity')\n\
85 sv$ID$ = Solver(name='solver_viscosity$ID$', gridSize=gs_sv$ID$, dim=dim_s$ID$)\n";
86 
88 // VARIABLES
90 
91 const std::string fluid_variables =
92  "\n\
93 mantaMsg('Fluid variables')\n\
94 dim_s$ID$ = $SOLVER_DIM$\n\
95 res_s$ID$ = $RES$\n\
96 gravity_s$ID$ = vec3($GRAVITY_X$, $GRAVITY_Y$, $GRAVITY_Z$) # in SI unit (e.g. m/s^2)\n\
97 gs_s$ID$ = vec3($RESX$, $RESY$, $RESZ$)\n\
98 maxVel_s$ID$ = 0\n\
99 \n\
100 domainClosed_s$ID$ = $DOMAIN_CLOSED$\n\
101 boundConditions_s$ID$ = '$BOUND_CONDITIONS$'\n\
102 boundaryWidth_s$ID$ = $BOUNDARY_WIDTH$\n\
103 deleteInObstacle_s$ID$ = $DELETE_IN_OBSTACLE$\n\
104 \n\
105 using_smoke_s$ID$ = $USING_SMOKE$\n\
106 using_liquid_s$ID$ = $USING_LIQUID$\n\
107 using_noise_s$ID$ = $USING_NOISE$\n\
108 using_adaptTime_s$ID$ = $USING_ADAPTIVETIME$\n\
109 using_obstacle_s$ID$ = $USING_OBSTACLE$\n\
110 using_guiding_s$ID$ = $USING_GUIDING$\n\
111 using_fractions_s$ID$ = $USING_FRACTIONS$\n\
112 using_invel_s$ID$ = $USING_INVEL$\n\
113 using_outflow_s$ID$ = $USING_OUTFLOW$\n\
114 using_sndparts_s$ID$ = $USING_SNDPARTS$\n\
115 using_speedvectors_s$ID$ = $USING_SPEEDVECTORS$\n\
116 using_diffusion_s$ID$ = $USING_DIFFUSION$\n\
117 \n\
118 # Fluid time params\n\
119 timeScale_s$ID$ = $TIME_SCALE$\n\
120 timeTotal_s$ID$ = $TIME_TOTAL$\n\
121 timePerFrame_s$ID$ = $TIME_PER_FRAME$\n\
122 \n\
123 # In Blender fluid.c: frame_length = DT_DEFAULT * (25.0 / fps) * time_scale\n\
124 # with DT_DEFAULT = 0.1\n\
125 frameLength_s$ID$ = $FRAME_LENGTH$\n\
126 frameLengthUnscaled_s$ID$ = frameLength_s$ID$ / timeScale_s$ID$\n\
127 frameLengthRaw_s$ID$ = 0.1 * 25 # dt = 0.1 at 25 fps\n\
128 \n\
129 dt0_s$ID$ = $DT$\n\
130 cflCond_s$ID$ = $CFL$\n\
131 timestepsMin_s$ID$ = $TIMESTEPS_MIN$\n\
132 timestepsMax_s$ID$ = $TIMESTEPS_MAX$\n\
133 \n\
134 # Start and stop for simulation\n\
135 current_frame_s$ID$ = $CURRENT_FRAME$\n\
136 start_frame_s$ID$ = $START_FRAME$\n\
137 end_frame_s$ID$ = $END_FRAME$\n\
138 \n\
139 # Fluid diffusion / viscosity\n\
140 domainSize_s$ID$ = $FLUID_DOMAIN_SIZE$ # longest domain side in meters\n\
141 kinViscosity_s$ID$ = $FLUID_VISCOSITY$ / (domainSize_s$ID$*domainSize_s$ID$) # kinematic viscosity in m^2/s\n\
142 \n\
143 # Factors to convert Blender units to Manta units\n\
144 ratioMetersToRes_s$ID$ = float(domainSize_s$ID$) / float(res_s$ID$) # [meters / cells]\n\
145 mantaMsg('1 Mantaflow cell is ' + str(ratioMetersToRes_s$ID$) + ' Blender length units long.')\n\
146 \n\
147 ratioResToBLength_s$ID$ = float(res_s$ID$) / float(domainSize_s$ID$) # [cells / blength] (blength: cm, m, or km, ... )\n\
148 mantaMsg('1 Blender length unit is ' + str(ratioResToBLength_s$ID$) + ' Mantaflow cells long.')\n\
149 \n\
150 ratioBTimeToTimestep_s$ID$ = float(1) / float(frameLengthRaw_s$ID$) # the time within 1 blender time unit, see also fluid.c\n\
151 mantaMsg('1 Blender time unit is ' + str(ratioBTimeToTimestep_s$ID$) + ' Mantaflow time units long.')\n\
152 \n\
153 ratioFrameToFramelength_s$ID$ = float(1) / float(frameLengthUnscaled_s$ID$ ) # the time within 1 frame\n\
154 mantaMsg('frame / frameLength is ' + str(ratioFrameToFramelength_s$ID$) + ' Mantaflow time units long.')\n\
155 \n\
156 scaleAcceleration_s$ID$ = ratioResToBLength_s$ID$ * (ratioBTimeToTimestep_s$ID$**2)# [meters/btime^2] to [cells/timestep^2] (btime: sec, min, or h, ...)\n\
157 mantaMsg('scaleAcceleration is ' + str(scaleAcceleration_s$ID$))\n\
158 \n\
159 scaleSpeedFrames_s$ID$ = ratioResToBLength_s$ID$ * ratioFrameToFramelength_s$ID$ # [blength/frame] to [cells/frameLength]\n\
160 mantaMsg('scaleSpeed is ' + str(scaleSpeedFrames_s$ID$))\n\
161 \n\
162 gravity_s$ID$ *= scaleAcceleration_s$ID$ # scale from world acceleration to cell based acceleration\n\
163 \n\
164 # OpenVDB options\n\
165 vdbCompression_s$ID$ = $COMPRESSION_OPENVDB$\n\
166 vdbPrecision_s$ID$ = $PRECISION_OPENVDB$\n\
167 vdbClip_s$ID$ = $CLIP_OPENVDB$\n\
168 \n\
169 # Cache file names\n\
170 file_data_s$ID$ = '$NAME_DATA$'\n\
171 file_noise_s$ID$ = '$NAME_NOISE$'\n\
172 file_mesh_s$ID$ = '$NAME_MESH$'\n\
173 file_meshvel_s$ID$ = '$NAME_MESH$'\n\
174 file_particles_s$ID$ = '$NAME_PARTICLES$'\n\
175 file_guiding_s$ID$ = '$NAME_GUIDING$'";
176 
177 const std::string fluid_variables_noise =
178  "\n\
179 mantaMsg('Fluid variables noise')\n\
180 upres_sn$ID$ = $NOISE_SCALE$\n\
181 gs_sn$ID$ = vec3(upres_sn$ID$*gs_s$ID$.x, upres_sn$ID$*gs_s$ID$.y, upres_sn$ID$*gs_s$ID$.z)\n";
182 
183 const std::string fluid_variables_mesh =
184  "\n\
185 mantaMsg('Fluid variables mesh')\n\
186 upres_sm$ID$ = $MESH_SCALE$\n\
187 gs_sm$ID$ = vec3(upres_sm$ID$*gs_s$ID$.x, upres_sm$ID$*gs_s$ID$.y, upres_sm$ID$*gs_s$ID$.z)\n";
188 
189 const std::string fluid_variables_particles =
190  "\n\
191 mantaMsg('Fluid variables particles')\n\
192 upres_sp$ID$ = $PARTICLE_SCALE$\n\
193 gs_sp$ID$ = vec3(upres_sp$ID$*gs_s$ID$.x, upres_sp$ID$*gs_s$ID$.y, upres_sp$ID$*gs_s$ID$.z)\n";
194 
195 const std::string fluid_variables_guiding =
196  "\n\
197 mantaMsg('Fluid variables guiding')\n\
198 gs_sg$ID$ = vec3($GUIDING_RESX$, $GUIDING_RESY$, $GUIDING_RESZ$)\n\
199 \n\
200 alpha_sg$ID$ = $GUIDING_ALPHA$\n\
201 beta_sg$ID$ = $GUIDING_BETA$\n\
202 gamma_sg$ID$ = $GUIDING_FACTOR$\n\
203 tau_sg$ID$ = 1.0\n\
204 sigma_sg$ID$ = 0.99/tau_sg$ID$\n\
205 theta_sg$ID$ = 1.0\n";
206 
207 const std::string fluid_variables_viscosity =
208  "\n\
209 gs_sv$ID$ = vec3($RESX$*2, $RESY$*2, $RESZ$*2)\n";
210 
211 const std::string fluid_with_obstacle =
212  "\n\
213 using_obstacle_s$ID$ = True\n";
214 
215 const std::string fluid_with_guiding =
216  "\n\
217 using_guiding_s$ID$ = True\n";
218 
219 const std::string fluid_with_fractions =
220  "\n\
221 using_fractions_s$ID$ = True\n";
222 
223 const std::string fluid_with_invel =
224  "\n\
225 using_invel_s$ID$ = True\n";
226 
227 const std::string fluid_with_outflow =
228  "\n\
229 using_outflow_s$ID$ = True\n";
230 
231 const std::string fluid_with_sndparts =
232  "\n\
233 using_sndparts_s$ID$ = True\n";
234 
236 // ADAPTIVE TIME STEPPING
238 
239 const std::string fluid_time_stepping =
240  "\n\
241 mantaMsg('Fluid adaptive time stepping')\n\
242 s$ID$.frameLength = frameLength_s$ID$\n\
243 s$ID$.timestepMin = s$ID$.frameLength / max(1, timestepsMax_s$ID$)\n\
244 s$ID$.timestepMax = s$ID$.frameLength / max(1, timestepsMin_s$ID$)\n\
245 s$ID$.cfl = cflCond_s$ID$\n\
246 s$ID$.timePerFrame = timePerFrame_s$ID$\n\
247 s$ID$.timestep = dt0_s$ID$\n\
248 s$ID$.timeTotal = timeTotal_s$ID$\n\
249 #mantaMsg('timestep: ' + str(s$ID$.timestep) + ' // timPerFrame: ' + str(s$ID$.timePerFrame) + ' // frameLength: ' + str(s$ID$.frameLength) + ' // timeTotal: ' + str(s$ID$.timeTotal) )\n";
250 
251 const std::string fluid_adapt_time_step =
252  "\n\
253 def fluid_adapt_time_step_$ID$():\n\
254  mantaMsg('Fluid adapt time step')\n\
255  \n\
256  # time params are animatable\n\
257  s$ID$.frameLength = frameLength_s$ID$\n\
258  s$ID$.cfl = cflCond_s$ID$\n\
259  s$ID$.timestepMin = s$ID$.frameLength / max(1, timestepsMax_s$ID$)\n\
260  s$ID$.timestepMax = s$ID$.frameLength / max(1, timestepsMin_s$ID$)\n\
261  \n\
262  # ensure that vel grid is full (remember: adaptive domain can reallocate solver)\n\
263  copyRealToVec3(sourceX=x_vel_s$ID$, sourceY=y_vel_s$ID$, sourceZ=z_vel_s$ID$, target=vel_s$ID$)\n\
264  maxVel_s$ID$ = vel_s$ID$.getMax() if vel_s$ID$ else 0\n\
265  if using_adaptTime_s$ID$:\n\
266  mantaMsg('Adapt timestep, maxvel: ' + str(maxVel_s$ID$))\n\
267  s$ID$.adaptTimestep(maxVel_s$ID$)\n";
268 
270 // GRIDS
272 
273 const std::string fluid_alloc =
274  "\n\
275 mantaMsg('Fluid alloc data')\n\
276 flags_s$ID$ = s$ID$.create(FlagGrid, name='$NAME_FLAGS$')\n\
277 vel_s$ID$ = s$ID$.create(MACGrid, name='$NAME_VELOCITY$', sparse=True)\n\
278 velTmp_s$ID$ = s$ID$.create(MACGrid, name='$NAME_VELOCITYTMP$', sparse=True)\n\
279 x_vel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_VELOCITY_X$')\n\
280 y_vel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_VELOCITY_Y$')\n\
281 z_vel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_VELOCITY_Z$')\n\
282 pressure_s$ID$ = s$ID$.create(RealGrid, name='$NAME_PRESSURE$')\n\
283 phiObs_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOBS$')\n\
284 phiSIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHISIN$') # helper for static flow objects\n\
285 phiIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIIN$')\n\
286 phiOut_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOUT$')\n\
287 forces_s$ID$ = s$ID$.create(Vec3Grid, name='$NAME_FORCES$')\n\
288 x_force_s$ID$ = s$ID$.create(RealGrid, name='$NAME_FORCES_X$')\n\
289 y_force_s$ID$ = s$ID$.create(RealGrid, name='$NAME_FORCES_Y$')\n\
290 z_force_s$ID$ = s$ID$.create(RealGrid, name='$NAME_FORCES_Z$')\n\
291 obvel_s$ID$ = None\n\
292 \n\
293 # Set some initial values\n\
294 phiObs_s$ID$.setConst(9999)\n\
295 phiSIn_s$ID$.setConst(9999)\n\
296 phiIn_s$ID$.setConst(9999)\n\
297 phiOut_s$ID$.setConst(9999)\n\
298 \n\
299 # Keep track of important objects in dict to load them later on\n\
300 fluid_data_dict_final_s$ID$ = { 'vel' : vel_s$ID$ }\n\
301 fluid_data_dict_resume_s$ID$ = { 'phiObs' : phiObs_s$ID$, 'phiIn' : phiIn_s$ID$, 'phiOut' : phiOut_s$ID$, 'flags' : flags_s$ID$, 'velTmp' : velTmp_s$ID$ }\n";
302 
303 const std::string fluid_alloc_obstacle =
304  "\n\
305 mantaMsg('Allocating obstacle data')\n\
306 numObs_s$ID$ = s$ID$.create(RealGrid, name='$NAME_NUMOBS$')\n\
307 phiObsSIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOBSSIN$') # helper for static obstacle objects\n\
308 phiObsIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOBSIN$')\n\
309 obvel_s$ID$ = s$ID$.create(MACGrid, name='$NAME_OBVEL$')\n\
310 obvelC_s$ID$ = s$ID$.create(Vec3Grid, name='$NAME_OBVELC$')\n\
311 x_obvel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_OBVEL_X$')\n\
312 y_obvel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_OBVEL_Y$')\n\
313 z_obvel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_OBVEL_Z$')\n\
314 \n\
315 # Set some initial values\n\
316 phiObsSIn_s$ID$.setConst(9999)\n\
317 phiObsIn_s$ID$.setConst(9999)\n\
318 \n\
319 if 'fluid_data_dict_resume_s$ID$' in globals():\n\
320  fluid_data_dict_resume_s$ID$.update(phiObsIn=phiObsIn_s$ID$)\n";
321 
322 const std::string fluid_alloc_guiding =
323  "\n\
324 mantaMsg('Allocating guiding data')\n\
325 velT_s$ID$ = s$ID$.create(MACGrid, name='$NAME_VELT$')\n\
326 weightGuide_s$ID$ = s$ID$.create(RealGrid, name='$NAME_WEIGHTGUIDE$')\n\
327 numGuides_s$ID$ = s$ID$.create(RealGrid, name='$NAME_NUMGUIDES$')\n\
328 phiGuideIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIGUIDEIN$')\n\
329 guidevelC_s$ID$ = s$ID$.create(Vec3Grid, name='$NAME_GUIDEVELC$')\n\
330 x_guidevel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_GUIDEVEL_X$')\n\
331 y_guidevel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_GUIDEVEL_Y$')\n\
332 z_guidevel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_GUIDEVEL_Z$')\n\
333 \n\
334 # Final guide vel grid needs to have independent size\n\
335 guidevel_sg$ID$ = sg$ID$.create(MACGrid, name='$NAME_VELOCITY_GUIDE$')\n\
336 \n\
337 # Keep track of important objects in dict to load them later on\n\
338 fluid_guiding_dict_s$ID$ = { 'guidevel' : guidevel_sg$ID$ }\n";
339 
340 const std::string fluid_alloc_fractions =
341  "\n\
342 mantaMsg('Allocating fractions data')\n\
343 fractions_s$ID$ = s$ID$.create(MACGrid, name='$NAME_FRACTIONS$')\n";
344 
345 const std::string fluid_alloc_invel =
346  "\n\
347 mantaMsg('Allocating initial velocity data')\n\
348 invelC_s$ID$ = s$ID$.create(VecGrid, name='$NAME_INVELC$')\n\
349 x_invel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_INVEL_X$')\n\
350 y_invel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_INVEL_Y$')\n\
351 z_invel_s$ID$ = s$ID$.create(RealGrid, name='$NAME_INVEL_Z$')\n";
352 
353 const std::string fluid_alloc_outflow =
354  "\n\
355 mantaMsg('Allocating outflow data')\n\
356 phiOutSIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOUTSIN$') # helper for static outflow objects\n\
357 phiOutIn_s$ID$ = s$ID$.create(LevelsetGrid, name='$NAME_PHIOUTIN$')\n\
358 \n\
359 # Set some initial values\n\
360 phiOutSIn_s$ID$.setConst(9999)\n\
361 phiOutIn_s$ID$.setConst(9999)\n\
362 \n\
363 if 'fluid_data_dict_resume_s$ID$' in globals():\n\
364  fluid_data_dict_resume_s$ID$.update(phiOutIn=phiOutIn_s$ID$)\n";
365 
367 // PRE / POST STEP
369 
370 const std::string fluid_pre_step =
371  "\n\
372 def fluid_pre_step_$ID$():\n\
373  mantaMsg('Fluid pre step')\n\
374  \n\
375  phiObs_s$ID$.setConst(9999)\n\
376  phiOut_s$ID$.setConst(9999)\n\
377  \n\
378  # Main vel grid is copied in adapt time step function\n\
379  \n\
380  if using_obstacle_s$ID$:\n\
381  # Average out velocities from multiple obstacle objects at one cell\n\
382  x_obvel_s$ID$.safeDivide(numObs_s$ID$)\n\
383  y_obvel_s$ID$.safeDivide(numObs_s$ID$)\n\
384  z_obvel_s$ID$.safeDivide(numObs_s$ID$)\n\
385  copyRealToVec3(sourceX=x_obvel_s$ID$, sourceY=y_obvel_s$ID$, sourceZ=z_obvel_s$ID$, target=obvelC_s$ID$)\n\
386  \n\
387  if using_invel_s$ID$:\n\
388  copyRealToVec3(sourceX=x_invel_s$ID$, sourceY=y_invel_s$ID$, sourceZ=z_invel_s$ID$, target=invelC_s$ID$)\n\
389  \n\
390  if using_guiding_s$ID$:\n\
391  weightGuide_s$ID$.multConst(0)\n\
392  weightGuide_s$ID$.addConst(alpha_sg$ID$)\n\
393  interpolateMACGrid(source=guidevel_sg$ID$, target=velT_s$ID$)\n\
394  velT_s$ID$.multConst(vec3(gamma_sg$ID$))\n\
395  \n\
396  x_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\
397  y_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\
398  z_force_s$ID$.multConst(scaleSpeedFrames_s$ID$)\n\
399  copyRealToVec3(sourceX=x_force_s$ID$, sourceY=y_force_s$ID$, sourceZ=z_force_s$ID$, target=forces_s$ID$)\n\
400  \n\
401  # If obstacle has velocity, i.e. is a moving obstacle, switch to dynamic preconditioner\n\
402  if using_smoke_s$ID$ and using_obstacle_s$ID$ and obvelC_s$ID$.getMax() > 0:\n\
403  mantaMsg('Using dynamic preconditioner')\n\
404  preconditioner_s$ID$ = PcMGDynamic\n\
405  else:\n\
406  mantaMsg('Using static preconditioner')\n\
407  preconditioner_s$ID$ = PcMGStatic\n";
408 
409 const std::string fluid_post_step =
410  "\n\
411 def fluid_post_step_$ID$():\n\
412  mantaMsg('Fluid post step')\n\
413  \n\
414  # Copy vel grid to reals grids (which Blender internal will in turn use for vel access)\n\
415  copyVec3ToReal(source=vel_s$ID$, targetX=x_vel_s$ID$, targetY=y_vel_s$ID$, targetZ=z_vel_s$ID$)\n\
416  if using_guiding_s$ID$:\n\
417  copyVec3ToReal(source=guidevel_sg$ID$, targetX=x_guidevel_s$ID$, targetY=y_guidevel_s$ID$, targetZ=z_guidevel_s$ID$)\n";
418 
420 // DESTRUCTION
422 
423 const std::string fluid_delete_all =
424  "\n\
425 mantaMsg('Deleting fluid')\n\
426 # Clear all helper dictionaries first\n\
427 mantaMsg('Clear helper dictionaries')\n\
428 if 'liquid_data_dict_final_s$ID$' in globals(): liquid_data_dict_final_s$ID$.clear()\n\
429 if 'liquid_data_dict_resume_s$ID$' in globals(): liquid_data_dict_resume_s$ID$.clear()\n\
430 if 'liquid_mesh_dict_s$ID$' in globals(): liquid_mesh_dict_s$ID$.clear()\n\
431 if 'liquid_meshvel_dict_s$ID$' in globals(): liquid_meshvel_dict_s$ID$.clear()\n\
432 if 'liquid_particles_final_dict_s$ID$' in globals(): liquid_particles_final_dict_s$ID$.clear()\n\
433 if 'liquid_particles_resume_dict_s$ID$' in globals(): liquid_particles_resume_dict_s$ID$.clear()\n\
434 \n\
435 if 'smoke_data_dict_final_s$ID$' in globals(): smoke_data_dict_final_s$ID$.clear()\n\
436 if 'smoke_data_dict_resume_s$ID$' in globals(): smoke_data_dict_resume_s$ID$.clear()\n\
437 if 'smoke_noise_dict_final_s$ID$' in globals(): smoke_noise_dict_final_s$ID$.clear()\n\
438 if 'smoke_noise_dict_resume_s$ID$' in globals(): smoke_noise_dict_resume_s$ID$.clear()\n\
439 \n\
440 if 'fluid_data_dict_final_s$ID$' in globals(): fluid_data_dict_final_s$ID$.clear()\n\
441 if 'fluid_data_dict_resume_s$ID$' in globals(): fluid_data_dict_resume_s$ID$.clear()\n\
442 if 'fluid_guiding_dict_s$ID$' in globals(): fluid_guiding_dict_s$ID$.clear()\n\
443 if 'fluid_vel_dict_s$ID$' in globals(): fluid_vel_dict_s$ID$.clear()\n\
444 \n\
445 # Delete all children from objects (e.g. pdata for particles)\n\
446 mantaMsg('Release solver childrens children')\n\
447 for var in list(globals()):\n\
448  if var.endswith('_pp$ID$') or var.endswith('_mesh$ID$'):\n\
449  del globals()[var]\n\
450 \n\
451 # Now delete children from solver objects\n\
452 mantaMsg('Release solver children')\n\
453 for var in list(globals()):\n\
454  if var.endswith('_s$ID$') or var.endswith('_sn$ID$') or var.endswith('_sm$ID$') or var.endswith('_sp$ID$') or var.endswith('_sg$ID$'):\n\
455  del globals()[var]\n\
456 \n\
457 # Extra cleanup for multigrid and fluid guiding\n\
458 mantaMsg('Release multigrid')\n\
459 if 's$ID$' in globals(): releaseMG(s$ID$)\n\
460 if 'sn$ID$' in globals(): releaseMG(sn$ID$)\n\
461 mantaMsg('Release fluid guiding')\n\
462 releaseBlurPrecomp()\n\
463 \n\
464 # Release unreferenced memory (if there is some left, can in fact happen)\n\
465 gc.collect()\n\
466 \n\
467 # Now it is safe to delete solver objects (always need to be deleted last)\n\
468 mantaMsg('Delete base solver')\n\
469 if 's$ID$' in globals(): del s$ID$\n\
470 mantaMsg('Delete noise solver')\n\
471 if 'sn$ID$' in globals(): del sn$ID$\n\
472 mantaMsg('Delete mesh solver')\n\
473 if 'sm$ID$' in globals(): del sm$ID$\n\
474 mantaMsg('Delete particle solver')\n\
475 if 'sp$ID$' in globals(): del sp$ID$\n\
476 mantaMsg('Delete guiding solver')\n\
477 if 'sg$ID$' in globals(): del sg$ID$\n\
478 \n\
479 # Release unreferenced memory (if there is some left)\n\
480 gc.collect()\n";
481 
483 // BAKE
485 
486 /* This has to match the behavior of BLI_path_frame,
487  * for positive and negative frame numbers. */
488 const std::string fluid_cache_helper =
489  "\n\
490 def fluid_cache_get_framenr_formatted_$ID$(framenr):\n\
491  return str(framenr).zfill(4) if framenr >= 0 else str(framenr).zfill(5)\n";
492 
493 const std::string fluid_bake_multiprocessing =
494  "\n\
495 def fluid_cache_multiprocessing_start_$ID$(function, framenr, file_name=None, format_data=None, format_noise=None, format_mesh=None, format_particles=None, format_guiding=None, path_data=None, path_noise=None, path_mesh=None, path_particles=None, path_guiding=None, dict=None, do_join=True, resumable=False):\n\
496  mantaMsg('Multiprocessing cache')\n\
497  if __name__ == '__main__':\n\
498  args = (framenr,)\n\
499  if file_name:\n\
500  args += (file_name,)\n\
501  if format_data:\n\
502  args += (format_data,)\n\
503  if format_noise:\n\
504  args += (format_noise,)\n\
505  if format_mesh:\n\
506  args += (format_mesh,)\n\
507  if format_particles:\n\
508  args += (format_particles,)\n\
509  if format_guiding:\n\
510  args += (format_guiding,)\n\
511  if path_data:\n\
512  args += (path_data,)\n\
513  if path_noise:\n\
514  args += (path_noise,)\n\
515  if path_mesh:\n\
516  args += (path_mesh,)\n\
517  if path_particles:\n\
518  args += (path_particles,)\n\
519  if path_guiding:\n\
520  args += (path_guiding,)\n\
521  if dict:\n\
522  args += (dict,)\n\
523  args += (resumable,)\n\
524  p$ID$ = multiprocessing.Process(target=function, args=args)\n\
525  p$ID$.start()\n\
526  if do_join:\n\
527  p$ID$.join()\n";
528 
529 const std::string fluid_bake_data =
530  "\n\
531 def bake_fluid_process_data_$ID$(framenr, format_data, path_data):\n\
532  mantaMsg('Bake fluid data')\n\
533  \n\
534  s$ID$.frame = framenr\n\
535  s$ID$.frameLength = frameLength_s$ID$\n\
536  s$ID$.timeTotal = timeTotal_s$ID$\n\
537  \n\
538  start_time = time.time()\n\
539  if using_smoke_s$ID$:\n\
540  smoke_adaptive_step_$ID$(framenr)\n\
541  if using_liquid_s$ID$:\n\
542  liquid_adaptive_step_$ID$(framenr)\n\
543  mantaMsg('--- Step: %s seconds ---' % (time.time() - start_time))\n\
544 \n\
545 def bake_fluid_data_$ID$(path_data, framenr, format_data):\n\
546  if not withMPBake or isWindows:\n\
547  bake_fluid_process_data_$ID$(framenr, format_data, path_data)\n\
548  else:\n\
549  fluid_cache_multiprocessing_start_$ID$(function=bake_fluid_process_data_$ID$, framenr=framenr, format_data=format_data, path_data=path_data, do_join=False)\n";
550 
551 const std::string fluid_bake_noise =
552  "\n\
553 def bake_noise_process_$ID$(framenr, format_noise, path_noise):\n\
554  mantaMsg('Bake fluid noise')\n\
555  \n\
556  sn$ID$.frame = framenr\n\
557  sn$ID$.frameLength = frameLength_s$ID$\n\
558  sn$ID$.timeTotal = timeTotal_s$ID$\n\
559  sn$ID$.timestep = frameLength_s$ID$ # no adaptive timestep for noise\n\
560  \n\
561  smoke_step_noise_$ID$(framenr)\n\
562 \n\
563 def bake_noise_$ID$(path_noise, framenr, format_noise):\n\
564  if not withMPBake or isWindows:\n\
565  bake_noise_process_$ID$(framenr, format_noise, path_noise)\n\
566  else:\n\
567  fluid_cache_multiprocessing_start_$ID$(function=bake_noise_process_$ID$, framenr=framenr, format_noise=format_noise, path_noise=path_noise)\n";
568 
569 const std::string fluid_bake_mesh =
570  "\n\
571 def bake_mesh_process_$ID$(framenr, format_data, format_mesh, path_mesh):\n\
572  mantaMsg('Bake fluid mesh')\n\
573  \n\
574  sm$ID$.frame = framenr\n\
575  sm$ID$.frameLength = frameLength_s$ID$\n\
576  sm$ID$.timeTotal = timeTotal_s$ID$\n\
577  sm$ID$.timestep = frameLength_s$ID$ # no adaptive timestep for mesh\n\
578  \n\
579  #if using_smoke_s$ID$:\n\
580  # TODO(sebbas): Future update could include smoke mesh (vortex sheets)\n\
581  if using_liquid_s$ID$:\n\
582  liquid_step_mesh_$ID$()\n\
583  liquid_save_mesh_$ID$(path_mesh, framenr, format_mesh)\n\
584  if using_speedvectors_s$ID$:\n\
585  liquid_save_meshvel_$ID$(path_mesh, framenr, format_data)\n\
586 \n\
587 def bake_mesh_$ID$(path_mesh, framenr, format_data, format_mesh):\n\
588  if not withMPBake or isWindows:\n\
589  bake_mesh_process_$ID$(framenr, format_data, format_mesh, path_mesh)\n\
590  else:\n\
591  fluid_cache_multiprocessing_start_$ID$(function=bake_mesh_process_$ID$, framenr=framenr, format_data=format_data, format_mesh=format_mesh, path_mesh=path_mesh)\n";
592 
593 const std::string fluid_bake_particles =
594  "\n\
595 def bake_particles_process_$ID$(framenr, format_particles, path_particles, resumable):\n\
596  mantaMsg('Bake secondary particles')\n\
597  \n\
598  sp$ID$.frame = framenr\n\
599  sp$ID$.frameLength = frameLength_s$ID$\n\
600  sp$ID$.timeTotal = timeTotal_s$ID$\n\
601  sp$ID$.timestep = frameLength_s$ID$ # no adaptive timestep for particles\n\
602  \n\
603  #if using_smoke_s$ID$:\n\
604  # TODO(sebbas): Future update could include smoke particles (e.g. fire sparks)\n\
605  if using_liquid_s$ID$:\n\
606  liquid_step_particles_$ID$()\n\
607  liquid_save_particles_$ID$(path_particles, framenr, format_particles, resumable)\n\
608 \n\
609 def bake_particles_$ID$(path_particles, framenr, format_particles, resumable):\n\
610  if not withMPBake or isWindows:\n\
611  bake_particles_process_$ID$(framenr, format_particles, path_particles, resumable)\n\
612  else:\n\
613  fluid_cache_multiprocessing_start_$ID$(function=bake_particles_process_$ID$, framenr=framenr, format_particles=format_particles, path_particles=path_particles, resumable=resumable)\n";
614 
615 const std::string fluid_bake_guiding =
616  "\n\
617 def bake_guiding_process_$ID$(framenr, format_guiding, path_guiding, resumable):\n\
618  mantaMsg('Bake fluid guiding')\n\
619  \n\
620  # Average out velocities from multiple guiding objects at one cell\n\
621  x_guidevel_s$ID$.safeDivide(numGuides_s$ID$)\n\
622  y_guidevel_s$ID$.safeDivide(numGuides_s$ID$)\n\
623  z_guidevel_s$ID$.safeDivide(numGuides_s$ID$)\n\
624  copyRealToVec3(sourceX=x_guidevel_s$ID$, sourceY=y_guidevel_s$ID$, sourceZ=z_guidevel_s$ID$, target=guidevelC_s$ID$)\n\
625  \n\
626  mantaMsg('Extrapolating guiding velocity')\n\
627  # ensure velocities inside of guiding object, slightly add guiding vels outside of object too\n\
628  extrapolateVec3Simple(vel=guidevelC_s$ID$, phi=phiGuideIn_s$ID$, distance=6, inside=True)\n\
629  extrapolateVec3Simple(vel=guidevelC_s$ID$, phi=phiGuideIn_s$ID$, distance=3, inside=False)\n\
630  resampleVec3ToMac(source=guidevelC_s$ID$, target=guidevel_sg$ID$)\n\
631  \n\
632  fluid_save_guiding_$ID$(path_guiding, framenr, format_guiding, resumable)\n\
633 \n\
634 def bake_guiding_$ID$(path_guiding, framenr, format_guiding, resumable):\n\
635  if not withMPBake or isWindows:\n\
636  bake_guiding_process_$ID$(framenr, format_guiding, path_guiding, resumable)\n\
637  else:\n\
638  fluid_cache_multiprocessing_start_$ID$(function=bake_guiding_process_$ID$, framenr=framenr, format_guiding=format_guiding, path_guiding=path_guiding, resumable=resumable)\n";
639 
641 // IMPORT
643 
644 const std::string fluid_file_import =
645  "\n\
646 def fluid_file_import_s$ID$(dict, path, framenr, file_format, file_name=None):\n\
647  mantaMsg('Fluid file import, frame: ' + str(framenr))\n\
648  try:\n\
649  framenr = fluid_cache_get_framenr_formatted_$ID$(framenr)\n\
650  # New cache: Try to load the data from a single file\n\
651  loadCombined = 0\n\
652  if file_name is not None:\n\
653  file = os.path.join(path, file_name + '_' + framenr + file_format)\n\
654  if os.path.isfile(file):\n\
655  if file_format == '.vdb':\n\
656  loadCombined = load(name=file, objects=list(dict.values()), worldSize=domainSize_s$ID$)\n\
657  elif file_format == '.bobj.gz' or file_format == '.obj':\n\
658  for name, object in dict.items():\n\
659  if os.path.isfile(file):\n\
660  loadCombined = object.load(file)\n\
661  \n\
662  # Old cache: Try to load the data from separate files, i.e. per object with the object based load() function\n\
663  if not loadCombined:\n\
664  for name, object in dict.items():\n\
665  file = os.path.join(path, name + '_' + framenr + file_format)\n\
666  if os.path.isfile(file):\n\
667  loadCombined = object.load(file)\n\
668  \n\
669  if not loadCombined:\n\
670  mantaMsg('Could not load file ' + str(file))\n\
671  \n\
672  except Exception as e:\n\
673  mantaMsg('Exception in Python fluid file import: ' + str(e))\n\
674  pass # Just skip file load errors for now\n";
675 
676 const std::string fluid_load_guiding =
677  "\n\
678 def fluid_load_guiding_$ID$(path, framenr, file_format):\n\
679  mantaMsg('Fluid load guiding, frame ' + str(framenr))\n\
680  guidevel_sg$ID$.setName('$NAME_VELOCITY_GUIDE$')\n\
681  fluid_file_import_s$ID$(dict=fluid_guiding_dict_s$ID$, path=path, framenr=framenr, file_format=file_format, file_name=file_guiding_s$ID$)\n\
682  \n\
683  copyVec3ToReal(source=guidevel_sg$ID$, targetX=x_guidevel_s$ID$, targetY=y_guidevel_s$ID$, targetZ=z_guidevel_s$ID$)\n";
684 
685 const std::string fluid_load_vel =
686  "\n\
687 def fluid_load_vel_$ID$(path, framenr, file_format):\n\
688  mantaMsg('Fluid load vel, frame ' + str(framenr))\n\
689  guidevel_sg$ID$.setName('$NAME_VELOCITY$') # for loading data the guidevel grid will pretend to be the vel grid\n\
690  fluid_vel_dict_s$ID$ = { 'vel' : guidevel_sg$ID$ }\n\
691  fluid_file_import_s$ID$(dict=fluid_vel_dict_s$ID$, path=path, framenr=framenr, file_format=file_format, file_name=file_data_s$ID$)\n";
692 
694 // EXPORT
696 
697 const std::string fluid_file_export =
698  "\n\
699 def fluid_file_export_s$ID$(framenr, file_format, path, dict, file_name=None, mode_override=True, skip_subframes=True, clipGrid=None):\n\
700  if skip_subframes and ((timePerFrame_s$ID$ + dt0_s$ID$) < frameLength_s$ID$):\n\
701  return\n\
702  mantaMsg('Fluid file export, frame: ' + str(framenr))\n\
703  try:\n\
704  framenr = fluid_cache_get_framenr_formatted_$ID$(framenr)\n\
705  if not os.path.exists(path):\n\
706  os.makedirs(path)\n\
707  \n\
708  # New cache: Try to save the data to a single file\n\
709  saveCombined = 0\n\
710  if file_name is not None:\n\
711  file = os.path.join(path, file_name + '_' + framenr + file_format)\n\
712  if not os.path.isfile(file) or mode_override:\n\
713  if file_format == '.vdb':\n\
714  saveCombined = save(name=file, objects=list(dict.values()), worldSize=domainSize_s$ID$, skipDeletedParts=True, compression=vdbCompression_s$ID$, precision=vdbPrecision_s$ID$, clip=vdbClip_s$ID$, clipGrid=clipGrid, meta=True)\n\
715  elif file_format == '.bobj.gz' or file_format == '.obj':\n\
716  for name, object in dict.items():\n\
717  if not os.path.isfile(file) or mode_override:\n\
718  saveCombined = object.save(file)\n\
719  \n\
720  # Old cache: Try to save the data to separate files, i.e. per object with the object based save() function\n\
721  if not saveCombined:\n\
722  for name, object in dict.items():\n\
723  file = os.path.join(path, name + '_' + framenr + file_format)\n\
724  if not os.path.isfile(file) or mode_override: object.save(file)\n\
725  \n\
726  except Exception as e:\n\
727  mantaMsg('Exception in Python fluid file export: ' + str(e))\n\
728  pass # Just skip file save errors for now\n";
729 
730 const std::string fluid_save_guiding =
731  "\n\
732 def fluid_save_guiding_$ID$(path, framenr, file_format, resumable):\n\
733  mantaMsg('Fluid save guiding, frame ' + str(framenr))\n\
734  dict = fluid_guiding_dict_s$ID$\n\
735  if not withMPSave or isWindows:\n\
736  fluid_file_export_s$ID$(dict=dict, framenr=framenr, file_format=file_format, path=path, file_name=file_guiding_s$ID$)\n\
737  else:\n\
738  fluid_cache_multiprocessing_start_$ID$(function=fluid_file_export_s$ID$, file_name=file_guiding_s$ID$, framenr=framenr, format_data=file_format, path_data=path, dict=dict, do_join=False)\n";
739 
741 // STANDALONE MODE
743 
744 const std::string fluid_standalone =
745  "\n\
746 gui = None\n\
747 if (GUI):\n\
748  gui=Gui()\n\
749  gui.show()\n\
750  gui.pause()\n\
751 \n\
752 cache_resumable = $CACHE_RESUMABLE$\n\
753 cache_dir = '$CACHE_DIR$'\n\
754 file_format_data = '$CACHE_DATA_FORMAT$'\n\
755 file_format_mesh = '$CACHE_MESH_FORMAT$'\n\
756 \n\
757 # How many frame to load from cache\n\
758 from_cache_cnt = 100\n\
759 \n\
760 loop_cnt = 0\n\
761 while current_frame_s$ID$ <= end_frame_s$ID$:\n\
762  \n\
763  # Load already simulated data from cache:\n\
764  if loop_cnt < from_cache_cnt:\n\
765  load_data(current_frame_s$ID$, cache_resumable)\n\
766  \n\
767  # Otherwise simulate new data\n\
768  else:\n\
769  while(s$ID$.frame <= current_frame_s$ID$):\n\
770  if using_adaptTime_s$ID$:\n\
771  fluid_adapt_time_step_$ID$()\n\
772  step(current_frame_s$ID$)\n\
773  \n\
774  current_frame_s$ID$ += 1\n\
775  loop_cnt += 1\n\
776  \n\
777  if gui:\n\
778  gui.pause()\n";
779 
781 // SCRIPT SECTION HEADERS
783 
784 const std::string header_libraries =
785  "\n\
786 ######################################################################\n\
787 ## LIBRARIES\n\
788 ######################################################################\n";
789 
790 const std::string header_main =
791  "\n\
792 ######################################################################\n\
793 ## MAIN\n\
794 ######################################################################\n";
795 
796 const std::string header_prepost =
797  "\n\
798 ######################################################################\n\
799 ## PRE/POST STEPS\n\
800 ######################################################################\n";
801 
802 const std::string header_steps =
803  "\n\
804 ######################################################################\n\
805 ## STEPS\n\
806 ######################################################################\n";
807 
808 const std::string header_import =
809  "\n\
810 ######################################################################\n\
811 ## IMPORT\n\
812 ######################################################################\n";
813 
814 const std::string header_grids =
815  "\n\
816 ######################################################################\n\
817 ## GRIDS\n\
818 ######################################################################\n";
819 
820 const std::string header_solvers =
821  "\n\
822 ######################################################################\n\
823 ## SOLVERS\n\
824 ######################################################################\n";
825 
826 const std::string header_variables =
827  "\n\
828 ######################################################################\n\
829 ## VARIABLES\n\
830 ######################################################################\n";
831 
832 const std::string header_time =
833  "\n\
834 ######################################################################\n\
835 ## ADAPTIVE TIME\n\
836 ######################################################################\n";
837 
838 const std::string header_gridinit =
839  "\n\
840 ######################################################################\n\
841 ## DOMAIN INIT\n\
842 ######################################################################\n";
const std::string header_import
Definition: fluid_script.h:808
const std::string fluid_save_guiding
Definition: fluid_script.h:730
const std::string fluid_with_guiding
Definition: fluid_script.h:215
const std::string fluid_file_import
Definition: fluid_script.h:644
const std::string fluid_alloc_fractions
Definition: fluid_script.h:340
const std::string fluid_pre_step
Definition: fluid_script.h:370
const std::string header_gridinit
Definition: fluid_script.h:838
const std::string fluid_with_outflow
Definition: fluid_script.h:227
const std::string header_main
Definition: fluid_script.h:790
const std::string fluid_alloc
Definition: fluid_script.h:273
const std::string fluid_bake_data
Definition: fluid_script.h:529
const std::string fluid_standalone
Definition: fluid_script.h:744
const std::string fluid_variables_noise
Definition: fluid_script.h:177
const std::string fluid_with_fractions
Definition: fluid_script.h:219
const std::string header_steps
Definition: fluid_script.h:802
const std::string fluid_alloc_invel
Definition: fluid_script.h:345
const std::string fluid_solver_viscosity
Definition: fluid_script.h:82
const std::string fluid_variables_particles
Definition: fluid_script.h:189
const std::string fluid_with_invel
Definition: fluid_script.h:223
const std::string fluid_solver_particles
Definition: fluid_script.h:72
const std::string fluid_solver_mesh
Definition: fluid_script.h:67
const std::string fluid_alloc_obstacle
Definition: fluid_script.h:303
const std::string manta_import
Definition: fluid_script.h:30
const std::string fluid_load_vel
Definition: fluid_script.h:685
const std::string fluid_file_export
Definition: fluid_script.h:697
const std::string fluid_bake_noise
Definition: fluid_script.h:551
const std::string fluid_alloc_outflow
Definition: fluid_script.h:353
const std::string fluid_bake_guiding
Definition: fluid_script.h:615
const std::string fluid_delete_all
Definition: fluid_script.h:423
const std::string manta_debuglevel
Definition: fluid_script.h:48
const std::string header_grids
Definition: fluid_script.h:814
const std::string fluid_bake_multiprocessing
Definition: fluid_script.h:493
const std::string fluid_with_sndparts
Definition: fluid_script.h:231
const std::string header_time
Definition: fluid_script.h:832
const std::string header_libraries
Definition: fluid_script.h:784
const std::string fluid_variables
Definition: fluid_script.h:91
const std::string header_solvers
Definition: fluid_script.h:820
const std::string fluid_adapt_time_step
Definition: fluid_script.h:251
const std::string header_variables
Definition: fluid_script.h:826
const std::string header_prepost
Definition: fluid_script.h:796
const std::string fluid_solver_noise
Definition: fluid_script.h:62
const std::string fluid_alloc_guiding
Definition: fluid_script.h:322
const std::string fluid_variables_viscosity
Definition: fluid_script.h:207
const std::string fluid_cache_helper
Definition: fluid_script.h:488
const std::string fluid_time_stepping
Definition: fluid_script.h:239
const std::string fluid_bake_mesh
Definition: fluid_script.h:569
const std::string fluid_solver
Definition: fluid_script.h:57
const std::string fluid_bake_particles
Definition: fluid_script.h:593
const std::string fluid_solver_guiding
Definition: fluid_script.h:77
const std::string fluid_post_step
Definition: fluid_script.h:409
const std::string fluid_load_guiding
Definition: fluid_script.h:676
const std::string fluid_variables_guiding
Definition: fluid_script.h:195
const std::string fluid_variables_mesh
Definition: fluid_script.h:183
const std::string fluid_with_obstacle
Definition: fluid_script.h:211