diff --git a/CfdOF/CfdAnalysis.py b/CfdOF/CfdAnalysis.py index 469248c2..65c6f155 100644 --- a/CfdOF/CfdAnalysis.py +++ b/CfdOF/CfdAnalysis.py @@ -238,13 +238,18 @@ def makePartTransparent(self, vobj): doc = FreeCAD.getDocument(docName) for obj in doc.Objects: if obj.isDerivedFrom("Part::Feature") and not ("CfdFluidBoundary" in obj.Name): - vobj2 = FreeCAD.getDocument(docName).getObject(obj.Name).ViewObject - if hasattr(vobj2, 'Transparency'): - vobj2.Transparency = 70 - if obj.isDerivedFrom("PartDesign::Feature"): - doc.getObject(obj.Name).ViewObject.LineWidth = 1 - doc.getObject(obj.Name).ViewObject.LineColor = (0.5, 0.5, 0.5) - doc.getObject(obj.Name).ViewObject.PointColor = (0.5, 0.5, 0.5) + try: + vobj2 = FreeCAD.getDocument(docName).getObject(obj.Name).ViewObject + if hasattr(vobj2, 'Transparency'): + vobj2.Transparency = 70 + if obj.isDerivedFrom("PartDesign::Feature"): + vobj2.LineWidth = 1 + vobj2.LineColor = (0.5, 0.5, 0.5) + vobj2.PointColor = (0.5, 0.5, 0.5) + except Exception: + # Some FreeCAD 1.1 view providers can lack optional GUI extensions. + # Transparency is cosmetic, so avoid interrupting macro generation. + pass def __getstate__(self): return None diff --git a/CfdOF/CfdTools.py b/CfdOF/CfdTools.py index c3fa496d..751781e9 100644 --- a/CfdOF/CfdTools.py +++ b/CfdOF/CfdTools.py @@ -166,7 +166,7 @@ def getInitialisationZoneObjects(analysis_object): def getZoneObjects(analysis_object): from CfdOF.Solve.CfdZone import CfdZone - return [i for i in analysis_object.Group if isinstance(i.Proxy, CfdZone)] + return [i for i in analysis_object.Group if isinstance(getattr(i, 'Proxy', None), CfdZone)] def getInitialConditions(analysis_object): @@ -174,7 +174,16 @@ def getInitialConditions(analysis_object): def getMaterials(analysis_object): - return [i for i in analysis_object.Group if i.isDerivedFrom('App::MaterialObjectPython')] + from CfdOF.Solve.CfdSolidMaterial import CfdSolidMaterial + return [i for i in analysis_object.Group + if i.isDerivedFrom('App::MaterialObjectPython') + and not isinstance(getattr(i, 'Proxy', None), CfdSolidMaterial)] + + +def getSolidMaterials(analysis_object): + from CfdOF.Solve.CfdSolidMaterial import CfdSolidMaterial + return [i for i in analysis_object.Group + if isinstance(getattr(i, 'Proxy', None), CfdSolidMaterial)] def getSolver(analysis_object): diff --git a/CfdOF/Mesh/CfdMeshTools.py b/CfdOF/Mesh/CfdMeshTools.py index aac41f8c..03ff098f 100644 --- a/CfdOF/Mesh/CfdMeshTools.py +++ b/CfdOF/Mesh/CfdMeshTools.py @@ -35,6 +35,97 @@ import Part + +def _get_interior_point(solid): + """Return a point guaranteed to be inside the solid material. + + CenterOfMass can lie outside the material for non-convex solids (e.g. an air + shell surrounding an object, or a heatsink whose centroid falls between fins). + Falls back to face centroids (always on the surface, which passes isInside + with allowFace=True), then to a 3-D bounding-box grid. + """ + tol = 1e-3 + centroid = solid.CenterOfMass + if solid.isInside(centroid, tol, True): + return centroid + # Face centroids are guaranteed to lie on the solid surface and always pass + # isInside(allowFace=True). This handles thin-feature solids (e.g. heatsink + # fins) where the centroid and a coarse grid both land outside the material. + for face in solid.Faces: + pt = face.CenterOfMass + if solid.isInside(pt, tol, True): + return pt + bb = solid.BoundBox + for xi in (0.1, 0.3, 0.5, 0.7, 0.9): + for yi in (0.1, 0.3, 0.5, 0.7, 0.9): + for zi in (0.1, 0.3, 0.5, 0.7, 0.9): + pt = FreeCAD.Vector( + bb.XMin + xi * bb.XLength, + bb.YMin + yi * bb.YLength, + bb.ZMin + zi * bb.ZLength, + ) + if solid.isInside(pt, tol, True): + return pt + return centroid + + +def _getCompoundLinks(part_obj): + """Return the list of child body objects from a compound or BooleanFragments shape object.""" + if hasattr(part_obj, 'Links'): # Part::Compound + return list(part_obj.Links) + if hasattr(part_obj, 'Objects'): # Part::BooleanFragments + return list(part_obj.Objects) + return [part_obj] + + +def _get_shape_ref_solids(shape_ref): + """Return solid shapes from a FreeCAD LinkSub reference. + + Solid materials can reference selected sub-solids in a compound/body. Use + those sub-elements when present instead of broadening the selection to the + whole source object, otherwise unrelated volumes can be assigned to the + solid region after BooleanFragments. + """ + obj = shape_ref[0] + sub_names = shape_ref[1] if len(shape_ref) > 1 else [] + solids = [] + for sub_name in sub_names: + try: + sub_shape = obj.Shape.getElement(sub_name) + except (Part.OCCError, ValueError, IndexError, AttributeError): + continue + solids.extend(sub_shape.Solids if sub_shape.Solids else [sub_shape]) + if solids: + return solids + return list(obj.Shape.Solids if obj.Shape.Solids else [obj.Shape]) + + +def _solid_belongs_to_reference(result_solid, reference_solids): + """Return True if a BooleanFragments result solid belongs to a source solid. + + The normal test is containment of an interior point, but OCC can occasionally + report false for solids that came from BooleanFragments. In that case, fall + back to the boolean common volume, which is slower but much more reliable for + assigning CHT physical volumes. + """ + interior = _get_interior_point(result_solid) + for ref_solid in reference_solids: + if ref_solid.isInside(interior, 1e-3, False): + return True + + result_volume = abs(result_solid.Volume) + if result_volume <= 0: + return False + for ref_solid in reference_solids: + try: + common_volume = abs(result_solid.common(ref_solid).Volume) + except Part.OCCError: + continue + if common_volume / result_volume > 0.5: + return True + return False + + class CfdMeshTools: def __init__(self, cart_mesh_obj): self.mesh_obj = cart_mesh_obj @@ -689,6 +780,52 @@ def writeMeshCase(self): self.gmsh_settings['ClMin'] = self.clmin sols = (''.join((str(n+1) + ', ') for n in range(len(self.mesh_obj.Part.Shape.Solids)))).rstrip(', ') self.gmsh_settings['Solids'] = sols + + # Build per-region volume map for CHT (chtMultiRegionSimpleFoam/chtMultiRegionFoam) + solid_material_objs = CfdTools.getSolidMaterials(self.analysis) + if solid_material_objs: + region_volume_map = {} + mesh_part = self.mesh_obj.Part + links = _getCompoundLinks(mesh_part) + result_solids = mesh_part.Shape.Solids + solid_vol_indices_all = set() + # For each CfdSolidMaterial, find the result solid volumes that belong + # to it by geometric containment/overlap in its referenced shapes. Each + # material is handled independently so that multiple solid regions + # receive distinct volume index sets rather than the same combined set. + for solid_obj in solid_material_objs: + rname = getattr(solid_obj, 'RegionName', '') or solid_obj.Label + body_names = set() + body_solids = {} + for ref in solid_obj.ShapeRefs: + body_names.add(ref[0].Name) + body_solids.setdefault(ref[0].Name, []).extend(_get_shape_ref_solids(ref)) + for lnk in links: + if lnk.Name in body_names and lnk.Name not in body_solids: + body_solids[lnk.Name] = list(lnk.Shape.Solids if lnk.Shape.Solids else [lnk.Shape]) + flat_solids = [solid for solids in body_solids.values() for solid in solids] + vol_indices = [i for i, s in enumerate(result_solids, start=1) + if _solid_belongs_to_reference(s, flat_solids)] + solid_vol_indices_all.update(vol_indices) + if vol_indices: + region_volume_map[rname] = ', '.join(str(v) for v in vol_indices) + # Fluid volumes are those not claimed by any solid material + fluid_vol_indices = [i for i in range(1, len(result_solids) + 1) + if i not in solid_vol_indices_all] + mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) + internal_zones = [o for o in mr_objs if o.Internal] + if internal_zones: + fluid_rname = internal_zones[0].Label + else: + fluid_mats = CfdTools.getMaterials(self.analysis) + fluid_rname = fluid_mats[0].Label if fluid_mats else 'fluid' + if fluid_vol_indices: + region_volume_map[fluid_rname] = ', '.join(str(v) for v in fluid_vol_indices) + self.gmsh_settings['IsMultiRegion'] = True + self.gmsh_settings['RegionVolumeMap'] = region_volume_map + else: + self.gmsh_settings['IsMultiRegion'] = False + self.gmsh_settings['BoundaryFaceMap'] = {} for k in range(len(self.patch_faces)): for l in range(len(self.patch_faces[k])): diff --git a/CfdOF/Solve/CfdCaseWriterFoam.py b/CfdOF/Solve/CfdCaseWriterFoam.py index 0987b78f..b891ab62 100644 --- a/CfdOF/Solve/CfdCaseWriterFoam.py +++ b/CfdOF/Solve/CfdCaseWriterFoam.py @@ -33,6 +33,31 @@ from CfdOF.CfdTools import cfdMessage from CfdOF.Mesh import CfdMeshTools from CfdOF.Mesh import CfdDynamicMeshRefinement +from CfdOF.Solve.CfdSolidMaterial import CfdSolidMaterial + + + +def _getCompoundLinks(part_obj): + """Return the list of child body objects from a compound or BooleanFragments shape object.""" + if hasattr(part_obj, 'Links'): # Part::Compound + return list(part_obj.Links) + if hasattr(part_obj, 'Objects'): # Part::BooleanFragments + return list(part_obj.Objects) + return [part_obj] + + +def _face_overlaps_any_shape(face, shapes): + face_area = abs(getattr(face, 'Area', 0.0)) + if face_area <= 0: + return False + for shape in shapes: + try: + if abs(face.common(shape).Area) / face_area > 0.5: + return True + except Exception: + continue + return False + class CfdCaseWriterFoam: def __init__(self, analysis_obj): @@ -51,7 +76,8 @@ def __init__(self, analysis_obj): if not self.mesh_obj: raise RuntimeError("No mesh object was found in analysis " + analysis_obj.Label) self.material_objs = CfdTools.getMaterials(analysis_obj) - if not self.material_objs: + self.solid_material_objs = CfdTools.getSolidMaterials(analysis_obj) + if not self.material_objs and not self.solid_material_objs: raise RuntimeError("No material properties were found in analysis " + analysis_obj.Label) self.bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj) self.initial_conditions = CfdTools.getInitialConditions(analysis_obj) @@ -112,6 +138,14 @@ def writeCase(self): self.settings = { 'physics': phys_settings, 'fluidProperties': [], # Order is important, so use a list + 'solidProperties': [], # Solid material properties for multi-region CHT + 'multiRegionEnabled': False, + 'multiRegionFluidNames': [], + 'multiRegionSolidNames': [], + 'multiRegionFluidNamesDict': {}, + 'multiRegionSolidNamesDict': {}, + 'multiRegionFluidBoundaries': {}, + 'multiRegionSolidBoundaries': {}, 'initialValues': CfdTools.propsToDict(self.initial_conditions), 'boundaries': dict((b.Label, CfdTools.propsToDict(b)) for b in self.bc_group), 'reportingFunctions': dict((fo.Label, CfdTools.propsToDict(fo)) for fo in self.reporting_functions), @@ -182,6 +216,12 @@ def writeCase(self): self.processPorousZoneProperties() self.processInitialisationZoneProperties() + if self.settings['multiRegionEnabled']: + cfdMessage('Multi-region CHT case detected\n') + self._checkChtGeometryConformality() + self.exportChtRegionStlSurfaces() + self.processMultiRegionBoundaries() + if self.mean_velocity_force_cellzone_objs: cfdMessage('Mean velocity force cell zone(s) present\n') self.exportMeanVelocityForceCellZoneStlSurfaces() @@ -208,6 +248,10 @@ def writeCase(self): TemplateBuilder(self.case_folder, self.template_path, self.settings) + # For CHT: rename template fluid/solid subdirs to actual region names so the + # solver finds thermophysicalProperties, fvSchemes, etc. in the right places. + self._renameChtRegionDirs() + # Update Allrun permission - will fail silently on Windows file_name = os.path.join(self.case_folder, "Allrun") import stat @@ -220,6 +264,28 @@ def writeCase(self): return True + def _renameChtRegionDirs(self): + """Rename constant/fluid, 0/fluid, system/fluid template subdirs to actual fluid region name. + + Solid region files are written directly to per-region paths by the template's + filename-loop mechanism, so no renaming is needed for solids. + """ + import shutil + fluid_names = self.settings.get('multiRegionFluidNames', []) + if not fluid_names: + return + for top in ('constant', '0', 'system'): + src = os.path.join(self.case_folder, top, 'fluid') + dst = os.path.join(self.case_folder, top, fluid_names[0]) + if src == dst or not os.path.isdir(src): + continue + if os.path.isdir(dst): + for fname in os.listdir(src): + shutil.copy2(os.path.join(src, fname), os.path.join(dst, fname)) + shutil.rmtree(src) + else: + os.rename(src, dst) + def getSolverName(self): """ Solver name is selected based on selected physics. This should only be extended as additional physics are @@ -262,6 +328,14 @@ def getSolverName(self): raise RuntimeError("Only isothermal analysis is currently supported for free surface flow simulation.") else: raise RuntimeError("Only transient analysis is supported for free surface flow simulation.") + elif self.physics_model.Phase == 'MultiRegion': + if self.physics_model.Flow == 'NonIsothermal': + if self.physics_model.Time == 'Steady': + solver = 'chtMultiRegionSimpleFoam' + else: + solver = 'chtMultiRegionFoam' + else: + raise RuntimeError("Only NonIsothermal flow is supported for MultiRegion (CHT) simulation.") else: raise RuntimeError(self.physics_model.Phase + " phase model currently not supported.") @@ -316,9 +390,47 @@ def processFluidProperties(self): # self.material_obj currently stores everything as a string # Convert to (mostly) SI numbers for OpenFOAM settings = self.settings + solver_name = settings['solver']['SolverName'] + is_multiregion = solver_name in ['chtMultiRegionSimpleFoam', 'chtMultiRegionFoam'] + + # Process dedicated CfdSolidMaterial objects (new path) + for solid_obj in self.solid_material_objs: + mp = solid_obj.Material.copy() + mp['Name'] = solid_obj.Label + region_name = getattr(solid_obj, 'RegionName', '') or solid_obj.Label + if 'ThermalConductivity' in mp: + mp['ThermalConductivity'] = Units.Quantity(mp['ThermalConductivity']).getValueAs("W/m/K").Value + if 'Density' in mp: + mp['Density'] = Units.Quantity(mp['Density']).getValueAs("kg/m^3").Value + if 'SpecificHeat' in mp: + mp['Cp'] = Units.Quantity(mp['SpecificHeat']).getValueAs("J/kg/K").Value + if hasattr(solid_obj, 'HeatGeneration'): + heat_gen = solid_obj.HeatGeneration.getValueAs("W/m^3").Value + else: + heat_gen = 0.0 + mp['HeatGeneration'] = heat_gen + mp['HeatGenerationActive'] = 'true' if heat_gen > 0 else 'false' + settings['solidProperties'].append(mp) + settings['multiRegionSolidNames'].append(region_name) + for material_obj in self.material_objs: - mp = material_obj.Material + mp = material_obj.Material.copy() mp['Name'] = material_obj.Label + explicit_region = getattr(material_obj, 'RegionName', '') + if explicit_region: + region_name = explicit_region + elif is_multiregion: + # Derive fluid region name from the Internal mesh refinement zone label so the + # user does not have to set RegionName manually. Falls back to material Label. + mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) + internal_zones = [o.Label for o in mr_objs if o.Internal] + region_name = internal_zones[0] if internal_zones else material_obj.Label + else: + region_name = material_obj.Label + + if is_multiregion: + settings['multiRegionFluidNames'].append(region_name) + # Add type if absent mat_type = mp.get('Type', 'Isothermal') mp['Type'] = mat_type @@ -362,6 +474,17 @@ def processFluidProperties(self): settings['fluidProperties'].append(mp) + if is_multiregion: + if not settings['multiRegionSolidNames']: + raise ValueError("{} requires at least one solid material region. " + "Add a Solid Properties object for the solid body, or use a " + "single-region buoyant solver with a fixed-temperature wall " + "if the body is only meant to be a boundary." + .format(solver_name)) + settings['multiRegionEnabled'] = True + settings['multiRegionFluidNamesDict'] = {n: {} for n in settings['multiRegionFluidNames']} + settings['multiRegionSolidNamesDict'] = {n: {} for n in settings['multiRegionSolidNames']} + def processBoundaryConditions(self): """ Compute any quantities required before case build """ # Copy keys so that we can delete while iterating @@ -689,6 +812,219 @@ def processDynamicMeshRefinement(self): else: settings['dynamicMesh']['Type'] = 'interface' + # Multi-region CHT + def exportChtRegionStlSurfaces(self): + """Export STL surfaces for CHT regions. + + Fluid regions: sourced from Internal CfdMeshRefinement zones matching multiRegionFluidNames. + Solid regions: sourced from CfdSolidMaterial.ShapeRefs. + """ + settings = self.settings + path = os.path.join(self.working_dir, self.solver_obj.InputCaseName, "constant", "triSurface") + + # Fluid regions: use Internal mesh refinement zones + mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) + fluid_zones_written = set() + for mr_obj in mr_objs: + if mr_obj.Internal and mr_obj.Label in settings['multiRegionFluidNames']: + if not os.path.exists(path): + os.makedirs(path) + for r in mr_obj.ShapeRefs: + CfdMeshTools.writeSurfaceMeshFromShape(r[0].Shape, path, mr_obj.Label, self.mesh_obj) + cfdMessage("Wrote STL for fluid region '{}'\n".format(mr_obj.Label)) + settings['zones'][mr_obj.Label] = {'PartNameList': (mr_obj.Label,)} + fluid_zones_written.add(mr_obj.Label) + + # Fallback: if no Internal zone was found for a fluid region, derive the fluid body + # from the compound mesh part by excluding known solid bodies. + solid_obj_names = set() + for solid_obj in self.solid_material_objs: + for r in solid_obj.ShapeRefs: + solid_obj_names.add(r[0].Name) + for region_name in settings['multiRegionFluidNames']: + if region_name not in fluid_zones_written: + mesh_part = self.mesh_obj.Part + compound_links = _getCompoundLinks(mesh_part) + fluid_bodies = [lnk for lnk in compound_links if lnk.Name not in solid_obj_names] + if fluid_bodies: + if not os.path.exists(path): + os.makedirs(path) + for body in fluid_bodies: + CfdMeshTools.writeSurfaceMeshFromShape(body.Shape, path, region_name, self.mesh_obj) + cfdMessage("Wrote STL for fluid region '{}' (auto-derived from compound)\n".format(region_name)) + settings['zones'][region_name] = {'PartNameList': (region_name,)} + else: + cfdMessage("Warning: no geometry found for fluid region '{}' - " + "add an Internal mesh refinement zone\n".format(region_name)) + + # Solid regions: use CfdSolidMaterial.ShapeRefs directly + for solid_obj in self.solid_material_objs: + region_name = getattr(solid_obj, 'RegionName', '') or solid_obj.Label + if not os.path.exists(path): + os.makedirs(path) + for r in solid_obj.ShapeRefs: + CfdMeshTools.writeSurfaceMeshFromShape(r[0].Shape, path, region_name, self.mesh_obj) + cfdMessage("Wrote STL for solid region '{}'\n".format(region_name)) + settings['zones'][region_name] = {'PartNameList': (region_name,)} + + # For gmsh CHT cases, cell zones come directly from per-region Physical Volumes + # in the .geo file (written by CfdMeshTools). topoSet zone creation is not needed. + if (settings['multiRegionFluidNames'] or settings['multiRegionSolidNames']) and \ + self.mesh_obj.MeshUtility != 'gmsh': + settings['zonesPresent'] = True + + def _checkChtGeometryConformality(self): + """Warn if solid bodies are enclosed inside fluid bodies without shared faces (non-conformal topology). + + A simple Part::Compound of an enclosed heatsink produces non-conformal mesh — + splitMeshRegions won't create an interface patch in the fluid region. + BooleanFragments is required to create shared surfaces. + """ + if self.mesh_obj.MeshUtility != 'gmsh': + return + if not self.solid_material_objs: + return + mesh_part = self.mesh_obj.Part + links = _getCompoundLinks(mesh_part) + if len(links) < 2: + return + solid_names = set() + for solid_obj in self.solid_material_objs: + for r in solid_obj.ShapeRefs: + solid_names.add(r[0].Name) + fluid_links = [lnk for lnk in links if lnk.Name not in solid_names] + solid_links = [lnk for lnk in links if lnk.Name in solid_names] + if not fluid_links or not solid_links: + return + # Check whether any solid body shares a face with the fluid compound shape. + # If the compound has no shared/fused topology (i.e. it was created with + # Part::Compound rather than BooleanFragments), the sub-shapes are independent + # and share no faces. We detect this by checking if the compound compound object + # has a Links attribute (Part::Compound) and its Placement is default — a proxy + # for "not Boolean-fused". A more robust check: count faces in compound vs sum of faces. + compound_shape = mesh_part.Shape + sub_face_count = sum(len(lnk.Shape.Faces) for lnk in links) + compound_face_count = len(compound_shape.Faces) + if compound_face_count >= sub_face_count: + # No face reduction means no shared faces — BooleanFragments reduces face count + # by merging coincident faces into one shared face. + # Only warn if at least one solid body is spatially enclosed in a fluid body + # (bounding box of solid is inside bounding box of a fluid link). + for s_lnk in solid_links: + s_bb = s_lnk.Shape.BoundBox + for f_lnk in fluid_links: + f_bb = f_lnk.Shape.BoundBox + if (f_bb.XMin <= s_bb.XMin and f_bb.XMax >= s_bb.XMax and + f_bb.YMin <= s_bb.YMin and f_bb.YMax >= s_bb.YMax and + f_bb.ZMin <= s_bb.ZMin and f_bb.ZMax >= s_bb.ZMax): + cfdMessage( + "WARNING: Solid body '{}' appears to be enclosed inside fluid body '{}', " + "but the compound was not created with BooleanFragments. " + "This will produce a non-conformal mesh — splitMeshRegions will NOT create " + "a defaultFaces interface patch in the fluid region and the CHT solver will " + "fail. Use Part → Boolean → BooleanFragments instead of Part → Create a " + "compound.\n".format(s_lnk.Label, f_lnk.Label)) + + def processMultiRegionBoundaries(self): + """Categorize boundary conditions by region (fluid vs solid) for CHT cases.""" + settings = self.settings + fluid_region_shapes = set() + solid_region_shapes = set() + + # Fluid region shapes from Internal mesh refinement zones + mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj) + for mr_obj in mr_objs: + if mr_obj.Internal: + for r in mr_obj.ShapeRefs: + if mr_obj.Label in settings['multiRegionFluidNames']: + fluid_region_shapes.add(r[0].Name) + + # Solid region shapes from CfdSolidMaterial.ShapeRefs (new path) + solid_shapes_geom = [] # actual FreeCAD shapes for geometry-based fallback + for solid_obj in self.solid_material_objs: + for r in solid_obj.ShapeRefs: + solid_region_shapes.add(r[0].Name) + # Also add names of all child features (e.g. PartDesign Pad inside Body) + for child in getattr(r[0], 'OutList', []): + if hasattr(child, 'Shape'): + solid_region_shapes.add(child.Name) + # Collect actual shape for geometry fallback + try: + solid_shapes_geom.append(r[0].Shape) + except Exception: + pass + + # Auto-detect fluid shapes: compound children that are not solid bodies + fluid_shapes_geom = [] # actual FreeCAD shapes for geometry-based fallback + if not fluid_region_shapes and settings.get('multiRegionFluidNames'): + mesh_part = self.mesh_obj.Part + links = _getCompoundLinks(mesh_part) + for lnk in links: + if lnk.Name not in solid_region_shapes: + fluid_region_shapes.add(lnk.Name) + try: + fluid_shapes_geom.append(lnk.Shape) + except Exception: + pass + + fluid_bcs = {} + solid_bcs = {} + for bc_obj in self.bc_group: + matched = False + for ref in bc_obj.ShapeRefs: + if ref[0].Name in fluid_region_shapes: + fluid_bcs[bc_obj.Label] = settings['boundaries'][bc_obj.Label] + matched = True + break + elif ref[0].Name in solid_region_shapes: + solid_bcs[bc_obj.Label] = settings['boundaries'][bc_obj.Label] + matched = True + break + if not matched and solid_shapes_geom: + # Geometry fallback for BCs referencing the BooleanFragments result. + # A selected face that overlaps a solid material body belongs to the + # solid region even if it is also inside the original fluid volume. + # Otherwise classify by whether the face centroid is in the fluid. + is_solid = False + for ref in bc_obj.ShapeRefs: + try: + shape_obj = ref[0] + sub_list = ref[1] if ref[1] else [] + faces = [shape_obj.Shape.getElement(s) for s in sub_list] if sub_list \ + else [shape_obj.Shape] + for face in faces: + if face is None: + continue + if _face_overlaps_any_shape(face, solid_shapes_geom): + is_solid = True + break + centroid = face.CenterOfMass + if fluid_shapes_geom: + # Solid BC only if centroid is outside every fluid shape + in_fluid = any(fs.isInside(centroid, 1e-3, True) + for fs in fluid_shapes_geom) + if not in_fluid: + is_solid = True + break + else: + for solid_shape in solid_shapes_geom: + if solid_shape.isInside(centroid, 1e-3, True): + is_solid = True + break + if is_solid: + break + except Exception: + pass + if is_solid: + break + if is_solid: + solid_bcs[bc_obj.Label] = settings['boundaries'][bc_obj.Label] + else: + fluid_bcs[bc_obj.Label] = settings['boundaries'][bc_obj.Label] + + settings['multiRegionFluidBoundaries'] = fluid_bcs + settings['multiRegionSolidBoundaries'] = solid_bcs + # Zones def exportZoneStlSurfaces(self): for zo in self.zone_objs: diff --git a/CfdOF/Solve/CfdFluidMaterial.py b/CfdOF/Solve/CfdFluidMaterial.py index d9abc3a7..33198bc8 100644 --- a/CfdOF/Solve/CfdFluidMaterial.py +++ b/CfdOF/Solve/CfdFluidMaterial.py @@ -139,7 +139,12 @@ def onDocumentRestored(self, obj): class ViewProviderCfdFluidMaterial: def __init__(self, vobj): vobj.Proxy = self + self.ViewObject = vobj + self.Object = vobj.Object self.taskd = None + # FreeCAD 1.1 builds may not provide this optional GUI extension and + # print ExtensionContainer warnings before raising. Suppressible view + # behaviour is cosmetic, so skip it for compatibility. def getIcon(self): icon_path = os.path.join(CfdTools.getModulePath(), "Gui", "Icons", "material.svg") @@ -157,6 +162,9 @@ def updateData(self, obj, prop): def onChanged(self, vobj, prop): return + def isShow(self): + return self.ViewObject.Visibility + def setEdit(self, vobj, mode): analysis_object = CfdTools.getParentAnalysisObject(self.Object) if analysis_object is None: diff --git a/CfdOF/Solve/CfdPhysicsSelection.py b/CfdOF/Solve/CfdPhysicsSelection.py index 9d971025..f1148c13 100644 --- a/CfdOF/Solve/CfdPhysicsSelection.py +++ b/CfdOF/Solve/CfdPhysicsSelection.py @@ -146,7 +146,7 @@ def initProperties(self, obj): if addObjectProperty( obj, "Phase", - ["Single", "FreeSurface"], + ["Single", "FreeSurface", "MultiRegion"], "App::PropertyEnumeration", "Physics modelling", QT_TRANSLATE_NOOP("App::Property", "Type of phases present"), diff --git a/CfdOF/Solve/CfdSolidMaterial.py b/CfdOF/Solve/CfdSolidMaterial.py new file mode 100644 index 00000000..dc5d36a7 --- /dev/null +++ b/CfdOF/Solve/CfdSolidMaterial.py @@ -0,0 +1,177 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +# SPDX-FileCopyrightText: © 2024 CfdOF contributors +# SPDX-FileNotice: Part of the CfdOF addon. + +import os +import FreeCAD + +if FreeCAD.GuiUp: + import FreeCADGui +from CfdOF import CfdTools +from CfdOF.CfdTools import addObjectProperty + +translate = FreeCAD.Qt.translate +QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP + + +def makeCfdSolidMaterial(name): + obj = FreeCAD.ActiveDocument.addObject("App::MaterialObjectPython", name) + CfdSolidMaterial(obj) + if FreeCAD.GuiUp: + ViewProviderCfdSolidMaterial(obj.ViewObject) + return obj + + +class CommandCfdSolidMaterial: + + def GetResources(self): + icon_path = os.path.join(CfdTools.getModulePath(), "Gui", "Icons", "solid_material.svg") + return { + 'Pixmap': icon_path, + 'MenuText': QT_TRANSLATE_NOOP("CfdOF_SolidMaterial", "Add solid properties"), + 'ToolTip': QT_TRANSLATE_NOOP("CfdOF_SolidMaterial", "Add solid material properties for CHT simulation")} + + def IsActive(self): + return CfdTools.getActiveAnalysis() is not None + + def Activated(self): + FreeCAD.Console.PrintMessage(translate("Console", "Set solid properties\n")) + FreeCAD.ActiveDocument.openTransaction("Set CfdSolidMaterial") + FreeCADGui.doCommand("from CfdOF import CfdTools") + FreeCADGui.doCommand("from CfdOF.Solve import CfdSolidMaterial") + FreeCADGui.doCommand( + "CfdTools.getActiveAnalysis().addObject(CfdSolidMaterial.makeCfdSolidMaterial('SolidProperties'))") + FreeCADGui.ActiveDocument.setEdit(FreeCAD.ActiveDocument.ActiveObject.Name) + + +class CfdSolidMaterial: + """Solid material properties for CHT multi-region simulation.""" + + def __init__(self, obj): + self.initProperties(obj) + + def initProperties(self, obj): + obj.Proxy = self + self.Type = 'CfdSolidMaterial' + + addObjectProperty( + obj, + "ShapeRefs", + [], + "App::PropertyLinkSubListGlobal", + "Solid region", + QT_TRANSLATE_NOOP("App::Property", "Solid body defining this region"), + ) + + addObjectProperty( + obj, + "RegionName", + "", + "App::PropertyString", + "Solid region", + QT_TRANSLATE_NOOP("App::Property", + "OpenFOAM region name (defaults to object Label if empty)"), + ) + + addObjectProperty( + obj, + "HeatGeneration", + "0 W/m^3", + "App::PropertyQuantity", + "Solid region", + QT_TRANSLATE_NOOP("App::Property", "Volumetric heat generation rate"), + ) + + if not obj.Material: + mats, name_path_list = CfdTools.importMaterials() + names = [np[0] for np in name_path_list] + if 'AluminiumSolid' in names: + obj.Material = mats[name_path_list[names.index('AluminiumSolid')][1]] + else: + obj.Material = {'Name': 'Custom', 'Description': '', 'Type': 'Solid', + 'ThermalConductivity': '1 W/m/K', + 'Density': '1000 kg/m^3', + 'SpecificHeat': '500 J/kg/K'} + + def onDocumentRestored(self, obj): + self.initProperties(obj) + if FreeCAD.GuiUp and obj.ViewObject.Proxy == 0: + ViewProviderCfdSolidMaterial(obj.ViewObject) + + def execute(self, obj): + return + + +class ViewProviderCfdSolidMaterial: + def __init__(self, vobj): + vobj.Proxy = self + self.ViewObject = vobj + self.Object = vobj.Object + self.taskd = None + # FreeCAD 1.1 builds may not provide this optional GUI extension and + # print ExtensionContainer warnings before raising. Suppressible view + # behaviour is cosmetic, so skip it for compatibility. + + def getIcon(self): + return os.path.join(CfdTools.getModulePath(), "Gui", "Icons", "solid_material.svg") + + def attach(self, vobj): + self.ViewObject = vobj + self.Object = vobj.Object + self.taskd = None + + def updateData(self, obj, prop): + analysis_obj = CfdTools.getParentAnalysisObject(obj) + if analysis_obj and not analysis_obj.Proxy.loading: + analysis_obj.NeedsCaseRewrite = True + + def onChanged(self, vobj, prop): + return + + def isShow(self): + return self.ViewObject.Visibility + + def setEdit(self, vobj, mode): + obj = getattr(self, 'Object', vobj.Object) + self.Object = obj + analysis_object = CfdTools.getParentAnalysisObject(obj) + if analysis_object is None: + CfdTools.cfdErrorBox("No parent analysis object found") + return False + import importlib + from CfdOF.Solve import TaskPanelCfdSolidProperties + importlib.reload(TaskPanelCfdSolidProperties) + self.taskd = TaskPanelCfdSolidProperties.TaskPanelCfdSolidProperties(obj) + self.taskd.obj = vobj.Object + FreeCADGui.Control.showDialog(self.taskd) + return True + + def doubleClicked(self, vobj): + doc = FreeCADGui.getDocument(vobj.Object.Document) + if not doc.getInEdit(): + doc.setEdit(vobj.Object.Name) + else: + FreeCAD.Console.PrintError('Task dialog already open\n') + FreeCADGui.Control.showTaskView() + return True + + def unsetEdit(self, vobj, mode): + if getattr(self, 'taskd', None): + self.taskd.closing() + self.taskd = None + FreeCADGui.Control.closeDialog() + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + def dumps(self): + return None + + def loads(self, state): + return None + + +FreeCADGui.addCommand('CfdOF_SolidMaterial', CommandCfdSolidMaterial()) diff --git a/CfdOF/Solve/CfdSolverFoam.py b/CfdOF/Solve/CfdSolverFoam.py index 27a8515d..3301a435 100644 --- a/CfdOF/Solve/CfdSolverFoam.py +++ b/CfdOF/Solve/CfdSolverFoam.py @@ -193,6 +193,36 @@ def initProperties(self, obj): "Relaxation", QT_TRANSLATE_NOOP("App::Property", "Density field relaxation factor (steady-state)"), ) + addObjectProperty( + obj, + "FluidNonOrthogonalCorrectors", + 3, + "App::PropertyInteger", + "Relaxation", + QT_TRANSLATE_NOOP( + "App::Property", "Number of fluid-region non-orthogonal correctors" + ), + ) + addObjectProperty( + obj, + "SolidEnergyRelaxation", + 0.9, + "App::PropertyFloat", + "Relaxation", + QT_TRANSLATE_NOOP( + "App::Property", "Solid energy equation relaxation factor (steady-state)" + ), + ) + addObjectProperty( + obj, + "SolidNonOrthogonalCorrectors", + 5, + "App::PropertyInteger", + "Relaxation", + QT_TRANSLATE_NOOP( + "App::Property", "Number of solid-region non-orthogonal correctors" + ), + ) if addObjectProperty( obj, diff --git a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py index 6e72fb78..ca355b19 100644 --- a/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py +++ b/CfdOF/Solve/TaskPanelCfdPhysicsSelection.py @@ -52,6 +52,7 @@ def __init__(self, obj): self.form.radioButtonTransient.toggled.connect(self.updateUI) self.form.radioButtonSinglePhase.toggled.connect(self.updateUI) self.form.radioButtonFreeSurface.toggled.connect(self.updateUI) + self.form.radioButtonMultiRegion.toggled.connect(self.updateUI) if hasattr(self.form.checkBoxIsothermal, "checkStateChanged"): self.form.checkBoxIsothermal.checkStateChanged.connect(self.updateUI) self.form.viscousCheckBox.checkStateChanged.connect(self.updateUI) @@ -80,6 +81,8 @@ def load(self): self.form.radioButtonSinglePhase.toggle() elif self.obj.Phase == 'FreeSurface': self.form.radioButtonFreeSurface.toggle() + elif self.obj.Phase == 'MultiRegion': + self.form.radioButtonMultiRegion.toggle() # Flow self.form.checkBoxIsothermal.setChecked(self.obj.Flow == 'Isothermal') @@ -127,9 +130,12 @@ def updateUI(self): self.form.FlowFrame.setVisible(True) self.form.turbulenceFrame.setVisible(True) + multi_region = self.form.radioButtonMultiRegion.isChecked() + # Steady / transient if self.form.radioButtonSteady.isChecked(): - self.form.radioButtonFreeSurface.setEnabled(False) + free_surface_allowed = not multi_region + self.form.radioButtonFreeSurface.setEnabled(free_surface_allowed) if self.form.radioButtonDES.isChecked() or self.form.radioButtonLES.isChecked(): self.form.radioButtonRANS.toggle() self.form.radioButtonDES.setEnabled(False) @@ -137,36 +143,48 @@ def updateUI(self): if self.form.radioButtonFreeSurface.isChecked(): self.form.radioButtonSinglePhase.toggle() else: - self.form.radioButtonFreeSurface.setEnabled(True) + self.form.radioButtonFreeSurface.setEnabled(not multi_region) self.form.radioButtonDES.setEnabled(True) self.form.radioButtonLES.setEnabled(True) + # Multi-region forces NonIsothermal flow and disables flow override controls + if multi_region: + self.form.checkBoxIsothermal.setChecked(False) + self.form.checkBoxIsothermal.setEnabled(False) + self.form.checkBoxHighMach.setChecked(False) + self.form.checkBoxHighMach.setEnabled(False) + else: + self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked()) + # Gravity self.form.gravityFrame.setEnabled( - self.form.radioButtonFreeSurface.isChecked() or + self.form.radioButtonFreeSurface.isChecked() or multi_region or (not self.form.checkBoxIsothermal.isChecked() and not self.form.checkBoxHighMach.isChecked())) # SRF model - srf_capable = (self.form.radioButtonSteady.isChecked() and self.form.checkBoxIsothermal.isChecked()) - srf_should_be_unchecked = ((not self.form.checkBoxIsothermal.isChecked()) + srf_capable = (self.form.radioButtonSteady.isChecked() and self.form.checkBoxIsothermal.isChecked() + and not multi_region) + srf_should_be_unchecked = ((not self.form.checkBoxIsothermal.isChecked()) or self.form.radioButtonTransient.isChecked() - or self.form.radioButtonFreeSurface.isChecked()) + or self.form.radioButtonFreeSurface.isChecked() + or multi_region) self.form.srfCheckBox.setEnabled(srf_capable) if srf_should_be_unchecked: self.form.srfCheckBox.setChecked(False) self.form.srfFrame.setEnabled(self.form.srfCheckBox.isChecked()) - # Free surface + # Free surface forces isothermal (only when not multi-region) if self.form.radioButtonFreeSurface.isChecked(): self.form.checkBoxIsothermal.setChecked(True) self.form.checkBoxIsothermal.setEnabled(False) - else: + elif not multi_region: self.form.checkBoxIsothermal.setEnabled(True) - # High Mach capability - self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked()) - if self.form.checkBoxIsothermal.isChecked(): - self.form.checkBoxHighMach.setChecked(False) + # High Mach capability (only when not multi-region) + if not multi_region: + self.form.checkBoxHighMach.setEnabled(not self.form.checkBoxIsothermal.isChecked()) + if self.form.checkBoxIsothermal.isChecked(): + self.form.checkBoxHighMach.setChecked(False) # Viscous if self.form.viscousCheckBox.isChecked(): @@ -212,6 +230,8 @@ def accept(self): storeIfChanged(self.obj, 'Phase', 'Single') elif self.form.radioButtonFreeSurface.isChecked(): storeIfChanged(self.obj, 'Phase', 'FreeSurface') + elif self.form.radioButtonMultiRegion.isChecked(): + storeIfChanged(self.obj, 'Phase', 'MultiRegion') if self.form.checkBoxIsothermal.isChecked(): storeIfChanged(self.obj, 'Flow', 'Isothermal') diff --git a/CfdOF/Solve/TaskPanelCfdSolidProperties.py b/CfdOF/Solve/TaskPanelCfdSolidProperties.py new file mode 100644 index 00000000..11694e8d --- /dev/null +++ b/CfdOF/Solve/TaskPanelCfdSolidProperties.py @@ -0,0 +1,194 @@ +# SPDX-License-Identifier: LGPL-3.0-or-later +# SPDX-FileCopyrightText: © 2024 CfdOF contributors +# SPDX-FileNotice: Part of the CfdOF addon. + +import os +import FreeCAD +if FreeCAD.GuiUp: + import FreeCADGui + from PySide import QtGui +from CfdOF import CfdTools, CfdFaceSelectWidget +from CfdOF.CfdTools import setQuantity, getQuantity, storeIfChanged + +translate = FreeCAD.Qt.translate + +SOLID_FIELDS = ['ThermalConductivity', 'Density', 'SpecificHeat'] +SOLID_FIELD_UNITS = { + 'ThermalConductivity': 'W/m/K', + 'Density': 'kg/m^3', + 'SpecificHeat': 'J/kg/K', +} +HEAT_GENERATION_UNIT = 'W/m^3' + + +class TaskPanelCfdSolidProperties: + """Task panel for CfdSolidMaterial objects.""" + + def __init__(self, obj): + self.obj = obj + + self.form = FreeCADGui.PySideUic.loadUi( + os.path.join(CfdTools.getModulePath(), 'Gui', "TaskPanelCfdSolidProperties.ui")) + + self.material = dict(self.obj.Material) + self.ShapeRefsOrig = list(self.obj.ShapeRefs) + + # Body selector — whole-solid selection only + self.solidSelector = CfdFaceSelectWidget.CfdFaceSelectWidget( + self.form.solidBodyWidget, self.obj, + allow_obj_sel=False, allow_face_sel=False, allow_solid_sel=True) + + # Build property input widgets dynamically into frame_solid + self.text_boxes = {} + layout = self.form.frame_solid.layout() + for name in SOLID_FIELDS: + widget = FreeCADGui.UiLoader().createWidget("Gui::InputField") + widget.setObjectName(name) + widget.setProperty("format", "g") + val = self.material.get(name, '0') + widget.setProperty("unit", SOLID_FIELD_UNITS.get(name, '')) + widget.setProperty("minimum", 0) + widget.setProperty("singleStep", 0.1) + layout.addRow(name + ":", widget) + self.text_boxes[name] = widget + setQuantity(widget, val) + widget.valueChanged.connect(self.manualEdit) + + if hasattr(self.obj, 'HeatGeneration'): + self.setHeatGenerationQuantity(self.obj.HeatGeneration) + self.form.inputHeatGeneration.editingFinished.connect(self.heatGenChanged) + + self.form.material_name.setText(self.obj.Label) + self.form.saveButton.clicked.connect(self.saveCustomMaterial) + self.form.saveButton.setVisible(False) + self.form.PredefinedMaterialLibraryComboBox.currentIndexChanged.connect(self.selectPredefined) + + self.selecting_predefined = True + try: + self.populateMaterialsList() + idx = self.form.PredefinedMaterialLibraryComboBox.findText(self.obj.Material.get('Name', '')) + if idx == -1: + idx = 0 + self.form.PredefinedMaterialLibraryComboBox.setCurrentIndex(idx) + self.selectPredefined() + finally: + self.selecting_predefined = False + # Restore the user's saved values: populateMaterialsList and selectPredefined + # both fire signals that overwrite self.material and the widget values. + self.material = dict(self.obj.Material) + self.selecting_predefined = True + try: + for name in SOLID_FIELDS: + setQuantity(self.text_boxes[name], self.material.get(name, '0')) + finally: + self.selecting_predefined = False + self.form.solidDescriptor.setText(self.material.get("Description", "")) + self.form.material_name.setText(self.obj.Label) + + def populateMaterialsList(self): + self.form.PredefinedMaterialLibraryComboBox.clear() + self.materials, material_name_path_list = CfdTools.importMaterials() + for mat in material_name_path_list: + if self.materials[mat[1]].get('Type') == 'Solid': + mat_name = self.materials[mat[1]]['Name'] + self.form.PredefinedMaterialLibraryComboBox.addItem( + QtGui.QIcon(":/Icons/freecad.svg"), mat_name, mat[1]) + self.form.PredefinedMaterialLibraryComboBox.addItem("Custom") + + def selectPredefined(self): + index = self.form.PredefinedMaterialLibraryComboBox.currentIndex() + mat_file_path = self.form.PredefinedMaterialLibraryComboBox.itemData(index) + if mat_file_path: + self.material = dict(self.materials[mat_file_path]) + self.selecting_predefined = True + try: + for name in SOLID_FIELDS: + setQuantity(self.text_boxes[name], self.material.get(name, '0')) + finally: + self.selecting_predefined = False + self.form.material_name.setText(self.material['Name']) + self.form.solidDescriptor.setText(self.material.get("Description", "")) + + def manualEdit(self): + if not self.selecting_predefined: + self.form.PredefinedMaterialLibraryComboBox.setCurrentIndex( + self.form.PredefinedMaterialLibraryComboBox.findText('Custom')) + self.form.solidDescriptor.setText("User-entered properties") + self.material = {'Name': 'Custom', 'Description': 'User-entered properties', 'Type': 'Solid'} + for name in SOLID_FIELDS: + self.material[name] = getQuantity(self.text_boxes[name]) + self.form.saveButton.setVisible(True) + + def heatGenChanged(self): + quantity = self.getHeatGenerationQuantity(show_error=False) + if quantity is not None: + self.form.inputHeatGeneration.setText(quantity) + + def setHeatGenerationQuantity(self, quantity): + if isinstance(quantity, str): + q = FreeCAD.Units.Quantity(quantity) + else: + q = quantity + display_value = q.getValueAs(HEAT_GENERATION_UNIT).Value + self.form.inputHeatGeneration.setText("{:g} {}".format(display_value, HEAT_GENERATION_UNIT)) + + def getHeatGenerationQuantity(self, show_error=True): + text = self.form.inputHeatGeneration.text().strip() + if not text: + text = "0 {}".format(HEAT_GENERATION_UNIT) + try: + q = FreeCAD.Units.Quantity(text).getValueAs(HEAT_GENERATION_UNIT) + except Exception: + if show_error: + CfdTools.cfdErrorBox("Invalid heat generation value: {}".format(text)) + return None + if q.Value < 0: + if show_error: + CfdTools.cfdErrorBox("Heat generation must be greater than or equal to 0") + return None + return "{:g} {}".format(q.Value, HEAT_GENERATION_UNIT) + + def saveCustomMaterial(self): + system_mat_dir = os.path.join(CfdTools.getModulePath(), "Data", "CfdFluidMaterialProperties") + d = QtGui.QFileDialog(None, "Save Custom Solid Material", system_mat_dir, "FCMat (*.FCMat)") + d.setDefaultSuffix("FCMat") + d.setAcceptMode(QtGui.QFileDialog.AcceptMode.AcceptSave) + d.exec() + file_names = d.selectedFiles() + if file_names: + mat_name = self.form.material_name.text() + with open(file_names[0], 'w') as f: + f.write('; {}\n;\n; FreeCAD Material card: see https://www.freecad.org/wiki/Material\n\n[FCMat]\n'.format(mat_name)) + f.write('Name = {}\nType = Solid\n'.format(mat_name)) + for key, val in self.material.items(): + if key not in ('Name', 'Type'): + f.write('{} = {}\n'.format(key, val)) + FreeCAD.Console.PrintMessage(translate("Console", "Custom solid material saved\n")) + + def accept(self): + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() + doc.Document.recompute() + storeIfChanged(self.obj, 'Material', self.material) + storeIfChanged(self.obj, 'Label', self.form.material_name.text()) + if hasattr(self.obj, 'HeatGeneration'): + heat_generation = self.getHeatGenerationQuantity() + if heat_generation is None: + return False + self.form.inputHeatGeneration.setText(heat_generation) + storeIfChanged(self.obj, 'HeatGeneration', heat_generation) + if self.obj.ShapeRefs != self.ShapeRefsOrig: + refstr = "FreeCAD.ActiveDocument.{}.ShapeRefs = [\n".format(self.obj.Name) + refstr += ",\n".join( + "(FreeCAD.ActiveDocument.getObject('{}'), {})".format(ref[0].Name, ref[1]) + for ref in self.obj.ShapeRefs) + refstr += "]\n" + FreeCADGui.doCommand(refstr) + + def reject(self): + self.obj.ShapeRefs = self.ShapeRefsOrig + doc = FreeCADGui.getDocument(self.obj.Document) + doc.resetEdit() + + def closing(self): + return diff --git a/CfdOF/TemplateBuilder.py b/CfdOF/TemplateBuilder.py index 96c465fd..496d8465 100644 --- a/CfdOF/TemplateBuilder.py +++ b/CfdOF/TemplateBuilder.py @@ -83,7 +83,7 @@ def writeToFile(self, rel_file, contents): def buildFile(self, rel_file, params): """ Open the specified template file, make replacements, and return as a string """ try: - fid = open(os.path.join(self.template_path, rel_file)) + fid = open(os.path.join(self.template_path, rel_file), encoding='utf-8') except IOError: # Special cases: # 1. Don't worry if files that end with "None" do not exist @@ -92,7 +92,7 @@ def buildFile(self, rel_file, params): # 2. If a file is not found, try the same file with 'default' after the last underscore rel_file_default = rel_file.rsplit("_", 1)[0] + "_default" try: - fid = open(os.path.join(self.template_path, rel_file_default)) + fid = open(os.path.join(self.template_path, rel_file_default), encoding='utf-8') except IOError: raise IOError("Error reading file {} in template path {}".format(rel_file, self.template_path)) finally: @@ -218,9 +218,11 @@ def processBraces(self, contents, curr_file, params): filename_param = None if trailing_nl is not None: filename_param = contents[end+2:trailing_nl].strip() - # Loop the content passing values + # Loop the content passing values (skip empty strings from empty dict/list substitution) replacement = "" for v in keys: + if not v: + continue filename = None if filename_param: # Process filename with parameter diff --git a/Data/CfdFluidMaterialProperties/AcrylicSolid.FCMat b/Data/CfdFluidMaterialProperties/AcrylicSolid.FCMat new file mode 100644 index 00000000..69c6651f --- /dev/null +++ b/Data/CfdFluidMaterialProperties/AcrylicSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; AcrylicSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Acrylic_solid +Type = Solid +Description = Acrylic PMMA solid thermal properties +Density = 1180 kg/m^3 +ThermalConductivity = 0.189 W/m/K +SpecificHeat = 1500 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=a5e93a1f1fff43bcbac5b6ca51b8981f diff --git a/Data/CfdFluidMaterialProperties/AluminiumSolid.FCMat b/Data/CfdFluidMaterialProperties/AluminiumSolid.FCMat new file mode 100644 index 00000000..a66b9d89 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/AluminiumSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; AluminiumSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Aluminium_solid +Type = Solid +Description = Aluminium solid thermal properties for CHT simulation +Density = 2699 kg/m^3 +ThermalConductivity = 237 W/m/K +SpecificHeat = 897 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=0cd1edf33ac145ee93a0aa6fc666c0e0 diff --git a/Data/CfdFluidMaterialProperties/BrassSolid.FCMat b/Data/CfdFluidMaterialProperties/BrassSolid.FCMat new file mode 100644 index 00000000..476dd65a --- /dev/null +++ b/Data/CfdFluidMaterialProperties/BrassSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; BrassSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Brass_solid +Type = Solid +Description = Brass solid thermal properties +Density = 8510 kg/m^3 +ThermalConductivity = 85.1 W/m/K +SpecificHeat = 379 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=d3bd4617903543ada92f4c101c2a20e5 diff --git a/Data/CfdFluidMaterialProperties/BrickSolid.FCMat b/Data/CfdFluidMaterialProperties/BrickSolid.FCMat new file mode 100644 index 00000000..d7bba341 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/BrickSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; BrickSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Brick_solid +Type = Solid +Description = Common brick solid thermal properties +Density = 1920 kg/m^3 +ThermalConductivity = 0.72 W/m/K +SpecificHeat = 840 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=4288c4f80e77437893b26c4a56a98171 diff --git a/Data/CfdFluidMaterialProperties/CopperSolid.FCMat b/Data/CfdFluidMaterialProperties/CopperSolid.FCMat new file mode 100644 index 00000000..365ff292 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/CopperSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; CopperSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Copper_solid +Type = Solid +Description = Copper solid thermal properties +Density = 8930 kg/m^3 +ThermalConductivity = 385 W/m/K +SpecificHeat = 385 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=9aebe83845c04c1db5126fada6f76f7e diff --git a/Data/CfdFluidMaterialProperties/FiberglassSolid.FCMat b/Data/CfdFluidMaterialProperties/FiberglassSolid.FCMat new file mode 100644 index 00000000..e568992c --- /dev/null +++ b/Data/CfdFluidMaterialProperties/FiberglassSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; FiberglassSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Fiberglass_solid +Type = Solid +Description = E-glass fiber solid thermal properties +Density = 2570 kg/m^3 +ThermalConductivity = 1.30 W/m/K +SpecificHeat = 810 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=d9c18047c49147a2a7c0b0bb1743e812 diff --git a/Data/CfdFluidMaterialProperties/GlassSolid.FCMat b/Data/CfdFluidMaterialProperties/GlassSolid.FCMat new file mode 100644 index 00000000..630233a2 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/GlassSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; GlassSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Glass_solid +Type = Solid +Description = Borosilicate glass solid thermal properties +Density = 2230 kg/m^3 +ThermalConductivity = 1.12 W/m/K +SpecificHeat = 830 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=7546f96147b847a5ab00091f3c36b8ce diff --git a/Data/CfdFluidMaterialProperties/Inconel601Solid.FCMat b/Data/CfdFluidMaterialProperties/Inconel601Solid.FCMat new file mode 100644 index 00000000..1b15066f --- /dev/null +++ b/Data/CfdFluidMaterialProperties/Inconel601Solid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; Inconel601Solid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Inconel601_solid +Type = Solid +Description = INCONEL alloy 601 solid thermal properties +Density = 8110 kg/m^3 +ThermalConductivity = 11.2 W/m/K +SpecificHeat = 448 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=f3fb3ae6ebe54d98ad8fa01c74b6a3e8 diff --git a/Data/CfdFluidMaterialProperties/IronSolid.FCMat b/Data/CfdFluidMaterialProperties/IronSolid.FCMat new file mode 100644 index 00000000..762f08c8 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/IronSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; IronSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Iron_solid +Type = Solid +Description = Iron solid thermal properties +Density = 7870 kg/m^3 +ThermalConductivity = 76.2 W/m/K +SpecificHeat = 440 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=654ca9c358264b5392d43315d8535b7d diff --git a/Data/CfdFluidMaterialProperties/PolypropyleneSolid.FCMat b/Data/CfdFluidMaterialProperties/PolypropyleneSolid.FCMat new file mode 100644 index 00000000..fcafc4c3 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/PolypropyleneSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; PolypropyleneSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Polypropylene_solid +Type = Solid +Description = Polypropylene solid thermal properties +Density = 918 kg/m^3 +ThermalConductivity = 0.249 W/m/K +SpecificHeat = 1920 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=a882a1c603374e278d062f106dfda95b diff --git a/Data/CfdFluidMaterialProperties/SandSolid.FCMat b/Data/CfdFluidMaterialProperties/SandSolid.FCMat new file mode 100644 index 00000000..89106625 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/SandSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; SandSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Sand_solid +Type = Solid +Description = Dry sand solid thermal properties +Density = 1650 kg/m^3 +ThermalConductivity = 0.305 W/m/K +SpecificHeat = 776 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=18a1e365613b478f880e5506d6fb2ec1 diff --git a/Data/CfdFluidMaterialProperties/SilverSolid.FCMat b/Data/CfdFluidMaterialProperties/SilverSolid.FCMat new file mode 100644 index 00000000..b045f1aa --- /dev/null +++ b/Data/CfdFluidMaterialProperties/SilverSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; SilverSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Silver_solid +Type = Solid +Description = Silver solid thermal properties +Density = 10491 kg/m^3 +ThermalConductivity = 419 W/m/K +SpecificHeat = 234 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=63cbd043a31f4f739ddb7632c1443d33 diff --git a/Data/CfdFluidMaterialProperties/StainlessSteel316Solid.FCMat b/Data/CfdFluidMaterialProperties/StainlessSteel316Solid.FCMat new file mode 100644 index 00000000..7cff8964 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/StainlessSteel316Solid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; StainlessSteel316Solid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = StainlessSteel316_solid +Type = Solid +Description = AISI 316 stainless steel solid thermal properties +Density = 8000 kg/m^3 +ThermalConductivity = 14.6 W/m/K +SpecificHeat = 500 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=1336be6d0c594b55afb5ca8bf1f3e042 diff --git a/Data/CfdFluidMaterialProperties/StyrofoamSolid.FCMat b/Data/CfdFluidMaterialProperties/StyrofoamSolid.FCMat new file mode 100644 index 00000000..32e543e8 --- /dev/null +++ b/Data/CfdFluidMaterialProperties/StyrofoamSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; StyrofoamSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Styrofoam_solid +Type = Solid +Description = Rigid polystyrene foam solid thermal properties +Density = 32 kg/m^3 +ThermalConductivity = 0.029 W/m/K +SpecificHeat = 1210 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=c98422e64d1f4bfa98e9d82dc89eaee6 diff --git a/Data/CfdFluidMaterialProperties/TitaniumSolid.FCMat b/Data/CfdFluidMaterialProperties/TitaniumSolid.FCMat new file mode 100644 index 00000000..03fc4f9a --- /dev/null +++ b/Data/CfdFluidMaterialProperties/TitaniumSolid.FCMat @@ -0,0 +1,13 @@ +; SPDX-FileCopyrightText: 2026 CfdOF contributors +; TitaniumSolid +; +; FreeCAD Material card: see https://www.freecad.org/wiki/Material + +[FCMat] +Name = Titanium_solid +Type = Solid +Description = Titanium solid thermal properties +Density = 4500 kg/m^3 +ThermalConductivity = 17.0 W/m/K +SpecificHeat = 528 J/kg/K +ReferenceSource = https://www.matweb.com/search/DataSheet.aspx?MatGUID=66a15d609a3f4c829cb6ad08f0dafc01 diff --git a/Data/Templates/case/0/ReThetat b/Data/Templates/case/0/ReThetat index b20f631f..19d12663 100644 --- a/Data/Templates/case/0/ReThetat +++ b/Data/Templates/case/0/ReThetat @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS %{%(physics/TurbulenceModel%) @@ -64,3 +67,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/T b/Data/Templates/case/0/T index 7b3c37d8..d2de62c1 100644 --- a/Data/Templates/case/0/T +++ b/Data/Templates/case/0/T @@ -48,7 +48,7 @@ boundaryField %(0%) { type externalWallHeatFluxTemperature; - mode coefficient; + mode flux; q uniform %(boundaries/%(0%)/HeatFlux%); kappaMethod fluidThermo; value $internalField; diff --git a/Data/Templates/case/0/U b/Data/Templates/case/0/U index b99b9302..2f40d9de 100644 --- a/Data/Templates/case/0/U +++ b/Data/Templates/case/0/U @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:SRFSimpleFoam +%:SRFSimpleFoam chtMultiRegionSimpleFoam chtMultiRegionFoam %:default %[_header%] FoamFile diff --git a/Data/Templates/case/0/epsilon b/Data/Templates/case/0/epsilon index 5e0acac3..4c221dd5 100644 --- a/Data/Templates/case/0/epsilon +++ b/Data/Templates/case/0/epsilon @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS %{%(physics/TurbulenceModel%) @@ -78,3 +81,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/fluid/T b/Data/Templates/case/0/fluid/T new file mode 100644 index 00000000..0854dc3c --- /dev/null +++ b/Data/Templates/case/0/fluid/T @@ -0,0 +1,109 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object T; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform %(initialValues/Temperature%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall +%{%(multiRegionFluidBoundaries/%(0%)/ThermalBoundaryType%) +%:fixedValue + %(0%) + { + type fixedValue; + value uniform %(multiRegionFluidBoundaries/%(0%)/Temperature%); + } +%:zeroGradient + %(0%) + { + type zeroGradient; + value $internalField; + } +%:fixedGradient + %(0%) + { + type externalWallHeatFluxTemperature; + mode flux; + q uniform %(multiRegionFluidBoundaries/%(0%)/HeatFlux%); + kappaMethod fluidThermo; + value $internalField; + } +%:totalPower + %(0%) + { + type externalWallHeatFluxTemperature; + mode power; + Q %(multiRegionFluidBoundaries/%(0%)/Power%); + kappaMethod fluidThermo; + value $internalField; + } +%:heatTransferCoeff + %(0%) + { + type externalWallHeatFluxTemperature; + mode coefficient; + Ta constant %(multiRegionFluidBoundaries/%(0%)/Temperature%); + h uniform %(multiRegionFluidBoundaries/%(0%)/HeatTransferCoeff%); + kappaMethod fluidThermo; + value $internalField; + } +%} +%:inlet + %(0%) + { + type inletOutlet; + inletValue uniform %(multiRegionFluidBoundaries/%(0%)/Temperature%); + value uniform %(multiRegionFluidBoundaries/%(0%)/Temperature%); + } +%:outlet + %(0%) + { + type inletOutlet; + inletValue uniform %(multiRegionFluidBoundaries/%(0%)/Temperature%); + value $internalField; + } +%:open + %(0%) + { + type inletOutlet; + inletValue uniform %(multiRegionFluidBoundaries/%(0%)/Temperature%); + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type compressible::turbulentTemperatureCoupledBaffleMixed; + value $internalField; + Tnbr T; + kappaMethod fluidThermo; + } + +%} + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/0/fluid/U b/Data/Templates/case/0/fluid/U new file mode 100644 index 00000000..922b162e --- /dev/null +++ b/Data/Templates/case/0/fluid/U @@ -0,0 +1,134 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volVectorField; + location "0/%(multiRegionFluidNames/0%)"; + object U; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (%(initialValues/Ux%) %(initialValues/Uy%) %(initialValues/Uz%)); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall +%{%(multiRegionFluidBoundaries/%(0%)/BoundarySubType%) +%:fixedWall roughWall + %(0%) + { + type noSlip; + } +%:slipWall + %(0%) + { + type slip; + } +%:partialSlipWall + %(0%) + { + type partialSlip; + valueFraction %(multiRegionFluidBoundaries/%(0%)/SlipRatio%); + value uniform (0 0 0); + } +%:translatingWall + %(0%) + { + type fixedValue; + value uniform (%(multiRegionFluidBoundaries/%(0%)/Ux%) %(multiRegionFluidBoundaries/%(0%)/Uy%) %(multiRegionFluidBoundaries/%(0%)/Uz%)); + } +%:rotatingWall + %(0%) + { + type rotatingWallVelocity; + origin (%(multiRegionFluidBoundaries/%(0%)/CentreOfRotationx%) %(multiRegionFluidBoundaries/%(0%)/CentreOfRotationy%) %(multiRegionFluidBoundaries/%(0%)/CentreOfRotationz%)); + axis (%(multiRegionFluidBoundaries/%(0%)/RotationAxisx%) %(multiRegionFluidBoundaries/%(0%)/RotationAxisy%) %(multiRegionFluidBoundaries/%(0%)/RotationAxisz%)); + omega %(multiRegionFluidBoundaries/%(0%)/AngularVelocity%); + } +%} +%:inlet +%{%(multiRegionFluidBoundaries/%(0%)/BoundarySubType%) +%:uniformVelocityInlet + %(0%) + { + type fixedValue; + value uniform (%(multiRegionFluidBoundaries/%(0%)/Ux%) %(multiRegionFluidBoundaries/%(0%)/Uy%) %(multiRegionFluidBoundaries/%(0%)/Uz%)); + } +%:volumetricFlowRateInlet + %(0%) + { + type flowRateInletVelocity; + volumetricFlowRate %(multiRegionFluidBoundaries/%(0%)/VolumetricFlowRate%); + value uniform (0 0 0); + } +%:massFlowRateInlet + %(0%) + { + type flowRateInletVelocity; + massFlowRate %(multiRegionFluidBoundaries/%(0%)/MassFlowRate%); + rho rho; + value uniform (0 0 0); + } +%:totalPressureInlet staticPressureInlet + %(0%) + { + type pressureInletOutletVelocity; + value uniform (0 0 0); + } +%} +%:outlet +%{%(multiRegionFluidBoundaries/%(0%)/BoundarySubType%) +%:staticPressureOutlet + %(0%) + { + type pressureInletOutletVelocity; + value uniform (0 0 0); + } +%:uniformVelocityOutlet + %(0%) + { + type fixedValue; + value uniform (%(multiRegionFluidBoundaries/%(0%)/Ux%) %(multiRegionFluidBoundaries/%(0%)/Uy%) %(multiRegionFluidBoundaries/%(0%)/Uz%)); + } +%:outFlowOutlet + %(0%) + { + type outletInlet; + outletValue uniform (0 0 0); + value uniform (0 0 0); + } +%} +%:open + %(0%) + { + type pressureInletOutletVelocity; + value uniform (0 0 0); + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type noSlip; + } + +%} + defaultFaces + { + type noSlip; + } +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/0/fluid/alphat b/Data/Templates/case/0/fluid/alphat new file mode 100644 index 00000000..2ce8a3dd --- /dev/null +++ b/Data/Templates/case/0/fluid/alphat @@ -0,0 +1,62 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object alphat; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +%:inlet outlet open + %(0%) + { + type calculated; + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } + +%} + defaultFaces + { + type compressible::alphatWallFunction; + Prt 0.85; + value $internalField; + } +} + +// ************************************************************************* // +%} +%} diff --git a/Data/Templates/case/0/fluid/epsilon b/Data/Templates/case/0/fluid/epsilon new file mode 100644 index 00000000..859c5a5e --- /dev/null +++ b/Data/Templates/case/0/fluid/epsilon @@ -0,0 +1,63 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(physics/Turbulence%) +%:RANS +%{%(physics/TurbulenceModel%) +%:kEpsilon +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object epsilon; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -3 0 0 0 0]; + +internalField uniform %(initialValues/epsilon%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type epsilonWallFunction; + value $internalField; + } +%:inlet outlet open + %(0%) + { + type inletOutlet; + inletValue uniform %(initialValues/epsilon%); + value uniform %(initialValues/epsilon%); + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type epsilonWallFunction; + value $internalField; + } + +%} + defaultFaces + { + type epsilonWallFunction; + value $internalField; + } +} + +// ************************************************************************* // +%} +%} +%} diff --git a/Data/Templates/case/0/fluid/k b/Data/Templates/case/0/fluid/k new file mode 100644 index 00000000..a83fb3ad --- /dev/null +++ b/Data/Templates/case/0/fluid/k @@ -0,0 +1,63 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%{%(physics/TurbulenceModel%) +%:kOmegaSST kOmegaSSTDES kOmegaSSTDDES kOmegaSSTIDDES kEpsilon kOmegaSSTLM kEqn +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object k; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -2 0 0 0 0]; + +internalField uniform %(initialValues/k%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type kqRWallFunction; + value $internalField; + } +%:inlet outlet open + %(0%) + { + type inletOutlet; + inletValue uniform %(initialValues/k%); + value uniform %(initialValues/k%); + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type kqRWallFunction; + value $internalField; + } + +%} + defaultFaces + { + type kqRWallFunction; + value $internalField; + } +} + +// ************************************************************************* // +%} +%} +%} diff --git a/Data/Templates/case/0/fluid/nut b/Data/Templates/case/0/fluid/nut new file mode 100644 index 00000000..bec6aa8a --- /dev/null +++ b/Data/Templates/case/0/fluid/nut @@ -0,0 +1,59 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(physics/Turbulence%) +%:RANS DES LES +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object nut; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 2 -1 0 0 0 0]; + +internalField uniform 0; + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type nutkWallFunction; + value $internalField; + } +%:inlet outlet open + %(0%) + { + type calculated; + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type nutkWallFunction; + value $internalField; + } + +%} + defaultFaces + { + type nutkWallFunction; + value $internalField; + } +} + +// ************************************************************************* // +%} +%} diff --git a/Data/Templates/case/0/fluid/omega b/Data/Templates/case/0/fluid/omega new file mode 100644 index 00000000..cb154740 --- /dev/null +++ b/Data/Templates/case/0/fluid/omega @@ -0,0 +1,63 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(physics/Turbulence%) +%:RANS DES +%{%(physics/TurbulenceModel%) +%:kOmegaSST kOmegaSSTDES kOmegaSSTDDES kOmegaSSTIDDES kOmegaSSTLM +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object omega; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 -1 0 0 0 0]; + +internalField uniform %(initialValues/omega%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type omegaWallFunction; + value $internalField; + } +%:inlet outlet open + %(0%) + { + type inletOutlet; + inletValue uniform %(initialValues/omega%); + value uniform %(initialValues/omega%); + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type omegaWallFunction; + value $internalField; + } + +%} + defaultFaces + { + type omegaWallFunction; + value $internalField; + } +} + +// ************************************************************************* // +%} +%} +%} diff --git a/Data/Templates/case/0/fluid/p b/Data/Templates/case/0/fluid/p new file mode 100644 index 00000000..3a7a1959 --- /dev/null +++ b/Data/Templates/case/0/fluid/p @@ -0,0 +1,50 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform %(initialValues/Pressure%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:constraint +%[0/_boundary_constraint%] +%:default + %(0%) + { + type calculated; + value $internalField; + } +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type calculated; + value $internalField; + } + +%} + defaultFaces + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/0/fluid/p_rgh b/Data/Templates/case/0/fluid/p_rgh new file mode 100644 index 00000000..ca29c7b7 --- /dev/null +++ b/Data/Templates/case/0/fluid/p_rgh @@ -0,0 +1,94 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionFluidNames/0%)"; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform %(initialValues/Pressure%); + +boundaryField +{ +%{%(multiRegionFluidBoundaries%) +%{%(multiRegionFluidBoundaries/%(0%)/BoundaryType%) +%:wall + %(0%) + { + type fixedFluxPressure; + value $internalField; + } +%:inlet +%{%(multiRegionFluidBoundaries/%(0%)/BoundarySubType%) +%:totalPressureInlet + %(0%) + { + type totalPressure; + p0 uniform %(multiRegionFluidBoundaries/%(0%)/Pressure%); + value $internalField; + } +%:staticPressureInlet + %(0%) + { + type fixedValue; + value uniform %(multiRegionFluidBoundaries/%(0%)/Pressure%); + } +%:default + %(0%) + { + type fixedFluxPressure; + value $internalField; + } +%} +%:outlet +%{%(multiRegionFluidBoundaries/%(0%)/BoundarySubType%) +%:staticPressureOutlet + %(0%) + { + type fixedValue; + value uniform %(multiRegionFluidBoundaries/%(0%)/Pressure%); + } +%:default + %(0%) + { + type fixedFluxPressure; + value $internalField; + } +%} +%:open + %(0%) + { + type totalPressure; + p0 uniform %(multiRegionFluidBoundaries/%(0%)/Pressure%); + value $internalField; + } +%:constraint +%[0/_boundary_constraint%] +%} + +%} + +%{%(multiRegionSolidNames%) + %(multiRegionFluidNames/0%)_to_%(multiRegionSolidNames/%(0%)%) + { + type fixedFluxPressure; + value $internalField; + } + +%} + defaultFaces + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/0/gammaInt b/Data/Templates/case/0/gammaInt index f4075071..153de4e1 100644 --- a/Data/Templates/case/0/gammaInt +++ b/Data/Templates/case/0/gammaInt @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS %{%(physics/TurbulenceModel%) @@ -76,3 +79,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/k b/Data/Templates/case/0/k index a38eac73..ad82bfdc 100644 --- a/Data/Templates/case/0/k +++ b/Data/Templates/case/0/k @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS DES LES %{%(physics/TurbulenceModel%) @@ -84,3 +87,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/nuTilda b/Data/Templates/case/0/nuTilda index 36de2cb0..de1ff849 100644 --- a/Data/Templates/case/0/nuTilda +++ b/Data/Templates/case/0/nuTilda @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS DES %{%(physics/TurbulenceModel%) @@ -67,3 +70,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/nut b/Data/Templates/case/0/nut index 58adfb33..d16389e3 100644 --- a/Data/Templates/case/0/nut +++ b/Data/Templates/case/0/nut @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS DES LES %[_header%] @@ -91,3 +94,4 @@ boundaryField // ************************************************************************* // %} +%} diff --git a/Data/Templates/case/0/omega b/Data/Templates/case/0/omega index a7749e9d..84e23355 100644 --- a/Data/Templates/case/0/omega +++ b/Data/Templates/case/0/omega @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %{%(physics/Turbulence%) %:RANS DES %{%(physics/TurbulenceModel%) @@ -78,3 +81,4 @@ boundaryField // ************************************************************************* // %} %} +%} diff --git a/Data/Templates/case/0/p b/Data/Templates/case/0/p index 5bcba36b..6ef02b7e 100644 --- a/Data/Templates/case/0/p +++ b/Data/Templates/case/0/p @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:interFoam multiphaseInterFoam +%:interFoam multiphaseInterFoam chtMultiRegionSimpleFoam chtMultiRegionFoam %:default %[_header%] FoamFile diff --git a/Data/Templates/case/0/solid/T b/Data/Templates/case/0/solid/T new file mode 100644 index 00000000..6614cd24 --- /dev/null +++ b/Data/Templates/case/0/solid/T @@ -0,0 +1,88 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionSolidNames/%(0%)%)"; + object T; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform %(initialValues/Temperature%); + +boundaryField +{ +%{%(multiRegionSolidBoundaries%) +%{%(multiRegionSolidBoundaries/%(0%)/BoundaryType%) +%:wall +%{%(multiRegionSolidBoundaries/%(0%)/ThermalBoundaryType%) +%:fixedValue + %(0%) + { + type fixedValue; + value uniform %(multiRegionSolidBoundaries/%(0%)/Temperature%); + } +%:zeroGradient + %(0%) + { + type zeroGradient; + value $internalField; + } +%:fixedGradient + %(0%) + { + type externalWallHeatFluxTemperature; + mode flux; + q uniform %(multiRegionSolidBoundaries/%(0%)/HeatFlux%); + kappaMethod solidThermo; + value $internalField; + } +%:totalPower + %(0%) + { + type externalWallHeatFluxTemperature; + mode power; + Q %(multiRegionSolidBoundaries/%(0%)/Power%); + kappaMethod solidThermo; + value $internalField; + } +%:heatTransferCoeff + %(0%) + { + type externalWallHeatFluxTemperature; + mode coefficient; + Ta constant %(multiRegionSolidBoundaries/%(0%)/Temperature%); + h uniform %(multiRegionSolidBoundaries/%(0%)/HeatTransferCoeff%); + kappaMethod solidThermo; + value $internalField; + } +%} +%:constraint +%[0/_boundary_constraint%] +%} + +%} + + %(multiRegionSolidNames/%(0%)%)_to_%(multiRegionFluidNames/0%) + { + type compressible::turbulentTemperatureCoupledBaffleMixed; + value $internalField; + Tnbr T; + kappaMethod solidThermo; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // +%} 0/%(multiRegionSolidNames/%(0%)%)/T +%} diff --git a/Data/Templates/case/0/solid/p b/Data/Templates/case/0/solid/p new file mode 100644 index 00000000..0db3d80a --- /dev/null +++ b/Data/Templates/case/0/solid/p @@ -0,0 +1,45 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/%(multiRegionSolidNames/%(0%)%)"; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform %(initialValues/Pressure%); + +boundaryField +{ +%{%(multiRegionSolidBoundaries%) +%{%(multiRegionSolidBoundaries/%(0%)/BoundaryType%) +%:constraint +%[0/_boundary_constraint%] +%:default + %(0%) + { + type calculated; + value $internalField; + } +%} + +%} + + // Derived from p_rgh at fluid-solid interface + defaultFaces + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // +%} 0/%(multiRegionSolidNames/%(0%)%)/p +%} diff --git a/Data/Templates/case/Allrun b/Data/Templates/case/Allrun index 217a146a..792e1b0d 100644 --- a/Data/Templates/case/Allrun +++ b/Data/Templates/case/Allrun @@ -177,6 +177,168 @@ else runCommand topoSet -dict system/topoSetZonesDict fi +%} +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +# Split combined mesh into fluid and solid regions +runCommand splitMeshRegions -cellZonesOnly -overwrite + +%{%(meanVelocityForceCellZonesPresent%) +%:True +# Create mean velocity force cell zones inside the split fluid region mesh +runCommand topoSet -region %(multiRegionFluidNames/0%) -dict system/topoSetMeanVelocityForceZonesDict + +%} +# Upgrade sampleMode on all mappedWall interface patches to nearestPatchFaceAMI (parallel-safe) +# and add missing patch entries to 0/ field files for any interface patches splitMeshRegions created. +# splitMeshRegions already creates correct _to_ / _to_ mappedWall pairs; +# we only need to upgrade their sampleMode and populate the 0/ field files. +env -u PYTHONPATH -u PYTHONHOME python3 << 'PYEOF' +import re, os, glob + +FLUID_REGIONS = [] +SOLID_REGIONS = [] +try: + with open('constant/regionProperties') as f: + txt = f.read() + fm = re.search(r'fluid\s*\(\s*([^)]*?)\s*\)', txt) + sm = re.search(r'solid\s*\(\s*([^)]*?)\s*\)', txt) + if fm: + FLUID_REGIONS = fm.group(1).split() + if sm: + SOLID_REGIONS = sm.group(1).split() +except FileNotFoundError: + pass + +def fix_samplemode_to_ami(bfile): + """Upgrade sampleMode nearestPatchFace → nearestPatchFaceAMI for parallel CHT.""" + with open(bfile) as f: + txt = f.read() + txt = txt.replace('sampleMode nearestPatchFace;\n', + 'sampleMode nearestPatchFaceAMI;\n') + with open(bfile, 'w') as f: + f.write(txt) + +def get_mapped_patches(bfile): + """Return names of all mappedWall patches in boundary file.""" + with open(bfile) as f: + txt = f.read() + return re.findall(r'\n\s+(\w+)\s*\n\s*\{[^}]*type\s+mappedWall', txt) + +def get_nonmapped_patches(bfile): + """Return names of all non-mappedWall patches in boundary file.""" + with open(bfile) as f: + txt = f.read() + all_patches = re.findall(r'\n (\w+)\n \{', txt) + mapped = set(re.findall(r'\n\s+(\w+)\s*\n\s*\{[^}]*type\s+mappedWall', txt)) + return [p for p in all_patches if p not in mapped] + +def patch_present(field_txt, patch_name): + return bool(re.search(r'\n\s+' + re.escape(patch_name) + r'\s*\n\s*\{', field_txt)) + +def add_patch_entry(field_file, patch_name, bc_lines): + with open(field_file) as f: + txt = f.read() + if patch_present(txt, patch_name): + return + entry = '\n ' + patch_name + '\n {\n' + for line in bc_lines: + entry += ' ' + line + '\n' + entry += ' }\n' + # Insert before the LAST closing brace (end of boundaryField, not FoamFile header) + matches = list(re.finditer(r'\n}', txt)) + if matches: + pos = matches[-1].start() + 1 + txt = txt[:pos] + entry + txt[pos:] + with open(field_file, 'w') as f: + f.write(txt) + +def add_missing_patches_to_fields(region, patches, is_fluid): + field_dir = os.path.join('0', region) + if not os.path.isdir(field_dir): + return + for patch in patches: + for field_file in glob.glob(os.path.join(field_dir, '*')): + fname = os.path.basename(field_file) + if fname == 'T': + kappa = 'fluidThermo' if is_fluid else 'solidThermo' + add_patch_entry(field_file, patch, [ + 'type compressible::turbulentTemperatureCoupledBaffleMixed;', + 'value $internalField;', + 'Tnbr T;', + 'kappaMethod ' + kappa + ';', + ]) + elif fname == 'U' and is_fluid: + add_patch_entry(field_file, patch, ['type noSlip;']) + elif fname == 'p_rgh' and is_fluid: + add_patch_entry(field_file, patch, [ + 'type fixedFluxPressure;', + 'value $internalField;', + ]) + elif fname == 'p': + add_patch_entry(field_file, patch, [ + 'type calculated;', + 'value $internalField;', + ]) + elif fname == 'nut' and is_fluid: + add_patch_entry(field_file, patch, [ + 'type nutkWallFunction;', + 'value $internalField;', + ]) + elif fname in ('k', 'epsilon', 'omega', 'nuTilda', 'alphat') and is_fluid: + add_patch_entry(field_file, patch, ['type zeroGradient;']) + +for r in FLUID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue +%{%(solver/Parallel%) +%:True + fix_samplemode_to_ami(bfile) +%} +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue +%{%(solver/Parallel%) +%:True + fix_samplemode_to_ami(bfile) +%} + +# Add 0/ field entries for all mappedWall interface patches splitMeshRegions created +for r in FLUID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + mapped = [p for p in get_mapped_patches(bfile) if p != 'defaultFaces'] + add_missing_patches_to_fields(r, mapped, is_fluid=True) +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + mapped = [p for p in get_mapped_patches(bfile) if p != 'defaultFaces'] + add_missing_patches_to_fields(r, mapped, is_fluid=False) + +# Add default BC entries for non-mapped patches in solid regions that aren't yet in +# the field files (e.g. inlet/outlet/wall faces that fall inside a solid volume after +# splitMeshRegions). OpenFOAM requires an explicit entry for every patch in boundaryField. +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + nonmapped = get_nonmapped_patches(bfile) + for patch in nonmapped: + for field_file in glob.glob(os.path.join('0', r, '*')): + fname = os.path.basename(field_file) + if fname == 'T': + add_patch_entry(field_file, patch, ['type zeroGradient;']) + elif fname == 'p': + add_patch_entry(field_file, patch, [ + 'type calculated;', + 'value $internalField;', + ]) +PYEOF + %} %{%(initialisationZonesPresent%) %:True @@ -214,12 +376,23 @@ PNAME=p %} %{%(solver/Parallel%) %:True +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +# Parallel decomposition +# Copy decomposeParDict to each region's system directory +for region in $(ls -d constant/*/polyMesh 2>/dev/null | cut -d/ -f2); do + mkdir -p system/$region + cp system/decomposeParDict system/$region/decomposeParDict +done +runCommand decomposePar -allRegions -force +%:default # Parallel decomposition if [ ! -d processor0 ] then runCommand decomposePar -force fi +%} # Pick up number of parallel processes NPROC=$(foamDictionary -entry "numberOfSubdomains" -value system/decomposeParDict) @@ -229,10 +402,14 @@ NPROC=$(foamDictionary -entry "numberOfSubdomains" -value system/decomposeParDic %:False %{%(MovingMeshRegionsPresent%) %:False +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default # Mesh renumbering runParallel $NPROC renumberMesh -overwrite %} %} +%} %:True # Mesh renumbering does not work in Foundation with dynamic mesh %} @@ -254,6 +431,10 @@ fi %} %{%(initialValues/PotentialFlow%) %:True +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +# potentialFoam not applicable to multi-region CHT (no single-region polyMesh after splitMeshRegions) +%:default # Initialise flow %{%(bafflesPresent%) %:True @@ -272,6 +453,7 @@ runParallel $NPROC potentialFoam -initialiseUBCs -pName $PNAME # Remove phi with wrong units rm -f processor*/0/phi %} +%} %} # Run application in parallel @@ -294,6 +476,7 @@ then else runParallel $NPROC %(solver/SolverName%) fi + %:False %{%(dynamicMeshEnabled%) %:False @@ -301,10 +484,14 @@ fi %:False %{%(MovingMeshRegionsPresent%) %:False +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default # Mesh renumbering runCommand renumberMesh -overwrite %} %} +%} %:True # Mesh renumbering does not currently work in Foundation with dynamic mesh # runCommand renumberMesh -overwrite @@ -352,5 +539,5 @@ then else runCommand %(solver/SolverName%) fi -%} +%} diff --git a/Data/Templates/case/constant/fluid/fvOptions b/Data/Templates/case/constant/fluid/fvOptions new file mode 100644 index 00000000..797bcfdf --- /dev/null +++ b/Data/Templates/case/constant/fluid/fvOptions @@ -0,0 +1,68 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionFluidNames/0%)"; + object fvOptions; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +limitT +{ + type limitTemperature; + active yes; + selectionMode all; + min 200; + max 5000; +} + +#includeIfPresent "fvOptionsScalarTransport" + +%{%(meanVelocityForceEnabled%) +%:True +momentumSource +{ + type meanVelocityForce; + selectionMode all; + active yes; + meanVelocityForceCoeffs + { + selectionMode all; + fields (U); + direction (%(meanVelocityForce/Direction%)); + Ubar (%(meanVelocityForce/Ubar%)); + relaxation %(meanVelocityForce/Relaxation%); + } +} + +%} +%{%(meanVelocityForceCellZonesPresent%) +%:True +%{%(meanVelocityForceCellZones%) +%{%(meanVelocityForceCellZones/%(0%)/PartNameList%) +%(0%)MomentumSource +{ + type meanVelocityForce; + selectionMode cellZone; + cellZone %(0%); + active yes; + meanVelocityForceCoeffs + { + selectionMode cellZone; + cellZone %(0%); + fields (U); + direction (%(meanVelocityForceCellZones/%(1%)/Direction%)); + Ubar (%(meanVelocityForceCellZones/%(1%)/Ubar%)); + relaxation %(meanVelocityForceCellZones/%(1%)/Relaxation%); + } +} + +%} +%} +%} +// ************************************************************************* // +%} diff --git a/Data/Templates/case/constant/fluid/momentumTransport b/Data/Templates/case/constant/fluid/momentumTransport new file mode 100644 index 00000000..67610c9d --- /dev/null +++ b/Data/Templates/case/constant/fluid/momentumTransport @@ -0,0 +1,15 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionFluidNames/0%)"; + object momentumTransport; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "turbulenceProperties" +%} diff --git a/Data/Templates/case/constant/fluid/thermophysicalProperties b/Data/Templates/case/constant/fluid/thermophysicalProperties new file mode 100644 index 00000000..bed7a2f2 --- /dev/null +++ b/Data/Templates/case/constant/fluid/thermophysicalProperties @@ -0,0 +1,82 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionFluidNames/0%)"; + object thermophysicalProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture pureMixture; + specie specie; +%{%(fluidProperties/0/Type%) +%:Incompressible + equationOfState icoPolynomial; + thermo hPolynomial; + transport polynomial; +%:default + equationOfState perfectGas; + thermo hConst; +%{%(physics/Turbulence%) +%:Inviscid + transport const; +%:default + transport sutherland; +%} +%} + energy sensibleEnthalpy; +} + +mixture +{ + specie + { + nMoles 1; + molWeight %(fluidProperties/0/MolarMass%); + } +%{%(fluidProperties/0/Type%) +%:Incompressible + equationOfState + { + rhoCoeffs<8> (%(fluidProperties/0/DensityPolynomial%)); + } + thermodynamics + { + CpCoeffs<8> (%(fluidProperties/0/CpPolynomial%)); + Hf 0; + Sf 0; + } + transport + { + muCoeffs<8> (%(fluidProperties/0/DynamicViscosityPolynomial%)); + kappaCoeffs<8> (%(fluidProperties/0/ThermalConductivityPolynomial%)); + } +%:default + thermodynamics + { + Cp %(fluidProperties/0/Cp%); + Hf 0; + } + transport + { +%{%(physics/Turbulence%) +%:Inviscid + mu 0; + Pr 1; +%:default + As %(fluidProperties/0/SutherlandConstant%); + Ts %(fluidProperties/0/SutherlandTemperature%); +%} + } +%} +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/constant/fluid/turbulenceProperties b/Data/Templates/case/constant/fluid/turbulenceProperties new file mode 100644 index 00000000..49056467 --- /dev/null +++ b/Data/Templates/case/constant/fluid/turbulenceProperties @@ -0,0 +1,57 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionFluidNames/0%)"; + object turbulenceProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +%{%(physics/Turbulence%) +%:RANS +simulationType RAS; + +RAS +{ + RASModel %(physics/TurbulenceModel%); + turbulence on; + printCoeffs on; +} +%:DES +simulationType LES; + +LES +{ + LESModel %(physics/TurbulenceModel%); + turbulence on; + printCoeffs on; + delta cubeRootVol; + cubeRootVolCoeffs + { + deltaCoeff 1; + } +} +%:LES +simulationType LES; + +LES +{ + LESModel %(physics/TurbulenceModel%); + turbulence on; + printCoeffs on; + delta cubeRootVol; + cubeRootVolCoeffs + { + deltaCoeff 1; + } +} +%:default +simulationType laminar; +%} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/constant/g b/Data/Templates/case/constant/g index 0ca29fe2..7de9b48f 100644 --- a/Data/Templates/case/constant/g +++ b/Data/Templates/case/constant/g @@ -1,5 +1,5 @@ %{%(solver/SolverName%) -%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam +%:buoyantSimpleFoam buoyantPimpleFoam interFoam multiphaseInterFoam chtMultiRegionSimpleFoam chtMultiRegionFoam %[_header%] FoamFile { diff --git a/Data/Templates/case/constant/momentumTransport b/Data/Templates/case/constant/momentumTransport index a7298c5c..78f0baef 100644 --- a/Data/Templates/case/constant/momentumTransport +++ b/Data/Templates/case/constant/momentumTransport @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %[_header%] FoamFile { @@ -8,4 +11,4 @@ FoamFile } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -#include "turbulenceProperties" \ No newline at end of file +#include "turbulenceProperties"%} diff --git a/Data/Templates/case/constant/regionProperties b/Data/Templates/case/constant/regionProperties new file mode 100644 index 00000000..28ca31a0 --- /dev/null +++ b/Data/Templates/case/constant/regionProperties @@ -0,0 +1,28 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object regionProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +regions +( + fluid ( +%{%(multiRegionFluidNames%) + %(multiRegionFluidNames/%(0%)%) +%} + ) + solid ( +%{%(multiRegionSolidNames%) + %(multiRegionSolidNames/%(0%)%) +%} + ) +); + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/constant/solid/fvOptions b/Data/Templates/case/constant/solid/fvOptions new file mode 100644 index 00000000..8ab6090c --- /dev/null +++ b/Data/Templates/case/constant/solid/fvOptions @@ -0,0 +1,29 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionSolidNames/%(0%)%)"; + object fvOptions; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +heatSource +{ + type scalarSemiImplicitSource; + active %(solidProperties/%(0%)/HeatGenerationActive%); + selectionMode all; + volumeMode specific; + injectionRateSuSp + { + h (%(solidProperties/%(0%)/HeatGeneration%) 0); + } +} + +// ************************************************************************* // +%} constant/%(multiRegionSolidNames/%(0%)%)/fvOptions +%} diff --git a/Data/Templates/case/constant/solid/thermophysicalProperties b/Data/Templates/case/constant/solid/thermophysicalProperties new file mode 100644 index 00000000..cce33ddb --- /dev/null +++ b/Data/Templates/case/constant/solid/thermophysicalProperties @@ -0,0 +1,50 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/%(multiRegionSolidNames/%(0%)%)"; + object thermophysicalProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heSolidThermo; + mixture pureMixture; + transport constIso; + thermo hConst; + equationOfState rhoConst; + specie specie; + energy sensibleEnthalpy; +} + +mixture +{ + specie + { + nMoles 1; + molWeight 1; + } + transport + { + kappa %(solidProperties/%(0%)/ThermalConductivity%); + } + thermodynamics + { + Cp %(solidProperties/%(0%)/Cp%); + Hf 0; + } + equationOfState + { + rho %(solidProperties/%(0%)/Density%); + } +} + +// ************************************************************************* // +%} constant/%(multiRegionSolidNames/%(0%)%)/thermophysicalProperties +%} diff --git a/Data/Templates/case/constant/turbulenceProperties b/Data/Templates/case/constant/turbulenceProperties index 967c15a7..3747ba06 100644 --- a/Data/Templates/case/constant/turbulenceProperties +++ b/Data/Templates/case/constant/turbulenceProperties @@ -1,3 +1,6 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%:default %[_header%] FoamFile { @@ -50,3 +53,4 @@ LES %:default simulationType laminar; %} +%} diff --git a/Data/Templates/case/system/decomposeParDict b/Data/Templates/case/system/decomposeParDict index 0f55c7d5..73daa9a2 100644 --- a/Data/Templates/case/system/decomposeParDict +++ b/Data/Templates/case/system/decomposeParDict @@ -20,7 +20,39 @@ constraints { type preserveBaffles; } +%{%(createPatchesForPeriodics%) +%:True +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(createPeriodics%) + preservePatches_%(0%) + { + type preservePatches; + patches ( %(0%)_master %(0%)_slave ); + enabled true; + } +%} +%} +%} } +%:False +%{%(createPatchesForPeriodics%) +%:True +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +constraints +{ +%{%(createPeriodics%) + preservePatches_%(0%) + { + type preservePatches; + patches ( %(0%)_master %(0%)_slave ); + enabled true; + } +%} +} +%} +%} %} // ************************************************************************* // diff --git a/Data/Templates/case/system/fluid/decomposeParDict b/Data/Templates/case/system/fluid/decomposeParDict new file mode 100644 index 00000000..a0f23020 --- /dev/null +++ b/Data/Templates/case/system/fluid/decomposeParDict @@ -0,0 +1,19 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionFluidNames/0%)"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains %(solver/ParallelCores%); + +method scotch; + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/system/fluid/fvSchemes b/Data/Templates/case/system/fluid/fvSchemes new file mode 100644 index 00000000..2a94b9af --- /dev/null +++ b/Data/Templates/case/system/fluid/fvSchemes @@ -0,0 +1,74 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionFluidNames/0%)"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default steadyState; +} + +gradSchemes +{ + default Gauss linear; + grad(U) cellLimited Gauss linear 1; +} + +divSchemes +{ + default none; + div(phi,U) bounded Gauss upwind; + div(phi,K) bounded Gauss upwind; + div(phi,h) bounded Gauss upwind; + div(((rho*nuEff)*dev2(T(grad(U))))) Gauss linear; +%{%(physics/Turbulence%) +%:RANS +%{%(physics/TurbulenceModel%) +%:kOmegaSST + div(phi,k) bounded Gauss upwind; + div(phi,omega) bounded Gauss upwind; +%:kEpsilon + div(phi,k) bounded Gauss upwind; + div(phi,epsilon) bounded Gauss upwind; +%:SpalartAllmaras + div(phi,nuTilda) bounded Gauss upwind; +%} +%} +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} + +fluxRequired +{ + default no; + p_rgh; +} + +wallDist +{ + method meshWave; +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/system/fluid/fvSolution b/Data/Templates/case/system/fluid/fvSolution new file mode 100644 index 00000000..c4fa7c8b --- /dev/null +++ b/Data/Templates/case/system/fluid/fvSolution @@ -0,0 +1,146 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionFluidNames/0%)"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +SIMPLE +{ + nNonOrthogonalCorrectors %(solver/FluidNonOrthogonalCorrectors%); + consistent no; + + residualControl + { + p_rgh %(solver/ConvergenceTol%); + U %(solver/ConvergenceTol%); + h %(solver/ConvergenceTol%); + "(k|epsilon|omega|nuTilda)" %(solver/ConvergenceTol%); + } + + pRefValue 0; + pRefCell 0; +} + +solvers +{ + "(p_rgh|pcorr)" + { + solver GAMG; + tolerance 1e-8; + relTol 0.01; + smoother symGaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + maxIter 20; + } + + "(p_rghFinal|pcorrFinal)" + { + $p_rgh; + relTol 0; + } + + U + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 0.1; + nSweeps 1; + } + + UFinal + { + $U; + relTol 0; + } + + h + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 0.1; + nSweeps 5; + } + + hFinal + { + $h; + relTol 0; + } + +%{%(physics/Turbulence%) +%:RANS DES LES + "(k|omega|epsilon|nuTilda)" + { + solver smoothSolver; + smoother GaussSeidel; + tolerance 1e-8; + relTol 0.1; + nSweeps 1; + minIter 1; + } + + "(k|omega|epsilon|nuTilda)Final" + { + $k; + relTol 0; + } + +%} + Phi + { + solver GAMG; + tolerance 1e-7; + relTol 0.01; + smoother GaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + } +} + +relaxationFactors +{ + equations + { + U %(solver/URelaxation%); + h %(solver/energyRelaxation%); +%{%(physics/Turbulence%) +%:RANS DES +%{%(physics/TurbulenceModel%) +%:kOmegaSST kOmegaSSTDES kOmegaSSTDDES kOmegaSSTIDDES + k 0.3; + omega 0.3; +%:kEpsilon + k 0.3; + epsilon 0.3; +%:SpalartAllmaras SpalartAllmarasDES SpalartAllmarasDDES SpalartAllmarasIDDES + nuTilda 0.3; +%} +%} + } + fields + { + p_rgh %(solver/pRelaxation%); + rho %(solver/rhoRelaxation%); + } +} + +// ************************************************************************* // +%} diff --git a/Data/Templates/case/system/fluid/topoSetMeanVelocityForceZonesDict b/Data/Templates/case/system/fluid/topoSetMeanVelocityForceZonesDict new file mode 100644 index 00000000..8103a36f --- /dev/null +++ b/Data/Templates/case/system/fluid/topoSetMeanVelocityForceZonesDict @@ -0,0 +1,55 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(meanVelocityForceCellZonesPresent%) +%:True +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionFluidNames/0%)"; + object topoSetMeanVelocityForceZonesDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +actions +( +%{%(meanVelocityForceCellZones%) +%{%(meanVelocityForceCellZones/%(0%)/PartNameList%) + { + name %(0%)SelectedSurface; + type cellSet; + action new; + source surfaceToCell; + sourceInfo + { + file "constant/triSurface/%(0%).stl"; + useSurfaceOrientation true; + outsidePoints (); + includeCut false; + includeInside true; + includeOutside false; + nearDistance -1; + curvature -100; + } + } + + { + name %(0%); + type cellZoneSet; + action new; + source setToCellZone; + sourceInfo + { + set %(0%)SelectedSurface; + } + } + +%} +%} +); + +// ************************************************************************* // +%} +%} diff --git a/Data/Templates/case/system/fvSchemes b/Data/Templates/case/system/fvSchemes index ad7ece9c..99d8d3de 100644 --- a/Data/Templates/case/system/fvSchemes +++ b/Data/Templates/case/system/fvSchemes @@ -1,3 +1,24 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes {} +gradSchemes {} +divSchemes {} +laplacianSchemes {} +interpolationSchemes {} +snGradSchemes {} + +// ************************************************************************* // +%:default %[_header%] FoamFile { @@ -258,3 +279,4 @@ wallDist // ************************************************************************* // +%} \ No newline at end of file diff --git a/Data/Templates/case/system/fvSolution b/Data/Templates/case/system/fvSolution index 842c32d2..abb5f86e 100644 --- a/Data/Templates/case/system/fvSolution +++ b/Data/Templates/case/system/fvSolution @@ -1,3 +1,19 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// (intentionally empty - solver settings are in system//fvSolution) + +// ************************************************************************* // +%:default %[_header%] FoamFile { @@ -442,3 +458,4 @@ cache } // ************************************************************************* // +%} diff --git a/Data/Templates/case/system/solid/decomposeParDict b/Data/Templates/case/system/solid/decomposeParDict new file mode 100644 index 00000000..c0e3746d --- /dev/null +++ b/Data/Templates/case/system/solid/decomposeParDict @@ -0,0 +1,21 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionSolidNames/%(0%)%)"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains %(solver/ParallelCores%); + +method scotch; + +// ************************************************************************* // +%} system/%(multiRegionSolidNames/%(0%)%)/decomposeParDict +%} diff --git a/Data/Templates/case/system/solid/fvSchemes b/Data/Templates/case/system/solid/fvSchemes new file mode 100644 index 00000000..91f6a788 --- /dev/null +++ b/Data/Templates/case/system/solid/fvSchemes @@ -0,0 +1,47 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionSolidNames/%(0%)%)"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default steadyState; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default none; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} + +// ************************************************************************* // +%} system/%(multiRegionSolidNames/%(0%)%)/fvSchemes +%} diff --git a/Data/Templates/case/system/solid/fvSolution b/Data/Templates/case/system/solid/fvSolution new file mode 100644 index 00000000..56371a7d --- /dev/null +++ b/Data/Templates/case/system/solid/fvSolution @@ -0,0 +1,58 @@ +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam +%{%(multiRegionSolidNames%) +%[_header%] +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/%(multiRegionSolidNames/%(0%)%)"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +SIMPLE +{ + nNonOrthogonalCorrectors %(solver/SolidNonOrthogonalCorrectors%); + + residualControl + { + h %(solver/ConvergenceTol%); + } +} + +solvers +{ + h + { + solver GAMG; + tolerance 1e-8; + relTol 0.1; + smoother symGaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + } + + hFinal + { + $h; + relTol 0; + } +} + +relaxationFactors +{ + equations + { + h %(solver/SolidEnergyRelaxation%); + } +} + +// ************************************************************************* // +%} system/%(multiRegionSolidNames/%(0%)%)/fvSolution +%} diff --git a/Data/Templates/case/system/topoSetZonesDict b/Data/Templates/case/system/topoSetZonesDict index 733acdd2..45ba80d5 100644 --- a/Data/Templates/case/system/topoSetZonesDict +++ b/Data/Templates/case/system/topoSetZonesDict @@ -13,6 +13,99 @@ FoamFile actions ( +%{%(solver/SolverName%) +%:chtMultiRegionSimpleFoam chtMultiRegionFoam + // Remove any pre-existing cell zones created by the mesh converter (e.g. gmshToFoam) + // before creating the CHT region cell zones + { + name Internal; + type cellZoneSet; + action clear; + } + + // --- Solid region cellSets (created first so they can be subtracted from fluid) --- +%{%(multiRegionSolidNamesDict%) + { + name %(0%)SelectedSurface; + type cellSet; + action new; + source surfaceToCell; + sourceInfo + { + file "constant/triSurface/%(0%).stl"; + useSurfaceOrientation true; + outsidePoints (); + includeCut false; + includeInside true; + includeOutside false; + nearDistance -1; + curvature -100; + } + } + +%} + // --- Fluid region cellSets (solid cells subtracted to handle solid-inside-fluid) --- +%{%(multiRegionFluidNamesDict%) + { + name %(0%)SelectedSurface; + type cellSet; + action new; + source surfaceToCell; + sourceInfo + { + file "constant/triSurface/%(0%).stl"; + useSurfaceOrientation true; + outsidePoints (); + includeCut false; + includeInside true; + includeOutside false; + nearDistance -1; + curvature -100; + } + } + +%{%(multiRegionSolidNamesDict%) + { + name %(1%)SelectedSurface; + type cellSet; + action subtract; + source cellToCell; + sourceInfo + { + set %(0%)SelectedSurface; + } + } + +%} +%} + // --- Create cellZones from cellSets --- +%{%(multiRegionSolidNamesDict%) + { + name %(0%); + type cellZoneSet; + action new; + source setToCellZone; + sourceInfo + { + set %(0%)SelectedSurface; + } + } + +%} +%{%(multiRegionFluidNamesDict%) + { + name %(0%); + type cellZoneSet; + action new; + source setToCellZone; + sourceInfo + { + set %(0%)SelectedSurface; + } + } + +%} +%:default %{%(zones%) %{%(zones/%(0%)/PartNameList%) { @@ -44,6 +137,7 @@ actions } } +%} %} %} ); diff --git a/Data/Templates/mesh/gmsh/geometry.geo b/Data/Templates/mesh/gmsh/geometry.geo index 3de2fa64..5e2090be 100644 --- a/Data/Templates/mesh/gmsh/geometry.geo +++ b/Data/Templates/mesh/gmsh/geometry.geo @@ -37,7 +37,14 @@ Mesh.Algorithm = 2; Mesh.Algorithm3D = 10; // Internal mesh +%{%(GmshSettings/IsMultiRegion%) +%:True +%{%(GmshSettings/RegionVolumeMap%) +Physical Volume ("%(0%)") = {%(GmshSettings/RegionVolumeMap/%(0%)%)}; +%} +%:default Physical Volume ("Internal") = {%(GmshSettings/Solids%)}; +%} // Boundaries %{%(GmshSettings/BoundaryFaceMap%) diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/T b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/T new file mode 100644 index 00000000..e07a88ff --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/T @@ -0,0 +1,58 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/FluidProperties"; + object T; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 293.0; + +boundaryField +{ + wall001 + { + type zeroGradient; + value $internalField; + } + + inlet + { + type inletOutlet; + inletValue uniform 293.0; + value uniform 293.0; + } + + outlet + { + type inletOutlet; + inletValue uniform 293.0; + value $internalField; + } + + + FluidProperties_to_aluminium + { + type compressible::turbulentTemperatureCoupledBaffleMixed; + value $internalField; + Tnbr T; + kappaMethod fluidThermo; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/U b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/U new file mode 100644 index 00000000..185b848d --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/U @@ -0,0 +1,52 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volVectorField; + location "0/FluidProperties"; + object U; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -1 0 0 0 0]; + +internalField uniform (0.0 0.0 0.0); + +boundaryField +{ + wall001 + { + type noSlip; + } + + inlet + { + type fixedValue; + value uniform (0.0 0.1 0.0); + } + + outlet + { + type pressureInletOutletVelocity; + value uniform (0 0 0); + } + + + FluidProperties_to_aluminium + { + type noSlip; + } + + defaultFaces + { + type noSlip; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p new file mode 100644 index 00000000..36ebf968 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p @@ -0,0 +1,55 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/FluidProperties"; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 100000.0; + +boundaryField +{ + wall001 + { + type calculated; + value $internalField; + } + + inlet + { + type calculated; + value $internalField; + } + + outlet + { + type calculated; + value $internalField; + } + + + FluidProperties_to_aluminium + { + type calculated; + value $internalField; + } + + defaultFaces + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p_rgh b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p_rgh new file mode 100644 index 00000000..27e50a00 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/FluidProperties/p_rgh @@ -0,0 +1,55 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/FluidProperties"; + object p_rgh; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 100000.0; + +boundaryField +{ + wall001 + { + type fixedFluxPressure; + value $internalField; + } + + inlet + { + type fixedFluxPressure; + value $internalField; + } + + outlet + { + type fixedValue; + value uniform 100000.0; + } + + + FluidProperties_to_aluminium + { + type fixedFluxPressure; + value $internalField; + } + + defaultFaces + { + type fixedFluxPressure; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/T b/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/T new file mode 100644 index 00000000..5b90c718 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/T @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/aluminium"; + object T; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 0 0 1 0 0 0]; + +internalField uniform 293.0; + +boundaryField +{ + heatsource001 + { + type fixedValue; + value uniform 393.0; + } + + + aluminium_to_FluidProperties + { + type compressible::turbulentTemperatureCoupledBaffleMixed; + value $internalField; + Tnbr T; + kappaMethod solidThermo; + } + + defaultFaces + { + type zeroGradient; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/p b/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/p new file mode 100644 index 00000000..da6f7573 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/0/aluminium/p @@ -0,0 +1,38 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class volScalarField; + location "0/aluminium"; + object p; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [1 -1 -2 0 0 0 0]; + +internalField uniform 100000.0; + +boundaryField +{ + heatsource001 + { + type calculated; + value $internalField; + } + + + // Derived from p_rgh at fluid-solid interface + defaultFaces + { + type calculated; + value $internalField; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/Allrun b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun new file mode 100755 index 00000000..9c3ef1ae --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun @@ -0,0 +1,236 @@ +#!/bin/bash + +runParallel() +{ + nproc="$1" + shift + exe="$(which $1)" + sol=$(basename -- "$1") + sol="${sol%.*}" + shift + if [ -f log."$sol" ]; then rm log."$sol"; fi + export OMPI_MCA_btl_vader_single_copy_mechanism=none # Workaround for open-mpi/docker bug + mpiexec -np $nproc "$exe" -parallel "$@" 1> >(tee -a log."$sol") 2> >(tee -a log."$sol" >&2) + err=$? + if [ ! $err -eq 0 ]; then exit $err; fi +} + +runCommand() +{ + sol=$(basename -- "$1") + sol="${sol%.*}" + if [ -f log."$sol" ]; then rm log."$sol"; fi + "$@" 1> >(tee -a log."$sol") 2> >(tee -a log."$sol" >&2) + err=$? + if [ ! $err -eq 0 ]; then exit $err; fi +} + +# Unset and source bashrc +FOAMDIR="/usr/lib/openfoam/openfoam2606" +if [ ! -z "$FOAMDIR" ] +then + source "$FOAMDIR/etc/config.sh/unset" 2> /dev/null + source "$FOAMDIR/etc/bashrc" +fi + +# Copy mesh from mesh case dir if available +MESHDIR="../meshCaseSimpleHeatFin" +if [ -f "$MESHDIR"/constant/polyMesh/faces ] +then + rm -rf constant/polyMesh + cp -r "$MESHDIR"/constant/polyMesh constant/polyMesh +elif [ ! -f constant/polyMesh/faces ] +then + echo "Fatal error: Unable to find mesh in directory $MESHDIR" 1>&2 + exit 1 +fi + +# Detect available turbulence lib +if [ -f "$FOAM_LIBBIN/libmomentumTransportModels.so" ] || [ -f "$FOAM_LIBBIN/libmomentumTransportModels.dll" ] +then + echo \"libmomentumTransportModels.so\" > system/turbulenceLib +else + echo \"libturbulenceModels.so\" > system/turbulenceLib +fi + +# Change specification of interface compression in OF 13+ +if [ -z ${FOAM_API+x} ] && [ "$WM_PROJECT_VERSION" -ge 13 ] +then + echo "div(phi,alpha) Gauss interfaceCompression vanLeer 1;" > system/alphaDivScheme + echo "" > system/cAlpha +else + echo "div(phi,alpha) Gauss vanLeer;" > system/alphaDivScheme + echo "cAlpha 1;" > system/cAlpha +fi + +# Update patch name and type +runCommand createPatch -overwrite + +# Split combined mesh into fluid and solid regions +runCommand splitMeshRegions -cellZonesOnly -overwrite + +# Upgrade sampleMode on all mappedWall interface patches to nearestPatchFaceAMI (parallel-safe) +# and add missing patch entries to 0/ field files for any interface patches splitMeshRegions created. +# splitMeshRegions already creates correct _to_ / _to_ mappedWall pairs; +# we only need to upgrade their sampleMode and populate the 0/ field files. +env -u PYTHONPATH -u PYTHONHOME python3 << 'PYEOF' +import re, os, glob + +FLUID_REGIONS = [] +SOLID_REGIONS = [] +try: + with open('constant/regionProperties') as f: + txt = f.read() + fm = re.search(r'fluid\s*\(\s*([^)]*?)\s*\)', txt) + sm = re.search(r'solid\s*\(\s*([^)]*?)\s*\)', txt) + if fm: + FLUID_REGIONS = fm.group(1).split() + if sm: + SOLID_REGIONS = sm.group(1).split() +except FileNotFoundError: + pass + +def fix_samplemode_to_ami(bfile): + """Upgrade sampleMode nearestPatchFace → nearestPatchFaceAMI for parallel CHT.""" + with open(bfile) as f: + txt = f.read() + txt = txt.replace('sampleMode nearestPatchFace;\n', + 'sampleMode nearestPatchFaceAMI;\n') + with open(bfile, 'w') as f: + f.write(txt) + +def get_mapped_patches(bfile): + """Return names of all mappedWall patches in boundary file.""" + with open(bfile) as f: + txt = f.read() + return re.findall(r'\n\s+(\w+)\s*\n\s*\{[^}]*type\s+mappedWall', txt) + +def get_nonmapped_patches(bfile): + """Return names of all non-mappedWall patches in boundary file.""" + with open(bfile) as f: + txt = f.read() + all_patches = re.findall(r'\n (\w+)\n \{', txt) + mapped = set(re.findall(r'\n\s+(\w+)\s*\n\s*\{[^}]*type\s+mappedWall', txt)) + return [p for p in all_patches if p not in mapped] + +def patch_present(field_txt, patch_name): + return bool(re.search(r'\n\s+' + re.escape(patch_name) + r'\s*\n\s*\{', field_txt)) + +def add_patch_entry(field_file, patch_name, bc_lines): + with open(field_file) as f: + txt = f.read() + if patch_present(txt, patch_name): + return + entry = '\n ' + patch_name + '\n {\n' + for line in bc_lines: + entry += ' ' + line + '\n' + entry += ' }\n' + # Insert before the LAST closing brace (end of boundaryField, not FoamFile header) + matches = list(re.finditer(r'\n}', txt)) + if matches: + pos = matches[-1].start() + 1 + txt = txt[:pos] + entry + txt[pos:] + with open(field_file, 'w') as f: + f.write(txt) + +def add_missing_patches_to_fields(region, patches, is_fluid): + field_dir = os.path.join('0', region) + if not os.path.isdir(field_dir): + return + for patch in patches: + for field_file in glob.glob(os.path.join(field_dir, '*')): + fname = os.path.basename(field_file) + if fname == 'T': + kappa = 'fluidThermo' if is_fluid else 'solidThermo' + add_patch_entry(field_file, patch, [ + 'type compressible::turbulentTemperatureCoupledBaffleMixed;', + 'value $internalField;', + 'Tnbr T;', + 'kappaMethod ' + kappa + ';', + ]) + elif fname == 'U' and is_fluid: + add_patch_entry(field_file, patch, ['type noSlip;']) + elif fname == 'p_rgh' and is_fluid: + add_patch_entry(field_file, patch, [ + 'type fixedFluxPressure;', + 'value $internalField;', + ]) + elif fname == 'p': + add_patch_entry(field_file, patch, [ + 'type calculated;', + 'value $internalField;', + ]) + elif fname == 'nut' and is_fluid: + add_patch_entry(field_file, patch, [ + 'type nutkWallFunction;', + 'value $internalField;', + ]) + elif fname in ('k', 'epsilon', 'omega', 'nuTilda', 'alphat') and is_fluid: + add_patch_entry(field_file, patch, ['type zeroGradient;']) + +for r in FLUID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + fix_samplemode_to_ami(bfile) +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + fix_samplemode_to_ami(bfile) + +# Add 0/ field entries for all mappedWall interface patches splitMeshRegions created +for r in FLUID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + mapped = [p for p in get_mapped_patches(bfile) if p != 'defaultFaces'] + add_missing_patches_to_fields(r, mapped, is_fluid=True) +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + mapped = [p for p in get_mapped_patches(bfile) if p != 'defaultFaces'] + add_missing_patches_to_fields(r, mapped, is_fluid=False) + +# Add default BC entries for non-mapped patches in solid regions that aren't yet in +# the field files (e.g. inlet/outlet/wall faces that fall inside a solid volume after +# splitMeshRegions). OpenFOAM requires an explicit entry for every patch in boundaryField. +for r in SOLID_REGIONS: + bfile = 'constant/' + r + '/polyMesh/boundary' + if not os.path.exists(bfile): + continue + nonmapped = get_nonmapped_patches(bfile) + for patch in nonmapped: + for field_file in glob.glob(os.path.join('0', r, '*')): + fname = os.path.basename(field_file) + if fname == 'T': + add_patch_entry(field_file, patch, ['type zeroGradient;']) + elif fname == 'p': + add_patch_entry(field_file, patch, [ + 'type calculated;', + 'value $internalField;', + ]) +PYEOF + +# Parallel decomposition +# Copy decomposeParDict to each region's system directory +for region in $(ls -d constant/*/polyMesh 2>/dev/null | cut -d/ -f2); do + mkdir -p system/$region + cp system/decomposeParDict system/$region/decomposeParDict +done +runCommand decomposePar -allRegions -force +# Pick up number of parallel processes +NPROC=$(foamDictionary -entry "numberOfSubdomains" -value system/decomposeParDict) + + +# Run application in parallel +# Detect new foamRun in Foundation versions >= 11 and translate solver +which foamRun > /dev/null 2>&1 +if [ $? == 0 ] +then + runParallel $NPROC chtMultiRegionSimpleFoam +else + runParallel $NPROC chtMultiRegionSimpleFoam +fi + diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.bat b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.bat new file mode 100644 index 00000000..0a7fd8b8 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.bat @@ -0,0 +1,30 @@ +@echo off + +REM Source runtime environment + +set FOAMDIR="/usr/lib/openfoam/openfoam2606" +set CWD=%CD% +set FOAMVER=None + +if %FOAMVER% GEQ 1000 goto OPENCFD +:FOUNDATION +set OLDPATH=%PATH% +call %FOAMDIR%\setvars_OF%FOAMVER%.bat +set PATH=%PATH%;%OLDPATH% + +REM Fix for error in FOAM_MPI in setvars-OF.bat +FOR /F "tokens=* USEBACKQ" %%F IN (`dir /b %%FOAM_LIBBIN%%\MS-MPI*`) DO ( +SET FOAM_MPI=%%F +) +set PATH=%FOAM_LIBBIN%\%FOAM_MPI%;%PATH% +goto CONTINUE + +:OPENCFD +call %FOAMDIR%\setEnvVariables-v%FOAMVER%.bat + +:CONTINUE + +cd /d %CWD% + +REM Run PowerShell script +PowerShell -NoProfile -ExecutionPolicy Bypass -File Allrun.ps1 diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.ps1 b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.ps1 new file mode 100644 index 00000000..481f11d1 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/Allrun.ps1 @@ -0,0 +1,76 @@ +function runCommand([string]$cmd) +{ + $sol = (Split-Path -Leaf $cmd) + & $cmd $args 2>&1 | tee log.$sol + $err = $LASTEXITCODE + if( ! $LASTEXITCODE -eq 0 ) + { + exit $err + } +} + +function runParallel([int]$NumProcs, [string]$cmd) +{ + $sol = (Split-Path -Leaf $cmd) + & mpiexec -affinity -affinity_layout spr:P:L -np $NumProcs $cmd -parallel $args 2>&1 | tee log.$sol + $err = $LASTEXITCODE + if( ! $LASTEXITCODE -eq 0 ) + { + exit $err + } +} + +# Set piping to file to ascii +$PSDefaultParameterValues['Out-File:Encoding'] = 'ascii' + +# Copy mesh from mesh case dir if available +$MESHDIR = "../meshCaseSimpleHeatFin" +if( Test-Path -PathType Leaf $MESHDIR/constant/polyMesh/faces ) +{ + rm -ErrorAction SilentlyContinue -Recurse -Force constant/polyMesh + cp -Recurse $MESHDIR/constant/polyMesh constant/polyMesh +} +elseif( !(Test-Path -PathType Leaf constant/polyMesh/faces) ) +{ + throw "Fatal error: Unable to find mesh in directory $MESHDIR" +} + +# Set turbulence lib +if ( $Env:WM_PROJECT_VERSION[0] -eq "v" -or 10 -gt $Env:WM_PROJECT_VERSION ) +{ + echo '"libturbulenceModels"' > system/turbulenceLib +} +else +{ + echo '"libmomentumTransportModels"' > system/turbulenceLib +} + +# Set interface compression +echo "div(phi,alpha) Gauss vanLeer;" > system/alphaDivScheme +echo "cAlpha 1;" > system/cAlpha + +# Update patch name and type +runCommand createPatch -overwrite + +# Parallel decomposition +if( !(Test-Path -PathType Container processor0) ) +{ + runCommand decomposePar -force +} + +# Pick up number of parallel processes +$NPROC = foamDictionary -entry "numberOfSubdomains" -value system/decomposeParDict + +# Mesh renumbering +runParallel $NPROC renumberMesh -overwrite + +# Run application in parallel +# Detect new foamRun in Foundation versions >= 11 and translate solver +if( (Get-Command -ErrorAction SilentlyContinue foamRun) ) +{ + runParallel $NPROC chtMultiRegionSimpleFoam +} +else +{ + runParallel $NPROC chtMultiRegionSimpleFoam +} diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/fvOptions b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/fvOptions new file mode 100644 index 00000000..8a9b7783 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/fvOptions @@ -0,0 +1,28 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/FluidProperties"; + object fvOptions; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +limitT +{ + type limitTemperature; + active yes; + selectionMode all; + min 200; + max 5000; +} + +#includeIfPresent "fvOptionsScalarTransport" + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/momentumTransport b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/momentumTransport new file mode 100644 index 00000000..27831758 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/momentumTransport @@ -0,0 +1,17 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/FluidProperties"; + object momentumTransport; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "turbulenceProperties" diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/thermophysicalProperties b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/thermophysicalProperties new file mode 100644 index 00000000..b586c48e --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/thermophysicalProperties @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/FluidProperties"; + object thermophysicalProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heRhoThermo; + mixture pureMixture; + specie specie; + equationOfState perfectGas; + thermo hConst; + transport sutherland; + energy sensibleEnthalpy; +} + +mixture +{ + specie + { + nMoles 1; + molWeight 28.964389774888698; + } + thermodynamics + { + Cp 1004.703; + Hf 0; + } + transport + { + As 1.4579326545176254e-06; + Ts 110.4; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/turbulenceProperties b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/turbulenceProperties new file mode 100644 index 00000000..54fbcaf4 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/FluidProperties/turbulenceProperties @@ -0,0 +1,19 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/FluidProperties"; + object turbulenceProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +simulationType laminar; + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/fvOptions b/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/fvOptions new file mode 100644 index 00000000..661dea11 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/fvOptions @@ -0,0 +1,29 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/aluminium"; + object fvOptions; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +heatSource +{ + type scalarSemiImplicitSource; + active false; + selectionMode all; + volumeMode specific; + injectionRateSuSp + { + h (0.0 0); + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/thermophysicalProperties b/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/thermophysicalProperties new file mode 100644 index 00000000..9fcfc918 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/aluminium/thermophysicalProperties @@ -0,0 +1,50 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "constant/aluminium"; + object thermophysicalProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +thermoType +{ + type heSolidThermo; + mixture pureMixture; + transport constIso; + thermo hConst; + equationOfState rhoConst; + specie specie; + energy sensibleEnthalpy; +} + +mixture +{ + specie + { + nMoles 1; + molWeight 1; + } + transport + { + kappa 237.0; + } + thermodynamics + { + Cp 897.0; + Hf 0; + } + equationOfState + { + rho 2699.0; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/g b/Data/TestFiles/cases/SimpleHeatFin/case/constant/g new file mode 100644 index 00000000..3a17d8b3 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/g @@ -0,0 +1,19 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class uniformDimensionedVectorField; + object g; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +dimensions [0 1 -2 0 0 0 0]; +value (0.0 0.0 -9.81); + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/polyMesh/boundary b/Data/TestFiles/cases/SimpleHeatFin/case/constant/polyMesh/boundary new file mode 100644 index 00000000..999d67dd --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/polyMesh/boundary @@ -0,0 +1,44 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class polyBoundaryMesh; + object boundary; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +( + wall001 + { + type wall; + } + + heatsource001 + { + type wall; + } + + inlet + { + type patch; + } + + outlet + { + type patch; + } + + defaultFaces + { + type wall; + } + +) + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/regionProperties b/Data/TestFiles/cases/SimpleHeatFin/case/constant/regionProperties new file mode 100644 index 00000000..b9df13b5 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/regionProperties @@ -0,0 +1,26 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object regionProperties; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +regions +( + fluid ( + FluidProperties + ) + solid ( + aluminium + ) +); + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/FluidProperties.stl b/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/FluidProperties.stl new file mode 100644 index 00000000..caadbcea --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/FluidProperties.stl @@ -0,0 +1,86 @@ +solid FluidProperties + facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 0.0 0.0 0.05 + vertex 0.0 0.03 0.0 + endloop + endfacet + facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 0.03 0.0 + vertex 0.0 0.0 0.05 + vertex 0.0 0.03 0.05 + endloop + endfacet + facet normal 1.0 0.0 0.0 + outer loop + vertex 0.07 0.0 0.05 + vertex 0.07 0.0 0.0 + vertex 0.07 0.03 0.0 + endloop + endfacet + facet normal 1.0 -0.0 0.0 + outer loop + vertex 0.07 0.0 0.05 + vertex 0.07 0.03 0.0 + vertex 0.07 0.03 0.05 + endloop + endfacet + facet normal 0.0 -1.0 0.0 + outer loop + vertex 0.07 0.0 0.0 + vertex 0.07 0.0 0.05 + vertex 0.0 0.0 0.0 + endloop + endfacet + facet normal 0.0 -1.0 0.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 0.07 0.0 0.05 + vertex 0.0 0.0 0.05 + endloop + endfacet + facet normal 0.0 1.0 0.0 + outer loop + vertex 0.07 0.03 0.05 + vertex 0.07 0.03 0.0 + vertex 0.0 0.03 0.0 + endloop + endfacet + facet normal 0.0 1.0 0.0 + outer loop + vertex 0.07 0.03 0.05 + vertex 0.0 0.03 0.0 + vertex 0.0 0.03 0.05 + endloop + endfacet + facet normal 0.0 0.0 -1.0 + outer loop + vertex 0.0 0.03 0.0 + vertex 0.07 0.03 0.0 + vertex 0.0 0.0 0.0 + endloop + endfacet + facet normal 0.0 0.0 -1.0 + outer loop + vertex 0.0 0.0 0.0 + vertex 0.07 0.03 0.0 + vertex 0.07 0.0 0.0 + endloop + endfacet + facet normal 0.0 0.0 1.0 + outer loop + vertex 0.07 0.03 0.05 + vertex 0.0 0.03 0.05 + vertex 0.0 0.0 0.05 + endloop + endfacet + facet normal 0.0 0.0 1.0 + outer loop + vertex 0.07 0.03 0.05 + vertex 0.0 0.0 0.05 + vertex 0.07 0.0 0.05 + endloop + endfacet +endsolid FluidProperties diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/aluminium.stl b/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/aluminium.stl new file mode 100644 index 00000000..3c6c55e0 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/constant/triSurface/aluminium.stl @@ -0,0 +1,86 @@ +solid aluminium + facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 0.015 0.013000000000000001 + vertex 0.0 0.015 0.023 + vertex 0.0 0.016 0.013000000000000001 + endloop + endfacet + facet normal -1.0 0.0 0.0 + outer loop + vertex 0.0 0.016 0.013000000000000001 + vertex 0.0 0.015 0.023 + vertex 0.0 0.016 0.023 + endloop + endfacet + facet normal 1.0 0.0 0.0 + outer loop + vertex 0.06 0.015 0.023 + vertex 0.06 0.015 0.013000000000000001 + vertex 0.06 0.016 0.013000000000000001 + endloop + endfacet + facet normal 1.0 -0.0 0.0 + outer loop + vertex 0.06 0.015 0.023 + vertex 0.06 0.016 0.013000000000000001 + vertex 0.06 0.016 0.023 + endloop + endfacet + facet normal 0.0 -1.0 0.0 + outer loop + vertex 0.06 0.015 0.013000000000000001 + vertex 0.06 0.015 0.023 + vertex 0.0 0.015 0.013000000000000001 + endloop + endfacet + facet normal 0.0 -1.0 0.0 + outer loop + vertex 0.0 0.015 0.013000000000000001 + vertex 0.06 0.015 0.023 + vertex 0.0 0.015 0.023 + endloop + endfacet + facet normal 0.0 1.0 0.0 + outer loop + vertex 0.06 0.016 0.023 + vertex 0.06 0.016 0.013000000000000001 + vertex 0.0 0.016 0.013000000000000001 + endloop + endfacet + facet normal 0.0 1.0 0.0 + outer loop + vertex 0.06 0.016 0.023 + vertex 0.0 0.016 0.013000000000000001 + vertex 0.0 0.016 0.023 + endloop + endfacet + facet normal 0.0 0.0 -1.0 + outer loop + vertex 0.0 0.016 0.013000000000000001 + vertex 0.06 0.016 0.013000000000000001 + vertex 0.0 0.015 0.013000000000000001 + endloop + endfacet + facet normal 0.0 0.0 -1.0 + outer loop + vertex 0.0 0.015 0.013000000000000001 + vertex 0.06 0.016 0.013000000000000001 + vertex 0.06 0.015 0.013000000000000001 + endloop + endfacet + facet normal 0.0 0.0 1.0 + outer loop + vertex 0.06 0.016 0.023 + vertex 0.0 0.016 0.023 + vertex 0.0 0.015 0.023 + endloop + endfacet + facet normal 0.0 0.0 1.0 + outer loop + vertex 0.06 0.016 0.023 + vertex 0.0 0.015 0.023 + vertex 0.06 0.015 0.023 + endloop + endfacet +endsolid aluminium diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/pv.foam b/Data/TestFiles/cases/SimpleHeatFin/case/pv.foam new file mode 100644 index 00000000..1a45dea5 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/pv.foam @@ -0,0 +1 @@ +Dummy file for loading case in paraview diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/pvScript.py b/Data/TestFiles/cases/SimpleHeatFin/case/pvScript.py new file mode 100644 index 00000000..8b5a2870 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/pvScript.py @@ -0,0 +1,58 @@ +#### import the simple module from the paraview +from paraview.simple import * +#### disable automatic camera reset on 'Show' +paraview.simple._DisableFirstRenderCameraReset() + +# create a new OpenFOAMReader +pfoam = OpenFOAMReader(FileName=r'pv.foam') +pfoam.CaseType = 'Decomposed Case' +if hasattr(pfoam, 'Decomposepolyhedra'): + pfoam.Decomposepolyhedra = 0 + +if hasattr(pfoam, 'Createcelltopointfiltereddata'): + pfoam.Createcelltopointfiltereddata = 1 + +# get active view +renderView1 = GetActiveViewOrCreate('RenderView') + +# reset view to fit data +renderView1.ResetCamera() + +# create a new 'Clean to Grid' +cleantoGrid1 = CleantoGrid(Input=pfoam) + +# show data in view +cleantoGrid1Display = Show(cleantoGrid1, renderView1) + +# hide data in view +Hide(pfoam, renderView1) + +# get color transfer function/color map for 'U' +ULUT = GetColorTransferFunction('U') + +# trace defaults for the display properties. +cleantoGrid1Display.ColorArrayName = ['POINTS', 'U'] +cleantoGrid1Display.LookupTable = ULUT +cleantoGrid1Display.EdgeColor = [0.0, 0.0, 0.5] +cleantoGrid1Display.ScalarOpacityUnitDistance = 0.05 + +# get animation scene +animationScene1 = GetAnimationScene() + +# update animation scene based on data timesteps +animationScene1.UpdateAnimationUsingDataTimeSteps() + +# go to the final timestep of the simulation +timesteps = pfoam.TimestepValues +finalTime = timesteps[-1] +animationScene1.AnimationTime = finalTime + +# rescale color and/or opacity maps used to exactly fit the current data range +cleantoGrid1Display.RescaleTransferFunctionToDataRange(False, True) + +# update the view to ensure updated data information +renderView1.Update() + +# reset view to fit data +renderView1.ResetCamera(False) + diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/decomposeParDict b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/decomposeParDict new file mode 100644 index 00000000..bb5f98bb --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/decomposeParDict @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/FluidProperties"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 4; + +method scotch; + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSchemes b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSchemes new file mode 100644 index 00000000..6398510d --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSchemes @@ -0,0 +1,63 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/FluidProperties"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default steadyState; +} + +gradSchemes +{ + default Gauss linear; + grad(U) cellLimited Gauss linear 1; +} + +divSchemes +{ + default none; + div(phi,U) bounded Gauss upwind; + div(phi,K) bounded Gauss upwind; + div(phi,h) bounded Gauss upwind; + div(((rho*nuEff)*dev2(T(grad(U))))) Gauss linear; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} + +fluxRequired +{ + default no; + p_rgh; +} + +wallDist +{ + method meshWave; +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSolution b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSolution new file mode 100644 index 00000000..66a7b5cf --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/FluidProperties/fvSolution @@ -0,0 +1,116 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/FluidProperties"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +SIMPLE +{ + nNonOrthogonalCorrectors 3; + consistent no; + + residualControl + { + p_rgh 0.001; + U 0.001; + h 0.001; + "(k|epsilon|omega|nuTilda)" 0.001; + } + + pRefValue 0; + pRefCell 0; +} + +solvers +{ + "(p_rgh|pcorr)" + { + solver GAMG; + tolerance 1e-8; + relTol 0.01; + smoother symGaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + maxIter 20; + } + + "(p_rghFinal|pcorrFinal)" + { + $p_rgh; + relTol 0; + } + + U + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 0.1; + nSweeps 1; + } + + UFinal + { + $U; + relTol 0; + } + + h + { + solver smoothSolver; + smoother symGaussSeidel; + tolerance 1e-8; + relTol 0.1; + nSweeps 5; + } + + hFinal + { + $h; + relTol 0; + } + + Phi + { + solver GAMG; + tolerance 1e-7; + relTol 0.01; + smoother GaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + } +} + +relaxationFactors +{ + equations + { + U 0.7; + h 0.7; + } + fields + { + p_rgh 0.3; + rho 0.5; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/decomposeParDict b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/decomposeParDict new file mode 100644 index 00000000..b8e4e247 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/decomposeParDict @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/aluminium"; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 4; + +method scotch; + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSchemes b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSchemes new file mode 100644 index 00000000..d85ce817 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSchemes @@ -0,0 +1,47 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/aluminium"; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes +{ + default steadyState; +} + +gradSchemes +{ + default Gauss linear; +} + +divSchemes +{ + default none; +} + +laplacianSchemes +{ + default Gauss linear corrected; +} + +interpolationSchemes +{ + default linear; +} + +snGradSchemes +{ + default corrected; +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSolution b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSolution new file mode 100644 index 00000000..34897ac6 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/aluminium/fvSolution @@ -0,0 +1,58 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system/aluminium"; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +SIMPLE +{ + nNonOrthogonalCorrectors 5; + + residualControl + { + h 0.001; + } +} + +solvers +{ + h + { + solver GAMG; + tolerance 1e-8; + relTol 0.1; + smoother symGaussSeidel; + nPreSweeps 0; + nPostSweeps 2; + cacheAgglomeration on; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + } + + hFinal + { + $h; + relTol 0; + } +} + +relaxationFactors +{ + equations + { + h 0.9; + } +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/controlDict b/Data/TestFiles/cases/SimpleHeatFin/case/system/controlDict new file mode 100644 index 00000000..08e2f10e --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/controlDict @@ -0,0 +1,50 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object controlDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application chtMultiRegionSimpleFoam; + +startFrom startTime; + +startTime 0; + +stopAt endTime; + +deltaT 1; + +endTime 2000; + +writeControl timeStep; + +writeInterval 100; + +purgeWrite 0; + +writeFormat ascii; + +writePrecision 15; + +runTimeModifiable true; + +libs +( + // Needed for availability of porous baffle boundary in potentialFoam + #include "turbulenceLib" +); + +functions +{ +} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/createPatchDict b/Data/TestFiles/cases/SimpleHeatFin/case/system/createPatchDict new file mode 100644 index 00000000..d54abb65 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/createPatchDict @@ -0,0 +1,69 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object createPatchDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +pointSync false; + +// Patches to create. +patches +( + { + name wall001; + patchInfo + { + type wall; + } + constructFrom patches; + patches ( "patch_1_.*" ); + } + { + name heatsource001; + patchInfo + { + type wall; + } + constructFrom patches; + patches ( "patch_2_.*" ); + } + { + name inlet; + patchInfo + { + type patch; + } + constructFrom patches; + patches ( "patch_3_.*" ); + } + { + name outlet; + patchInfo + { + type patch; + } + constructFrom patches; + patches ( "patch_4_.*" ); + } + { + name defaultFaces; + patchInfo + { + type patch; + } + constructFrom patches; + patches ( "patch_0_0" ); + } +); + + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/decomposeParDict b/Data/TestFiles/cases/SimpleHeatFin/case/system/decomposeParDict new file mode 100644 index 00000000..25b04043 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/decomposeParDict @@ -0,0 +1,21 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object decomposeParDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +numberOfSubdomains 4; + +method scotch; + + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSchemes b/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSchemes new file mode 100644 index 00000000..a68a5f54 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSchemes @@ -0,0 +1,23 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes {} +gradSchemes {} +divSchemes {} +laplacianSchemes {} +interpolationSchemes {} +snGradSchemes {} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSolution b/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSolution new file mode 100644 index 00000000..65326149 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/case/system/fvSolution @@ -0,0 +1,18 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// (intentionally empty - solver settings are in system//fvSolution) + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh new file mode 100755 index 00000000..35c86e92 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh @@ -0,0 +1,56 @@ +#!/bin/bash + +runCommand() +{ + sol=$(basename -- "$1") + sol="${sol%.*}" + if [ -f log."$sol" ]; then rm log."$sol"; fi + "$@" 1> >(tee -a log."$sol") 2> >(tee -a log."$sol" >&2) + err=$? + if [ ! $err -eq 0 ]; then exit $err; fi +} + +runParallel() +{ + nproc="$1" + shift + exe="$(which $1)" + sol=$(basename -- "$1") + sol="${sol%.*}" + shift + if [ -f log."$sol" ]; then rm log."$sol"; fi + export OMPI_MCA_btl_vader_single_copy_mechanism=none # Workaround for open-mpi/docker bug + mpiexec -np $nproc "$exe" -parallel "$@" 1> >(tee -a log."$sol") 2> >(tee -a log."$sol" >&2) + err=$? + if [ ! $err -eq 0 ]; then exit $err; fi +} + +GMSH_EXE='/tmp/.mount_FreeCAhpCCgg/usr/bin/gmsh' +if [[ $OSTYPE == 'darwin'* ]] +then + NTHREADS=$(sysctl -n hw.logicalcpu) +else + NTHREADS=$(nproc) +fi +runCommand "$GMSH_EXE" -nt $NTHREADS - "gmsh/BooleanFragments_Geometry.geo" + +# Unset and source bashrc +FOAMDIR="/usr/lib/openfoam/openfoam2606" +if [ ! -z "$FOAMDIR" ] +then + source "$FOAMDIR/etc/config.sh/unset" 2> /dev/null + source "$FOAMDIR/etc/bashrc" +fi + +runCommand gmshToFoam "gmsh/BooleanFragments_Geometry.msh" + +if [ ! -z $FOAM_API ] || [ $WM_PROJECT_VERSION -lt 9 ] 2>/dev/null +then + runCommand transformPoints -scale "(0.001 0.001 0.001)" +else + runCommand transformPoints "scale=(0.001 0.001 0.001)" +fi + + +# Extract surface mesh and convert to mm for visualisation in FreeCAD +runCommand foamToSurface -scale 1000 -tri surfaceMesh.vtk diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.bat b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.bat new file mode 100644 index 00000000..98a3429f --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.bat @@ -0,0 +1,31 @@ +@echo off + +REM Source runtime environment + +set FOAMDIR="/usr/lib/openfoam/openfoam2606" +set CWD=%CD% +set FOAMVER=None + +if %FOAMVER% GEQ 1000 goto OPENCFD + +:FOUNDATION +set OLDPATH=%PATH% +call %FOAMDIR%\setvars_OF%FOAMVER%.bat +set PATH=%PATH%;%OLDPATH% + +REM Fix for error in FOAM_MPI in setvars-OF.bat +FOR /F "tokens=* USEBACKQ" %%F IN (`dir /b %%FOAM_LIBBIN%%\MS-MPI*`) DO ( +SET FOAM_MPI=%%F +) +set PATH=%FOAM_LIBBIN%\%FOAM_MPI%;%PATH% +goto CONTINUE + +:OPENCFD +call %FOAMDIR%\setEnvVariables-v%FOAMVER%.bat + +:CONTINUE + +cd /d %CWD% + +REM Run PowerShell script +PowerShell -NoProfile -ExecutionPolicy Bypass -File Allmesh.ps1 diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.ps1 b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.ps1 new file mode 100644 index 00000000..8599a63f --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/Allmesh.ps1 @@ -0,0 +1,46 @@ +function runCommand([string]$cmd) +{ + $sol = (Split-Path -Leaf $cmd) + & $cmd $args 2>&1 | tee log.$sol + $err = $LASTEXITCODE + if( ! $LASTEXITCODE -eq 0 ) + { + exit $err + } +} + +function runParallel([int]$NumProcs, [string]$cmd) +{ + $sol = (Split-Path -Leaf $cmd) + & mpiexec -affinity -affinity_layout spr:P:L -np $NumProcs $cmd -parallel $args 2>&1 | tee log.$sol + $err = $LASTEXITCODE + if( ! $LASTEXITCODE -eq 0 ) + { + exit $err + } +} + +# Set piping to file to ascii +$PSDefaultParameterValues['Out-File:Encoding'] = 'ascii' + +# Less verbose error reporting +$ErrorView = 'ConciseView' + +$GMSH_EXE = "/tmp/.mount_FreeCAhpCCgg/usr/bin/gmsh" +$NTHREADS = (Get-CimInstance Win32_ComputerSystem).NumberOfLogicalProcessors +runCommand "$GMSH_EXE" -nt $NTHREADS - "gmsh/BooleanFragments_Geometry.geo" + +runCommand gmshToFoam "gmsh/BooleanFragments_Geometry.msh" + +if ( $Env:WM_PROJECT_VERSION[0] -eq "v" -or 9 -gt $Env:WM_PROJECT_VERSION ) +{ + runCommand transformPoints -scale "(0.001 0.001 0.001)" +} +else +{ + runCommand transformPoints "scale=(0.001 0.001 0.001)" +} + + +# Extract surface mesh and convert to mm for visualisation in FreeCAD +runCommand foamToSurface -scale 1000 -tri surfaceMesh.vtk diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.brep b/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.brep new file mode 100644 index 00000000..34888be3 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.brep @@ -0,0 +1,572 @@ +DBRep_DrawableShape + +CASCADE Topology V1, (c) Matra-Datavision +Locations 4 +1 + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 +1 + 1 0 0 0 + 0 1 0 0 + 0 0 1 0 +1 + 1 0 0 0 + 0 1 0 15 + 0 0 1 13 +2 3 -1 0 +Curve2ds 48 +1 0 0 1 0 +1 0 0 1 0 +1 50 0 0 -1 +1 0 0 0 1 +1 0 0 0 -1 +1 0 0 0 1 +1 0 -30 1 0 +1 0 0 1 0 +1 0 0 0 -1 +1 0 0 0 1 +1 0 -1 1 0 +1 0 0 1 0 +1 10 0 0 -1 +1 0 0 0 1 +1 0 0 1 0 +1 0 0 1 0 +1 0 0 0 1 +1 0 0 1 0 +1 0 0 1 0 +1 0 70 1 0 +1 50 0 0 1 +1 0 0 1 0 +1 50 0 0 1 +1 0 30 1 0 +1 50 0 0 -1 +1 70 0 0 1 +1 0 0 0 1 +1 0 30 1 0 +1 0 0 0 -1 +1 70 0 0 1 +1 0 -30 1 0 +1 0 70 1 0 +1 0 0 0 1 +1 0 1 1 0 +1 0 0 0 -1 +1 60 0 0 1 +1 0 0 0 1 +1 0 0 1 0 +1 0 -1 1 0 +1 0 60 1 0 +1 10 0 0 1 +1 0 1 1 0 +1 10 0 0 -1 +1 60 0 0 1 +1 10 0 0 1 +1 0 0 1 0 +1 0 0 1 0 +1 0 60 1 0 +Curves 24 +1 0 0 0 0 0 1 +1 0 0 50 -0 1 0 +1 0 0 0 -0 1 0 +1 0 30 0 0 0 1 +1 0 0 0 -0 1 0 +1 0 1 0 0 0 1 +1 0 0 10 -0 1 0 +1 0 0 0 0 0 1 +1 0 0 0 1 0 -0 +1 70 0 0 0 0 1 +1 0 0 50 1 0 -0 +1 0 30 50 1 0 -0 +1 70 0 50 -0 1 0 +1 0 30 0 1 0 -0 +1 70 0 0 -0 1 0 +1 70 30 0 0 0 1 +1 0 1 0 1 0 -0 +1 60 0 0 -0 1 0 +1 0 0 0 1 0 -0 +1 60 1 0 0 0 1 +1 0 1 10 1 0 -0 +1 60 0 10 -0 1 0 +1 0 0 10 1 0 -0 +1 60 0 0 0 0 1 +Polygon3D 0 +PolygonOnTriangulations 0 +Surfaces 12 +1 0 0 0 1 0 -0 0 0 1 0 -1 0 +1 0 0 0 -0 1 0 0 0 1 1 0 -0 +1 0 0 50 0 0 1 1 0 -0 -0 1 0 +1 0 0 0 0 0 1 1 0 -0 -0 1 0 +1 0 30 0 -0 1 0 0 0 1 1 0 -0 +1 0 0 0 1 0 -0 0 0 1 0 -1 0 +1 0 0 0 0 0 1 1 0 -0 -0 1 0 +1 0 1 0 -0 1 0 0 0 1 1 0 -0 +1 0 0 10 0 0 1 1 0 -0 -0 1 0 +1 0 0 0 -0 1 0 0 0 1 1 0 -0 +1 70 0 0 1 0 -0 0 0 1 0 -1 0 +1 60 0 0 1 0 -0 0 0 1 0 -1 0 +Triangulations 0 + +TShapes 70 +Ve +1e-07 +0 0 50 +0 0 + +0101101 +* +Ve +1e-07 +0 0 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 1 0 0 50 +2 1 1 0 0 50 +2 2 2 0 0 50 +0 + +0101000 +-70 0 +69 0 * +Ve +1e-07 +0 30 50 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 2 0 0 30 +2 3 1 0 0 30 +2 4 3 0 0 30 +0 + +0101000 +-67 0 +70 0 * +Ve +1e-07 +0 30 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 3 0 0 30 +2 5 1 0 0 30 +2 6 4 0 0 30 +0 + +0101000 +-65 0 +69 0 * +Ed + 1e-07 1 1 0 +1 4 0 0 50 +2 7 1 0 0 50 +2 8 5 0 0 50 +0 + +0101000 +-67 0 +65 0 * +Wi + +0101100 +-68 2 -66 2 +64 2 +63 2 * +Ve +1e-07 +0 15 13 +0 0 + +0101101 +* +Ve +1e-07 +0 16 13 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 5 0 0 1 +2 9 6 0 0 1 +2 10 7 0 0 1 +0 + +0101000 ++61 4 -60 4 * +Ve +1e-07 +0 16 23 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 6 0 0 10 +2 11 6 0 0 10 +2 12 8 0 0 10 +0 + +0101000 ++60 4 -58 4 * +Ve +1e-07 +0 15 23 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 7 0 0 1 +2 13 6 0 0 1 +2 14 9 0 0 1 +0 + +0101000 ++56 4 -58 4 * +Ed + 1e-07 1 1 0 +1 8 0 0 10 +2 15 6 0 0 10 +2 16 10 0 0 10 +0 + +0101000 ++61 4 -56 4 * +Wi + +0101100 +-59 3 -57 3 +55 3 +54 3 * +Fa +0 1e-07 1 2 + +0101000 ++62 0 +53 0 * +Ve +1e-07 +70 0 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 9 0 0 70 +2 17 2 0 0 70 +2 18 4 0 0 70 +0 + +0101000 +-51 0 +69 0 * +Ve +1e-07 +70 0 50 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 10 0 0 50 +2 19 11 0 0 50 +2 20 2 0 0 50 +0 + +0101000 +-49 0 +51 0 * +Ed + 1e-07 1 1 0 +1 11 0 0 70 +2 21 2 0 0 70 +2 22 3 0 0 70 +0 + +0101000 +-49 0 +70 0 * +Wi + +0101100 +-50 0 -48 0 +47 0 +68 0 * +Fa +0 1e-07 2 0 + +0101000 ++46 0 * +Ve +1e-07 +70 30 50 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 12 0 0 70 +2 23 5 0 0 70 +2 24 3 0 0 70 +0 + +0101000 +-44 0 +67 0 * +Ed + 1e-07 1 1 0 +1 13 0 0 30 +2 25 11 0 0 30 +2 26 3 0 0 30 +0 + +0101000 +-44 0 +49 0 * +Wi + +0101100 +-66 0 -43 0 +42 0 +47 0 * +Fa +0 1e-07 3 0 + +0101000 ++41 0 * +Ve +1e-07 +70 30 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 14 0 0 70 +2 27 5 0 0 70 +2 28 4 0 0 70 +0 + +0101000 +-39 0 +65 0 * +Ed + 1e-07 1 1 0 +1 15 0 0 30 +2 29 11 0 0 30 +2 30 4 0 0 30 +0 + +0101000 +-39 0 +51 0 * +Wi + +0101100 +-64 0 -38 0 +37 0 +50 0 * +Fa +0 1e-07 4 0 + +0101000 ++36 0 * +Ed + 1e-07 1 1 0 +1 16 0 0 50 +2 31 11 0 0 50 +2 32 5 0 0 50 +0 + +0101000 +-44 0 +39 0 * +Wi + +0101100 +-38 0 -34 0 +43 0 +63 0 * +Fa +0 1e-07 5 0 + +0101000 ++33 0 * +Ve +1e-07 +60 1 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 17 0 0 60 +2 33 8 0 0 60 +2 34 7 0 0 60 +0 + +0101000 ++60 4 -31 0 * +Ve +1e-07 +60 0 0 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 18 0 0 1 +2 35 12 0 0 1 +2 36 7 0 0 1 +0 + +0101000 +-31 0 +29 0 * +Ed + 1e-07 1 1 0 +1 19 0 0 60 +2 37 10 0 0 60 +2 38 7 0 0 60 +0 + +0101000 ++61 4 -29 0 * +Wi + +0101100 +-59 3 -30 3 +28 3 +27 3 * +Fa +0 1e-07 7 3 + +0101000 ++26 0 * +Ve +1e-07 +60 1 10 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 20 0 0 10 +2 39 12 0 0 10 +2 40 8 0 0 10 +0 + +0101000 +-24 0 +31 0 * +Ed + 1e-07 1 1 0 +1 21 0 0 60 +2 41 8 0 0 60 +2 42 9 0 0 60 +0 + +0101000 ++58 4 -24 0 * +Wi + +0101100 +-30 3 -23 3 +22 3 +57 3 * +Fa +0 1e-07 8 3 + +0101000 ++21 0 * +Ve +1e-07 +60 0 10 +0 0 + +0101101 +* +Ed + 1e-07 1 1 0 +1 22 0 0 1 +2 43 12 0 0 1 +2 44 9 0 0 1 +0 + +0101000 +-24 0 +19 0 * +Ed + 1e-07 1 1 0 +1 23 0 0 60 +2 45 10 0 0 60 +2 46 9 0 0 60 +0 + +0101000 ++56 4 -19 0 * +Wi + +0101100 +-55 3 -22 3 +18 3 +17 3 * +Fa +0 1e-07 9 3 + +0101000 ++16 0 * +Ed + 1e-07 1 1 0 +1 24 0 0 10 +2 47 12 0 0 10 +2 48 10 0 0 10 +0 + +0101000 +-19 0 +29 0 * +Wi + +0101100 +-27 3 -14 3 +17 3 +54 3 * +Fa +0 1e-07 10 3 + +0101000 ++13 0 * +Wi + +0101100 +-48 0 -42 0 +34 0 +37 0 * +Fa +0 1e-07 11 0 + +0101000 ++11 0 * +Wi + +0101100 +-14 0 -18 0 +23 0 +28 0 * +Fa +0 1e-07 12 0 + +0101000 ++9 0 * +Sh + +0101100 +-52 0 -45 2 +40 2 -35 2 +32 2 +25 0 -20 0 -15 0 +12 0 +10 2 +-8 3 * +So + +0100000 ++7 0 * +Wi + +0101100 +-54 3 -55 3 +57 3 +59 3 * +Fa +0 1e-07 1 2 + +0101000 ++5 0 * +Sh + +0101100 +-4 0 +8 3 -12 0 +20 0 -25 0 +15 0 * +So + +0100000 ++3 0 * +Co + +1100000 ++6 0 +2 0 * + ++1 1 \ No newline at end of file diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.geo b/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.geo new file mode 100644 index 00000000..5820bc16 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/gmsh/BooleanFragments_Geometry.geo @@ -0,0 +1,51 @@ +// geo file created by FreeCAD for meshing with GMSH meshing software + +// Open brep geometry +Merge "BooleanFragments_Geometry.brep"; + +// Characteristic Length + +// min, max Characteristic Length +Mesh.CharacteristicLengthMax = 3.0; +Mesh.CharacteristicLengthMin = 0.0; + +// Other mesh options +Mesh.RecombineAll = 0; + +// GMSH tetra optimizer +Mesh.Optimize = 1; +// Netgen optimizer in GMSH +Mesh.OptimizeNetgen = 0; +Mesh.HighOrderOptimize = 0; + +// Mesh order +Mesh.ElementOrder = 1; + +// 2D mesh algorithm (1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=BAMG, 8=DelQuad) +Mesh.Algorithm = 2; +// 3D mesh algorithm (1=Delaunay, 2=New Delaunay, 4=Frontal, 5=Frontal Delaunay, 6=Frontal Hex, 7=MMG3D, 9=R-tree, 10=hxtDelaunay) +Mesh.Algorithm3D = 10; + +// Internal mesh +Physical Volume ("aluminium") = {2}; +Physical Volume ("FluidProperties") = {1}; + +// Boundaries +Physical Surface ("patch_0_0") = {6, 7, 8, 9, 11}; +Physical Surface ("patch_1_0") = {1, 3, 4, 10}; +Physical Surface ("patch_2_0") = {12}; +Physical Surface ("patch_3_0") = {2}; +Physical Surface ("patch_4_0") = {5}; + +// Meshing +Mesh 3; + +// Save +Mesh.Format = 10; // Auto according to extension +Mesh.MshFileVersion = 2.2; + +// Ignore Physical definitions and save all elements +Mesh.SaveAll = 0; + +// Save in msh for OpenFOAM as its unv converter is outdated +Save "BooleanFragments_Geometry.msh"; diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/pv.foam b/Data/TestFiles/cases/SimpleHeatFin/meshCase/pv.foam new file mode 100644 index 00000000..1a45dea5 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/pv.foam @@ -0,0 +1 @@ +Dummy file for loading case in paraview diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/pvScriptMesh.py b/Data/TestFiles/cases/SimpleHeatFin/meshCase/pvScriptMesh.py new file mode 100644 index 00000000..97ce1b47 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/pvScriptMesh.py @@ -0,0 +1,51 @@ +#### import the simple module from the paraview +from paraview.simple import * +#### disable automatic camera reset on 'Show' +paraview.simple._DisableFirstRenderCameraReset() + +# create a new OpenFOAMReader +pfoam = OpenFOAMReader(FileName=r'pv.foam') +pfoam.CaseType = 'Reconstructed Case' +if hasattr(pfoam, 'Decompoasepolyhedra'): + pfoam.Decomposepolyhedra = 0 + +# get active view +renderView1 = GetActiveViewOrCreate('RenderView') + +# reset view to fit data +renderView1.ResetCamera() + +# show data in view +pfoamDisplay = Show(pfoam, renderView1) +# trace defaults for the display properties. +pfoamDisplay.ColorArrayName = [None, ''] +#pfoamDisplay.LookupTable = pLUT +pfoamDisplay.EdgeColor = [0.0, 0.0, 0.5] + +# change representation type +pfoamDisplay.SetRepresentationType('Surface With Edges') + +# Properties modified on pfoamDisplay +#pfoamDisplay.Opacity = 0.5 + +# create a new 'Extract Cells By Region' +extractCellsByRegion1 = ExtractCellsByRegion(Input=pfoam) +extractCellsByRegion1.IntersectWith.Normal = [1.0, 1.0, 1.0] + +# show data in view +extractCellsByRegion1Display = Show(extractCellsByRegion1, renderView1) +# trace defaults for the display properties. +extractCellsByRegion1Display.ColorArrayName = [None, ''] +extractCellsByRegion1Display.EdgeColor = [0.0, 0.0, 0.5] + +# Properties modified on extractCellsByRegion1 +extractCellsByRegion1.Extractonlyintersected = 1 +extractCellsByRegion1.Extractintersected = 1 + +# change representation type +extractCellsByRegion1Display.SetRepresentationType('Surface With Edges') + +SetActiveSource(pfoam) + +# reset view to fit data +renderView1.ResetCamera() diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/controlDict b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/controlDict new file mode 100644 index 00000000..1e38e762 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/controlDict @@ -0,0 +1,48 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + location "system"; + object mesh; +} + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +application gmsh; + +deltaT 1; + +endTime 1; + +purgeWrite 1; + +runTimeModifiable yes; + +startFrom latestTime; + +startTime 0; + +stopAt endTime; + +writeControl timeStep; + +writeFormat ascii; + +writeInterval 1; + +writePrecision 15; + +libs +( + "libdecompositionMethods.so" "libscotchDecomp.so" +); + + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/createPatchDict b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/createPatchDict new file mode 100644 index 00000000..0f0921ee --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/createPatchDict @@ -0,0 +1,3 @@ + + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSchemes b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSchemes new file mode 100644 index 00000000..0a935ed4 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSchemes @@ -0,0 +1,28 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSchemes; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +ddtSchemes {} + +gradSchemes {} + +divSchemes {} + +laplacianSchemes {} + +interpolationSchemes {} + +snGradSchemes{} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSolution b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSolution new file mode 100644 index 00000000..b76321e8 --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/fvSolution @@ -0,0 +1,18 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object fvSolution; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +solvers {} + +// ************************************************************************* // diff --git a/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/meshQualityDict b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/meshQualityDict new file mode 100644 index 00000000..432040cf --- /dev/null +++ b/Data/TestFiles/cases/SimpleHeatFin/meshCase/system/meshQualityDict @@ -0,0 +1,70 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| | +| Generated by the CfdOF workbench for FreeCAD | +| https://github.com/jaheyns/CfdOF | +| | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 4.0; + format ascii; + class dictionary; + location "system"; + object meshQualityDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +//- Maximum non-orthogonality allowed. Set to 180 to disable. +maxNonOrtho 90; + +//- Max skewness allowed. Set to <0 to disable. +maxBoundarySkewness 100; +maxInternalSkewness 50; + +//- Max concaveness allowed. Is angle (in degrees) below which concavity +// is allowed. 0 is straight face, <0 would be convex face. +// Set to 180 to disable. +maxConcave 90; + +//- Minimum pyramid volume. Is absolute volume of cell pyramid. +// Set to a sensible fraction of the smallest cell volume expected. +// Set to very negative number (e.g. -1E30) to disable. +minVol 0; + +//- Minimum quality of the tet formed by the face-centre +// and variable base point minimum decomposition triangles and +// the cell centre. Set to very negative number (e.g. -1E30) to +// disable. +// <0 = inside out tet, +// 0 = flat tet +// 1 = regular tet +minTetQuality -1e30; + +//- Minimum face area. Set to <0 to disable. +minArea 0; + +//- Minimum face twist. Set to <-1 to disable. dot product of face normal +// (itself the average of the triangle normals) +// and face centre triangles normal +minTwist -1; + +//- Minimum normalised cell determinant. This is the determinant of all +// the areas of internal faces. It is a measure of how much of the +// outside area of the cell is to other cells. The idea is that if all +// outside faces of the cell are 'floating' (zeroGradient) the +// 'fixedness' of the cell is determined by the area of the internal faces. +// 1 = hex, <= 0 = folded or flattened illegal cell +minDeterminant 0; + +//- Relative position of face in relation to cell centres (0.5 for orthogonal +// mesh) (0 -> 0.5) +minFaceWeight 0.0001; + +//- Volume ratio of neighbouring cells (0 -> 1) +minVolRatio 0.0001; + +//- Per triangle normal compared to that of preceding triangle. Must be >0 +// for Fluent compatibility +minTriangleTwist -1; + +// ************************************************************************* // diff --git a/Demos/ConjugatedHeatTransferSteadyState/README.md.txt b/Demos/ConjugatedHeatTransferSteadyState/README.md.txt new file mode 100644 index 00000000..2b4a7c66 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/README.md.txt @@ -0,0 +1,32 @@ +# Steady-State Conjugate Heat Transfer Examples + +This directory contains two CfdOF examples for steady-state conjugate heat +transfer (CHT) using OpenFOAM's `chtMultiRegionSimpleFoam` solver. CHT solves +heat conduction in one or more solid regions together with heat transfer and +fluid flow in the surrounding fluid region. + +## Simple Heat Fin + +Folder: `simple_heat_fin` + +This example contains one aluminium solid region surrounded by +air. Heat conducts through the fin and is transferred to the air, producing a +steady temperature distribution and buoyancy-driven flow. The case uses a +laminar flow model and demonstrates: + + +![screenshot](simple_heat_fin/simple_heat_fin_result_paraview.png) + +## Microchip Cooling + +Folder: `microchip_cooling` + +This example represents a more involved electronics-cooling problem. It +contains four solid regions for the heat sink, printed circuit board, +microchip, and polymer component. Heat passes between contacting solids and +from their surfaces into the surrounding air. The microchip uses a +volumetric heat source. A mean-velocity-force cell zone drives the air +flow and acts as a simplified fan model. + +![Microchip cooling result](microchip_cooling/microchip_cooling_result_paraview.png) + diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/00-RunAll.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/00-RunAll.FCMacro new file mode 100644 index 00000000..250600e0 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/00-RunAll.FCMacro @@ -0,0 +1,13 @@ +from os import path +import FreeCAD as App +if App.GuiUp: + import FreeCADGui as Gui +from CfdOF.CfdTools import executeMacro + +macro_dir = path.dirname(__file__) +executeMacro(path.join(macro_dir, "01-geometry.FCMacro")) +executeMacro(path.join(macro_dir, "02-analysis.FCMacro")) +executeMacro(path.join(macro_dir, "03-mesh.FCMacro")) +executeMacro(path.join(macro_dir, "04-boundaries.FCMacro")) +executeMacro(path.join(macro_dir, "05-solidMaterials.FCMacro")) +executeMacro(path.join(macro_dir, "06-meanVelocityForce.FCMacro")) diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/01-geometry.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/01-geometry.FCMacro new file mode 100644 index 00000000..669aabeb --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/01-geometry.FCMacro @@ -0,0 +1,66 @@ +import FreeCAD as App +import Part +import Draft +import BOPTools.SplitFeatures as SplitFeatures + + +doc = App.newDocument("microchip_cooling") +App.setActiveDocument(doc.Name) + + +def make_box(name, label, length, width, height, placement): + obj = doc.addObject("Part::Box", name) + obj.Label = label + obj.Length = f"{length} mm" + obj.Width = f"{width} mm" + obj.Height = f"{height} mm" + obj.Placement.Base = App.Vector(*placement) + return obj + + +Box = make_box("Box", "room", 120, 120, 100, (89, 90, 0)) +Box001 = make_box("Box001", "PCB", 100, 100, 1, (100, 100, 10)) +Box002 = make_box("Box002", "microchip", 30, 30, 1, (135, 135, 11)) + +# Heat sink: 30 x 30 x 3 mm base plus seven 1 mm thick fins, then placed on +# top of the microchip. Keep object names aligned with the original demo. +Box003 = make_box("Box003", "Cube", 30, 30, 3, (0, 0, 0)) +Box004 = make_box("Box004", "Cube001", 30, 1, 12, (0, 0, 0)) +Array = Draft.make_ortho_array( + Box004, + v_x=App.Vector(100, 0, 0), + v_y=App.Vector(0, 4.833333, 0), + v_z=App.Vector(0, 0, 100), + n_x=1, + n_y=7, + n_z=1, + use_link=False, +) +Array.Label = "Array" + +Fusion = doc.addObject("Part::MultiFuse", "Fusion") +Fusion.Label = "heat_sink" +Fusion.Shapes = [Box003, Array] +Fusion.Placement.Base = App.Vector(135, 135, 12) + +# Fan case: square 30 x 30 x 10 mm duct with a 28 mm diameter cylindrical hole. +outer = Part.makeBox(30, 30, 10, App.Vector(-15, -15, 0)) +inner = Part.makeCylinder(14, 10, App.Vector(0, 0, 0)) +Body = doc.addObject("Part::Feature", "Body") +Body.Label = "fan case" +Body.Shape = outer.cut(inner) +Body.Placement.Base = App.Vector(150, 150, 24) + +Cylinder = doc.addObject("Part::Cylinder", "Cylinder") +Cylinder.Label = "fan" +Cylinder.Radius = "13 mm" +Cylinder.Height = "8 mm" +Cylinder.Placement.Base = App.Vector(150, 150, 26) + +doc.recompute() + +BooleanFragments = SplitFeatures.makeBooleanFragments(name="BooleanFragments") +BooleanFragments.Label = "BooleanFragments" +BooleanFragments.Objects = [Fusion, Box002, Box001, Box, Body] +BooleanFragments.Mode = "Standard" +doc.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/02-analysis.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/02-analysis.FCMacro new file mode 100644 index 00000000..e4f0feb2 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/02-analysis.FCMacro @@ -0,0 +1,72 @@ +from CfdOF import CfdAnalysis, CfdTools +from CfdOF.Solve import CfdPhysicsSelection +from CfdOF.Solve import CfdFluidMaterial +from CfdOF.Solve import CfdInitialiseFlowField +from CfdOF.Solve import CfdSolverFoam + +analysis = CfdAnalysis.makeCfdAnalysis("CfdAnalysis") +analysis.Label = "CfdAnalysis001" +CfdTools.setActiveAnalysis(analysis) +analysis.addObject(CfdPhysicsSelection.makeCfdPhysicsSelection()) +analysis.addObject(CfdFluidMaterial.makeCfdFluidMaterial("FluidProperties")) +analysis.addObject(CfdInitialiseFlowField.makeCfdInitialFlowField()) +analysis.addObject(CfdSolverFoam.makeCfdSolverFoam()) + +physics = App.ActiveDocument.PhysicsModel +physics.Label = "PhysicsModel001" +physics.Time = "Steady" +physics.Flow = "NonIsothermal" +physics.Phase = "MultiRegion" +physics.Turbulence = "Laminar" +physics.TurbulenceModel = "kEpsilon" +physics.gx = "0 mm/s^2" +physics.gy = "0 mm/s^2" +physics.gz = "0 mm/s^2" + +fluid = App.ActiveDocument.FluidProperties +fluid.Label = "FluidProperties001" +fluid.Material = { + "CardName": "AirIncompressible", + "CpPolynomial": "1004.703 0 0 0 0 0 0 0", + "DensityPolynomial": "1.2 0 0 0 0 0 0 0", + "Description": "Constant-property air for low-Mach forced convection", + "DynamicViscosityPolynomial": "1.8e-5 0 0 0 0 0 0 0", + "MolarMass": "0.0289643897748887 kg/mol", + "Name": "Air", "ThermalConductivityPolynomial": "0.0262 0 0 0 0 0 0 0", + "Type": "Incompressible", +} + +initial = App.ActiveDocument.InitialiseFields +initial.Label = "InitialiseFields001" +initial.PotentialFlow = False +initial.PotentialFlowP = False +initial.Pressure = "100 kPa" +initial.Temperature = "293 K" +initial.UseInletTemperatureValue = False +initial.UseInletTurbulenceValues = False +initial.UseInletUValues = False +initial.UseOutletPValue = False +initial.Ux = initial.Uy = "0 mm/s" +initial.Uz = "20 mm/s" +initial.k = "10000 mm^2/s^2" +initial.epsilon = "50000000 mm^2/s^3" +initial.omega = "1 1/s" + +solver = App.ActiveDocument.CfdSolver +solver.Label = "CfdSolver001" +solver.MaxIterations = 2000 +solver.SteadyWriteInterval = 200 +solver.PurgeWrite = 2 +solver.MaxCFLNumber = 2.0 +solver.MaxInterfaceCFLNumber = 2.0 +solver.Parallel = True +solver.ParallelCores = 6 +solver.TurbulenceRelaxation = 0.3 +solver.URelaxation = 0.5 +solver.pRelaxation = 0.3 +solver.rhoRelaxation = 0.3 +solver.energyRelaxation = 0.3 +solver.FluidNonOrthogonalCorrectors = 3 +solver.SolidEnergyRelaxation = 0.2 +solver.SolidNonOrthogonalCorrectors = 0 +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/03-mesh.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/03-mesh.FCMacro new file mode 100644 index 00000000..3d36f8a9 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/03-mesh.FCMacro @@ -0,0 +1,16 @@ +from CfdOF import CfdTools +from CfdOF.Mesh import CfdMesh + +CfdMesh.makeCfdMesh("BooleanFragments_Mesh") +mesh = App.ActiveDocument.ActiveObject +mesh.Label = "BooleanFragments_Mesh001" +mesh.Part = App.ActiveDocument.BooleanFragments +CfdTools.getActiveAnalysis().addObject(mesh) +mesh.CharacteristicLengthMax = "3 mm" +mesh.MeshUtility = "gmsh" +mesh.ElementDimension = "3D" +mesh.CellsBetweenLevels = 3 +mesh.EdgeRefinement = 1.0 +mesh.PointInMesh = {"x": "0 m", "y": "0 m", "z": "0 m"} +mesh.NumberOfProcesses = 1 +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/04-boundaries.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/04-boundaries.FCMacro new file mode 100644 index 00000000..7e5fdfa4 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/04-boundaries.FCMacro @@ -0,0 +1,30 @@ +from CfdOF import CfdTools +from CfdOF.Solve import CfdFluidBoundary + +def add_boundary(label, boundary_type, subtype, thermal_type, faces, + velocity=(0, 0, 0), pressure="100 kPa"): + boundary = CfdFluidBoundary.makeCfdFluidBoundary() + CfdTools.getActiveAnalysis().addObject(boundary) + boundary.Label = label + boundary.BoundaryType = boundary_type + boundary.BoundarySubType = subtype + boundary.ThermalBoundaryType = thermal_type + boundary.Temperature = "293 K" + boundary.Pressure = pressure + boundary.Ux = f"{velocity[0]} mm/s" + boundary.Uy = f"{velocity[1]} mm/s" + boundary.Uz = f"{velocity[2]} mm/s" + boundary.TurbulenceIntensityPercentage = 1.0 + boundary.TurbulenceLengthScale = "100 mm" + boundary.ShapeRefs = [(App.ActiveDocument.BooleanFragments, tuple(faces))] + return boundary + +add_boundary("inlet", "wall", "fixedWall", "zeroGradient", + ("Face17",), velocity=(0, 0, 20)) +add_boundary("outlet", "outlet", "staticPressureOutlet", "zeroGradient", + ("Face15",)) + +# The isothermal casing gives the steady CHT problem a robust thermal reference. +add_boundary("side_walls", "wall", "slipWall", "fixedValue", + ("Face13", "Face16", "Face14", "Face18")) +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/05-solidMaterials.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/05-solidMaterials.FCMacro new file mode 100644 index 00000000..51330ddd --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/05-solidMaterials.FCMacro @@ -0,0 +1,40 @@ +from CfdOF import CfdTools +from CfdOF.Solve import CfdSolidMaterial + +aluminium = { + "Author": "AluminiumSolid", "AuthorAndLicense": "", + "CardName": "AluminiumSolid", "Density": "2699 kg/m^3", + "Description": "Aluminium solid thermal properties for CHT simulation", + "License": "", "Name": "Aluminium_solid", "ReferenceSource": "", + "SourceURL": "", "SpecificHeat": "897 J/kg/K", + "ThermalConductivity": "237 W/m/K", "Type": "Solid", +} + +def add_solid(name, label, shape, material, heat_generation="0 W/m^3"): + solid = CfdSolidMaterial.makeCfdSolidMaterial(name) + CfdTools.getActiveAnalysis().addObject(solid) + solid.Label = label + solid.Material = material + solid.HeatGeneration = heat_generation + solid.ShapeRefs = [(shape, ("Solid1",))] + return solid + +add_solid("SolidProperties", "heat_sink_volume001", + App.ActiveDocument.Fusion, aluminium) +add_solid("SolidProperties", "PCB_volume001", App.ActiveDocument.Box001, { + "Density": "1.1e-06 kg/mm^3", "Description": "User-entered properties", + "Name": "Custom", "SpecificHeat": "200000000.0 mm^2/(s^2*K)", + "ThermalConductivity": "200.0 mm*kg/(s^3*K)", "Type": "Solid", +}) +add_solid("SolidProperties", "hot_microchip_volume001", + App.ActiveDocument.Box002, aluminium, "1e+09 W/m^3") +add_solid("SolidProperties", "Polypropylene_solid001", App.ActiveDocument.Body, { + "Author": "PolypropyleneSolid", "AuthorAndLicense": "", + "CardName": "PolypropyleneSolid", "Density": "918 kg/m^3", + "Description": "Polypropylene solid thermal properties", "License": "", + "Name": "Polypropylene_solid", + "ReferenceSource": "https://www.matweb.com/search/DataSheet.aspx?MatGUID=a882a1c603374e278d062f106dfda95b", + "SourceURL": "", "SpecificHeat": "1920 J/kg/K", + "ThermalConductivity": "0.249 W/m/K", "Type": "Solid", +}) +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/06-meanVelocityForce.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/06-meanVelocityForce.FCMacro new file mode 100644 index 00000000..0ef846f9 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/06-meanVelocityForce.FCMacro @@ -0,0 +1,12 @@ +from CfdOF import CfdTools +from CfdOF.Solve import CfdMeanVelocityForce + +force = CfdMeanVelocityForce.makeCfdMeanVelocityForce() +CfdTools.getActiveAnalysis().addObject(force) +force.Label = "MeanVelocityForce001" +force.SelectionMode = "cellZone" +force.ShapeRefs = [(App.ActiveDocument.Cylinder, ("Solid1",))] +force.Direction = App.Vector(0, 0, 1) +force.Ubar = App.Vector(0, 0, 0.2) +force.Relaxation = 0.05 +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/microchip_cooling_result_paraview.png b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/microchip_cooling_result_paraview.png new file mode 100644 index 00000000..41a81d9f Binary files /dev/null and b/Demos/ConjugatedHeatTransferSteadyState/microchip_cooling/microchip_cooling_result_paraview.png differ diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/00-RunAll.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/00-RunAll.FCMacro new file mode 100644 index 00000000..b9e1cc69 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/00-RunAll.FCMacro @@ -0,0 +1,12 @@ +from os import path +import FreeCAD as App +if App.GuiUp: + import FreeCADGui as Gui +from CfdOF.CfdTools import executeMacro + +macro_dir = path.dirname(__file__) +executeMacro(path.join(macro_dir, "01-geometry.FCMacro")) +executeMacro(path.join(macro_dir, "02-analysis.FCMacro")) +executeMacro(path.join(macro_dir, "03-mesh.FCMacro")) +executeMacro(path.join(macro_dir, "04-boundaries.FCMacro")) +executeMacro(path.join(macro_dir, "05-solidMaterial.FCMacro")) diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/01-geometry.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/01-geometry.FCMacro new file mode 100644 index 00000000..0012ff1f --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/01-geometry.FCMacro @@ -0,0 +1,35 @@ +import FreeCAD as App +import BOPTools.SplitFeatures as SplitFeatures +if App.GuiUp: + import FreeCADGui as Gui + +document_name = "simple_heat_fin" + +if document_name in App.listDocuments(): + doc = App.getDocument(document_name) + App.closeDocument(doc.Name) + doc = App.newDocument(document_name) +else: + doc = App.newDocument(document_name) +App.setActiveDocument(doc.Name) + +fluid_domain = doc.addObject("Part::Box", "Box") +fluid_domain.Label = "fluid_domain" +fluid_domain.Length = "70 mm" +fluid_domain.Width = "30 mm" +fluid_domain.Height = "50 mm" + +heatsink = doc.addObject("Part::Box", "Box001") +heatsink.Label = "heatsink" +heatsink.Length = "60 mm" +heatsink.Width = "1 mm" +heatsink.Height = "10 mm" +heatsink.Placement.Base = App.Vector(0, 15, 13) + +doc.recompute() + +BooleanFragments = SplitFeatures.makeBooleanFragments(name="BooleanFragments") +BooleanFragments.Label = "BooleanFragments" +BooleanFragments.Objects = [fluid_domain, heatsink] +BooleanFragments.Mode = "Standard" +doc.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/02-analysis.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/02-analysis.FCMacro new file mode 100644 index 00000000..8f4a9452 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/02-analysis.FCMacro @@ -0,0 +1,59 @@ +from CfdOF import CfdAnalysis, CfdTools +from CfdOF.Solve import CfdPhysicsSelection +from CfdOF.Solve import CfdFluidMaterial +from CfdOF.Solve import CfdInitialiseFlowField +from CfdOF.Solve import CfdSolverFoam + +analysis = CfdAnalysis.makeCfdAnalysis("CfdAnalysis") +CfdTools.setActiveAnalysis(analysis) +analysis.addObject(CfdPhysicsSelection.makeCfdPhysicsSelection()) +analysis.addObject(CfdFluidMaterial.makeCfdFluidMaterial("FluidProperties")) +analysis.addObject(CfdInitialiseFlowField.makeCfdInitialFlowField()) +analysis.addObject(CfdSolverFoam.makeCfdSolverFoam()) + +physics = App.ActiveDocument.PhysicsModel +physics.Time = "Steady" +physics.Flow = "NonIsothermal" +physics.Phase = "MultiRegion" +physics.Turbulence = "Laminar" +physics.TurbulenceModel = "kOmegaSST" +physics.gx = "0 mm/s^2" +physics.gy = "0 mm/s^2" +physics.gz = "-9810 mm/s^2" + +App.ActiveDocument.FluidProperties.Material = { + "Author": "SPDX-FileCopyrightText: 2021 Oliver Oxtoby", + "AuthorAndLicense": "", "CardName": "AirCompressible", + "Cp": "1004.703 J/kg/K", "Description": "Compressible air properties", + "License": "", "MolarMass": "0.0289643897748887 kg/mol", + "Name": "Air", "ReferenceSource": "", "SourceURL": "", + "SutherlandRefTemperature": "273.15 K", + "SutherlandRefViscosity": "1.716e-5 kg/m/s", + "SutherlandTemperature": "110.4 K", "Type": "Compressible", +} + +initial = App.ActiveDocument.InitialiseFields +initial.PotentialFlow = False +initial.PotentialFlowP = False +initial.Pressure = "100 kPa" +initial.Temperature = "293 K" +initial.UseInletTemperatureValue = False +initial.UseInletTurbulenceValues = False +initial.UseInletUValues = False +initial.UseOutletPValue = False +initial.Ux = initial.Uy = initial.Uz = "0 mm/s" +initial.k = "10000 mm^2/s^2" +initial.epsilon = "50000000 mm^2/s^3" +initial.omega = "1 1/s" + +solver = App.ActiveDocument.CfdSolver +solver.MaxIterations = 2000 +solver.SteadyWriteInterval = 100 +solver.Parallel = True +solver.ParallelCores = 4 +solver.TurbulenceRelaxation = 0.7 +solver.URelaxation = 0.7 +solver.pRelaxation = 0.3 +solver.rhoRelaxation = 0.5 +solver.energyRelaxation = 0.7 +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/03-mesh.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/03-mesh.FCMacro new file mode 100644 index 00000000..59d9666b --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/03-mesh.FCMacro @@ -0,0 +1,15 @@ +from CfdOF import CfdTools +from CfdOF.Mesh import CfdMesh + +CfdMesh.makeCfdMesh("BooleanFragments_Mesh") +mesh = App.ActiveDocument.ActiveObject +mesh.Part = App.ActiveDocument.BooleanFragments +CfdTools.getActiveAnalysis().addObject(mesh) +mesh.CharacteristicLengthMax = "3 mm" +mesh.MeshUtility = "gmsh" +mesh.ElementDimension = "3D" +mesh.CellsBetweenLevels = 3 +mesh.EdgeRefinement = 1.0 +mesh.PointInMesh = {"x": "0 m", "y": "0 m", "z": "0 m"} +mesh.NumberOfProcesses = 1 +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/04-boundaries.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/04-boundaries.FCMacro new file mode 100644 index 00000000..75e7cfae --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/04-boundaries.FCMacro @@ -0,0 +1,29 @@ +from CfdOF import CfdTools +from CfdOF.Solve import CfdFluidBoundary + +def add_boundary(label, boundary_type, subtype, thermal_type, faces, + temperature="293 K", velocity=(0, 0, 0)): + boundary = CfdFluidBoundary.makeCfdFluidBoundary() + CfdTools.getActiveAnalysis().addObject(boundary) + boundary.Label = label + boundary.BoundaryType = boundary_type + boundary.BoundarySubType = subtype + boundary.ThermalBoundaryType = thermal_type + boundary.Temperature = temperature + boundary.Ux = "{} mm/s".format(velocity[0]) + boundary.Uy = "{} mm/s".format(velocity[1]) + boundary.Uz = "{} mm/s".format(velocity[2]) + boundary.TurbulenceIntensityPercentage = 1.0 + boundary.TurbulenceLengthScale = "100 mm" + boundary.ShapeRefs = [(App.ActiveDocument.BooleanFragments, tuple(faces))] + return boundary + +add_boundary("wall001", "wall", "fixedWall", "zeroGradient", + ("Face4", "Face1", "Face3", "Face10")) +add_boundary("heatsource001", "wall", "fixedWall", "fixedValue", + ("Face12",), "393 K") +add_boundary("inlet", "inlet", "uniformVelocityInlet", "fixedValue", + ("Face2",), velocity=(0, 100, 0)) +add_boundary("outlet", "outlet", "staticPressureOutlet", "fixedValue", + ("Face5",)) +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/05-solidMaterial.FCMacro b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/05-solidMaterial.FCMacro new file mode 100644 index 00000000..fc04d673 --- /dev/null +++ b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/05-solidMaterial.FCMacro @@ -0,0 +1,17 @@ +from CfdOF import CfdTools +from CfdOF.Solve import CfdSolidMaterial + +solid = CfdSolidMaterial.makeCfdSolidMaterial("SolidProperties") +CfdTools.getActiveAnalysis().addObject(solid) +solid.Label = "aluminium" +solid.Material = { + "Author": "AluminiumSolid", "AuthorAndLicense": "", + "CardName": "AluminiumSolid", "Density": "2699 kg/m^3", + "Description": "Aluminium solid thermal properties for CHT simulation", + "License": "", "Name": "Aluminium_solid", "ReferenceSource": "", + "SourceURL": "", "SpecificHeat": "897 J/kg/K", + "ThermalConductivity": "237 W/m/K", "Type": "Solid", +} +solid.HeatGeneration = "0 W/m^3" +solid.ShapeRefs = [(App.ActiveDocument.Box001, ("Solid1",))] +App.ActiveDocument.recompute() diff --git a/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/simple_heat_fin_result_paraview.png b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/simple_heat_fin_result_paraview.png new file mode 100644 index 00000000..c2281764 Binary files /dev/null and b/Demos/ConjugatedHeatTransferSteadyState/simple_heat_fin/simple_heat_fin_result_paraview.png differ diff --git a/Gui/Icons/solid_material.svg b/Gui/Icons/solid_material.svg new file mode 100644 index 00000000..1a18ad37 --- /dev/null +++ b/Gui/Icons/solid_material.svg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Gui/TaskPanelCfdSolidProperties.ui b/Gui/TaskPanelCfdSolidProperties.ui new file mode 100644 index 00000000..8ac19c07 --- /dev/null +++ b/Gui/TaskPanelCfdSolidProperties.ui @@ -0,0 +1,193 @@ + + + TaskPanelCfdSolidProperties + + + + 0 + 0 + 338 + 420 + + + + Solid properties + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 75 + true + + + + Material name + + + + + + + + + + + 75 + true + + + + Solid properties + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Heat generation: + + + + + + + 0 W/m^3 + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 10 + 75 + true + + + + Predefined solid library + + + + + + + + + + Material description + + + true + + + + + + + Save... + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 75 + true + + + + Solid body (region geometry) + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + material_name + PredefinedMaterialLibraryComboBox + + + + diff --git a/Gui/TaskPanelPhysics.ui b/Gui/TaskPanelPhysics.ui index 2c5d0b2f..111721f7 100644 --- a/Gui/TaskPanelPhysics.ui +++ b/Gui/TaskPanelPhysics.ui @@ -418,6 +418,19 @@ + + + + + 0 + 0 + + + + Multi-region (conjugate heat transfer) + + + diff --git a/InitGui.py b/InitGui.py index 74813cee..0d4281b6 100644 --- a/InitGui.py +++ b/InitGui.py @@ -60,6 +60,7 @@ def Initialize(self): import CfdOF.Mesh.CfdMeshRefinement import CfdOF.Solve.CfdPhysicsSelection import CfdOF.Solve.CfdFluidMaterial + import CfdOF.Solve.CfdSolidMaterial import CfdOF.Solve.CfdSolverFoam import CfdOF.Solve.CfdInitialiseFlowField import CfdOF.Solve.CfdFluidBoundary @@ -107,7 +108,7 @@ def Initialize(self): ('M', QT_TRANSLATE_NOOP("Workbench", "Dynamic mesh refinement"), ['CfdOF_DynamicMeshInterfaceRefinement','CfdOF_DynamicMeshShockRefinement',]), ('T', 'CfdOF_GroupDynamicMeshRefinement',), - 'CfdOF_PhysicsModel', 'CfdOF_FluidMaterial', + 'CfdOF_PhysicsModel', 'CfdOF_FluidMaterial', 'CfdOF_SolidMaterial', 'CfdOF_FluidBoundary', 'CfdOF_InitialiseInternal', 'CfdOF_InitialisationZone', 'CfdOF_PorousZone', 'CfdOF_MeanVelocityForce', 'CfdOF_ReportingFunctions', 'CfdOF_ScalarTransportFunctions', diff --git a/TestCfdOF.py b/TestCfdOF.py index 5a8eb437..47ccc6f2 100644 --- a/TestCfdOF.py +++ b/TestCfdOF.py @@ -297,7 +297,10 @@ def writeCaseFiles(self): self.writer = CfdCaseWriterFoam.CfdCaseWriterFoam(FreeCAD.ActiveDocument.CfdAnalysis) self.writer.writeCase() - def runTest(self, dir_name, macro_names): + def runTest(self, dir_name, macro_names, case_name=None): + if case_name is None: + case_name = dir_name + # Unset the appending of the document name to the output path to get a predictable place where # files are stored prefs = CfdTools.getPreferencesLocation() @@ -313,16 +316,16 @@ def runTest(self, dir_name, macro_names): fccPrint('Writing {} case files ...'.format(dir_name)) analysis = CfdTools.getActiveAnalysis() analysis.OutputPath = temp_dir - CfdTools.getSolver(analysis).InputCaseName = "case" + dir_name - CfdTools.getMeshObject(analysis).CaseName = "meshCase" + dir_name + CfdTools.getSolver(analysis).InputCaseName = "case" + case_name + CfdTools.getMeshObject(analysis).CaseName = "meshCase" + case_name self.writeCaseFiles() self.child_instance.assertTrue(self.writer, "CfdTest of writer failed") - mesh_ref_dir = os.path.join(test_file_dir, "cases", dir_name, "meshCase") + mesh_ref_dir = os.path.join(test_file_dir, "cases", case_name, "meshCase") mesh_case_dir = self.meshwriter.mesh_case_dir comparePaths(mesh_ref_dir, mesh_case_dir, self.child_instance) - ref_dir = os.path.join(test_file_dir, "cases", dir_name, "case") + ref_dir = os.path.join(test_file_dir, "cases", case_name, "case") case_dir = self.writer.case_folder comparePaths(ref_dir, case_dir, self.child_instance) @@ -533,6 +536,23 @@ def tearDown(self): self.closeDoc() +class SimpleHeatFinTest(unittest.TestCase, MacroTest): + __dir_name = os.path.join('ConjugatedHeatTransferSteadyState', 'simple_heat_fin') + __case_name = 'SimpleHeatFin' + __macros = ['01-geometry.FCMacro', '02-analysis.FCMacro', '03-mesh.FCMacro', '04-boundaries.FCMacro', + '05-solidMaterial.FCMacro'] + + def __init__(self, var): + super().__init__(var) + MacroTest.child_instance = self + + def test_run(self): + self.runTest(self.__class__.__dir_name, self.__class__.__macros, self.__class__.__case_name) + + def tearDown(self): + self.closeDoc() + + def compareInpFiles(file_name1, file_name2): file1 = open(file_name1, 'r') f1 = file1.readlines()