Example #1
0
class Shaft:
    "The axis of the shaft is always assumed to correspond to the X-axis"
    # List of shaft segments (each segment has a different diameter)
    segments = []
    # The sketch
    sketch = 0
    #featureWindow = None
    # The diagrams
    diagrams = {} # map of function name against Diagram object
    # Calculation of shaft
    Qy = 0 # force in direction of y axis
    Qz = 0 # force in direction of z axis
    Mbz = 0 # bending moment around z axis
    Mby = 0 # bending moment around y axis
    Mtz = 0 # torsion moment around z axis

    def __init__(self, doc):
        self.sketch = ShaftFeature(doc)

    def getLengthTo(self, index):
        "Get the total length of all segments up to the given one"
        result = 0.0
        for i in range(index):
            result += self.segments[i].length
        return result

    def addSegment(self, l, d, di):
        #print "Adding segment: ", l, " : ", d
        self.segments.append(ShaftSegment(l,d,di))
        self.sketch.addSegment(l, d, di)
        # We don't call equilibrium() here because the new segment has no loads defined yet

    def updateSegment(self, index, length = None, diameter = None, innerdiameter = None):
        oldLength = self.segments[index].length
        #print "Old length of ", index, ": ", oldLength, ", new Length: ", length, " diameter: ", diameter
        if length is not None:
            self.segments[index].length = length
        if diameter is not None:
            self.segments[index].diameter = diameter
        if innerdiameter is not None:
            self.segments[index].innerdiameter = innerdiameter
        self.sketch.updateSegment(index, oldLength, self.segments[index].length,
                                  self.segments[index].diameter, self.segments[index].innerdiameter)
        self.equilibrium()
        self.updateDiagrams()

    def updateLoad(self, index, loadType = None, loadSize = None, loadLocation = None):
        if (loadType is not None):
            self.segments[index].loadType = loadType
        if (loadSize is not None):
            self.segments[index].loadSize = loadSize
        if (loadLocation is not None):
            if (loadLocation >= 0) and (loadLocation <= self.segments[index].length):
                self.segments[index].loadLocation = loadLocation
            else:
                # TODO: Show warning
                FreeCAD.Console.PrintMessage("Load location must be inside segment\n")

        #self.feature.updateForces() graphical representation of the forces
        self.equilibrium()
        self.updateDiagrams()

    def updateEdge(self, column, start):
        App.Console.PrintMessage("Not implemented yet - waiting for robust references...")
        return
        if self.sketchClosed is not True:
            return
        # Create a chamfer or fillet at the start or end edge of the segment
        if start is True:
            row = rowStartEdgeType
            idx = 0
        else:
            row = rowEndEdgeType
            idx = 1

        edgeType = self.tableWidget.item(row, column).text().toAscii()[0].upper()
        if not ((edgeType == "C") or (edgeType == "F")):
            return # neither chamfer nor fillet defined

        if edgeType == "C":
            objName = self.doc.addObject("PartDesign::Chamfer","ChamferShaft%u" % (column * 2 + idx))
        else:
            objName = self.doc.addObject("PartDesign::Fillet","FilletShaft%u" % (column * 2 + idx))
        if objName == "":
            return

        edgeName = "Edge%u" % self.getEdgeIndex(column, idx, edgeType)
        self.doc.getObject(objName).Base = (self.doc.getObject("RevolutionShaft"),"[%s]" % edgeName)
        # etc. etc.

    def getEdgeIndex(self, column, startIdx):
        # FIXME: This is impossible without robust references anchored in the sketch!!!
        return

    def updateDiagrams(self):
        if (self.Qy == 0) or (self.Mbz == 0):
            return
        if self.Qy.name in self.diagrams:
            # Update diagram
            self.diagrams[self.Qy.name].update(self.Qy, self.getLengthTo(len(self.segments)) / 1000.0)
        else:
            # Create diagram
            self.diagrams[self.Qy.name] = Diagram()
            self.diagrams[self.Qy.name].create("Shear force", self.Qy, self.getLengthTo(len(self.segments)) / 1000.0, "x", "mm", 1000.0, "Q_y", "N", 1.0, 10)
        if self.Mbz.name in self.diagrams:
            # Update diagram
            self.diagrams[self.Mbz.name].update(self.Mbz, self.getLengthTo(len(self.segments)) / 1000.0)
        else:
            # Create diagram
            self.diagrams[self.Mbz.name] = Diagram()
            self.diagrams[self.Mbz.name].create("Bending moment", self.Mbz, self.getLengthTo(len(self.segments)) / 1000.0, "x", "mm", 1000.0, "M_{b,z}", "Nm", 1.0, 10)

    def equilibrium(self):
        # Build equilibrium equations
        forces = {0.0:0.0} # dictionary of (location : outer force)
        moments = {0.0:0.0} # dictionary of (location : outer moment)
        variableNames = [""] # names of all variables
        locations = {} # dictionary of (variableName : location)
        coefficientsFy = [0] # force equilibrium equation
        coefficientsMbz = [0] # moment equilibrium equation

        for i in range(len(self.segments)):
            lType = self.segments[i].loadType
            load = -1 # -1 means unknown (just for debug printing)
            location = -1

            if lType == "Fixed":
                # Fixed segment
                if i == 0:
                    location = 0
                    variableNames.append("Fy%u" % i)
                    coefficientsFy.append(1)
                    coefficientsMbz.append(0)
                    variableNames.append("Mz%u" % i)
                    coefficientsFy.append(0)
                    coefficientsMbz.append(1) # Force does not contribute because location is zero
                elif i == len(self.segments) - 1:
                    location = self.getLengthTo(len(self.segments)) / 1000
                    variableNames.append("Fy%u" % i)
                    coefficientsFy.append(1)
                    coefficientsMbz.append(location)
                    variableNames.append("Mz%u" % i)
                    coefficientsFy.append(0)
                    coefficientsMbz.append(1)
                else:
                    # TODO: Better error message
                    FreeCAD.Console.PrintMessage("Fixed constraint must be at beginning or end of shaft\n")
                    return

                locations["Fy%u" % i] = location
                locations["Mz%u" % i] = location
            elif lType == "Static":
                # Static load (currently force only)
                load = self.segments[i].loadSize
                location = (self.getLengthTo(i) + self.segments[i].loadLocation)  / 1000 # convert to meters
                coefficientsFy[0] = coefficientsFy[0] - load
                forces[location] = load
                coefficientsMbz[0] = coefficientsMbz[0] - load * location
                moments[location] = 0
            #elif lType == "None":
            #    # No loads on segment

            FreeCAD.Console.PrintMessage("Segment: %u, type: %s, load: %f, location: %f\n" % (i, lType, load, location))

        self.printEquilibrium(variableNames, coefficientsFy)
        self.printEquilibrium(variableNames, coefficientsMbz)

        # Build matrix and vector for linear algebra solving algorithm
        try:
            import numpy as np
        except ImportError:
            FreeCAD.Console.PrintMessage("numpy is not installed on your system\n")
            raise ImportError("numpy not installed")
        if (len(coefficientsFy) < 3) or (len(coefficientsMbz) < 3):
            return
        A = np.array([coefficientsFy[1:], coefficientsMbz[1:]])
        b = np.array([coefficientsFy[0], coefficientsMbz[0]])
        solution = np.linalg.solve(A, b)

        # Complete dictionary of forces and moments
        if variableNames[1][0] == "F":
            forces[locations[variableNames[1]]] = solution[0]
        else:
            moments[locations[variableNames[1]]] = solution[0]

        if variableNames[2][0] == "F":
            forces[locations[variableNames[2]]] = solution[1]
        else:
            moments[locations[variableNames[2]]] = solution[1]

        FreeCAD.Console.PrintMessage(forces)
        FreeCAD.Console.PrintMessage(moments)
        self.Qy = SegmentFunction("Qy")
        self.Qy.buildFromDict("x", forces)
        self.Qy.output()
        self.Mbz = self.Qy.integrated().negate()
        self.Mbz.addSegments(moments) # takes care of boundary conditions
        self.Mbz.name = "Mbz"
        self.Mbz.output()

    def printEquilibrium(self, var, coeff):
        # Auxiliary method for debugging purposes
        for i in range(len(var)):
            if i == 0:
                FreeCAD.Console.PrintMessage("%f = " % coeff[i])
            else:
                FreeCAD.Console.PrintMessage("%f * %s" % (coeff[i], var[i]))
            if (i < len(var) - 1) and (i != 0):
                FreeCAD.Console.PrintMessage(" + ")
        FreeCAD.Console.PrintMessage("\n")