Example #1
0
    def processDynamicMeshRefinement(self):
        settings = self.settings
        settings['dynamicMeshEnabled'] = True

        # Check whether transient
        if not self.physics_model.Time == 'Transient':
            raise RuntimeError(
                "Dynamic mesh refinement is not supported by steady-state solvers"
            )

        # Check whether cellLevel supported
        if self.mesh_obj.MeshUtility not in ['cfMesh', 'snappyHexMesh']:
            raise RuntimeError(
                "Dynamic mesh refinement is only supported by cfMesh and snappyHexMesh"
            )

        # Check whether 2D extrusion present
        mesh_refinements = CfdTools.getMeshRefinementObjs(self.mesh_obj)
        for mr in mesh_refinements:
            if mr.Extrusion:
                if mr.ExtrusionType == '2DPlanar' or mr.ExtrusionType == '2DWedge':
                    raise RuntimeError(
                        "Dynamic mesh refinement will not work with 2D or wedge mesh"
                    )

        settings['dynamicMesh'] = CfdTools.propsToDict(
            self.dynamic_mesh_refinement_obj)
Example #2
0
    def setupPatchNames(self):
        print('Populating createPatchDict to update BC names')
        settings = self.settings
        settings['createPatches'] = {}
        settings['createPatchesSnappyBaffles'] = {}
        bc_group = self.bc_group

        defaultPatchType = "patch"
        for bc_id, bc_obj in enumerate(bc_group):
            bcType = bc_obj.BoundaryType
            bcSubType = bc_obj.BoundarySubType
            patchType = CfdTools.getPatchType(bcType, bcSubType)
            settings['createPatches'][bc_obj.Label] = {
                'PatchNamesList': '"patch_' + str(bc_id + 1) + '_.*"',
                'PatchType': patchType
            }
            if bc_obj.DefaultBoundary:
                defaultPatchType = patchType

            if bcType == 'baffle' and self.mesh_obj.MeshUtility == 'snappyHexMesh':
                settings['createPatchesFromSnappyBaffles'] = True
                settings['createPatchesSnappyBaffles'][bc_obj.Label] = {
                    'PatchNamesList': '"' + bc_obj.Name + '_[^_]*"',
                    'PatchNamesListSlave': '"' + bc_obj.Name + '_.*_slave"'
                }

        # Set up default BC for unassigned faces
        settings['createPatches']['defaultFaces'] = {
            'PatchNamesList': '"patch_0_0"',
            'PatchType': defaultPatchType
        }

        # Assign any extruded patches as the appropriate type
        mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj)
        for mr_id, mr_obj in enumerate(mr_objs):
            if mr_obj.Extrusion and mr_obj.ExtrusionType == "2DPlanar":
                settings['createPatches'][mr_obj.Label] = {
                    'PatchNamesList': '"patch_.*_' + str(mr_id + 1) + '"',
                    'PatchType': "empty"
                }
                settings['createPatches'][mr_obj.Label + 'BackFace'] = {
                    'PatchNamesList': '"patch_.*_' + str(mr_id + 1) + '_back"',
                    'PatchType': "empty"
                }
            elif mr_obj.Extrusion and mr_obj.ExtrusionType == "2DWedge":
                settings['createPatches'][mr_obj.Label] = {
                    'PatchNamesList': '"patch_.*_' + str(mr_id + 1) + '"',
                    'PatchType': "symmetry"
                }
                settings['createPatches'][mr_obj.Label + 'BackFace'] = {
                    'PatchNamesList': '"patch_.*_' + str(mr_id + 1) + '_back"',
                    'PatchType': "symmetry"
                }
            else:
                # Add others to default faces list
                settings['createPatches']['defaultFaces'][
                    'PatchNamesList'] += ' "patch_0_' + str(mr_id + 1) + '"'
