def localElasticForces(PosDef, PsiDef, PosIni, PsiIni, XBELEM, NodeList): """@brief Approximate shear force and moments from local strain and stiffness matrix. @param PosDef Deformed nodal coordinates. @param PsiDef Deformed nodal rotation vectors. @param XBELEM Xbeam element derived type containing element data. @param NodeList List of element numbers for strain approximation, starts from zero. @warning Untested. """ F = np.zeros((len(NodeList),6)) strains = localStrains(PosDef, PsiDef, PosIni, PsiIni, XBELEM, NodeList) iOut = 0 # Output index for iNode in NodeList: iElem, iiElem = iNode2iElem(iNode, PosDef.shape[0]-1, XBELEM.NumNodes[0]) del iiElem elemStrain = strains[iOut,:] elemK = XBELEM.Stiff[iElem*6:(iElem+1)*6,:] F[iOut,:] = np.dot(elemK,elemStrain) iOut += 1 # END of iElem return F
def CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, BeamForces, PsiA_G=np.zeros(3)): """@brief Calculates aero forces and moments on the beam nodes from those on the aerodynamic grid. @param XBINPUT Beam input options. @param PsiDefor Array of beam nodal rotations. @param Section Array containing sectional camberline coordinates. @param AeroForces Aerodynamic grid forces to be mapped to beam nodes. @param BeamForces BeamForces to overwrite. @param PsiA_G Attitude of a-frame w.r.t earth. Default zero. @details All Beam forces calculated in in a-frame, while aero forces are defined in earth frame.""" # Zero beam forces. BeamForces[:,:] = 0.0 # Get transformation matrix between a-frame and earth. CGa = Psi2TransMat(PsiA_G) # project from a -> G CaG = CGa.T # project from G -> a # Num Nodes NumNodes = XBINPUT.NumNodesTot # Get number of nodes per beam element. NumNodesElem = XBINPUT.NumNodesElem # Loop along beam length. for iNode in range(NumNodes): iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) # Calculate transformation matrix for each node. CaB = Psi2TransMat(PsiDefor[iElem,iiElem,:]) # Loop through each sectional coordinate. for jSection in range(Section.shape[0]): # Get Section coord and AeroForce in a-frame. Section_A = np.dot(CaB,Section[jSection,:]) AeroForce_A = np.dot(CaG,AeroForces[jSection][iNode][:]) # Calc moment. BeamForces[iNode,3:] += np.cross(Section_A, AeroForce_A) # Calc force. BeamForces[iNode,:3] += AeroForce_A
def CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, BeamForces): """@brief Calculates aero forces and moments on the beam nodes from those on the aerodynamic grid. @param XBINPUT Beam input options. @param PsiDefor Array of beam nodal rotations. @param Section Array containing sectional camberline coordinates. @param AeroForces Aerodynamic grid forces to be mapped to beam nodes. @param BeamForces BeamForces to overwrite. @details All Beam forces calculated in in a-frame, while aero forces are defined in earth frame.""" # Zero beam forces. BeamForces[:, :] = 0.0 # Get transformation matrix between a-frame and earth. CGa = Psi2TransMat(XBINPUT.PsiA_G) CaG = CGa.T # Num Nodes NumNodes = XBINPUT.NumNodesTot # Get number of nodes per beam element. NumNodesElem = XBINPUT.NumNodesElem # Loop along beam length. for iNode in range(NumNodes): iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) # Calculate transformation matrix for each node. CaB = Psi2TransMat(PsiDefor[iElem, iiElem, :]) # Loop through each sectional coordinate. for jSection in range(Section.shape[0]): # Get Section coord and AeroForce in a-frame. Section_A = np.dot(CaB, Section[jSection, :]) AeroForce_A = np.dot(CaG, AeroForces[jSection][iNode][:]) # Calc moment. BeamForces[iNode, 3:] += np.cross(Section_A, AeroForce_A) # Calc force. BeamForces[iNode, :3] += AeroForce_A
# Initialize PosDefor, PsiDefor PosDefor = np.zeros((N + 1, 3), dtype=ct.c_double, order='F') PosDeforElem = np.zeros((NumElems, 3, 3), dtype=ct.c_double, order='F') PsiDefor = np.zeros((NumElems, 3, 3), dtype=ct.c_double, order='F') data = np.loadtxt(refFile, skiprows=2) i0 = 0 for iElem in range(NumElems): for i in range(numNodesElem): PosDeforElem[iElem, i, :] = data[i0, 2:5] PsiDefor[iElem, i, :] = data[i0, 5:8] i0 += 1 # extract nodal information (PosDefor) for iNode in range(N + 1): iElem, iiElem = iNode2iElem(iNode, N + 1, numNodesElem) PosDefor[iNode, :] = PosDeforElem[iElem, iiElem, :] # get reference circulation strengths refFile = Settings.OutputDir + 'M' + str(M) + 'N' + str( N) + '_V25_alpha' + str(int(alpha * 180.0 / np.pi)) + '_Gamma0' myDict = loadmat(refFile) gam0 = myDict['Gamma0'] eta0 = (PosDefor, PsiDefor, gam0) else: eta0 = None # Generate a history of beam DoF motions (in A-frame) if beamDOFmotion: delEtaHisty = np.zeros((12 * N, len(Time)))
def CoincidentGrid(PosDefor, PsiDefor, Section, VelA_A, OmegaA_A, PosDotDef, PsiDotDef, XBINPUT, AeroGrid, AeroVels, OriginA_G=None, PsiA_G=None, ctrlSurf=None): """@brief Creates aero grid and velocities based on beam nodal information. @param PosDefor Array of beam nodal displacements. @param PsiDefor Array of beam nodal rotations. @param Section Array containing sectional camberline coordinates. @param VelA_A Velocity of a-frame projected in a-frame. @param OmegaA_A Angular vel of a-frame projected in a-frame. @param PosDotDef Array of beam nodal velocities. @param PsiDotDef Array of beam nodal angular velocities. @param XBINPUT Beam input options. @param AeroGrid Aerodynamic grid to be updated. @param AeroVels Aerodynamic grid velocities to be updated @param OriginA_G Origin of a-frame. @param PsiA_G Attitude of a-frame w.r.t earth. @param ctrlSurf Control surface definition. @details AeroGrid and AeroVels are projected in G-frame. """ NumNodes = PosDefor.shape[0] NumNodesElem = XBINPUT.NumNodesElem for iNode in range(PosDefor.shape[0]): # Work out what element we are in. iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) # Calculate transformation matrix for each node. CaB = Psi2TransMat(PsiDefor[iElem, iiElem, :]) # Loop through section coordinates. for jSection in range(Section.shape[0]): # Calculate instantaneous aero grid points AeroGrid[jSection,iNode,:] = PosDefor[iNode,:] \ + np.dot(CaB,Section[jSection,:]) # Calculate inertial angular velocity of B-frame projected in # the B-frame. Omega_B_B = (np.dot(Tangential(PsiDefor[iElem, iiElem, :]), PsiDotDef[iElem, iiElem, :]) + np.dot(CaB.T, OmegaA_A)) # Calculate inertial velocity at grid points (projected in A-frame). AeroVels[jSection, iNode, :] = ( VelA_A + np.dot(Skew(OmegaA_A), PosDefor[iNode, :]) + PosDotDef[iNode, :] + np.dot(CaB, np.dot(Skew(Omega_B_B), Section[jSection, :]))) if (ctrlSurf != None and jSection > ctrlSurf.iMin and jSection <= ctrlSurf.iMax and iNode >= ctrlSurf.jMin and iNode <= ctrlSurf.jMax): # Apply changes to Zeta and ZetaDot in a-frame due to control # surface movement. # Check if control surface indices are within range of grid assert (ctrlSurf.iMin >= 0), 'CtrlSurf iMin less then zero' assert (ctrlSurf.iMax < Section.shape[0]), ( 'CtrlSurf iMax ' + 'greater than section iMax') assert (ctrlSurf.jMin >= 0), 'CtrlSurf jMin less then zero' assert (ctrlSurf.jMax < PosDefor.shape[0]), ( 'CtrlSurf jMax ' + 'greater than section jMax') # Determine hinge point hingePoint = Section[ctrlSurf.iMin, :] # Use a CRV to define rotation in sectional frame hingeRot = np.array([ctrlSurf.beta, 0.0, 0.0]) hingeRotDot = np.array([ctrlSurf.betaDot, 0.0, 0.0]) # Find lever arm from hinge point leverArm = Section[jSection, :] - hingePoint # Deformed position of node in B-frame CBBnew = Psi2TransMat(hingeRot) newSectionPoint = hingePoint + np.dot(CBBnew, leverArm) # Overwrite corresponding element in Aerogrid AeroGrid[jSection, iNode, :] = (PosDefor[iNode, :] + np.dot(CaB, newSectionPoint)) # Overwrite corresponding element in AeroVels # There is an extra velocity contribution due to the rotation # of the hinge, with reference frame Bnew. Hence, # CBBnew * ( Omega_Bnew_Bnew X Point__Bnew), which is the # velocity at a point on the flap due to the rotation # of the hinge projected in the B-frame. is added. # Note: Skew(hingeRotDot) is an acceptable definition of the # angular rate as it corresponds to planar motion within the # section. In general Skew(T(\psi)\dot{\psi}) is required. # Warning: should omega_Bnew_Bnew (Skew(hingeRotDot)) # be an inertial velocity? AeroVels[jSection, iNode, :] = ( VelA_A + np.dot(Skew(OmegaA_A), PosDefor[iNode, :]) + PosDotDef[iNode, :] + np.dot( CaB, np.dot(Skew(Omega_B_B), newSectionPoint) + np.dot(CBBnew, np.dot(Skew(hingeRotDot), leverArm)))) #END for jSection #END for iNode if ((OriginA_G is not None) and (PsiA_G is not None)): # Get transformation from a-frame to earth frame. CaG = Psi2TransMat(PsiA_G) CGa = CaG.T # Add the origin to grids in earth frame and transform velocities. for iNode in range(PosDefor.shape[0]): for jSection in range(Section.shape[0]): AeroGrid[jSection,iNode,:] = OriginA_G + \ np.dot( CGa, AeroGrid[jSection,iNode,:]) AeroVels[jSection, iNode, :] = np.dot(CGa, AeroVels[jSection, iNode, :])
def localStrains(PosDef, PsiDef, PosIni, PsiIni, XBELEM, NodeList, SO3 = False): """@brief Approximate strains at the midpoint of nodes. @param PosDef Deformed nodal coordinates. @param PsiDef Deformed nodal rotation vectors. @param PosIni Initial (undeformed) nodal coordinates. @param PsiIni Initial (undeformed) nodal rotation vectors. @param XBELEM Xbeam element derived type containing element data. @param NodeList List of node numbers for strain approximation at adjacent midpoint in the direction of increasing node index, starts from zero: 0---x---1---x---2---x---3-- ... --(NumNodes-1) Strains are calculated at the x to the right of selected nodes. @param SO3 Flag for use of the SO(3) manifold in CRV interpolation. @details Assumes that all nodes are either two- or three- noded. @warning Untested. """ strains = np.zeros((len(NodeList),6)) NumNodes = PosDef.shape[0]-1 NumNodesElem = XBELEM.NumNodes[0] iOut = 0 # Output index for iNode in NodeList: if iNode == NumNodes or iNode == -1: raise ValueError("Midpoint strain requested beyond final node.") iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) if NumNodesElem == 2: if SO3 == True: # Consistent calculation of midpoint strains using SO(3) manifold. raise NotImplementedError("SO(3) manifold strains.") else: # Using fortran routines R0 = np.zeros((2,6)) R0[0,:3] = PosIni[iNode,:] R0[0,3:] = PsiIni[iElem,iiElem,:] R0[1,:3] = PosIni[iNode+1,:] R0[1,3:] = PsiIni[iElem,iiElem+1,:] Ri = np.zeros((2,6)) Ri[0,:3] = PosDef[iNode,:] Ri[0,3:] = PsiDef[iElem,iiElem,:] Ri[1,:3] = PosDef[iNode+1,:] Ri[1,3:] = PsiDef[iElem,iiElem+1,:] # Midpoint z = 0.0 strainz = Cbeam3_strainz(R0, Ri, z) # END if SO3 elif NumNodesElem == 3: if SO3 == True: raise NotImplementedError("Only available for 2-noded just now.") else: # Using fortran routines R0 = np.zeros((3,6)) R0[0,:3] = PosIni[2*iElem,:] R0[1,:3] = PosIni[2*iElem+2,:] R0[2,:3] = PosIni[2*iElem+1,:] R0[:,3:] = PsiIni[iElem] Ri = np.zeros((3,6)) Ri[0,:3] = PosDef[2*iElem,:] Ri[1,:3] = PosDef[2*iElem+2,:] Ri[2,:3] = PosDef[2*iElem+1,:] Ri[:,3:] = PsiDef[iElem] if iiElem == 0: z = -0.5 # mid-point adjacent to node iNode elif iiElem == 1: z = 0.5 # mid-point adjacent to node iNode elif iiElem == 2: raise ValueError("iiElem only 0, 1 for adjacent midpoint" + "calculation.") else: raise ValueError("iiElem can only take 0, 1 or 2 for " + "3-noded elements.") strainz = Cbeam3_strainz(R0, Ri, z) # END if SO(3) else: raise ValueError("Only 2- or 3-noded elements are supported.") strains[iOut,:] = strainz iOut += 1 # END for iElem return strains
def CoincidentGrid(PosDefor, PsiDefor, Section, VelA_A, OmegaA_A, PosDotDef, PsiDotDef, XBINPUT, AeroGrid, AeroVels, OriginA_a = None, PsiA_G = None, ctrlSurf = None): """@brief Creates aero grid and velocities based on beam nodal information. @param PosDefor Array of beam nodal displacements. @param PsiDefor Array of beam nodal rotations. @param Section Array containing sectional camberline coordinates. @param VelA_A Velocity of a-frame projected in a-frame. @param OmegaA_A Angular vel of a-frame projected in a-frame. @param PosDotDef Array of beam nodal velocities. @param PsiDotDef Array of beam nodal angular velocities. @param XBINPUT Beam input options. @param AeroGrid Aerodynamic grid to be updated. @param AeroVels Aerodynamic grid velocities to be updated @param OriginA_a Origin of a-frame (in a-frame components) @param PsiA_G Attitude of a-frame w.r.t earth. @param ctrlSurf Control surface definition. @details AeroGrid and AeroVels are projected in G-frame. """ NumNodes = PosDefor.shape[0] NumNodesElem = XBINPUT.NumNodesElem if ctrlSurf != None: NumCtrlSurf = len(ctrlSurf) for iNode in range(PosDefor.shape[0]): # Work out what element we are in. iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) # Calculate transformation matrix for each node. CaB = Psi2TransMat(PsiDefor[iElem,iiElem,:]) # Loop through section coordinates. for jSection in range(Section.shape[0]): # Calculate instantaneous aero grid points AeroGrid[jSection,iNode,:] = PosDefor[iNode,:] \ + np.dot(CaB,Section[jSection,:]) # Calculate inertial angular velocity of B-frame projected in # the B-frame. Omega_B_B = (np.dot(Tangential(PsiDefor[iElem,iiElem,:]), PsiDotDef[iElem,iiElem,:]) + np.dot(CaB.T, OmegaA_A)) # Calculate inertial velocity at grid points (projected in A-frame). AeroVels[jSection,iNode,:] = (VelA_A + np.dot(Skew(OmegaA_A),PosDefor[iNode,:]) + PosDotDef[iNode,:] + np.dot(CaB, np.dot( Skew(Omega_B_B), Section[jSection,:]))) #<<<<<<< HEAD # Control Surface Update if ctrlSurf != None: for cc in range(NumCtrlSurf): if (jSection > ctrlSurf[cc].iMin and jSection <= ctrlSurf[cc].iMax and iNode >= ctrlSurf[cc].jMin and iNode <= ctrlSurf[cc].jMax): # Apply changes to Zeta and ZetaDot in a-frame due to control # surface movement. # Check if control surface indices are within range of grid assert (ctrlSurf[cc].iMin >= 0), 'CtrlSurf iMin less then zero' assert (ctrlSurf[cc].iMax < Section.shape[0]), ('CtrlSurf iMax ' + 'greater than section iMax') assert (ctrlSurf[cc].jMin >= 0), 'CtrlSurf jMin less then zero' assert (ctrlSurf[cc].jMax < PosDefor.shape[0]), ('CtrlSurf jMax ' + 'greater than section jMax') # Determine hinge point hingePoint = Section[ctrlSurf[cc].iMin,:] # Use a CRV to define rotation in sectional frame hingeRot = np.array([ctrlSurf[cc].beta, 0.0, 0.0]) hingeRotDot = np.array([ctrlSurf[cc].betaDot, 0.0, 0.0]) #print('Beta detected static: %f' %ctrlSurf[cc].beta) #print('BetaDot detected static: %f' %ctrlSurf[cc].betaDot) #1/0 # Find lever arm from hinge point leverArm = Section[jSection,:] - hingePoint # Deformed position of node in B-frame CBBnew = Psi2TransMat(hingeRot) newSectionPoint = hingePoint + np.dot(CBBnew,leverArm) # Overwrite corresponding element in Aerogrid AeroGrid[jSection,iNode,:] = (PosDefor[iNode,:] + np.dot(CaB,newSectionPoint)) # Overwrite corresponding element in AeroVels # There is an extra velocity contribution due to the rotation # of the hinge, with reference frame Bnew. Hence, # CBBnew * ( Omega_Bnew_Bnew X Point__Bnew), which is the # velocity at a point on the flap due to the rotation # of the hinge projected in the B-frame. is added. # Note: Skew(hingeRotDot) is an acceptable definition of the # angular rate as it corresponds to planar motion within the # section. In general Skew(T(\psi)\dot{\psi}) is required. # Warning: should omega_Bnew_Bnew (Skew(hingeRotDot)) # be an inertial velocity? AeroVels[jSection,iNode,:] = (VelA_A + np.dot(Skew(OmegaA_A),PosDefor[iNode,:]) + PosDotDef[iNode,:] + np.dot(CaB, np.dot(Skew(Omega_B_B), newSectionPoint) + np.dot(CBBnew, np.dot(Skew(hingeRotDot), leverArm)))) # #======= # if (ctrlSurf != None and # jSection > ctrlSurf.iMin and jSection <= ctrlSurf.iMax and # iNode >= ctrlSurf.jMin and iNode <= ctrlSurf.jMax): # # Apply changes to Zeta and ZetaDot in a-frame due to control # # surface movement. # # Check if control surface indices are within range of grid # assert (ctrlSurf.iMin >= 0), 'CtrlSurf iMin less then zero' # assert (ctrlSurf.iMax < Section.shape[0]), ('CtrlSurf iMax ' # + 'greater than section iMax') # assert (ctrlSurf.jMin >= 0), 'CtrlSurf jMin less then zero' # assert (ctrlSurf.jMax < PosDefor.shape[0]), ('CtrlSurf jMax ' # + 'greater than section jMax') # # Determine hinge point # hingePoint = Section[ctrlSurf.iMin,:] # # Use a CRV to define rotation in sectional frame # hingeRot = np.array([ctrlSurf.beta, 0.0, 0.0]) # hingeRotDot = np.array([ctrlSurf.betaDot, 0.0, 0.0]) # # Find lever arm from hinge point # leverArm = Section[jSection,:] - hingePoint # # Deformed position of node in B-frame # CBBnew = Psi2TransMat(hingeRot) # newSectionPoint = hingePoint + np.dot(CBBnew,leverArm) # # Overwrite corresponding element in Aerogrid # AeroGrid[jSection,iNode,:] = (PosDefor[iNode,:] # + np.dot(CaB,newSectionPoint)) # # Overwrite corresponding element in AeroVels # # There is an extra velocity contribution due to the rotation # # of the hinge, with reference frame Bnew. Hence, # # CBBnew * ( Omega_Bnew_Bnew X Point__Bnew), which is the # # velocity at a point on the flap due to the rotation # # of the hinge projected in the B-frame. is added. # # Note: Skew(hingeRotDot) is an acceptable definition of the # # angular rate as it corresponds to planar motion within the # # section. In general Skew(T(\psi)\dot{\psi}) is required. # # Warning: should omega_Bnew_Bnew (Skew(hingeRotDot)) # # be an inertial velocity? # AeroVels[jSection,iNode,:] = (VelA_A # + np.dot(Skew(OmegaA_A),PosDefor[iNode,:]) # + PosDotDef[iNode,:] # + np.dot(CaB, # np.dot(Skew(Omega_B_B), newSectionPoint) # + np.dot(CBBnew, # np.dot(Skew(hingeRotDot), # leverArm)))) # >>>>>>> rob/master #END for jSection #END for iNode if ( (OriginA_a != None) and (PsiA_G != None) ): # Get transformation from a-frame to earth frame. #CaG = Psi2TransMat(PsiA_G) #CGa = CaG.T #] sm: this rotated, doesn't project CGa = Psi2TransMat(PsiA_G) ### sm: convert OriginA_a into FoR G components OriginA_G=np.dot(CGa,OriginA_a) for iNode in range(PosDefor.shape[0]): for jSection in range(Section.shape[0]): AeroGrid[jSection,iNode,:] = OriginA_G + \ np.dot( CGa, AeroGrid[jSection,iNode,:]) AeroVels[jSection,iNode,:] = np.dot( CGa, AeroVels[jSection,iNode,:])
def CoincidentGrid(PosDefor, PsiDefor, Section, VelA_A, OmegaA_A, PosDotDef, PsiDotDef, XBINPUT, AeroGrid, AeroVels, OriginA_G = None, PsiA_G = None, ctrlSurf = None): """@brief Creates aero grid and velocities centred on beam nodes. @param PosDefor Array of beam nodal displacements. @param PsiDefor Array of beam nodal rotations. @param Section Array containing sectional camberline coordinates. @param VelA_A Velocity of a-frame projected in a-frame. @param OmegaA_A Angular vel of a-frame projected in a-frame. @param PosDotDef Array of beam nodal velocities. @param PsiDotDef Array of beam nodal angular velocities. @param XBINPUT Beam input options. @param AeroGrid Aerodynamic grid to be updated. @param AeroVels Aerodynamic grid velocities to be updated @param OriginA_G Origin of a-frame. @param PsiA_G Attitude of a-frame w.r.t earth. @param ctrlSurf Control surface definition. @details AeroGrid and AeroVels are projected in G-frame. """ NumNodes = PosDefor.shape[0] NumNodesElem = XBINPUT.NumNodesElem for iNode in range(PosDefor.shape[0]): # Work out what element we are in. iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) # Calculate transformation matrix for each node. CaB = Psi2TransMat(PsiDefor[iElem,iiElem,:]) # Loop through section coordinates. for jSection in range(Section.shape[0]): # Calculate instantaneous aero grid points AeroGrid[jSection,iNode,:] = PosDefor[iNode,:] \ + np.dot(CaB,Section[jSection,:]) # Calculate inertial angular velocity of B-frame projected in # the B-frame. Omega_B_B = (np.dot(Tangential(PsiDefor[iElem,iiElem,:]), PsiDotDef[iElem,iiElem,:]) + np.dot(CaB.T, OmegaA_A)) # Calculate inertial velocity at grid points (projected in A-frame). AeroVels[jSection,iNode,:] = (VelA_A + np.dot(Skew(OmegaA_A),PosDefor[iNode,:]) + PosDotDef[iNode,:] + np.dot(CaB, np.dot( Skew(Omega_B_B), Section[jSection,:]))) if (ctrlSurf != None and jSection > ctrlSurf.iMin and jSection <= ctrlSurf.iMax and iNode >= ctrlSurf.jMin and iNode <= ctrlSurf.jMax): # Apply changes to Zeta and ZetaDot in a-frame due to control # surface movement. # Check if control surface indices are within range of grid assert (ctrlSurf.iMin >= 0), 'CtrlSurf iMin less then zero' assert (ctrlSurf.iMax < Section.shape[0]), ('CtrlSurf iMax ' + 'greater than section iMax') assert (ctrlSurf.jMin >= 0), 'CtrlSurf jMin less then zero' assert (ctrlSurf.jMax < PosDefor.shape[0]), ('CtrlSurf jMax ' + 'greater than section jMax') # Determine hinge point hingePoint = Section[ctrlSurf.iMin,:] # Use a CRV to define rotation in sectional frame hingeRot = np.array([ctrlSurf.beta, 0.0, 0.0]) hingeRotDot = np.array([ctrlSurf.betaDot, 0.0, 0.0]) # Find lever arm from hinge point leverArm = Section[jSection,:] - hingePoint # Deformed position of node in B-frame CBBnew = Psi2TransMat(hingeRot) newSectionPoint = hingePoint + np.dot(CBBnew,leverArm) # Overwrite corresponding element in Aerogrid AeroGrid[jSection,iNode,:] = (PosDefor[iNode,:] + np.dot(CaB,newSectionPoint)) # Overwrite corresponding element in AeroVels # There is an extra velocity contribution due to the rotation # of the hinge, with reference frame Bnew. Hence, # CBBnew * ( Omega_Bnew_Bnew X Point__Bnew), which is the # velocity at a point on the flap due to the rotation # of the hinge projected in the B-frame. is added. AeroVels[jSection,iNode,:] = (VelA_A + np.dot(Skew(OmegaA_A),PosDefor[iNode,:]) + PosDotDef[iNode,:] + np.dot(CaB, np.dot(Skew(Omega_B_B), newSectionPoint) + np.dot(CBBnew, np.dot(Skew(hingeRotDot), leverArm)))) #END for jSection #END for iNode if ( (OriginA_G != None) and (PsiA_G != None) ): # Get transformation from a-frame to earth frame. CaG = Psi2TransMat(PsiA_G) CGa = CaG.T # Add the origin to grids in earth frame and transform velocities. for iNode in range(PosDefor.shape[0]): for jSection in range(Section.shape[0]): AeroGrid[jSection,iNode,:] = OriginA_G + \ np.dot( CGa, AeroGrid[jSection,iNode,:]) AeroVels[jSection,iNode,:] = np.dot( CGa, AeroVels[jSection,iNode,:])
# Initialize PosDefor, PsiDefor PosDefor = np.zeros((N+1,3),dtype=ct.c_double, order='F') PosDeforElem = np.zeros((NumElems,3,3),dtype=ct.c_double, order='F') PsiDefor = np.zeros((NumElems,3,3),dtype=ct.c_double, order='F') data = np.loadtxt(refFile, skiprows=2) i0=0 for iElem in range(NumElems): for i in range(numNodesElem): PosDeforElem[iElem,i,:] = data[i0,2:5] PsiDefor[iElem,i,:] = data[i0,5:8] i0+=1 # extract nodal information (PosDefor) for iNode in range(N+1): iElem, iiElem = iNode2iElem(iNode, N+1, numNodesElem) PosDefor[iNode,:] = PosDeforElem[iElem,iiElem,:] # get reference circulation strengths refFile = Settings.OutputDir + 'M' + str(M) + 'N' + str(N) + '_V25_alpha' + str(int(alpha*180.0/np.pi)) + '_Gamma0' myDict = loadmat(refFile) gam0 = myDict['Gamma0'] eta0 = (PosDefor, PsiDefor, gam0) else: eta0 = None # Generate a history of beam DoF motions (in A-frame) if beamDOFmotion: delEtaHisty = np.zeros((12*N,len(Time))) PosDefHist=np.zeros((N+1,3,len(Time)),dtype=ct.c_double, order='F')
def localFstifz(PosDef, PsiDef, PosIni, PsiIni, XBELEM, NodeList, SO3 = False): """@brief Approximate element stiffness force @param PosDef Deformed nodal coordinates. @param PsiDef Deformed nodal rotation vectors. @param PosIni Initial (undeformed) nodal coordinates. @param PsiIni Initial (undeformed) nodal rotation vectors. @param XBELEM Xbeam element derived type containing element data. @param NodeList List of node numbers for strain approximation at adjacent midpoint in the direction of increasing node index, starts from zero: 0---x---1---x---2---x---3-- ... --(NumNodes-1) Strains are calculated at the x to the right of selected nodes. @param SO3 Flag for use of the SO(3) manifold in CRV interpolation. @details Assumes that all nodes are either two- or three- noded. @warning Untested. """ from PyBeam.Utils.BeamLib import Cbeam3_fstifz Fstiff = np.zeros((len(NodeList),6)) NumNodes = PosDef.shape[0]-1 NumNodesElem = XBELEM.NumNodes[0] iOut = 0 # Output index for iNode in NodeList: if iNode == NumNodes or iNode == -1: raise ValueError("Midpoint strain requested beyond final node.") iElem, iiElem = iNode2iElem(iNode, NumNodes, NumNodesElem) elemK = XBELEM.Stiff[iElem*6:(iElem+1)*6,:] if NumNodesElem == 2: if SO3 == True: # Consistent calculation of midpoint strains using SO(3) manifold. raise NotImplementedError("SO(3) manifold strains.") else: # Using fortran routines R0 = np.zeros((2,6)) R0[0,:3] = PosIni[iNode,:] R0[0,3:] = PsiIni[iElem,iiElem,:] R0[1,:3] = PosIni[iNode+1,:] R0[1,3:] = PsiIni[iElem,iiElem+1,:] Ri = np.zeros((2,6)) Ri[0,:3] = PosDef[iNode,:] Ri[0,3:] = PsiDef[iElem,iiElem,:] Ri[1,:3] = PosDef[iNode+1,:] Ri[1,3:] = PsiDef[iElem,iiElem+1,:] # Midpoint z = 0.0 fstifz = Cbeam3_fstifz(R0, Ri, elemK, z) # END if SO3 elif NumNodesElem == 3: if SO3 == True: raise NotImplementedError("Only available for 2-noded just now.") else: # Using fortran routines R0 = np.zeros((3,6)) R0[0,:3] = PosIni[2*iElem,:] R0[1,:3] = PosIni[2*iElem+2,:] R0[2,:3] = PosIni[2*iElem+1,:] R0[:,3:] = PsiIni[iElem] Ri = np.zeros((3,6)) Ri[0,:3] = PosDef[2*iElem,:] Ri[1,:3] = PosDef[2*iElem+2,:] Ri[2,:3] = PosDef[2*iElem+1,:] Ri[:,3:] = PsiDef[iElem] if iiElem == 0: z = -0.5 # mid-point adjacent to node iNode elif iiElem == 1: z = 0.5 # mid-point adjacent to node iNode elif iiElem == 2: raise ValueError("iiElem only 0, 1 for adjacent midpoint" + "calculation.") else: raise ValueError("iiElem can only take 0, 1 or 2 for " + "3-noded elements.") #----------------------- sm: working only for moments. fstifz = Cbeam3_fstifz(R0, Ri, elemK, z) # link top function #cbeam3_fstif = BeamLib.__lib_cbeam3_MOD_cbeam3_fstif # # prepare I/O #ctNNE = ct.c_int(NumNodesElem) #ctR0 = np.zeros((,),ct.c_double,'F') #ctRi #ctKelem #ctQstiff = np.zeros( () ) # --------------------- sm end # END if SO(3) else: raise ValueError("Only 2- or 3-noded elements are supported.") Fstiff[iOut,:] = fstifz iOut += 1 # END for iElem return Fstiff