Coverage for bim2sim/plugins/PluginOpenFOAM/bim2sim_openfoam/task/create_openfoam_meshing.py: 0%
201 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-01 10:24 +0000
1from collections import OrderedDict
3import stl
4from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut
5from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox
6from OCC.Core.StlAPI import StlAPI_Writer
7from OCC.Core.TopOpeBRep import TopOpeBRep_ShapeIntersector
8from OCC.Core.gp import gp_Pnt
9from stl import mesh
11from bim2sim.plugins.PluginOpenFOAM.bim2sim_openfoam.utils.openfoam_utils import \
12 OpenFOAMUtils as of_utils
13from bim2sim.tasks.base import ITask
14from bim2sim.utilities.common_functions import filter_elements
15from bim2sim.utilities.pyocc_tools import PyOCCTools
16from butterfly import (blockMeshDict, snappyHexMeshDict, foamfile,
17 surfaceFeatureExtractDict)
20class CreateOpenFOAMMeshing(ITask):
21 """This ITask initializes the OpenFOAM Meshing.
22 """
24 reads = ('openfoam_case', 'openfoam_elements')
25 touches = ('openfoam_case', 'openfoam_elements')
27 def __init__(self, playground):
28 super().__init__(playground)
30 def run(self, openfoam_case, openfoam_elements):
31 # create blockMesh based on surface geometry
32 self.create_blockMesh(openfoam_case)
33 # create snappyHexMesh based on surface types
34 self.create_snappyHexMesh(openfoam_case, openfoam_elements)
35 self.update_snappyHexMesh_heating(openfoam_case, openfoam_elements)
36 self.add_topoSetDict_for_heating(openfoam_case, openfoam_elements)
37 if self.playground.sim_settings.add_air_volume_evaluation:
38 self.modify_topoSet_for_evaluation(openfoam_case, openfoam_elements)
39 self.update_blockMeshDict_air(openfoam_case, openfoam_elements)
40 self.update_snappyHexMesh_air(openfoam_case, openfoam_elements)
41 self.update_snappyHexMesh_furniture(openfoam_case, openfoam_elements)
42 self.update_snappyHexMesh_people(openfoam_case, openfoam_elements)
43 self.update_snappyHexMesh_mesh_controls(openfoam_case)
44 if self.playground.sim_settings.mesh_feature_snapping:
45 self.create_surfaceFeatureExtract(openfoam_case)
47 return openfoam_case, openfoam_elements
49 def create_blockMesh(self, openfoam_case, resize_factor=0.1, shape=None):
50 mesh_size = self.playground.sim_settings.mesh_size
51 if not shape:
52 shape = openfoam_case.current_zone.space_shape
53 (min_pt, max_pt) = PyOCCTools.simple_bounding_box(shape)
54 scaled_min_pt = []
55 scaled_max_pt = []
56 len_xyz = []
57 for p1, p2 in zip(min_pt, max_pt):
58 p1 -= resize_factor
59 p2 += resize_factor
60 len_xyz.append(p2 - p1)
61 scaled_min_pt.append(p1)
62 scaled_max_pt.append(p2)
64 # calculate number of cells per xyz direction
65 n_div_xyz = (round(len_xyz[0] / mesh_size),
66 round(len_xyz[1] / mesh_size),
67 round(len_xyz[2] / mesh_size))
68 openfoam_case.blockMeshDict = blockMeshDict.BlockMeshDict.from_min_max(
69 scaled_min_pt, scaled_max_pt, n_div_xyz=n_div_xyz)
70 openfoam_case.blockMeshDict.save(openfoam_case.openfoam_dir)
72 @staticmethod
73 def create_surfaceFeatureExtract(openfoam_case):
74 """Initialize surfaceFeatureExtractDict"""
75 level = 0
76 features_string = "("
77 openfoam_case.surfaceFeatureExtract = (
78 surfaceFeatureExtractDict.SurfaceFeatureExtractDict())
79 for stl_file in openfoam_case.snappyHexMeshDict.stl_file_names:
80 openfoam_case.surfaceFeatureExtract.update_values(
81 surfaceFeatureExtractDict.SurfaceFeatureExtractDict.from_stl_file(
82 stl_file + '.stl').values)
83 features_string += (f'{{file "{stl_file}.eMesh";'
84 f'level {level};}}')
85 features_string += ")"
86 openfoam_case.snappyHexMeshDict.features = features_string
87 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
88 openfoam_case.surfaceFeatureExtract.save(openfoam_case.openfoam_dir)
90 def create_snappyHexMesh(self, openfoam_case, openfoam_elements, ):
91 stl_name = "space_" + openfoam_case.current_zone.guid
92 region_names = []
93 regions = {}
94 mesh_objects = mesh.Mesh.from_multi_file(
95 openfoam_case.openfoam_triSurface_dir /
96 str(stl_name + '.stl'),
97 mode=stl.Mode.ASCII)
98 for obj in mesh_objects:
99 region_names.append(str(obj.name, encoding='utf-8'))
100 for name in region_names:
101 regions.update({name: {'name': name}})
102 openfoam_case.snappyHexMeshDict = snappyHexMeshDict.SnappyHexMeshDict()
103 openfoam_case.snappyHexMeshDict.add_stl_geometry(
104 str("space_" + openfoam_case.current_zone.guid), regions)
106 # self.snappyHexMesh.values['geometry']['regions'].update(regions)
107 location_in_mesh = (
108 str('(' +
109 str(
110 openfoam_case.current_zone.space_center.Coord()[
111 0]) + ' ' +
112 str(
113 openfoam_case.current_zone.space_center.Coord()[
114 1]) + ' ' +
115 str(
116 openfoam_case.current_zone.space_center.Coord()[
117 2]) + ')'
118 ))
119 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
120 'locationInMesh'] = location_in_mesh
121 self.set_refinementSurfaces(openfoam_case, openfoam_elements,
122 region_names)
123 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
124 'refinementSurfaces'].update(openfoam_case.refinementSurfaces)
126 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
128 @staticmethod
129 def set_refinementSurfaces(openfoam_case, openfoam_elements,
130 region_names,
131 default_refinement_level=[1, 2]):
132 (stl_bounds, heaters, air_terminals,
133 furniture, people) = of_utils.split_openfoam_elements(
134 openfoam_elements)
135 stl_name = "space_" + openfoam_case.current_zone.guid
137 refinementSurface_regions = {}
139 for obj in stl_bounds:
140 refinementSurface_regions.update(
141 {obj.solid_name:
142 {
143 'level': '({} {})'.format(
144 int(obj.refinement_level[0]),
145 int(obj.refinement_level[1])),
146 'patchInfo':
147 {'type': obj.patch_info_type}}})
149 openfoam_case.refinementSurfaces = {stl_name:
150 {
151 'level': '({} {})'.format(
152 int(default_refinement_level[0]),
153 int(default_refinement_level[
154 1])),
155 'regions': refinementSurface_regions
156 }}
158 # todo: different approach for air terminals (partially patchInfo
159 # wall, partially inlet / outlet.
161 def update_snappyHexMesh_heating(self, openfoam_case, openfoam_elements):
162 heaters = filter_elements(openfoam_elements, 'Heater')
163 for heater in heaters:
164 openfoam_case.snappyHexMeshDict.values['geometry'].update(
165 {
166 heater.heater_surface.stl_name:
167 {
168 'type': 'triSurfaceMesh',
169 'name': heater.heater_surface.solid_name,
170 'regions':
171 {heater.heater_surface.solid_name:
172 {
173 'name': heater.heater_surface.solid_name
174 }
175 }
176 },
177 heater.porous_media.solid_name:
178 {
179 'type': 'searchableBox',
180 'min': f"({heater.porous_media.bbox_min_max[0][0]} "
181 f"{heater.porous_media.bbox_min_max[0][1]} "
182 f"{heater.porous_media.bbox_min_max[0][2]})",
183 'max': f"({heater.porous_media.bbox_min_max[1][0]} "
184 f"{heater.porous_media.bbox_min_max[1][1]} "
185 f"{heater.porous_media.bbox_min_max[1][2]})",
186 },
187 }
188 )
189 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
190 'refinementSurfaces'].update(
191 {
192 heater.heater_surface.solid_name:
193 {
194 'level': '(1 2)',
195 'regions':
196 {
197 heater.heater_surface.solid_name:
198 {
199 'level':
200 f"({heater.heater_surface.refinement_level[0]} "
201 f"{heater.heater_surface.refinement_level[1]})",
202 'patchInfo':
203 {
204 'type':
205 heater.heater_surface.patch_info_type
206 }
207 }
208 }
209 }
210 },
211 )
212 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
213 'refinementRegions'].update(
214 {
215 heater.porous_media.solid_name:
216 {
217 'mode': 'inside',
218 'levels':
219 f'(('
220 f'0 '
221 f'{heater.porous_media.refinement_level[1]}))'
222 },
223 heater.heater_surface.solid_name:
224 {'mode': 'distance',
225 'levels': f"((0.05 {heater.heater_surface.refinement_level[1]}) "
226 f"(0.15 {heater.heater_surface.refinement_level[1] - 1}))"
227 },
228 }
229 )
230 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
232 @staticmethod
233 def add_topoSetDict_for_heating(openfoam_case, openfoam_elements):
234 heaters = filter_elements(openfoam_elements, 'Heater')
235 openfoam_case.topoSetDict = foamfile.FoamFile(
236 name='topoSetDict', cls='dictionary', location='system',
237 default_values=OrderedDict()
238 )
239 openfoam_case.topoSetDict.values.update(
240 {'actions (': '//'})
241 for h in heaters:
242 openfoam_case.topoSetDict.values.update(
243 {f'//{h.solid_name}': {
244 'name': h.porous_media.solid_name,
245 'action': 'new',
246 'type': 'cellSet',
247 'source': 'surfaceToCell',
248 'sourceInfo':
249 {
250 'file': fr'"constant/triSurface/'
251 fr'{h.porous_media.stl_name}"',
252 'useSurfaceOrientation': 'true',
253 'outsidePoints': '((0 0 0))',
254 'includeCut': 'false',
255 'includeInside': 'true',
256 'includeOutside': 'false',
257 'nearDistance': '-1',
258 'curvature': '0',
259 }
260 },
261 }
262 )
263 openfoam_case.topoSetDict.values.update({');': '//'}) # required to
264 # close
265 # the
266 # round bracket. Replace by better option if you find any.
268 openfoam_case.topoSetDict.save(openfoam_case.openfoam_dir)
270 @staticmethod
271 def modify_topoSet_for_evaluation(openfoam_case, openfoam_elements):
272 furniture = filter_elements(openfoam_elements, 'Furniture')
273 people = filter_elements(openfoam_elements, 'People')
274 heaters = filter_elements(openfoam_elements, 'Heater')
275 airterminals = filter_elements(openfoam_elements, 'AirTerminal')
276 cut_shapes = []
277 for furn in furniture:
278 cut_shapes.append(
279 PyOCCTools.unify_shape(PyOCCTools.make_solid_from_shape(
280 PyOCCTools, PyOCCTools.simple_bounding_box_shape([
281 furn.tri_geom]))))
282 for heater in heaters:
283 cut_shapes.append(
284 PyOCCTools.unify_shape(PyOCCTools.make_solid_from_shape(
285 PyOCCTools,
286 PyOCCTools.scale_shape(heater.porous_media.tri_geom, 1.1))))
287 for person in people:
288 cut_shapes.append(
289 PyOCCTools.unify_shape(PyOCCTools.make_solid_from_shape(
290 PyOCCTools, PyOCCTools.simple_bounding_box_shape(
291 [person.scaled_surface]))))
292 for air in airterminals:
293 cut_shapes.append(
294 PyOCCTools.unify_shape(PyOCCTools.make_solid_from_shape(
295 PyOCCTools, PyOCCTools.scale_shape(air.bbox_min_max_shape,
296 1.1))))
297 space_solid_shrinked = (PyOCCTools.make_solid_from_shape(
298 PyOCCTools, PyOCCTools.scale_shape(
299 openfoam_case.current_zone.space_shape, 0.9)))
300 shape = space_solid_shrinked
301 for cut_shape in cut_shapes:
302 ins = TopOpeBRep_ShapeIntersector()
303 ins.InitIntersection(cut_shape, shape)
304 if ins.MoreIntersection():
305 shape = BRepAlgoAPI_Cut(shape, cut_shape).Shape()
306 shape = PyOCCTools.make_solid_from_shape(PyOCCTools, shape)
307 tri_eval_shape = PyOCCTools.triangulate_bound_shape(shape)
308 stl_writer = StlAPI_Writer()
309 stl_writer.SetASCIIMode(True)
310 stl_writer.Write(tri_eval_shape,
311 str(openfoam_case.openfoam_triSurface_dir /
312 'evaluate_air_volume.stl'))
313 openfoam_case.topoSetDict.values.pop(');') # required to
315 for person in people:
316 tri_eval_shape = PyOCCTools.triangulate_bound_shape(
317 person.scaled_surface)
318 stl_writer = StlAPI_Writer()
319 stl_writer.SetASCIIMode(True)
320 stl_writer.Write(tri_eval_shape,
321 str(openfoam_case.openfoam_triSurface_dir /
322 f"evaluate_{person.solid_name}.stl"))
323 openfoam_case.topoSetDict.values.update(
324 {f'//evaluate_{person.solid_name}': {
325 'name': f"evaluate_{person.solid_name}",
326 'action': 'new',
327 'type': 'cellSet',
328 'source': 'surfaceToCell',
329 'sourceInfo':
330 {
331 'file': fr'"constant/triSurface/'
332 fr'evaluate_{person.solid_name}.stl"',
333 'useSurfaceOrientation': 'false',
334 'outsidePoints':
335 f'(('
336 f'{openfoam_case.current_zone.space_center.X()} '
337 f'{openfoam_case.current_zone.space_center.Y()} '
338 f'{openfoam_case.current_zone.space_center.Z()}'
339 f' ))',
340 'includeCut': 'true',
341 'includeInside': 'false',
342 'includeOutside': 'false',
343 'nearDistance': '-1',
344 'curvature': '0',
345 }
346 },
347 }
348 )
349 openfoam_case.topoSetDict.values.update(
350 {f'//evaluate air volume': {
351 'name': 'evaluate_air_volume',
352 'action': 'new',
353 'type': 'cellSet',
354 'source': 'surfaceToCell',
355 'sourceInfo':
356 {
357 'file': fr'"constant/triSurface/'
358 fr'evaluate_air_volume.stl"',
359 'useSurfaceOrientation': 'true',
360 'outsidePoints': '((0 0 0))',
361 'includeCut': 'false',
362 'includeInside': 'true',
363 'includeOutside': 'false',
364 'nearDistance': '-1',
365 'curvature': '0',
366 }
367 },
368 }
369 )
370 openfoam_case.topoSetDict.values.update({');': '//'}) # required to
371 # close
372 # the
373 # round bracket. Replace by better option if you find any.
375 openfoam_case.topoSetDict.save(openfoam_case.openfoam_dir)
377 # update controlDict for evaluation
378 for person in people:
379 for operation in ['average', 'min', 'max']:
380 evaluate_dict = {
381 f"{person.solid_name}_{operation}": {
382 'type': 'volFieldValue',
383 'libs': '(fieldFunctionObjects)',
384 'log': 'true',
385 'writeFields': 'false',
386 'enabled': 'true',
387 'writeControl': 'timeStep',
388 'writeInterval': '1',
389 'regionType': 'cellZone',
390 'name': f"evaluate_{person.solid_name}",
391 'operation': operation,
392 'fields': '( T U PMV PPD )'
393 }}
394 openfoam_case.controlDict.values['functions'].update(
395 evaluate_dict)
396 for operation in ['average', 'min', 'max']:
397 evaluate_dict = {
398 f"evaluate_air_volume_{operation}": {
399 'type': 'volFieldValue',
400 'libs': '(fieldFunctionObjects)',
401 'log': 'true',
402 'writeFields': 'false',
403 'enabled': 'true',
404 'writeControl': 'timeStep',
405 'writeInterval': '1',
406 'regionType': 'cellZone',
407 'name': f"evaluate_air_volume",
408 'operation': operation,
409 'fields': '( T U PMV PPD )'
410 }}
411 openfoam_case.controlDict.values['functions'].update(
412 evaluate_dict)
414 openfoam_case.controlDict.save(openfoam_case.openfoam_dir)
416 def update_blockMeshDict_air(self, openfoam_case, openfoam_elements):
417 air_terminals = filter_elements(openfoam_elements, 'AirTerminal')
418 new_min_max = PyOCCTools.simple_bounding_box([
419 openfoam_case.current_zone.space_shape,
420 *[inlet_outlet.bbox_min_max_shape
421 for inlet_outlet in
422 air_terminals]])
423 new_blockmesh_box = BRepPrimAPI_MakeBox(
424 gp_Pnt(*new_min_max[0]),
425 gp_Pnt(*new_min_max[1])).Shape()
427 self.create_blockMesh(openfoam_case, shape=new_blockmesh_box)
429 def update_snappyHexMesh_air(self, openfoam_case, openfoam_elements):
430 air_terminals = filter_elements(openfoam_elements, 'AirTerminal')
431 for air_terminal in air_terminals:
432 for name in [air_terminal.diffuser.solid_name,
433 air_terminal.source_sink.solid_name,
434 air_terminal.box.solid_name]:
435 if not name:
436 continue
437 openfoam_case.snappyHexMeshDict.values['geometry'].update(
438 {
439 name + '.stl':
440 {
441 'type': 'triSurfaceMesh',
442 'name': name,
443 'regions':
444 {name:
445 {
446 'name': name
447 }
448 }
449 }
450 }
451 )
452 openfoam_case.snappyHexMeshDict.values['geometry'].update({
453 air_terminal.air_type + '_refinement_small':
454 {
455 'type': 'searchableBox',
456 'min': f"({air_terminal.refinement_zone_small[0][0]} "
457 f"{air_terminal.refinement_zone_small[0][1]} "
458 f"{air_terminal.refinement_zone_small[0][2]})",
459 'max': f"({air_terminal.refinement_zone_small[1][0]} "
460 f"{air_terminal.refinement_zone_small[1][1]} "
461 f"{air_terminal.refinement_zone_small[1][2]})",
462 },
463 air_terminal.air_type + '_refinement_large':
464 {
465 'type': 'searchableBox',
466 'min': f"({air_terminal.refinement_zone_large[0][0]} "
467 f"{air_terminal.refinement_zone_large[0][1]} "
468 f"{air_terminal.refinement_zone_large[0][2]})",
469 'max': f"({air_terminal.refinement_zone_large[1][0]} "
470 f"{air_terminal.refinement_zone_large[1][1]} "
471 f"{air_terminal.refinement_zone_large[1][2]})",
472 }
473 }
474 )
475 if air_terminal.diffuser.solid_name:
476 openfoam_case.snappyHexMeshDict.values[
477 'castellatedMeshControls'][
478 'refinementSurfaces'].update(
479 {air_terminal.diffuser.solid_name:
480 {'level':
481 f"({air_terminal.diffuser.refinement_level[0]}"
482 f" {air_terminal.diffuser.refinement_level[1]})",
483 'regions':
484 {air_terminal.diffuser.solid_name:
485 {'level':
486 f"({air_terminal.diffuser.refinement_level[0]}"
487 f" {air_terminal.diffuser.refinement_level[1]})",
488 'patchInfo': {
489 'type':
490 air_terminal.diffuser.patch_info_type}}}}
491 },
492 )
493 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
494 'refinementSurfaces'].update(
495 {air_terminal.source_sink.solid_name:
496 {'level': '(1 2)', 'regions':
497 {air_terminal.source_sink.solid_name: {
498 'level': f"({air_terminal.source_sink.refinement_level[0]}"
499 f" {air_terminal.source_sink.refinement_level[1]})",
500 'patchInfo': {
501 'type': air_terminal.air_type}}}}
502 },
503 )
504 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
505 'refinementSurfaces'].update(
506 {air_terminal.box.solid_name:
507 {'level': '(1 2)',
508 'regions': {
509 air_terminal.box.solid_name: {
510 'level': f"({air_terminal.box.refinement_level[0]}"
511 f" {air_terminal.box.refinement_level[1]})",
512 'patchInfo': {
513 'type':
514 air_terminal.box.patch_info_type}}}}
515 },
516 )
517 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
518 'refinementRegions'].update(
519 {air_terminal.source_sink.solid_name:
520 {'mode': 'distance',
521 'levels': f"((0.03 "
522 f"{air_terminal.source_sink.refinement_level[1]}) "
523 f"(0.08 "
524 f"{air_terminal.source_sink.refinement_level[1] - 1}))"},
525 air_terminal.diffuser.solid_name:
526 {'mode': 'distance',
527 'levels': f"((0.03 "
528 f"{air_terminal.diffuser.refinement_level[1]}) "
529 f"(0.08 "
530 f"{air_terminal.diffuser.refinement_level[1] - 1}))"},
531 }
532 )
533 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
535 def update_snappyHexMesh_furniture(self, openfoam_case, openfoam_elements):
536 furnitures = filter_elements(openfoam_elements, 'Furniture')
537 for furniture in furnitures:
538 openfoam_case.snappyHexMeshDict.values['geometry'].update(
539 {
540 furniture.stl_name:
541 {
542 'type': 'triSurfaceMesh',
543 'name': furniture.solid_name,
544 'regions':
545 {furniture.solid_name:
546 {
547 'name': furniture.solid_name
548 }
549 }
550 },
551 # furniture.solid_name + '_refinement_small':
552 # {
553 # 'type': 'searchableBox',
554 # 'min': f"({furniture.refinement_zone_small[0][0]} "
555 # f"{furniture.refinement_zone_small[0][1]} "
556 # f"{furniture.refinement_zone_small[0][2]})",
557 # 'max': f"({furniture.refinement_zone_small[1][0]} "
558 # f"{furniture.refinement_zone_small[1][1]} "
559 # f"{furniture.refinement_zone_small[1][2]})",
560 # },
561 # furniture.solid_name + '_refinement_large':
562 # {
563 # 'type': 'searchableBox',
564 # 'min': f"({furniture.refinement_zone_large[0][0]} "
565 # f"{furniture.refinement_zone_large[0][1]} "
566 # f"{furniture.refinement_zone_large[0][2]})",
567 # 'max': f"({furniture.refinement_zone_large[1][0]} "
568 # f"{furniture.refinement_zone_large[1][1]} "
569 # f"{furniture.refinement_zone_large[1][2]})",
570 # }
571 }
572 )
573 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
574 'refinementSurfaces'].update(
575 {
576 furniture.solid_name:
577 {
578 'level': '(1 2)',
579 'regions':
580 {
581 furniture.solid_name:
582 {
583 'level':
584 f"({furniture.refinement_level[0]} "
585 f"{furniture.refinement_level[1]})",
586 'patchInfo':
587 {
588 'type':
589 furniture.patch_info_type
590 }
591 }
592 }
593 }
594 },
595 )
596 openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
597 'refinementRegions'].update(
598 {
599 furniture.solid_name:
600 {'mode': 'distance',
601 'levels': f"((0.03 {furniture.refinement_level[1]})"
602 f"(0.06 {furniture.refinement_level[1] - 1}))"
603 }
604 },
605 )
606 # openfoam_case.snappyHexMeshDict.values['castellatedMeshControls'][
607 # 'refinementRegions'].update(
608 # {
609 # furniture.solid_name + '_refinement_small':
610 # {
611 # 'mode': 'inside',
612 # 'levels':
613 # f'(({furniture.refinement_zone_level_small[0]} '
614 # f'{furniture.refinement_zone_level_small[1]}))',
615 # 'regions':
616 # {furniture.stl_name:
617 # {'mode': 'outside'}},
618 # }
619 # }
620 # )
621 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
623 def update_snappyHexMesh_people(self, openfoam_case, openfoam_elements):
624 people = filter_elements(openfoam_elements, 'People')
625 for person in people:
626 for body_part in person.body_parts_dict.values():
627 openfoam_case.snappyHexMeshDict.values['geometry'].update(
628 {
629 body_part.stl_name:
630 {
631 'type': 'triSurfaceMesh',
632 'name': body_part.solid_name,
633 'regions':
634 {body_part.solid_name:
635 {
636 'name': body_part.solid_name
637 }
638 }
639 },
640 })
642 openfoam_case.snappyHexMeshDict.values[
643 'castellatedMeshControls'][
644 'refinementSurfaces'].update(
645 {
646 body_part.solid_name:
647 {
648 'level': '(1 2)',
649 'regions':
650 {
651 body_part.solid_name:
652 {
653 'level':
654 f"("
655 f"{person.refinement_level[0]} "
656 f"{person.refinement_level[1]})",
657 'patchInfo':
658 {
659 'type':
660 body_part.patch_info_type
661 }
662 }
663 }
664 }
665 },
666 )
667 openfoam_case.snappyHexMeshDict.values[
668 'castellatedMeshControls'][
669 'refinementRegions'].update(
670 {
671 body_part.solid_name:
672 {'mode': 'distance',
673 'levels': f"((0.05 {person.refinement_level[1]})"
674 f"(0.1 {person.refinement_level[1] - 1}))"
675 },
676 }
677 )
679 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)
681 def update_snappyHexMesh_mesh_controls(self, openfoam_case):
682 openfoam_case.snappyHexMeshDict.values[
683 'castellatedMeshControls'].update({'maxLocalCells': 1000000})
684 openfoam_case.snappyHexMeshDict.values[
685 'castellatedMeshControls'].update({
686 'maxGlobalCells':
687 self.playground.sim_settings.mesh_max_global_cells})
688 openfoam_case.snappyHexMeshDict.values[
689 'meshQualityControls'].update({'maxBoundarySkewness': 20})
690 openfoam_case.snappyHexMeshDict.values[
691 'meshQualityControls'].update({'maxInternalSkewness': 3})
692 openfoam_case.snappyHexMeshDict.values[
693 'castellatedMeshControls'].update({'resolveFeatureAngle': 60})
694 openfoam_case.snappyHexMeshDict.values[
695 'castellatedMeshControls'].update({'minRefinementCells': 5})
696 openfoam_case.snappyHexMeshDict.values[
697 'castellatedMeshControls'].update({'gapMode': 'mixed'})
698 openfoam_case.snappyHexMeshDict.values[
699 'castellatedMeshControls'].update({'gapLevel': '(3 0 7)'})
700 openfoam_case.snappyHexMeshDict.values[
701 'snapControls'].update({'nSolveIter': 50})
702 openfoam_case.snappyHexMeshDict.values[
703 'snapControls'].update({'nRelaxIter': 15})
704 openfoam_case.snappyHexMeshDict.values[
705 'snapControls'].update({'tolerance': 5.0})
706 openfoam_case.snappyHexMeshDict.values[
707 'snapControls'].update({'multiRegionFeatureSnap': 'false'})
708 openfoam_case.snappyHexMeshDict.values[
709 'snapControls'].update({'implicitFeatureSnap': 'true'})
710 openfoam_case.snappyHexMeshDict.values[
711 'snapControls'].update({'explicitFeatureSnap': 'false'})
712 if self.playground.sim_settings.mesh_feature_snapping:
713 openfoam_case.snappyHexMeshDict.values[
714 'snapControls'].update({'explicitFeatureSnap': 'true'})
715 openfoam_case.snappyHexMeshDict.values[
716 'snapControls'].update({'implicitFeatureSnap': 'false'})
717 openfoam_case.snappyHexMeshDict.values[
718 'snapControls'].update(
719 {'extractFeaturesRefineLevel': 'true'})
721 openfoam_case.snappyHexMeshDict.save(openfoam_case.openfoam_dir)