Example #3
0
    def processExtrusions(self):
        """ Find and process any extrusion objects """

        twoD_extrusion_objs = []
        other_extrusion_objs = []
        mesh_refinements = CfdTools.getMeshRefinementObjs(self.mesh_obj)
        self.extrusion_settings['ExtrusionsPresent'] = False
        self.extrusion_settings['ExtrudeTo2D'] = False
        self.extrusion_settings['Extrude2DPlanar'] = False
        for mr in mesh_refinements:
            if mr.Extrusion:
                self.extrusion_settings['ExtrusionsPresent'] = True
                if mr.ExtrusionType == '2DPlanar' or mr.ExtrusionType == '2DWedge':
                    twoD_extrusion_objs.append(mr)
                else:
                    other_extrusion_objs.append(mr)
                if mr.ExtrusionType == '2DPlanar':
                    self.extrusion_settings['Extrude2DPlanar'] = True

        if len(twoD_extrusion_objs) > 1:
            raise RuntimeError(
                "For 2D meshing, there must be exactly one 2D mesh extrusion object."
            )
        elif len(twoD_extrusion_objs) == 1:
            self.extrusion_settings['ExtrudeTo2D'] = True
        all_extrusion_objs = other_extrusion_objs + twoD_extrusion_objs  # Ensure 2D extrusion happens last

        self.extrusion_settings['Extrusions'] = []
        for extrusion_obj in all_extrusion_objs:
            extrusion_shape = extrusion_obj.Shape
            this_extrusion_settings = {}
            if len(extrusion_shape.Faces) == 0:
                raise RuntimeError("Extrusion object '{}' is empty.".format(
                    extrusion_obj.Label))

            this_extrusion_settings[
                'KeepExistingMesh'] = extrusion_obj.KeepExistingMesh
            if extrusion_obj.ExtrusionType == '2DPlanar' or extrusion_obj.ExtrusionType == '2DWedge':
                this_extrusion_settings['KeepExistingMesh'] = False
                all_faces_planar = True
                for faces in extrusion_shape.Faces:
                    if not isinstance(faces.Surface, Part.Plane):
                        all_faces_planar = False
                        break
                if not all_faces_planar:
                    raise RuntimeError(
                        "2D mesh extrusion surface must be a flat plane.")

            normal = extrusion_shape.Faces[0].Surface.Axis
            normal.multiply(1.0 / normal.Length)
            this_extrusion_settings['Normal'] = (normal.x, normal.y, normal.z)
            this_extrusion_settings[
                'ExtrusionType'] = extrusion_obj.ExtrusionType

            # Get the names of the faces being extruded
            mri = mesh_refinements.index(extrusion_obj)
            efl = []
            for l, ff in enumerate(self.patch_faces):
                f = ff[mri + 1]
                if len(f):
                    efl.append(self.patch_names[l][mri + 1])

            if not efl:
                raise RuntimeError(
                    "Extrusion patch for '{}' could not be found in the shape being meshed."
                    .format(extrusion_obj.Label))
            this_extrusion_settings['FrontFaceList'] = tuple(efl)
            this_extrusion_settings['BackFace'] = efl[0] + '_back'

            this_extrusion_settings[
                'Distance'] = extrusion_obj.ExtrusionThickness.getValueAs('m')
            this_extrusion_settings[
                'Angle'] = extrusion_obj.ExtrusionAngle.getValueAs('deg')
            this_extrusion_settings[
                'NumLayers'] = extrusion_obj.ExtrusionLayers
            this_extrusion_settings[
                'ExpansionRatio'] = extrusion_obj.ExtrusionRatio
            this_extrusion_settings['AxisPoint'] = \
                tuple(Units.Quantity(p, Units.Length).getValueAs('m') for p in extrusion_obj.ExtrusionAxisPoint)

            axis_direction = extrusion_obj.ExtrusionAxisDirection

            # Flip axis if necessary to go in same direction as patch normal (otherwise negative volume cells result)
            if len(extrusion_shape.Faces) > 0:
                in_plane_vector = extrusion_shape.Faces[
                    0].CenterOfMass - extrusion_obj.ExtrusionAxisPoint
                extrusion_normal = extrusion_obj.ExtrusionAxisDirection.cross(
                    in_plane_vector)
                face_normal = extrusion_shape.Faces[0].normalAt(0.5, 0.5)
                if extrusion_normal.dot(face_normal) < 0:
                    axis_direction = -axis_direction

            this_extrusion_settings['AxisDirection'] = tuple(
                d for d in axis_direction)
            self.extrusion_settings['Extrusions'].append(
                this_extrusion_settings)
