Exemplo n.º 1
0
def _bspline_restrict(solid, tol):
    """
    Attempt to re-fit OpenVSP surfaces with more continuity.
    """
    # Use dmax=1 because that was only way to get the tool actually refit the
    # surfaces other than another surface where the multiplicity equaled the
    # degree. Not sure why the tool operates this way.
    logger.info('\tApplying ShapeBSplineRestriction tool...')
    tool = ShapeBSplineRestriction(solid, dmax=1, tol3d=tol)
    if not tool.is_done:
        logger.info('Method unsuccessful. Using original solid.')
        return solid

    # Get new shape and solid
    new_solid = tool.modified_shape(solid)

    # Limit/fix tolerance
    FixShape.limit_tolerance(new_solid)
    tol = new_solid.tol_avg
    if not CheckShape(new_solid).is_valid:
        logger.info('Shape invalid. Using original solid.')
        return solid

    logger.info('\tMethod successful with surface error: {}'.format(
        tool.error_surface))
    logger.info('\tNew shape tolerance: {}'.format(tol))

    return new_solid
Exemplo n.º 2
0
    def __init__(self, name, shape, cref=None, sref=None, group=None):
        super(Part, self).__init__(name)

        # Shape holder
        type_ = (Shape,)
        if isinstance(self, CurvePart):
            type_ = (Edge, Wire, Compound)
        elif isinstance(self, SurfacePart):
            type_ = (Face, Shell, Compound)
        ShapeHolder.__init__(self, type_, shape)

        # Random color
        self.random_color()

        # Unique ID
        self._id = Part._indx
        Part._indx += 1

        # Geometry data
        self._cref, self._sref = None, None
        if cref is not None:
            self.set_cref(cref)
        if sref is not None:
            self.set_sref(sref)

        # Other data
        self._subparts = {}

        # Add to group
        GroupAPI.add_parts(group, self)

        # Log
        msg = ' '.join(['Creating part:', name])
        logger.info(msg)
Exemplo n.º 3
0
    def log_errors(self):
        """
        Log the errors at the "info" level.

        :return: None.
        """
        for msg in self._errors:
            logger.info(msg)
Exemplo n.º 4
0
Arquivo: join.py Projeto: trelau/AFEM
    def __init__(self, parts, tol=None):
        self._is_done = False

        for part in parts:
            if not isinstance(part, SurfacePart):
                msg = 'Part is not a surface part.'
                raise TypeError(msg)

        # Test all combinations of parts for intersection of reference curve
        join_parts = []
        main_parts = []
        nparts = len(parts)
        for i in range(0, nparts - 1):
            main = parts[i]
            other_parts = []
            for j in range(i + 1, nparts):
                other = parts[j]
                if not main.has_cref or not other.has_cref:
                    continue
                if tol is None:
                    tol1 = main.shape.tol_max
                    tol2 = other.shape.tol_max
                    _tol = max(tol1, tol2)
                else:
                    _tol = tol
                e1 = EdgeByCurve(main.cref).edge
                e2 = EdgeByCurve(other.cref).edge
                bop = IntersectShapes(e1, e2, fuzzy_val=_tol)
                if not bop.vertices:
                    continue
                # Store potential join
                msg = 'Found joint between {} and {}.'.format(
                    main.name, other.name)
                logger.info(msg)
                other_parts.append(other)
            if other_parts:
                main_parts.append(main)
                join_parts.append(other_parts)

        # Join the parts
        for main, other_parts in zip(main_parts, join_parts):
            main.fuse(*other_parts)
            self._is_done = True
Exemplo n.º 5
0
def _reloft_wing_surface(srf, tol):
    """
    Attempt to reloft an OpenVSP wing surface which was not split to achieve
    higher continuity.
    """
    logger.info('\tAttempting to reloft the surface...')
    # Gather isocurves at each section, tessellate, and approximate
    crvs = []
    for u in srf.uknots:
        c0 = srf.u_iso(u)
        adp_crv = AdaptorCurve.to_adaptor(c0)
        tool = GCPnts_QuasiUniformDeflection(adp_crv.object, tol)
        if not tool.IsDone():
            logger.info('\tTessellation failed. Using original surface.')
            return srf
        pnts = [c0.eval(tool.Parameter(i)) for i in
                range(1, tool.NbPoints() + 1)]
        c = NurbsCurveByApprox(pnts, tol=tol, continuity=Geometry.C1).curve
        crvs.append(c)
    return NurbsSurfaceByInterp(crvs, 1).surface
