def Solve_Py(XBINPUT, XBOPTS, moduleName=None): """Nonlinear dynamic structural solver in Python. Assembly of matrices is carried out with Fortran subroutines.""" "Check correct solution code" assert XBOPTS.Solution.value == 312, ('NonlinearDynamic requested' +\ ' with wrong solution code') "Initialise beam" XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \ = BeamInit.Static(XBINPUT,XBOPTS, moduleName) # Solve static solution XBOPTS.Solution.value = 112 PosDefor, PsiDefor = Solve_Py_Static(XBINPUT, XBOPTS, moduleName) XBOPTS.Solution.value = 312 "Write deformed configuration to file" ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_def.dat' if XBOPTS.PrintInfo == True: sys.stdout.write('Writing file %s ... ' % (ofile)) fp = open(ofile, 'w') fp.write('TITLE="Non-linear static solution: deformed geometry"\n') fp.write('VARIABLES="iElem" "iNode" "Px" "Py" "Pz" "Rx" "Ry" "Rz"\n') fp.close() if XBOPTS.PrintInfo == True: sys.stdout.write('done\n') WriteMode = 'a' BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \ PosDefor, PsiDefor, ofile, WriteMode) "Initialise variables for dynamic analysis" Time, NumSteps, ForceTime, ForcedVel, ForcedVelDot,\ PosDotDef, PsiDotDef,\ OutGrids, PosPsiTime, VelocTime, DynOut\ = BeamInit.Dynamic(XBINPUT,XBOPTS, moduleName) # Delete unused vars del OutGrids, VelocTime "Write _force file" ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_force.dat' fp = open(ofile, 'w') BeamIO.Write_force_File(fp, Time, ForceTime, ForcedVel, ForcedVelDot) fp.close() "Write _vel file" #TODO: write _vel file "Write .mrb file" #TODO: write .mrb file if XBOPTS.PrintInfo.value == True: sys.stdout.write('Solve nonlinear dynamic case in Python ... \n') "Initialise structural system tensors" MglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') CglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') KglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') FglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') Asys = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') ms = ct.c_int() cs = ct.c_int() ks = ct.c_int() fs = ct.c_int() Mvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') Cvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') X = np.zeros(NumDof.value, ct.c_double, 'F') DX = np.zeros(NumDof.value, ct.c_double, 'F') dXdt = np.zeros(NumDof.value, ct.c_double, 'F') dXddt = np.zeros(NumDof.value, ct.c_double, 'F') Force_Dof = np.zeros(NumDof.value, ct.c_double, 'F') Qglobal = np.zeros(NumDof.value, ct.c_double, 'F') "Initialise rotation operators" Unit = np.zeros((3, 3), ct.c_double, 'F') for i in range(3): Unit[i, i] = 1.0 Unit4 = np.zeros((4, 4), ct.c_double, 'F') for i in range(4): Unit4[i, i] = 1.0 Cao = Unit.copy('F') Temp = Unit4.copy('F') Quat = np.zeros(4, ct.c_double, 'F') Quat[0] = 1.0 "Extract initial displacements and velocities" BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef, X, dXdt) "Approximate initial accelerations" "Initialise accelerations as zero arrays" PosDotDotDef = np.zeros((NumNodes_tot.value, 3), ct.c_double, 'F') PsiDotDotDef = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),\ ct.c_double, 'F') "Force at the first time-step" Force = (XBINPUT.ForceStatic + XBINPUT.ForceDyn * ForceTime[0]).copy('F') "Assemble matrices for dynamic analysis" BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, ForcedVel[0,:], ForcedVelDot[0,:],\ NumDof, Settings.DimMat,\ ms, MglobalFull, Mvel,\ cs, CglobalFull, Cvel,\ ks, KglobalFull, fs, FglobalFull,\ Qglobal, XBOPTS, Cao) "Get force vector for unconstrained nodes (Force_Dof)" BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) "Get RHS at initial condition" Qglobal += -np.dot(FglobalFull, Force_Dof) #Separate assembly of follower and dead loads" tmpForceTime = ForceTime[0].copy('F') Qforces = XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ PosIni, PsiIni, PosDefor, PsiDefor, \ (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ Cao, 1)[0] Qglobal -= Qforces "Initial Accel" dXddt[:] = np.dot(np.linalg.inv(MglobalFull), -Qglobal) "Record position of all grid points in global FoR at initial time step" DynOut[0:NumNodes_tot.value, :] = PosDefor "Position/rotation of the selected node in initial deformed configuration" PosPsiTime[0, :3] = PosDefor[-1, :] PosPsiTime[0, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] "Get gamma and beta for Newmark scheme" gamma = 0.5 + XBOPTS.NewmarkDamp.value beta = 0.25 * np.power((gamma + 0.5), 2.0) "Time loop" for iStep in range(NumSteps.value): if XBOPTS.PrintInfo.value == True: sys.stdout.write(' Time: %-10.4e\n' % (Time[iStep + 1])) sys.stdout.write(' SubIter DeltaF DeltaX ResLog10\n') "calculate dt" dt = Time[iStep + 1] - Time[iStep] "Update transformation matrix for given angular velocity" Temp = np.linalg.inv(Unit4 + 0.25 * XbeamLib.QuadSkew(ForcedVel[iStep + 1, 3:]) * dt) Quat = np.dot( Temp, np.dot(Unit4 - 0.25 * XbeamLib.QuadSkew(ForcedVel[iStep, 3:]) * dt, Quat)) Quat = Quat / np.linalg.norm(Quat) Cao = XbeamLib.Rot(Quat) "Predictor step" X += dt * dXdt + (0.5 - beta) * dXddt * dt**2 dXdt += (1.0 - gamma) * dXddt * dt dXddt[:] = 0.0 "Force at current time-step" Force = (XBINPUT.ForceStatic + \ XBINPUT.ForceDyn*ForceTime[iStep+1]).copy('F') "Reset convergence parameters" Iter = 0 ResLog10 = 1.0 "Newton-Raphson loop" while ( (ResLog10 > XBOPTS.MinDelta.value) \ & (Iter < XBOPTS.MaxIterations.value) ): "set tensors to zero" Qglobal[:] = 0.0 Mvel[:, :] = 0.0 Cvel[:, :] = 0.0 MglobalFull[:, :] = 0.0 CglobalFull[:, :] = 0.0 KglobalFull[:, :] = 0.0 FglobalFull[:, :] = 0.0 "Update counter" Iter += 1 if XBOPTS.PrintInfo.value == True: sys.stdout.write(' %-7d ' % (Iter)) "nodal diplacements and velocities from state vector" BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef) "update matrices" BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, ForcedVel[iStep+1,:], ForcedVelDot[iStep+1,:],\ NumDof, Settings.DimMat,\ ms, MglobalFull, Mvel,\ cs, CglobalFull, Cvel,\ ks, KglobalFull, fs, FglobalFull,\ Qglobal, XBOPTS, Cao) "Get force vector for unconstrained nodes (Force_Dof)" BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) "Solve for update vector" "Residual" Qglobal += np.dot(MglobalFull, dXddt) \ + np.dot(Mvel,ForcedVelDot[iStep+1,:]) \ - np.dot(FglobalFull, Force_Dof) #Separate assembly of follower and dead loads tmpForceTime = ForceTime[iStep + 1].copy('F') Qforces = XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ PosIni, PsiIni, PosDefor, PsiDefor, \ (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ Cao,1)[0] Qglobal -= Qforces if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e ' % (max(abs(Qglobal)))) "Calculate system matrix for update calculation" Asys = KglobalFull + \ CglobalFull*gamma/(beta*dt) + \ MglobalFull/(beta*np.power(dt,2.0)) "Solve for update" DX[:] = np.dot(np.linalg.inv(Asys), -Qglobal) "Corrector step" X += DX dXdt += DX * gamma / (beta * dt) dXddt += DX / (beta * dt**2) "Residual at first iteration" if (Iter == 1): Res0_Qglobal = max(max(abs(Qglobal)), 1) Res0_DeltaX = max(max(abs(DX)), 1) "Update residual and compute log10" Res_Qglobal = max(abs(Qglobal)) Res_DeltaX = max(abs(DX)) ResLog10 = max(Res_Qglobal / Res0_Qglobal, Res_DeltaX / Res0_DeltaX) if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e %8.4f\n' % (max(abs(DX)), ResLog10)) "END Netwon-Raphson" "update to converged nodal displacements and velocities" BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt, PosDefor, PsiDefor, PosDotDef, PsiDotDef) PosPsiTime[iStep + 1, :3] = PosDefor[(NumNodes_tot.value - 1) / 2 + 1, :] PosPsiTime[iStep + 1, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] "Position of all grid points in global FoR" i1 = (iStep + 1) * NumNodes_tot.value i2 = (iStep + 2) * NumNodes_tot.value DynOut[i1:i2, :] = PosDefor "END Time loop" "Write _dyn file" ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_dyn.dat' fp = open(ofile, 'w') BeamIO.Write_dyn_File(fp, Time, PosPsiTime) fp.close() "Write _shape file" ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_shape.dat' fp = open(ofile, 'w') BeamIO.Write_shape_File(fp, len(Time), NumNodes_tot.value, Time, DynOut) fp.close() if XBOPTS.PrintInfo.value == True: sys.stdout.write(' ... done\n')
def Solve_Py(XBINPUT, XBOPTS, VMOPTS, VMINPUT, AELAOPTS, **kwords): """@brief Nonlinear dynamic solver using Python to solve aeroelastic equation. @details Assembly of structural matrices is carried out with Fortran subroutines. Aerodynamics solved using PyAero\.UVLM. @warning test outstanding: test for maintaining static deflections in same conditions. TODO: Maintain static deflections in same conditions. @param XBINPUT Beam inputs (for initialization in Python). @param XBOPTS Beam solver options (for Fortran). @param VMOPTS UVLM solver options (for C/C++). @param VMINPUT UVLM solver inputs (for initialization in Python). @param VMUNST Unsteady input information for aero solver. @param AELAOPTS Options relevant to coupled aeroelastic simulations. @param writeDict OrderedDict of 'name':tuple of outputs to write. """ # Check correct solution code. assert XBOPTS.Solution.value == 312, ('NonlinearDynamic requested' + ' with wrong solution code') # Initialise static beam data. XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \ = BeamInit.Static(XBINPUT,XBOPTS) # Calculate initial displacements. if AELAOPTS.ImpStart == False: XBOPTS.Solution.value = 112 # Modify options. VMOPTS.Steady = ct.c_bool(True) Rollup = VMOPTS.Rollup.value VMOPTS.Rollup.value = False # Solve Static Aeroelastic. PosDefor, PsiDefor, Zeta, ZetaStar, Gamma, GammaStar, Force = \ Static.Solve_Py(XBINPUT, XBOPTS, VMOPTS, VMINPUT, AELAOPTS) XBOPTS.Solution.value = 312 # Reset options. VMOPTS.Steady = ct.c_bool(False) VMOPTS.Rollup.value = Rollup elif AELAOPTS.ImpStart == True: PosDefor = PosIni.copy(order='F') PsiDefor = PsiIni.copy(order='F') Force = np.zeros((XBINPUT.NumNodesTot, 6), ct.c_double, 'F') # Write deformed configuration to file. TODO: tidy this away inside function. ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_def.dat' if XBOPTS.PrintInfo == True: sys.stdout.write('Writing file %s ... ' % (ofile)) fp = open(ofile, 'w') fp.write('TITLE="Non-linear static solution: deformed geometry"\n') fp.write('VARIABLES="iElem" "iNode" "Px" "Py" "Pz" "Rx" "Ry" "Rz"\n') fp.close() if XBOPTS.PrintInfo == True: sys.stdout.write('done\n') WriteMode = 'a' # Write BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, PosDefor, PsiDefor, ofile, WriteMode) # Initialise structural variables for dynamic analysis. Time, NumSteps, ForceTime, ForcedVel, ForcedVelDot,\ PosDotDef, PsiDotDef,\ OutGrids, PosPsiTime, VelocTime, DynOut\ = BeamInit.Dynamic(XBINPUT,XBOPTS) # Delete unused variables. del ForceTime, OutGrids, VelocTime # Write _force file # ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_force.dat' # fp = open(ofile,'w') # BeamIO.Write_force_File(fp, Time, ForceTime, ForcedVel, ForcedVelDot) # fp.close() # Write _vel file #TODO: write _vel file # Write .mrb file. #TODO: write .mrb file if XBOPTS.PrintInfo.value == True: sys.stdout.write('Solve nonlinear dynamic case in Python ... \n') # Initialise structural system tensors. MglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') CglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') KglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') FglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') Asys = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') ms = ct.c_int() cs = ct.c_int() ks = ct.c_int() fs = ct.c_int() Mvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') Cvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') # X0 = np.zeros(NumDof.value, ct.c_double, 'F') X = np.zeros(NumDof.value, ct.c_double, 'F') DX = np.zeros(NumDof.value, ct.c_double, 'F') dXdt = np.zeros(NumDof.value, ct.c_double, 'F') dXddt = np.zeros(NumDof.value, ct.c_double, 'F') Force_Dof = np.zeros(NumDof.value, ct.c_double, 'F') Qglobal = np.zeros(NumDof.value, ct.c_double, 'F') # Initialise rotation operators. Unit = np.zeros((3, 3), ct.c_double, 'F') for i in range(3): Unit[i, i] = 1.0 Unit4 = np.zeros((4, 4), ct.c_double, 'F') for i in range(4): Unit4[i, i] = 1.0 Cao = Unit.copy('F') Temp = Unit4.copy('F') Quat = np.zeros(4, ct.c_double, 'F') Quat[0] = 1.0 # Extract initial displacements and velocities. BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE, PosDefor, PsiDefor, PosDotDef, PsiDotDef, X, dXdt) # Approximate initial accelerations. PosDotDotDef = np.zeros((NumNodes_tot.value, 3), ct.c_double, 'F') PsiDotDotDef = np.zeros((XBINPUT.NumElems, Settings.MaxElNod, 3), ct.c_double, 'F') # Assemble matrices for dynamic analysis. BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, PosDefor, PsiDefor, PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef, Force, ForcedVel[0, :], ForcedVelDot[0, :], NumDof, Settings.DimMat, ms, MglobalFull, Mvel, cs, CglobalFull, Cvel, ks, KglobalFull, fs, FglobalFull, Qglobal, XBOPTS, Cao) # Get force vector for unconstrained nodes (Force_Dof). BeamLib.f_fem_m2v(ct.byref(NumNodes_tot), ct.byref(ct.c_int(6)), Force.ctypes.data_as(ct.POINTER(ct.c_double)), ct.byref(NumDof), Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)), XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int))) # Get RHS at initial condition. Qglobal = Qglobal - np.dot(FglobalFull, Force_Dof) # Initial Accel. dXddt[:] = np.dot(np.linalg.inv(MglobalFull), -Qglobal) # Record position of all grid points in global FoR at initial time step. DynOut[0:NumNodes_tot.value, :] = PosDefor # Record state of the selected node in initial deformed configuration. PosPsiTime[0, :3] = PosDefor[-1, :] PosPsiTime[0, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] # Get gamma and beta for Newmark scheme. gamma = 0.5 + XBOPTS.NewmarkDamp.value beta = 0.25 * pow((gamma + 0.5), 2) # Initialize Aero Section = InitSection(VMOPTS, VMINPUT, AELAOPTS.ElasticAxis) # Declare memory for Aero variables. ZetaDot = np.zeros((Section.shape[0], PosDefor.shape[0], 3), ct.c_double, 'C') K = VMOPTS.M.value * VMOPTS.N.value AIC = np.zeros((K, K), ct.c_double, 'C') BIC = np.zeros((K, K), ct.c_double, 'C') AeroForces = np.zeros((VMOPTS.M.value + 1, VMOPTS.N.value + 1, 3), ct.c_double, 'C') # Initialise A-frame location and orientation to be zero OriginA_G = np.zeros(3, ct.c_double, 'C') PsiA_G = np.zeros(3, ct.c_double, 'C') # Init external velocities. Ufree = InitSteadyExternalVels(VMOPTS, VMINPUT) # Init uninit vars if an impulsive start is specified. if AELAOPTS.ImpStart == True: Zeta = np.zeros((Section.shape[0], PosDefor.shape[0], 3), ct.c_double, 'C') Gamma = np.zeros((VMOPTS.M.value, VMOPTS.N.value), ct.c_double, 'C') # Generate surface, wake and gamma matrices. CoincidentGrid(PosDefor, PsiDefor, Section, ForcedVel[0, :3], ForcedVel[0, 3:], PosDotDef, PsiDotDef, XBINPUT, Zeta, ZetaDot, OriginA_G, PsiA_G, VMINPUT.ctrlSurf) # init wake grid and gamma matrix. ZetaStar, GammaStar = InitSteadyWake(VMOPTS, VMINPUT, Zeta, ForcedVel[0, :3]) # Init GammaDot GammaDot = np.zeros_like(Gamma, ct.c_double, 'C') # Define tecplot stuff if Settings.PlotTec == True: FileName = Settings.OutputDir + Settings.OutputFileRoot + 'AeroGrid.dat' Variables = ['X', 'Y', 'Z', 'Gamma'] FileObject = PostProcess.WriteAeroTecHeader(FileName, 'Default', Variables) # Plot results of static analysis PostProcess.WriteUVLMtoTec(FileObject, Zeta, ZetaStar, Gamma, GammaStar, TimeStep=0, NumTimeSteps=XBOPTS.NumLoadSteps.value, Time=0.0, Text=True) # Open output file for writing if 'writeDict' in kwords and Settings.WriteOut == True: fp = OpenOutFile(writeDict, XBOPTS, Settings) # Write initial outputs to file. if 'writeDict' in kwords and Settings.WriteOut == True: WriteToOutFile(writeDict, fp, Time[0], PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, ctrlSurf) # END if write # Time loop. for iStep in range(NumSteps.value): if XBOPTS.PrintInfo.value == True: sys.stdout.write('Time: %-10.4e\n' % (Time[iStep + 1])) sys.stdout.write(' SubIter DeltaF DeltaX ResLog10\n') dt = Time[iStep + 1] - Time[iStep] # Set dt for aero force calcs. VMOPTS.DelTime = ct.c_double(dt) # Save Gamma at iStep. GammaSav = Gamma.copy(order='C') # Force at current time-step if iStep > 0 and AELAOPTS.Tight == False: # zero aero forces. AeroForces[:, :, :] = 0.0 # Update CRV. PsiA_G = BeamLib.Cbeam3_quat2psi(Quat) # CRV at iStep # Update origin. OriginA_G[:] = OriginA_G[:] + ForcedVel[iStep - 1, :3] * dt # Update control surface deflection. if VMINPUT.ctrlSurf != None: if 'mpcCont' in kwords: uOpt = kwords['mpcCont'].getUopt( getState(Gamma, GammaStar, GammaDot, X, dXdt)) VMINPUT.ctrlSurf.update(Time[iStep], uOpt[0, 0]) else: VMINPUT.ctrlSurf.update(Time[iStep]) # Generate surface grid. CoincidentGrid(PosDefor, PsiDefor, Section, ForcedVel[iStep, :3], ForcedVel[iStep, 3:], PosDotDef, PsiDotDef, XBINPUT, Zeta, ZetaDot, OriginA_G, PsiA_G, VMINPUT.ctrlSurf) # Update wake geom #'roll' data. ZetaStar = np.roll(ZetaStar, 1, axis=0) GammaStar = np.roll(GammaStar, 1, axis=0) #overwrite grid points with TE. ZetaStar[0, :] = Zeta[VMOPTS.M.value, :] # overwrite Gamma with TE value from previous timestep. GammaStar[0, :] = Gamma[VMOPTS.M.value - 1, :] # Apply gust velocity. if VMINPUT.gust != None: Utot = Ufree + VMINPUT.gust.Vels(Zeta) else: Utot = Ufree # Solve for AeroForces. UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Utot, ZetaStar, VMOPTS, AeroForces, Gamma, GammaStar, AIC, BIC) # Get GammaDot. GammaDot[:] = Gamma[:] - GammaSav[:] # Apply density scaling. AeroForces[:, :, :] = AELAOPTS.AirDensity * AeroForces[:, :, :] if Settings.PlotTec == True: PostProcess.WriteUVLMtoTec( FileObject, Zeta, ZetaStar, Gamma, GammaStar, TimeStep=iStep, NumTimeSteps=XBOPTS.NumLoadSteps.value, Time=Time[iStep], Text=True) # map AeroForces to beam. CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, Force) # Add gravity loads. AddGravityLoads(Force, XBINPUT, XBELEM, AELAOPTS, PsiDefor, VMINPUT.c) #END if iStep > 0 # Quaternion update for orientation. Temp = np.linalg.inv(Unit4 + 0.25 * XbeamLib.QuadSkew(ForcedVel[iStep + 1, 3:]) * dt) Quat = np.dot( Temp, np.dot(Unit4 - 0.25 * XbeamLib.QuadSkew(ForcedVel[iStep, 3:]) * dt, Quat)) Quat = Quat / np.linalg.norm(Quat) Cao = XbeamLib.Rot(Quat) # transformation matrix at iStep+1 if AELAOPTS.Tight == True: # CRV at iStep+1 PsiA_G = BeamLib.Cbeam3_quat2psi(Quat) # Origin at iStep+1 OriginA_G[:] = OriginA_G[:] + ForcedVel[iStep, :3] * dt # Predictor step. X = X + dt * dXdt + (0.5 - beta) * dXddt * pow(dt, 2.0) dXdt = dXdt + (1.0 - gamma) * dXddt * dt dXddt[:] = 0.0 # Reset convergence parameters. Iter = 0 ResLog10 = 0.0 # Newton-Raphson loop. while ((ResLog10 > np.log10(XBOPTS.MinDelta.value)) & (Iter < XBOPTS.MaxIterations.value)): # set tensors to zero. Qglobal[:] = 0.0 Mvel[:, :] = 0.0 Cvel[:, :] = 0.0 MglobalFull[:, :] = 0.0 CglobalFull[:, :] = 0.0 KglobalFull[:, :] = 0.0 FglobalFull[:, :] = 0.0 # Update counter. Iter += 1 if XBOPTS.PrintInfo.value == True: sys.stdout.write(' %-7d ' % (Iter)) # nodal diplacements and velocities from state vector. BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt, PosDefor, PsiDefor, PosDotDef, PsiDotDef) # if tightly coupled is on then get new aeroforces. if AELAOPTS.Tight == True: # zero aero forces. AeroForces[:, :, :] = 0.0 # Set gamma at t-1 to saved solution. Gamma[:, :] = GammaSav[:, :] # get new grid. # The rigid-body DoFs (OriginA_G,PsiA_G,ForcedVel) at time step # i+1 are used to converge the aeroelastic equations. CoincidentGrid(PosDefor, PsiDefor, Section, ForcedVel[iStep + 1, :3], ForcedVel[iStep + 1, 3:], PosDotDef, PsiDotDef, XBINPUT, Zeta, ZetaDot, OriginA_G, PsiA_G, VMINPUT.ctrlSurf) # close wake. ZetaStar[0, :] = Zeta[VMOPTS.M.value, :] # save pereference and turn off rollup. Rollup = VMOPTS.Rollup.value VMOPTS.Rollup.value = False # Solve for AeroForces. UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Ufree, ZetaStar, VMOPTS, AeroForces, Gamma, GammaStar, AIC, BIC) # turn rollup back to original preference VMOPTS.Rollup.value = Rollup # apply density scaling. AeroForces[:, :, :] = AELAOPTS.AirDensity * AeroForces[:, :, :] # beam forces. CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, Force) # Add gravity loads. AddGravityLoads(Force, XBINPUT, XBELEM, AELAOPTS, PsiDefor, VMINPUT.c) #END if Tight ForcedVelLoc = ForcedVel[iStep + 1, :].copy('F') ForcedVelDotLoc = ForcedVelDot[iStep + 1, :].copy('F') # Update matrices. BeamLib.Cbeam3_Asbly_Dynamic( XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, PosDefor, PsiDefor, PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef, Force, ForcedVelLoc, ForcedVelDotLoc, NumDof, Settings.DimMat, ms, MglobalFull, Mvel, cs, CglobalFull, Cvel, ks, KglobalFull, fs, FglobalFull, Qglobal, XBOPTS, Cao) # Get force vector for unconstrained nodes (Force_Dof). BeamLib.f_fem_m2v( ct.byref(NumNodes_tot), ct.byref(ct.c_int(6)), Force.ctypes.data_as(ct.POINTER(ct.c_double)), ct.byref(NumDof), Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)), XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int))) # Solve for update vector. # Residual. Qglobal = Qglobal + np.dot(MglobalFull, dXddt) \ + np.dot(Mvel,ForcedVelDotLoc) \ - np.dot(FglobalFull, Force_Dof) if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e ' % (max(abs(Qglobal)))) # Calculate system matrix for update calculation. Asys = KglobalFull \ + CglobalFull*gamma/(beta*dt) \ + MglobalFull/(beta*pow(dt,2.0)) # Solve for update. DX[:] = np.dot(np.linalg.inv(Asys), -Qglobal) # Corrector step. X = X + DX dXdt = dXdt + DX * gamma / (beta * dt) dXddt = dXddt + DX / (beta * pow(dt, 2.0)) # Residual at first iteration. if (Iter == 1): Res0_Qglobal = max(abs(Qglobal)) + 1.e-16 Res0_DeltaX = max(abs(DX)) + 1.e-16 # Update residual and compute log10. Res_Qglobal = max(abs(Qglobal)) + 1.e-16 Res_DeltaX = max(abs(DX)) + 1.e-16 ResLog10 = max([ np.log10(Res_Qglobal / Res0_Qglobal), np.log10(Res_DeltaX / Res0_DeltaX) ]) if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e %8.4f\n' % (max(abs(DX)), ResLog10)) if ResLog10 > 2.0: print("Residual growing! Exit Newton-Raphson...") break # END Netwon-Raphson. if ResLog10 > 2.0: print("Residual growing! Exit time-loop...") debug = 'here' del debug break # Update to converged nodal displacements and velocities. BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt, PosDefor, PsiDefor, PosDotDef, PsiDotDef) PosPsiTime[iStep + 1, :3] = PosDefor[-1, :] PosPsiTime[iStep + 1, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] # Position of all grid points in global FoR. i1 = (iStep + 1) * NumNodes_tot.value i2 = (iStep + 2) * NumNodes_tot.value DynOut[i1:i2, :] = PosDefor # Write selected outputs if 'writeDict' in kwords and Settings.WriteOut == True: WriteToOutFile(writeDict, fp, Time[iStep + 1], PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, ctrlSurf) # END if write. # 'Rollup' due to external velocities. TODO: Must add gusts here! ZetaStar[:, :] = ZetaStar[:, :] + VMINPUT.U_infty * dt if VMINPUT.gust != None: ZetaStar[:, :, :] = ZetaStar[:, :, :] + VMINPUT.gust.Vels( ZetaStar) * dt # END Time loop # Write _dyn file. ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_dyn.dat' fp = open(ofile, 'w') BeamIO.Write_dyn_File(fp, Time, PosPsiTime) fp.close() # "Write _shape file" # ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_shape.dat' # fp = open(ofile,'w') # BeamIO.Write_shape_File(fp, len(Time), NumNodes_tot.value, Time, DynOut) # fp.close() # Close output file if it exists. if 'writeDict' in kwords and Settings.WriteOut == True: fp.close() # Close Tecplot ascii FileObject. if Settings.PlotTec == True: PostProcess.CloseAeroTecFile(FileObject) if XBOPTS.PrintInfo.value == True: sys.stdout.write(' ... done\n') # For interactive analysis at end of simulation set breakpoint. pass
def genSSbeam(XBINPUT, NumNodes_tot, NumDof, XBELEM, XBNODE, PosIni, PsiIni, PosDefor, PsiDefor, XBOPTS, cRef, vRef, modal=0): """@details Generate state-space matrices for linear beam dynamics. @param XBINPUT Beam inputs. @param NumNodes_tot Total number of nodes in the model. @param NumDof Total number of degrees of freedom. @param XBELEM Beam FE node information. @param XBNODE Beam FE node information. @param PosIni Undeformed beam displacements. @param PsiIni Undeformed beam FE rotations. @param PosDefor Deformed beam displacements. @param PsiDefor Deformed beam FE rotations. @param XBOPTS Beam options. @param cRef Reference chord for writing eqs in reduced time. @param vRef Reference velocity for writing eqs in reduced time. @param modal Generate a modal model, number of modes in model. @return A State transfer matrix. @return B Input matrix (assumed to be nodal forces and moments/modal forces). @return C Output matrix (nodal velocities and displacements/rotations, model vels,amplitudes). @return kMat matrix with diagonal up to modal-th reduced frequencies. @return phiSort Eigenvectors of all modes in ascending order of frequnecy. """ # Init zero-valued vectors PosDotDef = np.zeros((XBINPUT.NumNodesTot, 3), ct.c_double, 'F') PsiDotDef = np.zeros((XBINPUT.NumElems, Settings.MaxElNod, 3), ct.c_double, 'F') PosDotDotDef = np.zeros((XBINPUT.NumNodesTot, 3), ct.c_double, 'F') PsiDotDotDef = np.zeros((XBINPUT.NumElems, Settings.MaxElNod, 3), ct.c_double, 'F') ForcedVel = np.zeros( (1, 6), ct.c_double, 'F') # check this definition as 2d array is OK within interface ForcedVelDot = np.zeros((1, 6), ct.c_double, 'F') Qglobal = np.zeros(NumDof.value, ct.c_double, 'F') StaticForces = np.zeros((XBINPUT.NumNodesTot, 3), ct.c_double, 'F') # Init matrices and sparse counter for fortran MglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') CglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') KglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') FglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') ms = ct.c_int() cs = ct.c_int() ks = ct.c_int() fs = ct.c_int() Mvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') Cvel = np.zeros((NumDof.value, 6), ct.c_double, 'F') # Init under assumption the the inertial frame and a-frame are coincident Cao = np.zeros((3, 3), ct.c_double, 'F') for i in range(3): Cao[i, i] = 1.0 # Get mass and stiffness matrices BeamLib.Cbeam3_Asbly_Dynamic( XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, PosDefor, PsiDefor, PosDotDef, #zero PsiDotDef, #zero PosDotDotDef, #zero PsiDotDotDef, #zero StaticForces, # check if I affect matrices ForcedVel[0, :], # check if I make a difference ForcedVelDot[0, :], # zero NumDof, Settings.DimMat, ms, MglobalFull, Mvel, cs, CglobalFull, # check if non-zero? Cvel, ks, KglobalFull, fs, FglobalFull, # make sure forces are applied in B-frame Qglobal, # check how this is defined but I think it should be zero XBOPTS, Cao) # filter out errors from python/fortran memory sharing if False: for i in range(NumDof.value): for j in range(NumDof.value): if np.abs(MglobalFull[i, j]) < 1e-6 and np.abs( MglobalFull[i, j]) != 0.0: MglobalFull[i, j] = 0.0 print('mass matrix') if np.abs(KglobalFull[i, j]) < 1e-6 and np.abs( KglobalFull[i, j]) != 0.0: KglobalFull[i, j] = 0.0 print('stiffness matrix') if modal == 0: # continuous time FE state-space model mInv = scipy.linalg.inv(MglobalFull) # state transfer matrix A = np.zeros((2 * NumDof.value, 2 * NumDof.value)) A[:NumDof.value, NumDof.value:] = -pow(cRef, 2.0) / 4.0 / pow( vRef, 2.0) * np.dot(mInv, KglobalFull) A[NumDof.value:, :NumDof.value] = np.eye(NumDof.value) # input matrix (add columns of zeros corresponding to root node) B = np.zeros((2 * NumDof.value, NumDof.value + 6)) B[:NumDof.value, 6:NumDof.value + 6] = pow(cRef, 2.0) / 4.0 / pow(vRef, 2) * mInv #output matrix (add rows of zeros for root node) C = np.zeros((2 * (6 + NumDof.value), 2 * NumDof.value)) C[6:NumDof.value + 6, :NumDof.value] = np.eye(NumDof.value, NumDof.value) C[NumDof.value + 12:, NumDof.value:] = np.eye(NumDof.value, NumDof.value) # dummy kMat = False phiSort = False # checking and plotting if desired if False: # general eigensolution lam = scipy.linalg.eig(KglobalFull, MglobalFull)[0] indices = np.argsort(lam) print(np.sqrt(lam[indices]) / 2.0 / np.pi) # plot eigenvalues of system lam = scipy.linalg.eig(A)[0] indices = np.argsort(np.imag(lam)) print(np.imag(lam[indices[len(indices) / 2:]] / 2.0 / np.pi)) plt.plot( np.real(lam) / 2.0 / np.pi, np.imag(lam) / 2.0 / np.pi, 'ro') plt.show() elif modal > 0: # continuous time modal state-space model lam, Phi = scipy.linalg.eig(KglobalFull, MglobalFull) indices = np.argsort(lam) omega = np.sqrt(lam[indices]) phiSort = Phi[:, indices] # mass orthonormalize phiSort for mode in range(np.shape(MglobalFull)[0]): factor = np.dot(phiSort[:, mode].T, np.dot(MglobalFull, phiSort[:, mode])) phiSort[:, mode] = phiSort[:, mode] / np.sqrt(factor) # matrix of reduced frequcies kMat = cRef / 2.0 / vRef * np.diag(omega[0:modal]) kMat = np.power(kMat, 2.0) # this is elementwise # state transfer matrix A = np.zeros((2 * modal, 2 * modal)) A[:modal, modal:] = -kMat A[modal:, :modal] = np.eye(modal) # input matrix B = np.zeros((2 * modal, modal)) B[:modal, :] = pow(cRef / 2.0 / vRef, 2.0) * np.eye(modal) # output matrix C = np.eye(2 * modal) else: raise ValueError("modal must be zero or positive.") return A, B, C, kMat, phiSort
def Solve_Py(XBINPUT,XBOPTS,VMOPTS,VMINPUT,AELAOPTS,**kwords): """@brief Nonlinear dynamic solver using Python to solve aeroelastic equation. @details Assembly of structural matrices is carried out with Fortran subroutines. Aerodynamics solved using PyAero\.UVLM. @param XBINPUT Beam inputs (for initialization in Python). @param XBOPTS Beam solver options (for Fortran). @param VMOPTS UVLM solver options (for C/C++). @param VMINPUT UVLM solver inputs (for initialization in Python). @param VMUNST Unsteady input information for aero solver. @param AELAOPTS Options relevant to coupled aeroelastic simulations. @param writeDict OrderedDict of 'name':tuple of outputs to write. """ # Check correct solution code. assert XBOPTS.Solution.value == 912, ('NonlinearFlightDynamic requested' + ' with wrong solution code') # Initialise static beam data. XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \ = BeamInit.Static(XBINPUT,XBOPTS) # Calculate initial displacements. if AELAOPTS.ImpStart == False: XBOPTS.Solution.value = 112 # Modify options. VMOPTS.Steady = ct.c_bool(True) Rollup = VMOPTS.Rollup.value VMOPTS.Rollup.value = False # Solve Static Aeroelastic. PosDefor, PsiDefor, Zeta, ZetaStar, Gamma, GammaStar, Force = \ Static.Solve_Py(XBINPUT, XBOPTS, VMOPTS, VMINPUT, AELAOPTS) XBOPTS.Solution.value = 912 # Reset options. VMOPTS.Steady = ct.c_bool(False) VMOPTS.Rollup.value = Rollup elif AELAOPTS.ImpStart == True: PosDefor = PosIni.copy(order='F') PsiDefor = PsiIni.copy(order='F') Force = np.zeros((XBINPUT.NumNodesTot,6),ct.c_double,'F') # Write deformed configuration to file. TODO: tidy this away inside function. ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_def.dat' if XBOPTS.PrintInfo==True: sys.stdout.write('Writing file %s ... ' %(ofile)) fp = open(ofile,'w') fp.write('TITLE="Non-linear static solution: deformed geometry"\n') fp.write('VARIABLES="iElem" "iNode" "Px" "Py" "Pz" "Rx" "Ry" "Rz"\n') fp.close() if XBOPTS.PrintInfo==True: sys.stdout.write('done\n') WriteMode = 'a' # Write BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, PosDefor, PsiDefor, ofile, WriteMode) # Initialise structural variables for dynamic analysis. Time, NumSteps, ForceTime, Vrel, VrelDot,\ PosDotDef, PsiDotDef,\ OutGrids, PosPsiTime, VelocTime, DynOut\ = BeamInit.Dynamic(XBINPUT,XBOPTS) # Delete unused variables. del OutGrids, VelocTime if XBOPTS.PrintInfo.value==True: sys.stdout.write('Solve nonlinear dynamic case in Python ... \n') #Initialise structural system tensors MssFull = np.zeros((NumDof.value,NumDof.value), ct.c_double, 'F') CssFull = np.zeros((NumDof.value,NumDof.value), ct.c_double, 'F') KssFull = np.zeros((NumDof.value,NumDof.value), ct.c_double, 'F') FstrucFull = np.zeros((NumDof.value,NumDof.value), ct.c_double, 'F') ms = ct.c_int() cs = ct.c_int() ks = ct.c_int() fs = ct.c_int() Msr = np.zeros((NumDof.value,6), ct.c_double, 'F') Csr = np.zeros((NumDof.value,6), ct.c_double, 'F') X = np.zeros(NumDof.value, ct.c_double, 'F') dXdt = np.zeros(NumDof.value, ct.c_double, 'F') Force_Dof = np.zeros(NumDof.value, ct.c_double, 'F') Qstruc = np.zeros(NumDof.value, ct.c_double, 'F') #Initialise rigid-body system tensors MrsFull = np.zeros((6,NumDof.value), ct.c_double, 'F') CrsFull = np.zeros((6,NumDof.value), ct.c_double, 'F') KrsFull = np.zeros((6,NumDof.value), ct.c_double, 'F') FrigidFull = np.zeros((6,NumDof.value+6), ct.c_double, 'F') mr = ct.c_int() cr = ct.c_int() kr = ct.c_int() fr = ct.c_int() Mrr = np.zeros((6,6), ct.c_double, 'F') Crr = np.zeros((6,6), ct.c_double, 'F') Qrigid = np.zeros(6, ct.c_double, 'F') #Initialise full system tensors Q = np.zeros(NumDof.value+6+4, ct.c_double, 'F') DQ = np.zeros(NumDof.value+6+4, ct.c_double, 'F') dQdt = np.zeros(NumDof.value+6+4, ct.c_double, 'F') dQddt = np.zeros(NumDof.value+6+4, ct.c_double, 'F') Force_All = np.zeros(NumDof.value+6, ct.c_double, 'F') Msys = np.zeros((NumDof.value+6+4,NumDof.value+6+4), ct.c_double, 'F') Csys = np.zeros((NumDof.value+6+4,NumDof.value+6+4), ct.c_double, 'F') Ksys = np.zeros((NumDof.value+6+4,NumDof.value+6+4), ct.c_double, 'F') Asys = np.zeros((NumDof.value+6+4,NumDof.value+6+4), ct.c_double, 'F') Qsys = np.zeros(NumDof.value+6+4, ct.c_double, 'F') #Initialise rotation operators. TODO: include initial AOA here currVrel=Vrel[0,:].copy('F') AOA = np.arctan(currVrel[2]/-currVrel[1]) Quat = xbl.Euler2Quat(AOA,0,0) Cao = xbl.Rot(Quat) ACoa = np.zeros((6,6), ct.c_double, 'F') ACoa[:3,:3] = np.transpose(Cao) ACoa[3:,3:] = np.transpose(Cao) Cqr = np.zeros((4,6), ct.c_double, 'F') Cqq = np.zeros((4,4), ct.c_double, 'F') Unit4 = np.zeros((4,4), ct.c_double, 'F') for i in range(4): Unit4[i,i] = 1.0 # Extract initial displacements and velocities. BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE, PosDefor, PsiDefor, PosDotDef, PsiDotDef, X, dXdt) # Approximate initial accelerations. PosDotDotDef = np.zeros((NumNodes_tot.value,3),ct.c_double,'F') PsiDotDotDef = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3), ct.c_double, 'F') #Populate state vector Q[:NumDof.value]=X.copy('F') dQdt[:NumDof.value]=dXdt.copy('F') dQdt[NumDof.value:NumDof.value+6] = Vrel[0,:].copy('F') dQdt[NumDof.value+6:]= Quat.copy('F') #Force at the first time-step Force += (XBINPUT.ForceDyn*ForceTime[0]).copy('F') #Assemble matrices and loads for structural dynamic analysis currVrel=Vrel[0,:].copy('F') tmpQuat=Quat.copy('F') BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, currVrel, 0*currVrel,\ NumDof, Settings.DimMat,\ ms, MssFull, Msr,\ cs, CssFull, Csr,\ ks, KssFull, fs, FstrucFull,\ Qstruc, XBOPTS, Cao) BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) Qstruc -= np.dot(FstrucFull, Force_Dof) #Assemble matrices for rigid-body dynamic analysis BeamLib.Xbeam_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ currVrel, 0*currVrel, tmpQuat,\ NumDof, Settings.DimMat,\ mr, MrsFull, Mrr,\ cr, CrsFull, Crr, Cqr, Cqq,\ kr, KrsFull, fr, FrigidFull,\ Qrigid, XBOPTS, Cao) BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(ct.c_int(NumDof.value+6)),\ Force_All.ctypes.data_as(ct.POINTER(ct.c_double)) ) Qrigid -= np.dot(FrigidFull, Force_All) # #Separate assembly of follower and dead loads # tmpForceTime=ForceTime[0].copy('F') # tmpQforces,Dummy,tmpQrigid = xbl.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ # PosIni, PsiIni, PosDefor, PsiDefor, \ # (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ # (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ # Cao,1) # # Qstruc -= tmpQforces # Qrigid -= tmpQrigid #Assemble system matrices Msys[:NumDof.value,:NumDof.value] = MssFull.copy('F') Msys[:NumDof.value,NumDof.value:NumDof.value+6] = Msr.copy('F') Msys[NumDof.value:NumDof.value+6,:NumDof.value] = MrsFull.copy('F') Msys[NumDof.value:NumDof.value+6,NumDof.value:NumDof.value+6] = Mrr.copy('F') Msys[NumDof.value+6:,NumDof.value+6:] = Unit4.copy('F') Qsys[:NumDof.value] = Qstruc Qsys[NumDof.value:NumDof.value+6] = Qrigid Qsys[NumDof.value+6:] = np.dot(Cqq,dQdt[NumDof.value+6:]) # Initial Accel. dQddt[:] = np.dot(np.linalg.inv(Msys), -Qsys) #Record position of all grid points in global FoR at initial time step DynOut[0:NumNodes_tot.value,:] = PosDefor #Position/rotation of the selected node in initial deformed configuration PosPsiTime[0,:3] = PosDefor[-1,:] PosPsiTime[0,3:] = PsiDefor[-1,XBELEM.NumNodes[-1]-1,:] #Get gamma and beta for Newmark scheme gamma = 0.5 + XBOPTS.NewmarkDamp.value beta = 0.25*(gamma + 0.5)**2 # Initialise Aero Section = InitSection(VMOPTS,VMINPUT,AELAOPTS.ElasticAxis) # Declare memory for Aero variables. ZetaDot = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C') K = VMOPTS.M.value*VMOPTS.N.value AIC = np.zeros((K,K),ct.c_double,'C') BIC = np.zeros((K,K),ct.c_double,'C') AeroForces = np.zeros((VMOPTS.M.value+1,VMOPTS.N.value+1,3),ct.c_double,'C') # Initialise A-frame location and orientation to be zero. OriginA_G = np.zeros(3,ct.c_double,'C') PsiA_G = xbl.quat2psi(Quat) # CRV at iStep # Init external velocities. Ufree = InitSteadyExternalVels(VMOPTS,VMINPUT) if AELAOPTS.ImpStart == True: Zeta = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C') Gamma = np.zeros((VMOPTS.M.value,VMOPTS.N.value),ct.c_double,'C') # Generate surface, wake and gamma matrices. CoincidentGrid(PosDefor, PsiDefor, Section, currVrel[:3], currVrel[3:], PosDotDef, PsiDotDef, XBINPUT, Zeta, ZetaDot, OriginA_G, PsiA_G, VMINPUT.ctrlSurf) # init wake grid and gamma matrix. ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta,currVrel[:3]) # Define tecplot stuff if Settings.PlotTec==True: FileName = Settings.OutputDir + Settings.OutputFileRoot + 'AeroGrid.dat' Variables = ['X', 'Y', 'Z','Gamma'] FileObject = PostProcess.WriteAeroTecHeader(FileName, 'Default', Variables) # Plot results of static analysis PostProcess.WriteUVLMtoTec(FileObject, Zeta, ZetaStar, Gamma, GammaStar, TimeStep = 0, NumTimeSteps = XBOPTS.NumLoadSteps.value, Time = 0.0, Text = True) # Open output file for writing if 'writeDict' in kwords and Settings.WriteOut == True: writeDict = kwords['writeDict'] ofile = Settings.OutputDir + \ Settings.OutputFileRoot + \ '_SOL912_out.dat' fp = open(ofile,'w') fp.write("{:<14}".format("Time")) for output in writeDict.keys(): fp.write("{:<14}".format(output)) fp.write("\n") fp.flush() # Write initial outputs to file. if 'writeDict' in kwords and Settings.WriteOut == True: locForces = None # Stops recalculation of forces fp.write("{:<14,e}".format(Time[0])) for myStr in writeDict.keys(): if re.search(r'^R_.',myStr): if re.search(r'^R_._.', myStr): index = int(myStr[4]) elif re.search(r'root', myStr): index = 0 elif re.search(r'tip', myStr): index = -1 else: raise IOError("Node index not recognised.") if myStr[2] == 'x': component = 0 elif myStr[2] == 'y': component = 1 elif myStr[2] == 'z': component = 2 else: raise IOError("Displacement component not recognised.") fp.write("{:<14,e}".format(PosDefor[index,component])) elif re.search(r'^M_.',myStr): if re.search(r'^M_._.', myStr): index = int(myStr[4]) elif re.search(r'root', myStr): index = 0 elif re.search(r'tip', myStr): index = -1 else: raise IOError("Node index not recognised.") if myStr[2] == 'x': component = 0 elif myStr[2] == 'y': component = 1 elif myStr[2] == 'z': component = 2 else: raise IOError("Moment component not recognised.") if locForces == None: locForces = BeamIO.localElasticForces(PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, [index]) fp.write("{:<14,e}".format(locForces[0,3+component])) else: raise IOError("writeDict key not recognised.") # END for myStr fp.write("\n") fp.flush() # END if write # Time loop. for iStep in range(NumSteps.value): if XBOPTS.PrintInfo.value==True: sys.stdout.write('Time: %-10.4e\n' %(Time[iStep+1])) sys.stdout.write(' SubIter DeltaF DeltaX ResLog10\n') #calculate dt dt = Time[iStep+1] - Time[iStep] # Set dt for aero force calcs. VMOPTS.DelTime = ct.c_double(dt) #Predictor step Q += dt*dQdt + (0.5-beta)*dQddt*np.power(dt,2.0) dQdt += (1.0-gamma)*dQddt*dt dQddt[:] = 0.0 # Quaternion update for orientation. Quat = dQdt[NumDof.value+6:].copy('F') Quat = Quat/np.linalg.norm(Quat) Cao = xbl.Rot(Quat) #nodal diplacements and velocities from state vector X=Q[:NumDof.value].copy('F') dXdt=dQdt[:NumDof.value].copy('F'); BeamLib.Cbeam3_Solv_State2Disp(XBINPUT,NumNodes_tot,XBELEM,XBNODE, PosIni,PsiIni,NumDof,X,dXdt, PosDefor,PsiDefor,PosDotDef,PsiDotDef) # Force at current time-step. TODO: Check communication flow. if iStep > 0 and AELAOPTS.Tight == False: # zero aero forces. AeroForces[:,:,:] = 0.0 # Update CRV. PsiA_G = xbl.quat2psi(Quat) # CRV at iStep # Update origin. currVrel=Vrel[iStep-1,:].copy('F') OriginA_G[:] = OriginA_G[:] + currVrel[:3]*dt # Update control surface deflection. if VMINPUT.ctrlSurf != None: VMINPUT.ctrlSurf.update(Time[iStep]) # Generate surface grid. currVrel=Vrel[iStep,:].copy('F') CoincidentGrid(PosDefor, PsiDefor, Section, currVrel[:3], currVrel[3:], PosDotDef, PsiDotDef, XBINPUT, Zeta, ZetaDot, OriginA_G, PsiA_G, VMINPUT.ctrlSurf) # Update wake geom #'roll' data. ZetaStar = np.roll(ZetaStar,1,axis = 0) GammaStar = np.roll(GammaStar,1,axis = 0) #overwrite grid points with TE. ZetaStar[0,:] = Zeta[VMOPTS.M.value,:] # overwrite Gamma with TE value from previous timestep. GammaStar[0,:] = Gamma[VMOPTS.M.value-1,:] # Apply gust velocity. if VMINPUT.gust != None: Utot = Ufree + VMINPUT.gust.Vels(Zeta) else: Utot = Ufree # Solve for AeroForces UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Utot, ZetaStar, VMOPTS, AeroForces, Gamma, GammaStar, AIC, BIC) # Apply density scaling AeroForces[:,:,:] = AELAOPTS.AirDensity*AeroForces[:,:,:] if Settings.PlotTec==True: PostProcess.WriteUVLMtoTec(FileObject, Zeta, ZetaStar, Gamma, GammaStar, TimeStep = iStep, NumTimeSteps = XBOPTS.NumLoadSteps.value, Time = Time[iStep], Text = True) # map AeroForces to beam. CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, Force) # Add gravity loads. AddGravityLoads(Force, XBINPUT, XBELEM, AELAOPTS, PsiDefor, VMINPUT.c) # Add thrust and other point loads Force += (XBINPUT.ForceStatic + XBINPUT.ForceDyn*ForceTime[iStep+1]).copy('F') #END if iStep > 0 #Reset convergence parameters Iter = 0 ResLog10 = 1.0 #Newton-Raphson loop while ( (ResLog10 > XBOPTS.MinDelta.value) \ & (Iter < XBOPTS.MaxIterations.value) ): #set tensors to zero MssFull[:,:] = 0.0; CssFull[:,:] = 0.0 KssFull[:,:] = 0.0; FstrucFull[:,:] = 0.0 Msr[:,:] = 0.0; Csr[:,:] = 0.0 Qstruc[:] = 0.0 MrsFull[:,:] = 0.0; CrsFull[:,:] = 0.0 KrsFull[:,:] = 0.0; FrigidFull[:,:] = 0.0 Mrr[:,:] = 0.0; Crr[:,:] = 0.0 Qrigid[:] = 0.0 Msys[:,:] = 0.0; Csys[:,:] = 0.0 Ksys[:,:] = 0.0; Asys[:,:] = 0.0; Qsys[:] = 0.0 # Update counter. Iter += 1 if XBOPTS.PrintInfo.value==True: sys.stdout.write(' %-7d ' %(Iter)) #nodal diplacements and velocities from state vector X=Q[:NumDof.value].copy('F') dXdt=dQdt[:NumDof.value].copy('F'); BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt, PosDefor, PsiDefor, PosDotDef, PsiDotDef) #rigid-body velocities and orientation from state vector Vrel[iStep+1,:] = dQdt[NumDof.value:NumDof.value+6].copy('F') VrelDot[iStep+1,:] = dQddt[NumDof.value:NumDof.value+6].copy('F') Quat = dQdt[NumDof.value+6:].copy('F') Quat = Quat/np.linalg.norm(Quat) Cao = xbl.Rot(Quat) #Update matrices and loads for structural dynamic analysis tmpVrel=Vrel[iStep+1,:].copy('F') tmpQuat=Quat.copy('F') BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, tmpVrel, 0*tmpVrel,\ NumDof, Settings.DimMat,\ ms, MssFull, Msr,\ cs, CssFull, Csr,\ ks, KssFull, fs, FstrucFull,\ Qstruc, XBOPTS, Cao) BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) #Update matrices for rigid-body dynamic analysis BeamLib.Xbeam_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ tmpVrel, 0*tmpVrel, tmpQuat,\ NumDof, Settings.DimMat,\ mr, MrsFull, Mrr,\ cr, CrsFull, Crr, Cqr, Cqq,\ kr, KrsFull, fs, FrigidFull,\ Qrigid, XBOPTS, Cao) BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(ct.c_int(NumDof.value+6)),\ Force_All.ctypes.data_as(ct.POINTER(ct.c_double)) ) #Residual at first iteration if(Iter == 1): Res0_Qglobal = max(max(abs(Qsys)),1) Res0_DeltaX = max(max(abs(DQ)),1) #Assemble discrete system matrices with linearised quaternion equations Msys[:NumDof.value,:NumDof.value] = MssFull.copy('F') Msys[:NumDof.value,NumDof.value:NumDof.value+6] = Msr.copy('F') Msys[NumDof.value:NumDof.value+6,:NumDof.value] = MrsFull.copy('F') Msys[NumDof.value:NumDof.value+6,NumDof.value:NumDof.value+6] = Mrr.copy('F') Msys[NumDof.value+6:,NumDof.value+6:] = Unit4.copy('F') Csys[:NumDof.value,:NumDof.value] = CssFull.copy('F') Csys[:NumDof.value,NumDof.value:NumDof.value+6] = Csr.copy('F') Csys[NumDof.value:NumDof.value+6,:NumDof.value] = CrsFull.copy('F') Csys[NumDof.value:NumDof.value+6,NumDof.value:NumDof.value+6] = Crr.copy('F') Csys[NumDof.value+6:,NumDof.value:NumDof.value+6] = Cqr.copy('F') Csys[NumDof.value+6:,NumDof.value+6:] = Cqq.copy('F') Ksys[:NumDof.value,:NumDof.value] = KssFull.copy('F') Ksys[NumDof.value:NumDof.value+6,:NumDof.value] = KrsFull.copy('F') # #Separate assembly of follower and dead loads # tmpForceTime=ForceTime[iStep+1].copy('F') # tmpQforces,Dummy,tmpQrigid = xbl.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ # PosIni, PsiIni, PosDefor, PsiDefor, \ # (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ # (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ # Cao,1) # # Qstruc -= tmpQforces # Qrigid -= tmpQrigid #Compute residual to solve update vector Qstruc += -np.dot(FstrucFull, Force_Dof) Qrigid += -np.dot(FrigidFull, Force_All) Qsys[:NumDof.value] = Qstruc Qsys[NumDof.value:NumDof.value+6] = Qrigid Qsys[NumDof.value+6:] = np.dot(Cqq,dQdt[NumDof.value+6:]) Qsys += np.dot(Msys,dQddt) #Calculate system matrix for update calculation Asys = Ksys + \ Csys*gamma/(beta*dt) + \ Msys/(beta*dt**2) #Compute correction DQ[:] = np.dot(np.linalg.inv(Asys), -Qsys) Q += DQ dQdt += DQ*gamma/(beta*dt) dQddt += DQ/(beta*dt**2) #Update convergence criteria if XBOPTS.PrintInfo.value==True: sys.stdout.write('%-10.4e ' %(max(abs(Qsys)))) Res_Qglobal = max(abs(Qsys)) Res_DeltaX = max(abs(DQ)) ResLog10 = max(Res_Qglobal/Res0_Qglobal,Res_DeltaX/Res0_DeltaX) if XBOPTS.PrintInfo.value==True: sys.stdout.write('%-10.4e %8.4f\n' %(max(abs(DQ)),ResLog10)) # END Netwon-Raphson. #update to converged nodal displacements and velocities X=Q[:NumDof.value].copy('F') dXdt=dQdt[:NumDof.value].copy('F'); BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef) PosPsiTime[iStep+1,:3] = PosDefor[-1,:] PosPsiTime[iStep+1,3:] = PsiDefor[-1,XBELEM.NumNodes[-1]-1,:] #Position of all grid points in global FoR i1 = (iStep+1)*NumNodes_tot.value i2 = (iStep+2)*NumNodes_tot.value DynOut[i1:i2,:] = PosDefor #Export rigid-body velocities/accelerations if XBOPTS.OutInaframe.value==True: Vrel[iStep,:] = dQdt[NumDof.value:NumDof.value+6].copy('F') VrelDot[iStep,:] = dQddt[NumDof.value:NumDof.value+6].copy('F') else: Quat = dQdt[NumDof.value+6:].copy('F') Quat = Quat/np.linalg.norm(Quat) Cao = xbl.Rot(Quat) ACoa[:3,:3] = np.transpose(Cao) ACoa[3:,3:] = np.transpose(Cao) Vrel[iStep,:] = np.dot(ACoa,dQdt[NumDof.value:NumDof.value+6].copy('F')) VrelDot[iStep,:] = np.dot(ACoa,dQddt[NumDof.value:NumDof.value+6].copy('F')) # Write selected outputs # tidy this away using function. if 'writeDict' in kwords and Settings.WriteOut == True: locForces = None # Stops recalculation of forces fp.write("{:<14,e}".format(Time[iStep+1])) for myStr in writeDict.keys(): if re.search(r'^R_.',myStr): if re.search(r'^R_._.', myStr): index = int(myStr[4]) elif re.search(r'root', myStr): index = 0 elif re.search(r'tip', myStr): index = -1 else: raise IOError("Node index not recognised.") if myStr[2] == 'x': component = 0 elif myStr[2] == 'y': component = 1 elif myStr[2] == 'z': component = 2 else: raise IOError("Displacement component not recognised.") fp.write("{:<14,e}".format(PosDefor[index,component])) elif re.search(r'^M_.',myStr): if re.search(r'^M_._.', myStr): index = int(myStr[4]) elif re.search(r'root', myStr): index = 0 elif re.search(r'tip', myStr): index = -1 else: raise IOError("Node index not recognised.") if myStr[2] == 'x': component = 0 elif myStr[2] == 'y': component = 1 elif myStr[2] == 'z': component = 2 else: raise IOError("Moment component not recognised.") if locForces == None: locForces = BeamIO.localElasticForces(PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, [index]) fp.write("{:<14,e}".format(locForces[0,3+component])) else: raise IOError("writeDict key not recognised.") # END for myStr fp.write("\n") fp.flush() # 'Rollup' due to external velocities. TODO: Must add gusts here! ZetaStar[:,:] = ZetaStar[:,:] + VMINPUT.U_infty*dt # END Time loop # Write _dyn file. ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_dyn.dat' fp = open(ofile,'w') BeamIO.Write_dyn_File(fp, Time, PosPsiTime) fp.close() #Write _shape file ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_shape.dat' fp = open(ofile,'w') BeamIO.Write_shape_File(fp, len(Time), NumNodes_tot.value, Time, DynOut) fp.close() #Write rigid-body velocities ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_rigid.dat' fp = open(ofile,'w') BeamIO.Write_rigid_File(fp, Time, Vrel, VrelDot) fp.close() # Close output file if it exists. if 'writeDict' in kwords and Settings.WriteOut == True: fp.close() # Close Tecplot ascii FileObject. if Settings.PlotTec==True: PostProcess.CloseAeroTecFile(FileObject) if XBOPTS.PrintInfo.value==True: sys.stdout.write(' ... done\n') # For interactive analysis at end of simulation set breakpoint. pass
def Solve_Py(XBINPUT, XBOPTS): """Nonlinear dynamic structural solver in Python. Assembly of matrices is carried out with Fortran subroutines.""" #Check correct solution code assert XBOPTS.Solution.value == 912, ('NonlinearDynamic requested' +\ ' with wrong solution code') #Initialise beam XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \ = BeamInit.Static(XBINPUT,XBOPTS) #Solve static XBOPTS.Solution.value = 112 PosDefor, PsiDefor = Solve_Py_Static(XBINPUT, XBOPTS) XBOPTS.Solution.value = 912 #Write deformed configuration to file ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_def.dat' if XBOPTS.PrintInfo == True: sys.stdout.write('Writing file %s ... ' % (ofile)) fp = open(ofile, 'w') fp.write('TITLE="Non-linear static solution: deformed geometry"\n') fp.write('VARIABLES="iElem" "iNode" "Px" "Py" "Pz" "Rx" "Ry" "Rz"\n') fp.close() if XBOPTS.PrintInfo == True: sys.stdout.write('done\n') WriteMode = 'a' BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \ PosDefor, PsiDefor, ofile, WriteMode) #Initialise variables for dynamic analysis Time, NumSteps, ForceTime, Vrel, VrelDot,\ PosDotDef, PsiDotDef,\ OutGrids, PosPsiTime, VelocTime, DynOut\ = BeamInit.Dynamic(XBINPUT,XBOPTS) # Delete unused variables. del OutGrids, VelocTime #Write _force file ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_force.dat' fp = open(ofile, 'w') BeamIO.Write_force_File(fp, Time, ForceTime, Vrel, VrelDot) fp.close() if XBOPTS.PrintInfo.value == True: sys.stdout.write('Solve nonlinear dynamic case in Python ... \n') #Initialise structural system tensors MssFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') CssFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') KssFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') FstrucFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, 'F') ms = ct.c_int() cs = ct.c_int() ks = ct.c_int() fs = ct.c_int() Msr = np.zeros((NumDof.value, 6), ct.c_double, 'F') Csr = np.zeros((NumDof.value, 6), ct.c_double, 'F') X = np.zeros(NumDof.value, ct.c_double, 'F') dXdt = np.zeros(NumDof.value, ct.c_double, 'F') Force_Dof = np.zeros(NumDof.value, ct.c_double, 'F') Qstruc = np.zeros(NumDof.value, ct.c_double, 'F') #Initialise rigid-body system tensors MrsFull = np.zeros((6, NumDof.value), ct.c_double, 'F') CrsFull = np.zeros((6, NumDof.value), ct.c_double, 'F') KrsFull = np.zeros((6, NumDof.value), ct.c_double, 'F') FrigidFull = np.zeros((6, NumDof.value + 6), ct.c_double, 'F') mr = ct.c_int() cr = ct.c_int() kr = ct.c_int() fr = ct.c_int() Mrr = np.zeros((6, 6), ct.c_double, 'F') Crr = np.zeros((6, 6), ct.c_double, 'F') Qrigid = np.zeros(6, ct.c_double, 'F') #Initialise full system tensors Q = np.zeros(NumDof.value + 6 + 4, ct.c_double, 'F') DQ = np.zeros(NumDof.value + 6 + 4, ct.c_double, 'F') dQdt = np.zeros(NumDof.value + 6 + 4, ct.c_double, 'F') dQddt = np.zeros(NumDof.value + 6 + 4, ct.c_double, 'F') Force_All = np.zeros(NumDof.value + 6, ct.c_double, 'F') Msys = np.zeros((NumDof.value + 6 + 4, NumDof.value + 6 + 4), ct.c_double, 'F') Csys = np.zeros((NumDof.value + 6 + 4, NumDof.value + 6 + 4), ct.c_double, 'F') Ksys = np.zeros((NumDof.value + 6 + 4, NumDof.value + 6 + 4), ct.c_double, 'F') Asys = np.zeros((NumDof.value + 6 + 4, NumDof.value + 6 + 4), ct.c_double, 'F') Qsys = np.zeros(NumDof.value + 6 + 4, ct.c_double, 'F') #Initialise rotation operators Quat = np.zeros(4, ct.c_double, 'F') Quat[0] = 1.0 Cao = XbeamLib.Rot(Quat) ACoa = np.zeros((6, 6), ct.c_double, 'F') Cqr = np.zeros((4, 6), ct.c_double, 'F') Cqq = np.zeros((4, 4), ct.c_double, 'F') Unit4 = np.zeros((4, 4), ct.c_double, 'F') for i in range(4): Unit4[i, i] = 1.0 #Extract initial displacements and velocities BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef, X, dXdt) #Initialise accelerations as zero arrays PosDotDotDef = np.zeros((NumNodes_tot.value, 3), ct.c_double, 'F') PsiDotDotDef = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),\ ct.c_double, 'F') #Populate state vector Q[:NumDof.value] = X.copy('F') dQdt[:NumDof.value] = dXdt.copy('F') dQdt[NumDof.value:NumDof.value + 6] = Vrel[0, :].copy('F') dQdt[NumDof.value + 6:] = Quat.copy('F') #Force at the first time-step Force = (XBINPUT.ForceStatic + XBINPUT.ForceDyn * ForceTime[0]).copy('F') #Assemble matrices and loads for structural dynamic analysis tmpVrel = Vrel[0, :].copy('F') tmpQuat = Quat.copy('F') BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, tmpVrel, 0*tmpVrel,\ NumDof, Settings.DimMat,\ ms, MssFull, Msr,\ cs, CssFull, Csr,\ ks, KssFull, fs, FstrucFull,\ Qstruc, XBOPTS, Cao) BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) Qstruc -= np.dot(FstrucFull, Force_Dof) #Assemble matrices for rigid-body dynamic analysis BeamLib.Xbeam_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ tmpVrel, 0*tmpVrel, tmpQuat,\ NumDof, Settings.DimMat,\ mr, MrsFull, Mrr,\ cr, CrsFull, Crr, Cqr, Cqq,\ kr, KrsFull, fr, FrigidFull,\ Qrigid, XBOPTS, Cao) BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(ct.c_int(NumDof.value+6)),\ Force_All.ctypes.data_as(ct.POINTER(ct.c_double)) ) Qrigid -= np.dot(FrigidFull, Force_All) #Separate assembly of follower and dead loads tmpForceTime = ForceTime[0].copy('F') tmpQforces,Dummy,tmpQrigid = XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ PosIni, PsiIni, PosDefor, PsiDefor, \ (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ Cao,1) Qstruc -= tmpQforces Qrigid -= tmpQrigid #Assemble system matrices Msys[:NumDof.value, :NumDof.value] = MssFull.copy('F') Msys[:NumDof.value, NumDof.value:NumDof.value + 6] = Msr.copy('F') Msys[NumDof.value:NumDof.value + 6, :NumDof.value] = MrsFull.copy('F') Msys[NumDof.value:NumDof.value + 6, NumDof.value:NumDof.value + 6] = Mrr.copy('F') Msys[NumDof.value + 6:, NumDof.value + 6:] = Unit4.copy('F') Qsys[:NumDof.value] = Qstruc Qsys[NumDof.value:NumDof.value + 6] = Qrigid Qsys[NumDof.value + 6:] = np.dot(Cqq, dQdt[NumDof.value + 6:]) #Initial Accel dQddt[:] = np.dot(np.linalg.inv(Msys), -Qsys) #Record position of all grid points in global FoR at initial time step DynOut[0:NumNodes_tot.value, :] = PosDefor #Position/rotation of the selected node in initial deformed configuration PosPsiTime[0, :3] = PosDefor[-1, :] PosPsiTime[0, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] #Get gamma and beta for Newmark scheme gamma = 0.5 + XBOPTS.NewmarkDamp.value beta = 0.25 * (gamma + 0.5)**2 #Time loop for iStep in range(NumSteps.value): if XBOPTS.PrintInfo.value == True: sys.stdout.write(' Time: %-10.4e\n' % (Time[iStep + 1])) sys.stdout.write(' SubIter DeltaF DeltaX ResLog10\n') #calculate dt dt = Time[iStep + 1] - Time[iStep] #Predictor step Q += dt * dQdt + (0.5 - beta) * dQddt * dt**2 dQdt += (1.0 - gamma) * dQddt * dt dQddt[:] = 0.0 #Force at current time-step Force = (XBINPUT.ForceStatic + \ XBINPUT.ForceDyn*ForceTime[iStep+1]).copy('F') #Reset convergence parameters Iter = 0 ResLog10 = 1.0 #Newton-Raphson loop while ( (ResLog10 > XBOPTS.MinDelta.value) \ & (Iter < XBOPTS.MaxIterations.value) ): #set tensors to zero MssFull[:, :] = 0.0 CssFull[:, :] = 0.0 KssFull[:, :] = 0.0 FstrucFull[:, :] = 0.0 Msr[:, :] = 0.0 Csr[:, :] = 0.0 Qstruc[:] = 0.0 MrsFull[:, :] = 0.0 CrsFull[:, :] = 0.0 KrsFull[:, :] = 0.0 FrigidFull[:, :] = 0.0 Mrr[:, :] = 0.0 Crr[:, :] = 0.0 Qrigid[:] = 0.0 Msys[:, :] = 0.0 Csys[:, :] = 0.0 Ksys[:, :] = 0.0 Asys[:, :] = 0.0 Qsys[:] = 0.0 #Update counter Iter += 1 if XBOPTS.PrintInfo.value == True: sys.stdout.write(' %-7d ' % (Iter)) #nodal diplacements and velocities from state vector X = Q[:NumDof.value].copy('F') dXdt = dQdt[:NumDof.value].copy('F') BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef) #rigid-body velocities and orientation from state vector Vrel[iStep + 1, :] = dQdt[NumDof.value:NumDof.value + 6].copy('F') VrelDot[iStep + 1, :] = dQddt[NumDof.value:NumDof.value + 6].copy('F') Quat = dQdt[NumDof.value + 6:].copy('F') Quat = Quat / np.linalg.norm(Quat) Cao = XbeamLib.Rot(Quat) #Assemble matrices and loads for structural dynamic analysis tmpVrel = Vrel[iStep + 1, :].copy('F') tmpQuat = Quat.copy('F') BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ Force, tmpVrel, 0*tmpVrel,\ NumDof, Settings.DimMat,\ ms, MssFull, Msr,\ cs, CssFull, Csr,\ ks, KssFull, fs, FstrucFull,\ Qstruc, XBOPTS, Cao) BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(NumDof),\ Force_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\ XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) ) #Assemble matrices for rigid-body dynamic analysis BeamLib.Xbeam_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\ PosIni, PsiIni, PosDefor, PsiDefor,\ PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,\ tmpVrel, 0*tmpVrel, tmpQuat,\ NumDof, Settings.DimMat,\ mr, MrsFull, Mrr,\ cr, CrsFull, Crr, Cqr, Cqq,\ kr, KrsFull, fs, FrigidFull,\ Qrigid, XBOPTS, Cao) BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\ ct.byref(ct.c_int(6)),\ Force.ctypes.data_as(ct.POINTER(ct.c_double)),\ ct.byref(ct.c_int(NumDof.value+6)),\ Force_All.ctypes.data_as(ct.POINTER(ct.c_double)) ) #Residual at first iteration if (Iter == 1): Res0_Qglobal = max(max(abs(Qsys)), 1) Res0_DeltaX = max(max(abs(DQ)), 1) #Assemble discrete system matrices with linearised quaternion equations Msys[:NumDof.value, :NumDof.value] = MssFull.copy('F') Msys[:NumDof.value, NumDof.value:NumDof.value + 6] = Msr.copy('F') Msys[NumDof.value:NumDof.value + 6, :NumDof.value] = MrsFull.copy('F') Msys[NumDof.value:NumDof.value + 6, NumDof.value:NumDof.value + 6] = Mrr.copy('F') Msys[NumDof.value + 6:, NumDof.value + 6:] = Unit4.copy('F') Csys[:NumDof.value, :NumDof.value] = CssFull.copy('F') Csys[:NumDof.value, NumDof.value:NumDof.value + 6] = Csr.copy('F') Csys[NumDof.value:NumDof.value + 6, :NumDof.value] = CrsFull.copy('F') Csys[NumDof.value:NumDof.value + 6, NumDof.value:NumDof.value + 6] = Crr.copy('F') Csys[NumDof.value + 6:, NumDof.value:NumDof.value + 6] = Cqr.copy('F') Csys[NumDof.value + 6:, NumDof.value + 6:] = Cqq.copy('F') Ksys[:NumDof.value, :NumDof.value] = KssFull.copy('F') Ksys[NumDof.value:NumDof.value + 6, :NumDof.value] = KrsFull.copy('F') #Separate assembly of follower and dead loads tmpForceTime = ForceTime[iStep + 1].copy('F') tmpQforces,Dummy,tmpQrigid = XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \ PosIni, PsiIni, PosDefor, PsiDefor, \ (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \ (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \ Cao,1) Qstruc -= tmpQforces Qrigid -= tmpQrigid #Compute residual Qstruc += -np.dot(FstrucFull, Force_Dof) Qrigid += -np.dot(FrigidFull, Force_All) Qsys[:NumDof.value] = Qstruc Qsys[NumDof.value:NumDof.value + 6] = Qrigid Qsys[NumDof.value + 6:] = np.dot(Cqq, dQdt[NumDof.value + 6:]) Qsys += np.dot(Msys, dQddt) #Calculate system matrix for update calculation Asys = Ksys + \ Csys*gamma/(beta*dt) + \ Msys/(beta*dt**2) #Compute correction DQ[:] = np.dot(np.linalg.inv(Asys), -Qsys) Q += DQ dQdt += DQ * gamma / (beta * dt) dQddt += DQ / (beta * dt**2) #Update convergence criteria if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e ' % (max(abs(Qsys)))) Res_Qglobal = max(abs(Qsys)) Res_DeltaX = max(abs(DQ)) ResLog10 = max(Res_Qglobal / Res0_Qglobal, Res_DeltaX / Res0_DeltaX) if XBOPTS.PrintInfo.value == True: sys.stdout.write('%-10.4e %8.4f\n' % (max(abs(DQ)), ResLog10)) #END Netwon-Raphson #update to converged nodal displacements and velocities X = Q[:NumDof.value].copy('F') dXdt = dQdt[:NumDof.value].copy('F') BeamLib.Cbeam3_Solv_State2Disp(XBINPUT, NumNodes_tot, XBELEM, XBNODE, PosIni, PsiIni, NumDof, X, dXdt,\ PosDefor, PsiDefor, PosDotDef, PsiDotDef) PosPsiTime[iStep + 1, :3] = PosDefor[(NumNodes_tot.value - 1) / 2 + 1, :] PosPsiTime[iStep + 1, 3:] = PsiDefor[-1, XBELEM.NumNodes[-1] - 1, :] #Position of all grid points in global FoR i1 = (iStep + 1) * NumNodes_tot.value i2 = (iStep + 2) * NumNodes_tot.value DynOut[i1:i2, :] = PosDefor #Export rigid-body velocities/accelerations if XBOPTS.OutInaframe.value == True: Vrel[iStep + 1, :] = dQdt[NumDof.value:NumDof.value + 6].copy('F') VrelDot[iStep + 1, :] = dQddt[NumDof.value:NumDof.value + 6].copy('F') else: Quat = dQdt[NumDof.value + 6:].copy('F') Quat = Quat / np.linalg.norm(Quat) Cao = XbeamLib.Rot(Quat) ACoa[:3, :3] = np.transpose(Cao) ACoa[3:, 3:] = np.transpose(Cao) Vrel[iStep + 1, :] = np.dot( ACoa, dQdt[NumDof.value:NumDof.value + 6].copy('F')) VrelDot[iStep + 1, :] = np.dot( ACoa, dQddt[NumDof.value:NumDof.value + 6].copy('F')) #END Time loop #Write _dyn file ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_dyn.dat' fp = open(ofile, 'w') BeamIO.Write_dyn_File(fp, Time, PosPsiTime) fp.close() #Write _shape file ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_shape.dat' fp = open(ofile, 'w') BeamIO.Write_shape_File(fp, len(Time), NumNodes_tot.value, Time, DynOut) fp.close() #Write rigid-body velocities ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_rigid.dat' fp = open(ofile, 'w') BeamIO.Write_rigid_File(fp, Time, Vrel, VrelDot) fp.close() if XBOPTS.PrintInfo.value == True: sys.stdout.write(' ... done\n')