Example #4
0
    def processRefinements(self):
        """ Process mesh refinements """
        mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj)

        cf_settings = self.cf_settings
        cf_settings['MeshRegions'] = {}
        cf_settings['BoundaryLayers'] = {}
        cf_settings['InternalRegions'] = {}
        snappy_settings = self.snappy_settings
        snappy_settings['MeshRegions'] = {}
        snappy_settings['BoundaryLayers'] = {}
        snappy_settings['InternalRegions'] = {}

        # Make list of all faces in meshed shape with original index
        mesh_face_list = list(
            zip(self.mesh_obj.Part.Shape.Faces,
                range(len(self.mesh_obj.Part.Shape.Faces))))

        # Make list of all boundary references
        CfdTools.cfdMessage("Matching boundary patches\n")
        boundary_face_list = []
        bc_group = None
        analysis_obj = CfdTools.getParentAnalysisObject(self.mesh_obj)
        if not analysis_obj:
            analysis_obj = CfdTools.getActiveAnalysis()
        if analysis_obj:
            bc_group = CfdTools.getCfdBoundaryGroup(analysis_obj)
        for bc_id, bc_obj in enumerate(bc_group):
            for ri, ref in enumerate(bc_obj.ShapeRefs):
                try:
                    bf = CfdTools.resolveReference(ref)
                except RuntimeError as re:
                    raise RuntimeError(
                        "Error processing boundary condition {}: {}".format(
                            bc_obj.Label, str(re)))
                for si, s in enumerate(bf):
                    boundary_face_list += [(sf, (bc_id, ri, si))
                                           for sf in s[0].Faces]

        # Match them up to faces in the main geometry
        bc_matched_faces = CfdTools.matchFaces(boundary_face_list,
                                               mesh_face_list)

        # Check for and filter duplicates
        bc_match_per_shape_face = [-1] * len(mesh_face_list)
        for k in range(len(bc_matched_faces)):
            match = bc_matched_faces[k][1]
            prev_k = bc_match_per_shape_face[match]
            if prev_k >= 0:
                nb, ri, si = bc_matched_faces[k][0]
                nb2, ri2, si2 = bc_matched_faces[prev_k][0]
                bc = bc_group[nb]
                bc2 = bc_group[nb2]
                CfdTools.cfdWarning(
                    "Boundary '{}' reference {}:{} also assigned as "
                    "boundary '{}' reference {}:{} - ignoring duplicate\n".
                    format(bc.Label, bc.ShapeRefs[ri][0].Name,
                           bc.ShapeRefs[ri][1][si], bc2.Label,
                           bc.ShapeRefs[ri][0].Name, bc.ShapeRefs[ri][1][si]))
            else:
                bc_match_per_shape_face[match] = k

        # Match relevant mesh regions to the shape being meshed: boundary layer mesh regions for cfMesh,
        # all surface mesh refinements for snappyHexMesh, and extrusion patches for all meshers.
        # For cfMesh, surface mesh refinements are written as separate surfaces so need not be matched
        CfdTools.cfdMessage("Matching mesh refinement regions\n")
        mr_face_list = []
        for mr_id, mr_obj in enumerate(mr_objs):
            if mr_obj.Extrusion or (
                    self.mesh_obj.MeshUtility == 'cfMesh'
                    and not mr_obj.Internal and mr_obj.NumberLayers > 0) or (
                        self.mesh_obj.MeshUtility == 'snappyHexMesh'
                        and not mr_obj.Internal):
                for ri, r in enumerate(mr_obj.ShapeRefs):
                    try:
                        bf = CfdTools.resolveReference(r)
                    except RuntimeError as re:
                        raise RuntimeError(
                            "Error processing mesh refinement {}: {}".format(
                                mr_obj.Label, str(re)))
                    for si, s in enumerate(bf):
                        mr_face_list += [(f, (mr_id, ri, si))
                                         for f in s[0].Faces]

        # Match them up to the primary geometry
        mr_matched_faces = CfdTools.matchFaces(mr_face_list, mesh_face_list)

        # Check for and filter duplicates
        mr_match_per_shape_face = [-1] * len(mesh_face_list)
        for k in range(len(mr_matched_faces)):
            match = mr_matched_faces[k][1]
            prev_k = mr_match_per_shape_face[match]
            if prev_k >= 0:
                nr, ri, si = mr_matched_faces[k][0]
                nr2, ri2, si2 = mr_matched_faces[prev_k][0]
                CfdTools.cfdWarning(
                    "Mesh refinement '{}' reference {}:{} also assigned as "
                    "mesh refinement '{}' reference {}:{} - ignoring duplicate\n"
                    .format(mr_objs[nr].Label,
                            mr_objs[nr].ShapeRefs[ri][0].Name,
                            mr_objs[nr].ShapeRefs[ri][1][si],
                            mr_objs[nr2].Label,
                            mr_objs[nr2].ShapeRefs[ri2][0].Name,
                            mr_objs[nr2].ShapeRefs[ri2][1][si2]))
            else:
                mr_match_per_shape_face[match] = k

        self.patch_faces = []
        self.patch_names = []
        for k in range(len(bc_group) + 1):
            self.patch_faces.append([])
            self.patch_names.append([])
            for l in range(len(mr_objs) + 1):
                self.patch_faces[k].append([])
                self.patch_names[k].append("patch_" + str(k) + "_" + str(l))
        for i in range(len(mesh_face_list)):
            k = bc_match_per_shape_face[i]
            l = mr_match_per_shape_face[i]
            nb = -1
            nr = -1
            if k >= 0:
                nb, bri, bsi = bc_matched_faces[k][0]
            if l >= 0:
                nr, rri, ssi = mr_matched_faces[l][0]
            self.patch_faces[nb + 1][nr + 1].append(i)

        # For gmsh, match mesh refinement with vertices in original mesh
        mr_matched_vertices = []
        if self.mesh_obj.MeshUtility == 'gmsh':
            # Make list of all vertices in meshed shape with original index
            mesh_vertices_list = list(
                zip(self.mesh_obj.Part.Shape.Vertexes,
                    range(len(self.mesh_obj.Part.Shape.Vertexes))))

            CfdTools.cfdMessage("Matching mesh refinements\n")
            mr_vertices_list = []
            for mr_id, mr_obj in enumerate(mr_objs):
                if not mr_obj.Internal:
                    for ri, r in enumerate(mr_obj.ShapeRefs):
                        try:
                            bf = CfdTools.resolveReference(r)
                        except RuntimeError as re:
                            raise RuntimeError(
                                "Error processing mesh refinement {}: {}".
                                format(mr_obj.Label, str(re)))
                        for si, s in enumerate(bf):
                            mr_vertices_list += [(v, (mr_id, ri, si))
                                                 for v in s[0].Vertexes]

            mr_matched_vertices = CfdTools.matchFaces(mr_vertices_list,
                                                      mesh_vertices_list)
            self.ele_length_map = {}
            self.ele_node_map = {}

        # For snappyHexMesh, also match surface mesh refinements to the boundary conditions, to identify boundary
        # conditions on supplementary geometry defined by the surface mesh refinements
        # Also matches baffles to surface mesh refinements
        bc_mr_matched_faces = []
        if self.mesh_obj.MeshUtility == 'snappyHexMesh':
            bc_mr_matched_faces = CfdTools.matchFaces(boundary_face_list,
                                                      mr_face_list)

        # Handle baffles
        for bc_id, bc_obj in enumerate(bc_group):
            if bc_obj.BoundaryType == 'baffle':
                baffle_matches = [
                    m for m in bc_mr_matched_faces if m[0][0] == bc_id
                ]
                mr_match_per_baffle_ref = []
                for r in bc_obj.ShapeRefs:
                    mr_match_per_baffle_ref += [[-1] * len(r[1])]
                for m in baffle_matches:
                    mr_match_per_baffle_ref[m[0][1]][m[0][2]] = m[1][0]
                # For each mesh region, the refs that are part of this baffle
                baffle_patch_refs = [[] for ri in range(len(mr_objs) + 1)]
                for ri, mr in enumerate(mr_match_per_baffle_ref):
                    for si, mri in enumerate(mr_match_per_baffle_ref[ri]):
                        baffle_patch_refs[mri + 1].append(
                            (bc_obj.ShapeRefs[ri][0],
                             (bc_obj.ShapeRefs[ri][1][si], )))

                # Write these geometries
                for ri, refs in enumerate(baffle_patch_refs):
                    try:
                        shape = CfdTools.makeShapeFromReferences(refs)
                    except RuntimeError as re:
                        raise RuntimeError(
                            "Error processing baffle {}: {}".format(
                                bc_obj.Label, str(re)))
                    solid_name = bc_obj.Name + "_" + str(ri)
                    if shape:
                        CfdTools.cfdMessage(
                            "Triangulating baffle {}, section {}\n".format(
                                bc_obj.Label, ri))
                        writeSurfaceMeshFromShape(shape, self.triSurfaceDir,
                                                  solid_name, self.mesh_obj)

                        if ri > 0:  # The parts of the baffle corresponding to a surface mesh region obj
                            mr_obj = mr_objs[ri - 1]
                            refinement_level = CfdTools.relLenToRefinementLevel(
                                mr_obj.RelativeLength)
                            edge_level = CfdTools.relLenToRefinementLevel(
                                mr_obj.RegionEdgeRefinement)
                        else:  # The parts of the baffle with no refinement obj
                            refinement_level = 0
                            edge_level = 0
                        snappy_settings['MeshRegions'][solid_name] = {
                            'RefinementLevel': refinement_level,
                            'EdgeRefinementLevel': edge_level,
                            'MaxRefinementLevel': max(refinement_level,
                                                      edge_level),
                            'Baffle': True
                        }

        for mr_id, mr_obj in enumerate(mr_objs):
            Internal = mr_obj.Internal
            mr_rellen = mr_obj.RelativeLength
            if mr_rellen > 1.0:
                mr_rellen = 1.0
                FreeCAD.Console.PrintError(
                    "The mesh refinement region '{}' should not use a relative length greater "
                    "than unity.\n".format(mr_obj.Name))
            elif mr_rellen < 0.001:
                mr_rellen = 0.001  # Relative length should not be less than 0.1% of base length
                FreeCAD.Console.PrintError(
                    "The mesh refinement region '{}' should not use a relative length smaller "
                    "than 0.001.\n".format(mr_obj.Name))

            if self.mesh_obj.MeshUtility == 'gmsh':
                # Generate element maps for gmsh
                if not Internal:
                    mesh_vertex_idx = [
                        mf[1] for mf in mr_matched_vertices
                        if mf[0][0] == mr_id
                    ]
                    self.ele_length_map[mr_obj.Name] = mr_rellen * self.clmax
                    self.ele_node_map[mr_obj.Name] = mesh_vertex_idx
            else:
                # Find any matches with boundary conditions; mark those matching baffles for removal
                bc_matches = [
                    m for m in bc_mr_matched_faces if m[1][0] == mr_id
                ]
                bc_match_per_mr_ref = []
                for ri, r in enumerate(mr_obj.ShapeRefs):
                    bc_match_per_mr_ref.append([-1] * len(r[1]))
                for m in bc_matches:
                    bc_match_per_mr_ref[m[1][1]][m[1][2]] = -2 if bc_group[
                        m[0][0]].BoundaryType == 'baffle' else m[0][0]

                # Unmatch those in primary geometry
                main_geom_matches = [
                    m for m in mr_matched_faces if m[0][0] == mr_id
                ]
                for m in main_geom_matches:
                    bc_match_per_mr_ref[m[0][1]][m[0][2]] = -1

                # For each boundary, the refs that are part of this mesh region
                mr_patch_refs = [[] for ri in range(len(bc_group) + 1)]
                for ri, m in enumerate(bc_match_per_mr_ref):
                    for si, bci in enumerate(m):
                        if bci > -2:
                            mr_patch_refs[bci + 1].append(
                                (mr_obj.ShapeRefs[ri][0],
                                 (mr_obj.ShapeRefs[ri][1][si], )))

                # Loop over and write the sub-sections of this mesh object
                for bi in range(len(mr_patch_refs)):
                    if len(mr_patch_refs[bi]) and not mr_obj.Extrusion:
                        if bi == 0:
                            mr_patch_name = mr_obj.Name
                        else:
                            mr_patch_name = self.patch_names[bi][mr_id + 1]

                        CfdTools.cfdMessage(
                            "Triangulating mesh refinement region {}, section {}\n"
                            .format(mr_obj.Label, bi))

                        try:
                            shape = CfdTools.makeShapeFromReferences(
                                mr_patch_refs[bi])
                        except RuntimeError as re:
                            raise RuntimeError(
                                "Error processing mesh refinement region {}: {}"
                                .format(mr_obj.Label, str(re)))
                        if shape:
                            writeSurfaceMeshFromShape(shape,
                                                      self.triSurfaceDir,
                                                      mr_patch_name,
                                                      self.mesh_obj)

                        refinement_level = CfdTools.relLenToRefinementLevel(
                            mr_obj.RelativeLength)
                        if self.mesh_obj.MeshUtility == 'cfMesh':
                            if not Internal:
                                cf_settings['MeshRegions'][mr_patch_name] = {
                                    'RefinementLevel':
                                    refinement_level,
                                    'RefinementThickness':
                                    self.scale * Units.Quantity(
                                        mr_obj.RefinementThickness).Value,
                                }
                            else:
                                cf_settings['InternalRegions'][mr_obj.Name] = {
                                    'RefinementLevel':
                                    refinement_level,
                                    'RelativeLength':
                                    mr_rellen * self.clmax * self.scale
                                }

                        elif self.mesh_obj.MeshUtility == 'snappyHexMesh':
                            if not Internal:
                                edge_level = CfdTools.relLenToRefinementLevel(
                                    mr_obj.RegionEdgeRefinement)
                                snappy_settings['MeshRegions'][
                                    mr_patch_name] = {
                                        'RefinementLevel':
                                        refinement_level,
                                        'EdgeRefinementLevel':
                                        edge_level,
                                        'MaxRefinementLevel':
                                        max(refinement_level, edge_level),
                                        'Baffle':
                                        False
                                    }
                            else:
                                snappy_settings['InternalRegions'][
                                    mr_patch_name] = {
                                        'RefinementLevel': refinement_level
                                    }

            # In addition, for cfMesh and SnappyHesMesh, record matched boundary layer patches

            if (self.mesh_obj.MeshUtility == 'cfMesh' or self.mesh_obj.MeshUtility == 'snappyHexMesh') \
                    and mr_obj.NumberLayers > 0 and not Internal and not mr_obj.Extrusion:

                for k in range(len(self.patch_faces)):
                    if len(self.patch_faces[k][mr_id + 1]):
                        # Limit expansion ratio to greater than 1.0 and less than 1.2
                        expratio = mr_obj.ExpansionRatio
                        expratio = min(1.2, max(1.0, expratio))

                        if self.mesh_obj.MeshUtility == 'cfMesh':
                            cf_settings['BoundaryLayers'][self.patch_names[k][mr_id + 1]] = \
                            {
                                'NumberLayers': mr_obj.NumberLayers,
                                'ExpansionRatio': expratio,
                                'FirstLayerHeight': self.scale * Units.Quantity(mr_obj.FirstLayerHeight).Value
                            }
                        elif self.mesh_obj.MeshUtility == 'snappyHexMesh':
                            snappy_settings['BoundaryLayers'][self.patch_names[k][mr_id + 1]] = \
                            {
                                'NumberLayers': mr_obj.NumberLayers,
                                'ExpansionRatio': expratio,
                                # 'FinalLayerHeight': self.scale * Units.Quantity(mr_obj.FinalLayerHeight).Value
                            }