Exemplo n.º 6
0
    def __init__(self, name, shape, cref=None, sref=None, group=None):
        types = (Shape, )
        if isinstance(self, CurvePart):
            types = (Edge, Wire, Compound)
        elif isinstance(self, SurfacePart):
            types = (Face, Shell, Compound)
        super(Part, self).__init__(name, shape, cref, sref, types)

        # Unique ID
        self._id = Part._indx
        Part._indx += 1

        # Add to group
        GroupAPI.add_parts(group, self)

        # Log
        msg = ' '.join(['Creating part:', name])
        logger.info(msg)

        # Groups for meshing
        self._node_group = None
        self._edge_group = None
        self._face_group = None
Exemplo n.º 7
0
def _build_solid(compound, divide_closed):
    """
    Try to build a solid from the OpenVSP compound of faces.

    :param afem.topology.entities.Compound compound: The compound.
    :param bool divide_closed: Option to divide closed faces.

    :return: The solid.
    :rtype: afem.topology.entities.Solid
    """
    # Get all the faces in the compound. The surfaces must be split. Discard
    # any with zero area.
    faces = []
    for face in compound.faces:
        area = SurfaceProps(face).area
        if area > 1.0e-7:
            faces.append(face)

    # Replace any planar B-Spline surfaces with planes.
    non_planar_faces = []
    planar_faces = []
    for f in faces:
        srf = f.surface
        try:
            pln = srf.as_plane()
            if pln:
                w = f.outer_wire
                # Fix the wire because they are usually degenerate edges in
                # the planar end caps.
                builder = BRepBuilderAPI_MakeWire()
                for e in w.edges:
                    if LinearProps(e).length > 1.0e-7:
                        builder.Add(e.object)
                w = builder.Wire()
                fix = ShapeFix_Wire()
                fix.Load(w)
                fix.SetSurface(pln.object)
                fix.FixReorder()
                fix.FixConnected()
                fix.FixEdgeCurves()
                fix.FixDegenerated()
                w = Wire(fix.WireAPIMake())
                fnew = Face.by_wire(w)
                planar_faces.append(fnew)
            else:
                non_planar_faces.append(f)
        except RuntimeError:
            logger.info('Failed to check for planar face...')
            non_planar_faces.append(f)

    # Make a compound of the faces
    shape = Compound.by_shapes(non_planar_faces + planar_faces)

    # Split closed faces
    if divide_closed:
        shape = DivideClosedShape(shape).shape

    # Sew shape
    sewn_shape = SewShape(shape).sewed_shape
    if isinstance(sewn_shape, Face):
        sewn_shape = sewn_shape.to_shell()

    # Attempt to unify planar domains
    shell = UnifyShape(sewn_shape).shape

    # Make solid
    if not isinstance(shell, Shell):
        logger.info('\tA valid shell was not able to be generated.')
        check = CheckShape(shell)
        if not check.is_valid:
            logger.info('\tShape errors:')
            check.log_errors()
        return shell, check.invalid_shapes

    solid = Solid.by_shell(shell)

    # Limit tolerance
    FixShape.limit_tolerance(solid)

    # Check the solid and attempt to fix
    invalid = []
    check = CheckShape(solid)
    if not check.is_valid:
        logger.info('\tFixing the solid...')
        solid = FixShape(solid).shape
        check = CheckShape(solid)
        if not check.is_valid:
            logger.info('\t...solid could not be fixed.')
            logger.info('\tShape errors:')
            check.log_errors()
            failed = check.invalid_shapes
            invalid += failed
    else:
        tol = solid.tol_avg
        logger.info(
            '\tSuccessfully generated solid with tolerance={}'.format(tol))

    return solid, invalid
