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