def evolve(): global topMass, mainMass, oscForceAngle, oscAmp, omega, dt, t, mainMass_displacementTime_source, topMass_displacementTime_source, M, C, K, velOld, dispOld, lhs, F0, rhs, gamma, beta, accOld, x1, x2 oscForceAngle+=omega*dt t+=dt F1_next=-oscAmp*cos(oscForceAngle) #force applied to main mass F_next = np.array([[F1_next,0]]) # Newmark method with gamma = 0.5 and beta = 0.25: # v_n+1 = v_n+dt*[(1-gamma)*a_n+gamma*a_n+1] # d_n+1 = d_n+dt*v_n+dt*dt*[(0.5-beta)*a_n+beta*a_n+1] # Ma_n+1 + Cv_n+1 + Kd_n+1 = F_n+1 lhs = M+gamma*dt*C+beta*dt*dt*K rhs = F_next-C.dot(velOld+(1-gamma)*dt*accOld)-K.dot(dispOld+dt*velOld+dt*dt*(0.5-beta)*accOld) rhs_array = np.array([rhs[0][0],rhs[0][1]]) accNew = inv(lhs).dot(rhs_array) velNew = velOld+(1-gamma)*dt*accOld+gamma*dt*accNew dispNew = dispOld+dt*velOld+dt*dt*(0.5-beta)*accOld+dt*dt*beta*accNew mainMass.move(Coord(0,dispNew[0]-dispOld[0])*baseSpring.kappa/3) #move main mass by calculated displacement normalized with base spring stiffness topMass.move(Coord(0,dispNew[1]-dispOld[1])*baseSpring.kappa/3) #move top mass by calculated displacement normalized with base spring stiffness accOld = accNew #a_n for next time step velOld = velNew #v_n for next time step dispOld = dispNew #d_n for next time step h1=mainMass.getTop() h2=topMass.getTop() mainMass_position = mainMass.currentPos['y'][0] topMass_position = topMass.currentPos['y'][0] mainMass_displacement = (mainMass_position - x1)*3 topMass_displacement = (topMass_position - x2)*3 mainMass_displacementTime_source.stream(dict(x=[t],y=[mainMass_displacement])) topMass_displacementTime_source.stream(dict(x=[t],y=[topMass_displacement])) forceTime_source.stream(dict(x=[t],y=[F1_next])) # update force arrow F_for_vis = F1_next*5 #scale # draw arrow in correct direction if (F1_next<1e-10): arrow_line.stream(dict(x1=[3], x2=[3], y1=[h1-F_for_vis], y2=[h1]),rollover=1) arrow_offset.stream(dict(x1=[3], x2=[3], y1=[h1+0.1], y2=[h1]),rollover=1) else: arrow_line.stream(dict(x1=[3], x2=[3], y1=[h1-F_for_vis], y2=[h1]),rollover=1) arrow_offset.stream(dict(x1=[3], x2=[3], y1=[h1-0.1], y2=[h1]),rollover=1) # update position of m_1 and m_2 label m1_label_source.data=dict(x=[0], y=[h1-2.5], t=['m']) m2_label_source.data=dict(x=[-3], y=[h2-2], t=['m']) m1_index_label_source.data=dict(x=[0.6], y=[h1-2.8], t=['1']) m2_index_label_source.data=dict(x=[-2.4], y=[h2-2.3], t=['2'])
def __init__(self, start, end, lam=1.0): # define dashpot constant self.lam = lam # save points self.start = Coord(start[0], start[1]) self.end = Coord(end[0], end[1]) self.origStart = self.start self.origEnd = self.end self.startNow = self.start self.endNow = self.end # find direction along which dashpot lies # (not normalised) self.direction = self.end - self.start # define (normalised) perpendicular vector for spike directions perpVect = self.direction.perp() # define initial positions of dashpot coordinates self.CasingStart = dict( x=[ self.end.x - self.direction.x / 8.0 + perpVect.x / 2.0, self.start.x + self.direction.x / 8.0 + perpVect.x / 2.0, self.start.x + self.direction.x / 8.0 - perpVect.x / 2.0, self.end.x - self.direction.x / 8.0 - perpVect.x / 2.0 ], y=[ self.end.y - self.direction.y / 8.0 + perpVect.y / 2.0, self.start.y + self.direction.y / 8.0 + perpVect.y / 2.0, self.start.y + self.direction.y / 8.0 - perpVect.y / 2.0, self.end.y - self.direction.y / 8.0 - perpVect.y / 2.0 ]) self.Line1Start = dict( x=[self.start.x, self.start.x + self.direction.x / 8.0], y=[self.start.y, self.start.y + self.direction.y / 8.0]) self.PistonStart = dict( x=[ self.end.x - self.direction.x / 2.0 + perpVect.x / 2.0, self.end.x - self.direction.x / 2.0 - perpVect.x / 2.0 ], y=[ self.end.y - self.direction.y / 2.0 + perpVect.y / 2.0, self.end.y - self.direction.y / 2.0 - perpVect.y / 2.0 ]) self.Line2Start = dict( x=[self.end.x, self.end.x - self.direction.x / 2.0], y=[self.end.y, self.end.y - self.direction.y / 2.0]) # Create ColumnDataSources with initial positions self.Casing = ColumnDataSource(data=self.CasingStart) self.Line1 = ColumnDataSource(data=self.Line1Start) self.Piston = ColumnDataSource(data=self.PistonStart) self.Line2 = ColumnDataSource(data=self.Line2Start) # once positions have been calculated direction is normalised self.direction = self.direction.direction() # objects that are influenced by the dashpot self.actsOn = []
def __init__(self, mass): # initialise value self.mass = mass # create vector of external forces (besides gravity) acting on the mass self.nextStepForces = [] self.nextStepObjForces = [] # initialise velocity self.v = Coord(0, 0) # create vector of objects affected by this object self.affectedObjects = [] self.currentPos = dict() self.shape = ColumnDataSource
def __init__(self, start, end, x0, kappa=1.0, spacing=1.0): start = Coord(start[0], start[1]) end = Coord(end[0], end[1]) # define spring constant self.kappa = kappa # define rest length (directional) and direction self.length = (end - start) self.direction = self.length / self.length.norm() self.length = self.direction * x0 # define the number of coils with respect to the relaxed position of the spring self.nCoils = int(floor(x0 / spacing)) # Create ColumnDataSource self.Position = ColumnDataSource(data=dict(x=[], y=[])) # objects that are influenced by the spring self.actsOn = [] # draw spring self.draw(start, end)
def init_pos(): topMass.moveTo(-5,x2,4,4) mainMass.moveTo(-6,x1,12,5) spring.compressTo(Coord(-4,x2),Coord(-4,x1+5)) dashpot.compressTo(Coord(-2,x2),Coord(-2,x1+5),0) baseSpring.compressTo(Coord(-1,x1),Coord(-1,0)) baseDashpot.compressTo(Coord(1,x1),Coord(1,0),0) topMass.resetLinks(spring,(-4,x2)) topMass.resetLinks(dashpot,(-2,x2)) mainMass.resetLinks(spring,(-4,x1+5)) mainMass.resetLinks(dashpot,(-2,x1+5)) mainMass.resetLinks(baseSpring,(-1,x1)) mainMass.resetLinks(baseDashpot,(1,x1))
def getVelAcc(self): # find the total force: # Start with gravitational force F = Coord(0, -self.mass * 9.81) for i in range(0, len(self.thisStepForces)): # add all forces acting on mass (e.g. spring, dashpot) F += self.thisStepForces.pop() # Find acceleration print(F) a = F / self.mass return [self.v.copy(), a, self.currentPos['y'][0]]
def draw(self, start, end): self.start = start.copy() self.end = end.copy() # find direction along which spring lies # (not normalised) direction = end - start # find normalising constant (=length) length = direction.norm() # define (normalised) perpendicular vector for spike directions perpVect = Coord(direction.y / length, -direction.x / length) # create values to help with loop Pos = dict(x=[], y=[]) Zero = Coord(0, 0) wiggle = [Zero, perpVect, Zero, -perpVect] # add first points Pos['x'].append(start.x) Pos['y'].append(start.y) # loop over each coil for i in range(0, self.nCoils): # loop over the 4 points in the coil for j in range(0, 4): # save points step = direction * 0.05 + direction * 0.9 * float( i + j / 4.0) / self.nCoils + wiggle[j] Pos['x'].append(start.x + step.x) Pos['y'].append(start.y + step.y) # add final points Pos['x'].append(end.x - 0.05 * direction.x) Pos['y'].append(end.y - 0.05 * direction.y) Pos['x'].append(end.x) Pos['y'].append(end.y) # add calculated points to figure self.Position.data = Pos # return length (with direction for overly compressed spring) return direction
def resetLinks(self, obj, point): p = Coord(point[0], point[1]) # initialise values for loop n = len(self.affectedObjects) i = 0 while (i < n): # check for object if (self.affectedObjects[i][0] == obj): # if so update it to the new force self.affectedObjects[i][1] = p # and exit while loop i = n + 1 i += 1
class Mass(object): ## create mass def __init__(self, mass): # initialise value self.mass = mass # create vector of external forces (besides gravity) acting on the mass self.nextStepForces = [] self.nextStepObjForces = [] # initialise velocity self.v = Coord(0, 0) # create vector of objects affected by this object self.affectedObjects = [] self.currentPos = dict() self.shape = ColumnDataSource ## Add an object that is affected by the movement of the mass def linkObj(self, obj, point): p = Coord(point[0], point[1]) # save the object and the point where it touches the object self.affectedObjects.append([obj, p]) # tell the object that it is linked so it can also apply forces to the mass obj.linkTo(self, p) ## reset linking point between mass and object def resetLinks(self, obj, point): p = Coord(point[0], point[1]) # initialise values for loop n = len(self.affectedObjects) i = 0 while (i < n): # check for object if (self.affectedObjects[i][0] == obj): # if so update it to the new force self.affectedObjects[i][1] = p # and exit while loop i = n + 1 i += 1 ## Usually called by spring or dashpot to apply a force to the mass def applyForce(self, F, obj): # initialise values for loop n = len(self.nextStepForces) i = 0 while (i < n): # check if object has already applied a force if (self.nextStepObjForces[i] == obj): # if so update it to the new force self.nextStepForces[i] = F # and exit while loop i = n + 1 i += 1 # if object was not found if (i == n): # add to lists self.nextStepForces.append(F) self.nextStepObjForces.append(obj) # function which saves the forces so movement of other masses does not # affect this mass's behaviour def FreezeForces(self): # save forces self.thisStepForces = list(self.nextStepForces) ## get Velocity and Acceleration at this timestep def getVelAcc(self): # find the total force: # Start with gravitational force F = Coord(0, -self.mass * 9.81) for i in range(0, len(self.thisStepForces)): # add all forces acting on mass (e.g. spring, dashpot) F += self.thisStepForces.pop() # Find acceleration print(F) a = F / self.mass return [self.v.copy(), a, self.currentPos['y'][0]] # displace mass by disp def move(self, disp): for i in range(0, len(self.currentPos['x'])): # move x and y co-ordinates self.currentPos['x'][i] += disp.x self.currentPos['y'][i] += disp.y # update ColumnDataSource self.shape.data = deepcopy(self.currentPos) # This affects all the affectedObjects for i in range(0, len(self.affectedObjects)): # tell object that it has been affected and must move the end at # point self.affectedObjects[i][1] by displacement self.affectedObjects[i][0].movePoint(self.affectedObjects[i][1], disp) # N.B. calling this function refills nextStepForces for next timestep # change point so that it is accurate for next timestep self.affectedObjects[i][1] += disp def changeMass(self, mass): self.mass = mass def changeInitV(self, v): self.v = Coord(0, v)
def linkObj(self, obj, point): p = Coord(point[0], point[1]) # save the object and the point where it touches the object self.affectedObjects.append([obj, p]) # tell the object that it is linked so it can also apply forces to the mass obj.linkTo(self, p)
def changeInitV(self, v): self.v = Coord(0, v)