Exemplo n.º 8
0
    def import_step(self, fn):
        """
        Import a STEP file generated by the OpenVSP version that has been
        modified to include metadata.

        :param str fn: The full path to the file.

        :return: None.
        """
        # Store data as dictionaries.
        bodies = {}
        indx = 0

        # Dictionaries to attach wing reference surfaces to wing bodies using
        # reference surface ID as the key.
        wing_bodies = {}
        ref_surfs = {}

        # Data structures for fuselage reference surfaces
        fuselage_bodies = {}
        href_surfs = {}
        vref_surfs = {}

        # Read STEP file
        step_reader = StepRead(fn)
        master_shape = step_reader.shape

        # Iterate over master shape to find compounds for geometric sets. These
        # sets contain the metadata and the surfaces that make up the
        # component.
        for compound in master_shape.shape_iter:
            # Get the metadata
            name = step_reader.name_from_shape(compound)

            # Unnamed body
            if not name:
                indx += 1
                comp_name = '.'.join(['Body', str(indx)])
                msg = ' '.join(['---Processing OpenVSP component:', comp_name])
                logger.info(msg)
                solid, invalid = _build_solid(compound, self._divide)
                self._invalid += invalid
                if solid is not None:
                    body = Body(solid, comp_name)
                    bodies[comp_name] = body
                continue
            metadata = json.loads(name)

            # Process reference surfaces and continue
            key = 'm_SurfType'
            if key in metadata and metadata[key] == 99:
                # Get surface
                sref = ImportVSP.process_sref(compound)
                # Get Sref ID
                sref_id = metadata['ID']
                ref_surfs[sref_id] = sref
                continue
            elif key in metadata and metadata[key] == 100:
                # Fuselage horizontal sref
                f = compound.faces[0]
                sref = f.surface
                sref.set_udomain(-1., 1.)
                sref.set_vdomain(0., 1.)
                sref.object.ExchangeUV()
                sref.object.UReverse()
                sref_id = metadata['ID']
                href_surfs[sref_id] = sref
                continue
            elif key in metadata and metadata[key] == 101:
                f = compound.faces[0]
                sref = f.surface
                sref.set_udomain(-1., 1.)
                sref.set_vdomain(0., 1.)
                sref.object.ExchangeUV()
                sref.object.UReverse()
                sref_id = metadata['ID']
                vref_surfs[sref_id] = sref
                continue

            comp_name = metadata['m_Name']
            if comp_name in bodies:
                indx += 1
                comp_name = '.'.join([comp_name, str(indx)])

            # Process component.
            msg = ' '.join(['---Processing OpenVSP component:', comp_name])
            logger.info(msg)

            # Wing
            if metadata['m_Type'] == 5 and metadata['m_SurfType'] != 99:
                wing, invalid = _process_wing(compound, self._divide,
                                              self._restrict, self._tol,
                                              self._reloft, comp_name)
                self._invalid += invalid
                if wing is not None:
                    bodies[comp_name] = wing
                    sref_id = metadata['Sref ID']
                    wing_bodies[sref_id] = wing

            # Fuselage
            elif metadata['m_Type'] in [4, 9]:
                fuse, invalid = _process_fuse(compound, self._divide,
                                              comp_name)
                self._invalid += invalid
                if fuse is not None:
                    bodies[comp_name] = fuse
                    sref_id = metadata['Sref ID']
                    fuselage_bodies[sref_id] = fuse

            # Unknown
            else:
                solid, invalid = _build_solid(compound, self._divide)
                self._invalid += invalid
                if solid:
                    body = Body(solid, comp_name)
                    bodies[comp_name] = body

        # Attach wing reference surfaces to the bodies.
        for sref_id in wing_bodies:
            if sref_id not in ref_surfs:
                continue
            wing = wing_bodies[sref_id]
            sref = ref_surfs[sref_id]
            wing.set_sref(sref)

        # Attach fuselage reference surfaces to the bodies.
        for sref_id in fuselage_bodies:
            if sref_id in href_surfs:
                fuselage = fuselage_bodies[sref_id]
                sref = href_surfs[sref_id]
                fuselage.metadata.set('hsref', sref)
            if sref_id in vref_surfs:
                fuselage = fuselage_bodies[sref_id]
                sref = vref_surfs[sref_id]
                fuselage.metadata.set('vsref', sref)

        # Update
        self._bodies.update(bodies)