def getLineCoords(self, Time): # formerly UpdateLine '''Gets the updated line coordinates for drawing and plotting purposes.''' # if a quasi-static analysis, just call the catenary function to return the line coordinates if self.qs==1: depth = self.sys.depth dr = self.rB - self.rA LH = np.hypot(dr[0], dr[1]) # horizontal spacing of line ends LV = dr[2] # vertical offset from end A to end B if np.min([self.rA[2],self.rB[2]]) > -depth: self.cb = -depth - np.min([self.rA[2],self.rB[2]]) # if this line's lower end is off the seabed, set cb negative and to the distance off the seabed elif self.cb < 0: # if a line end is at the seabed, but the cb is still set negative to indicate off the seabed self.cb = 0.0 # set to zero so that the line includes seabed interaction. try: (fAH, fAV, fBH, fBV, info) = catenary(LH, LV, self.L, self.sys.lineTypes[self.type].EA, self.sys.lineTypes[self.type].w, self.cb, HF0=self.HF, VF0=self.VF, nNodes=self.nNodes, plots=1) except CatenaryError as error: raise LineError(self.number, error.message) Xs = self.rA[0] + info["X"]*dr[0]/LH Ys = self.rA[1] + info["X"]*dr[1]/LH Zs = self.rA[2] + info["Z"] return Xs, Ys, Zs # otherwise, count on read-in time-series data else: # figure out what time step to use ts = self.getTimestep(Time) # drawing rods if self.isRod > 0: k1 = np.array([ self.xp[ts,-1]-self.xp[ts,0], self.yp[ts,-1]-self.yp[ts,0], self.zp[ts,-1]-self.zp[ts,0] ]) / self.length # unit vector k = np.array(k1) # make copy Rmat = np.array(rotationMatrix(0, np.arctan2(np.hypot(k[0],k[1]), k[2]), np.arctan2(k[1],k[0]))) # <<< should fix this up at some point, MattLib func may be wrong # make points for appropriately sized cylinder d = self.sys.lineTypes[self.type].d Xs, Ys, Zs = makeTower(self.length, np.array([d, d])) # translate and rotate into proper position for Rod coords = np.vstack([Xs, Ys, Zs]) newcoords = np.matmul(Rmat,coords) Xs = newcoords[0,:] + self.xp[ts,0] Ys = newcoords[1,:] + self.yp[ts,0] Zs = newcoords[2,:] + self.zp[ts,0] return Xs, Ys, Zs # drawing lines else: return self.xp[ts,:], self.yp[ts,:], self.zp[ts,:]
def getStiffnessMatrix(self): '''Returns the stiffness matrix of a line derived from analytic terms in the jacobian of catenary Raises ------ LineError If a singluar matrix error occurs while taking the inverse of the Line's Jacobian matrix. Returns ------- K2_rot : matrix the analytic stiffness matrix of the line in the rotated frame. ''' # take the inverse of the Jacobian to get the starting analytic stiffness matrix ''' if np.isnan(self.jacobian[0,0]): #if self.LBot >= self.L and self.HF==0. and self.VF==0. << handle tricky cases here? K = np.array([[0., 0.], [0., 1.0/self.jacobian[1,1] ]]) else: try: K = np.linalg.inv(self.jacobian) except: raise LineError(self.number, f"Check Line Length ({self.L}), it might be too long, or check catenary ProfileType") ''' # solve for required variables to set up the perpendicular stiffness. Keep it horizontal L_xy = np.linalg.norm(self.rB[:2] - self.rA[:2]) T_xy = np.linalg.norm(self.fB[:2]) Kt = T_xy/L_xy # initialize the line's analytic stiffness matrix in the "in-line" plane KA = np.array([[self.KA2[0,0], 0 , self.KA2[0,1]], [ 0 , Kt, 0 ], [self.KA2[1,0], 0 , self.KA2[1,1]]]) KB = np.array([[self.KB2[0,0], 0 , self.KB2[0,1]], [ 0 , Kt, 0 ], [self.KB2[1,0], 0 , self.KB2[1,1]]]) # create the rotation matrix based on the heading angle that the line is from the horizontal R = rotationMatrix(0,0,self.th) # rotate the matrix to be about the global frame [K'] = [R][K][R]^T KA_rot = np.matmul(np.matmul(R, KA), R.T) KB_rot = np.matmul(np.matmul(R, KB), R.T) return KA_rot, KB_rot
def getStiffnessMatrix(self): '''Returns the stiffness matrix of a line derived from analytic terms in the jacobian of catenary Raises ------ LineError If a singluar matrix error occurs while taking the inverse of the Line's Jacobian matrix. Returns ------- K2_rot : matrix the analytic stiffness matrix of the line in the rotated frame. ''' # take the inverse of the Jacobian to get the starting analytic stiffness matrix try: K = np.linalg.inv(self.jacobian) except: raise LineError(self.number, f"Check Line Length ({self.L}), it might be too long, or check catenary ProfileType") # solve for required variables to set up the perpendicular stiffness. Keep it horizontal L_xy = np.linalg.norm(self.rB[:2] - self.rA[:2]) T_xy = np.linalg.norm(self.fB[:2]) Kt = T_xy/L_xy # initialize the line's analytic stiffness matrix in the "in-line" plane K2 = np.array([[K[0,0], 0 , K[0,1]], [0 , Kt, 0 ], [K[1,0], 0 , K[1,1]]]) # create the rotation matrix based on the heading angle that the line is from the horizontal R = rotationMatrix(0,0,self.th) # rotate the matrix to be about the global frame [K'] = [R][K][R]^T K2_rot = np.matmul(np.matmul(R, K2), R.T) # need to make sign changes if the end fairlead (B) of the line is lower than the starting point (A) if self.rB[2] < self.rA[2]: K2_rot[2,0] *= -1 K2_rot[0,2] *= -1 K2_rot[2,1] *= -1 K2_rot[1,2] *= -1 return K2_rot
def setPosition(self, r6): '''Sets the position of the Body, along with that of any dependent objects. Parameters ---------- r6 : array 6DOF position and orientation vector of the body [m, rad] Raises ------ ValueError If the length of the input r6 array is not of length 6 Returns ------- None. ''' if len(r6) == 6: self.r6 = np.array( r6, dtype=np.float_) # update the position of the Body itself else: raise ValueError( f"Body setPosition method requires an argument of size 6, but size {len(r6):d} was provided" ) self.R = rotationMatrix(self.r6[3], self.r6[4], self.r6[5]) # update body rotation matrix # update the position of any attached Points for PointID, rPointRel in zip(self.attachedP, self.rPointRel): rPoint = np.matmul( self.R, rPointRel ) + self.r6[:3] # rPoint = transformPosition(rPointRel, r6) self.sys.pointList[PointID - 1].setPosition(rPoint) if self.sys.display > 3: printVec(rPoint) breakpoint()
def staticSolve(self, reset=False, tol=0.0001, profiles=0): '''Solves static equilibrium of line. Sets the end forces of the line based on the end points' positions. Parameters ---------- reset : boolean, optional Determines if the previous fairlead force values will be used for the catenary iteration. The default is False. tol : float Convergence tolerance for catenary solver measured as absolute error of x and z values in m. profiles : int Values greater than 0 signal for line profile data to be saved (used for plotting, getting distributed tensions, etc). Raises ------ LineError If the horizontal force at the fairlead (HF) is less than 0 Returns ------- None. ''' depth = self.sys.depth dr = self.rB - self.rA LH = np.hypot(dr[0], dr[1]) # horizontal spacing of line ends LV = dr[2] # vertical offset from end A to end B if self.rA[2] < -depth: raise LineError("Line {} end A is lower than the seabed.".format(self.number)) elif self.rB[2] < -depth: raise LineError("Line {} end B is lower than the seabed.".format(self.number)) elif np.min([self.rA[2],self.rB[2]]) > -depth: self.cb = -depth - np.min([self.rA[2],self.rB[2]]) # if this line's lower end is off the seabed, set cb negative and to the distance off the seabed elif self.cb < 0: # if a line end is at the seabed, but the cb is still set negative to indicate off the seabed self.cb = 0.0 # set to zero so that the line includes seabed interaction. if self.HF < 0: # or self.VF < 0: <<<<<<<<<<< it shouldn't matter if VF is negative - this could happen for buoyant lines, etc. raise LineError("Line HF cannot be negative") # this could be a ValueError too... if reset==True: # Indicates not to use previous fairlead force values to start catenary self.HF = 0 # iteration with, and insteady use the default values. try: (fAH, fAV, fBH, fBV, info) = catenary(LH, LV, self.L, self.sys.lineTypes[self.type].EA, self.sys.lineTypes[self.type].w, CB=self.cb, Tol=tol, HF0=self.HF, VF0=self.VF, plots=profiles) # call line model except CatenaryError as error: raise LineError(self.number, error.message) self.th = np.arctan2(dr[1],dr[0]) # probably a more efficient way to handle this <<< self.HF = info["HF"] self.VF = info["VF"] self.KA2 = info["stiffnessA"] self.KB2 = info["stiffnessB"] self.LBot = info["LBot"] self.info = info self.fA[0] = fAH*dr[0]/LH self.fA[1] = fAH*dr[1]/LH self.fA[2] = fAV self.fB[0] = fBH*dr[0]/LH self.fB[1] = fBH*dr[1]/LH self.fB[2] = fBV self.TA = np.sqrt(fAH*fAH + fAV*fAV) # end tensions self.TB = np.sqrt(fBH*fBH + fBV*fBV) # ----- compute 3d stiffness matrix for both line ends (3 DOF + 3 DOF) ----- # solve for required variables to set up the perpendicular stiffness. Keep it horizontal #L_xy = np.linalg.norm(self.rB[:2] - self.rA[:2]) #T_xy = np.linalg.norm(self.fB[:2]) # create the rotation matrix based on the heading angle that the line is from the horizontal R = rotationMatrix(0,0,self.th) # initialize the line's analytic stiffness matrix in the "in-line" plane then rotate the matrix to be about the global frame [K'] = [R][K][R]^T def from2Dto3Drotated(K2D, Kt): K2 = np.array([[K2D[0,0], 0 , K2D[0,1]], [ 0 , Kt, 0 ], [K2D[1,0], 0 , K2D[1,1]]]) return np.matmul(np.matmul(R, K2), R.T) self.KA = from2Dto3Drotated(info['stiffnessA'], -fBH/LH) # stiffness matrix describing reaction force on end A due to motion of end A self.KB = from2Dto3Drotated(info['stiffnessB'], -fBH/LH) # stiffness matrix describing reaction force on end B due to motion of end B self.KAB = from2Dto3Drotated(info['stiffnessAB'], fBH/LH) # stiffness matrix describing reaction force on end B due to motion of end A #self.K6 = np.block([[ from2Dto3Drotated(self.KA), from2Dto3Drotated(self.KAB.T)], # [ from2Dto3Drotated(self.KAB), from2Dto3Drotated(self.KB) ]]) if profiles > 1: import matplotlib.pyplot as plt plt.plot(info['X'], info['Z']) plt.show()
def getForces(self, lines_only=False): '''Sums the forces and moments on the Body, including its own plus those from any attached objects. Parameters ---------- lines_only : boolean, optional An option for calculating forces from just the mooring lines or not. The default is False. Returns ------- f6 : array The 6DOF forces and moments applied to the body in its current position [N, Nm] ''' f6 = np.zeros(6) # TODO: could save time in below by storing the body's rotation matrix when it's position is set rather than # recalculating it in each of the following function calls. if lines_only == False: # add weight, which may result in moments as well as a force rCG_rotated = rotatePosition( self.rCG, self.r6[3:] ) # relative position of CG about body ref point in unrotated reference frame f6 += translateForce3to6DOF(rCG_rotated, np.array([ 0, 0, -self.m * self.sys.g ])) # add to net forces/moments # add buoyancy force and moments if applicable (this can include hydrostatic restoring moments # if rM is considered the metacenter location rather than the center of buoyancy) rM_rotated = rotatePosition( self.rM, self.r6[3:] ) # relative position of metacenter about body ref point in unrotated reference frame f6 += translateForce3to6DOF(rM_rotated, np.array([ 0, 0, self.sys.rho * self.sys.g * self.v ])) # add to net forces/moments # add hydrostatic heave stiffness (if AWP is nonzero) f6[2] -= self.sys.rho * self.sys.g * self.AWP * self.r6[2] # add any externally applied forces/moments (in global orientation) f6 += self.f6Ext # add forces from any attached Points (and their attached lines) for PointID, rPointRel in zip(self.attachedP, self.rPointRel): fPoint = self.sys.pointList[PointID - 1].getForces( lines_only=lines_only) # get net force on attached Point rPoint_rotated = rotatePosition( rPointRel, self.r6[3:] ) # relative position of Point about body ref point in unrotated reference frame f6 += translateForce3to6DOF( rPoint_rotated, fPoint ) # add net force and moment resulting from its position to the Body # All forces and moments on the body should now be summed, and are in global/unrotated orientations. # For application to the body DOFs, convert the moments to be about the body's local/rotated x/y/z axes <<< do we want this in all cases? rotMat = rotationMatrix(*self.r6[3:]) # get rotation matrix for body moment_about_body_ref = np.matmul( rotMat.T, f6[3:] ) # transform moments so that they are about the body's local/rotated axes f6[3:] = moment_about_body_ref # use these moments return f6