Example #5
0
    def processBoundaryConditions(self):
        """ Compute any quantities required before case build """
        settings = self.settings
        # Copy keys so that we can delete while iterating
        bc_names = list(settings['boundaries'].keys())
        for bc_name in bc_names:
            bc = settings['boundaries'][bc_name]
            if not bc['VelocityIsCartesian']:
                veloMag = bc['VelocityMag']
                face = bc['DirectionFace'].split(':')
                if not face[0]:
                    face = bc['ShapeRefs'][0].Name
                # See if entered face actually exists and is planar
                try:
                    selected_object = self.analysis_obj.Document.getObject(
                        face[0])
                    if hasattr(selected_object, "Shape"):
                        elt = selected_object.Shape.getElement(face[1])
                        if elt.ShapeType == 'Face' and CfdTools.isPlanar(elt):
                            n = elt.normalAt(0.5, 0.5)
                            if bc['ReverseNormal']:
                                n = [-ni for ni in n]
                            velocity = [ni * veloMag for ni in n]
                            bc['Ux'] = velocity[0]
                            bc['Uy'] = velocity[1]
                            bc['Uz'] = velocity[2]
                        else:
                            raise RuntimeError
                    else:
                        raise RuntimeError
                except (SystemError, RuntimeError):
                    raise RuntimeError(
                        str(bc['DirectionFace']) +
                        " is not a valid, planar face.")
            if settings['solver']['SolverName'] in [
                    'simpleFoam', 'porousSimpleFoam', 'pimpleFoam'
            ]:
                bc['KinematicPressure'] = bc['Pressure'] / settings[
                    'fluidProperties'][0]['Density']

            if bc['PorousBaffleMethod'] == 'porousScreen':
                wireDiam = bc['ScreenWireDiameter']
                spacing = bc['ScreenSpacing']
                CD = 1.0  # Drag coeff of wire (Simmons - valid for Re > ~300)
                beta = (1 - wireDiam / spacing)**2
                bc['PressureDropCoeff'] = CD * (1 - beta)

            if settings['solver']['SolverName'] in [
                    'interFoam', 'multiphaseInterFoam'
            ]:
                # Make sure the first n-1 alpha values exist, and write the n-th one
                # consistently for multiphaseInterFoam
                sum_alpha = 0.0
                alphas_new = {}
                for i, m in enumerate(settings['fluidProperties']):
                    alpha_name = m['Name']
                    if i == len(settings['fluidProperties']) - 1:
                        if settings['solver'][
                                'SolverName'] == 'multiphaseInterFoam':
                            alphas_new[alpha_name] = 1.0 - sum_alpha
                    else:
                        alpha = Units.Quantity(
                            bc.get('VolumeFractions', {}).get(alpha_name,
                                                              '0')).Value
                        alphas_new[alpha_name] = alpha
                        sum_alpha += alpha
                bc['VolumeFractions'] = alphas_new

            # Copy turbulence settings
            bc['TurbulenceIntensity'] = bc[
                'TurbulenceIntensityPercentage'] / 100.0
            physics = settings['physics']
            if physics['Turbulence'] == 'RANS' and physics[
                    'TurbulenceModel'] == 'SpalartAllmaras':
                if (bc['BoundaryType'] == 'inlet' or bc['BoundaryType'] == 'open') and \
                        bc['TurbulenceInletSpecification'] == 'intensityAndLengthScale':
                    if bc['BoundarySubType'] == 'uniformVelocityInlet' or bc[
                            'BoundarySubType'] == 'farField':
                        Uin = (bc['Ux']**2 + bc['Uy']**2 + bc['Uz']**2)**0.5

                        # Turb Intensity and length scale
                        I = bc['TurbulenceIntensity']
                        l = bc['TurbulenceLengthScale']

                        # Spalart Allmaras
                        bc['NuTilda'] = (3.0 / 2.0)**0.5 * Uin * I * l

                    else:
                        raise RuntimeError(
                            "Inlet type currently unsupported for calculating turbulence inlet conditions from "
                            "intensity and length scale.")

            if bc['DefaultBoundary']:
                if settings['boundaries'].get('defaultFaces'):
                    raise ValueError("More than one default boundary defined")
                settings['boundaries']['defaultFaces'] = bc
        if not settings['boundaries'].get('defaultFaces'):
            settings['boundaries']['defaultFaces'] = {
                'BoundaryType': 'wall',
                'BoundarySubType': 'slipWall',
                'ThermalBoundaryType': 'zeroGradient'
            }

        # Assign any extruded patches as the appropriate type
        mr_objs = CfdTools.getMeshRefinementObjs(self.mesh_obj)
        for mr_id, mr_obj in enumerate(mr_objs):
            if mr_obj.Extrusion and mr_obj.ExtrusionType == "2DPlanar":
                settings['boundaries'][mr_obj.Label] = {
                    'BoundaryType': 'constraint',
                    'BoundarySubType': 'empty'
                }
                settings['boundaries'][mr_obj.Label + "BackFace"] = {
                    'BoundaryType': 'constraint',
                    'BoundarySubType': 'empty'
                }
            if mr_obj.Extrusion and mr_obj.ExtrusionType == "2DWedge":
                settings['boundaries'][mr_obj.Label] = {
                    'BoundaryType': 'constraint',
                    'BoundarySubType': 'symmetry'
                }
                settings['boundaries'][mr_obj.Label + "BackFace"] = {
                    'BoundaryType': 'constraint',
                    'BoundarySubType': 'symmetry'
                }