Beispiel #1
0
def Solve_F90(XBINPUT,XBOPTS):
    """@brief Nonlinear static structural solver using f90 solve routine."""
    
    "Check correct solution code"
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic (F90) requested' +\
                                              ' with wrong solution code')
    
    "Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
    
    
    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')
    
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case (using .f90 routines) ... \n')
    
    BeamLib.Cbeam3_Solv_NonlinearStatic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
                                PosIni, PsiIni, XBNODE, NumDof,\
                                PosDefor, PsiDefor)
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')
    
    
    "Write deformed configuration to file"
    ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
    if XBOPTS.PrintInfo.value==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.value==True:
        sys.stdout.write('done\n')
    WriteMode = 'a'
    
    BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \
                       PosDefor, PsiDefor, ofile, WriteMode)
    
    "Print deformed configuration"
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('--------------------------------------\n')
        sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
        sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(' ')
            for inodj in range(3):
                sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
            sys.stdout.write('\n')
        sys.stdout.write('--------------------------------------\n')
        
    
    "Return solution as optional output argument"
    return PosDefor, PsiDefor
Beispiel #2
0
def LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \
                 PosIni, PsiIni, PosDefor, PsiDefor, \
                 Force_foll, Force_dead, CAG, iFactor):
    """@brief Assemble separate follower and dead loads.
    @param XBINPUT Xbeam inputs.
    @param XBELEM Xbeam element information.
    @param XBNODE Xbeam nodal information.
    @param XBOPTS Xbeam simulation options.
    @param NumDof numbers of DoF in the problem.
    @param PosInI nodal position at reference configuration.
    @param PsiIni nodal CRV at reference configuration.
    @param PosDefor nodal position at reference configuration.
    @param PsiDefor nodal CRV at reference configuration.
    @param Force_foll Matrix of applied nodal follower forces.
    @param Force_dead Matrix of applied nodal dead forces.
    @param CAG transformation matrix from inertial (G) to body-fixed frame (A).
    @param iFactor factor to account for optional load stepping.
    """

    # Initialise function variables
    NumNodes_tot = ct.c_int((XBINPUT.NumNodesElem - 1) * XBINPUT.NumElems + 1)

    KglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F')
    ksf = ct.c_int()

    FglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F')
    fsf = ct.c_int()

    FglobalFull_dead = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F')
    fsd = ct.c_int()

    Force_foll_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    Force_dead_Dof = np.zeros(NumDof.value, ct.c_double, 'F')

    # Assemble separate force coefficient matrices
    BeamLib.Cbeam3_Asbly_Fglobal(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                        PosIni, PsiIni, PosDefor, PsiDefor,\
                        Force_foll*iFactor,NumDof,\
                        ksf, KglobalFull_foll, fsf, FglobalFull_foll,\
                        fsd, FglobalFull_dead, CAG)

    # Get follower and dead forces on constrained nodes
    BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                      ct.byref(ct.c_int(6)),\
                      Force_foll.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      ct.byref(NumDof),\
                      Force_foll_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
    BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                      ct.byref(ct.c_int(6)),\
                      Force_dead.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      ct.byref(NumDof),\
                      Force_dead_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )

    # Project follower and dead forces
    Qforces = np.dot(FglobalFull_foll, Force_foll_Dof * iFactor) + np.dot(
        FglobalFull_dead, Force_dead_Dof * iFactor)

    #--------------------------------------
    # Repeat in case of rigid-body dynamics
    if XBOPTS.Solution.value == 912:
        FrigidFull_foll = np.zeros((6, NumDof.value + 6), ct.c_double, 'F')
        frf = ct.c_int()
        FrigidFull_dead = np.zeros((6, NumDof.value + 6), ct.c_double, 'F')
        frd = ct.c_int()

        Force_foll_All = np.zeros(NumDof.value + 6, ct.c_double, 'F')
        Force_dead_All = np.zeros(NumDof.value + 6, ct.c_double, 'F')

        # Assemble separate force coefficient matrices
        BeamLib.Xbeam_Asbly_Frigid(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                                     PosIni, PsiIni, PosDefor, PsiDefor,\
                                     NumDof, frf, FrigidFull_foll,\
                                     frd, FrigidFull_dead, CAG)

        # Get follower and dead forces on unconstrained nodes
        BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\
                                   ct.byref(ct.c_int(6)),\
                                   Force_foll.ctypes.data_as(ct.POINTER(ct.c_double)),\
                                   ct.byref(ct.c_int(NumDof.value+6)),\
                                   Force_foll_All.ctypes.data_as(ct.POINTER(ct.c_double)) )
        BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\
                                   ct.byref(ct.c_int(6)),\
                                   Force_dead.ctypes.data_as(ct.POINTER(ct.c_double)),\
                                   ct.byref(ct.c_int(NumDof.value+6)),\
                                   Force_dead_All.ctypes.data_as(ct.POINTER(ct.c_double)) )

        # Project follower and dead forces
        Qrigid = np.dot(FrigidFull_foll, Force_foll_All * iFactor) + np.dot(
            FrigidFull_dead, Force_dead_All * iFactor)

    else:
        Qrigid = 0.0

    #------------------
    # Return everything
    return Qforces, KglobalFull_foll, Qrigid
Beispiel #3
0
def Solve_F90(XBINPUT, XBOPTS):
    """@brief Nonlinear dynamic structural solver using f90 solve routine."""

    "Check correct solution code"
    assert XBOPTS.Solution.value == 312, ('NonlinearDynamic (F90) requested' +\
                                              ' with wrong solution code')

    "Initialise variables for static analysis"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT, XBOPTS)

    "Change solution code to NonlinearStatic"
    XBOPTS.Solution.value = 112

    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')

    "Solve static"

    BeamLib.Cbeam3_Solv_NonlinearStatic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
                            PosIni, PsiIni, XBNODE, NumDof,\
                            PosDefor, PsiDefor)

    "Change solution code back to NonlinearDynamic"
    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)

    "Change solution code to NonlinearDynamic"
    XBOPTS.Solution.value = 312

    "Initialise variables for dynamic analysis"
    Time, NumSteps, ForceTime, ForcedVel, ForcedVelDot,\
    PosDotDef, PsiDotDef,\
    OutGrids, PosPsiTime, VelocTime, DynOut\
        = BeamInit.Dynamic(XBINPUT,XBOPTS)

    "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

    "Solve dynamic using f90 solver"
    BeamLib.Cbeam3_Solv_NonlinearDynamic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
            PosIni, PsiIni, XBNODE, NumDof,\
            PosDefor, PsiDefor,\
            NumSteps, Time, ForceTime, ForcedVel, ForcedVelDot,\
            PosDotDef, PsiDotDef,\
            PosPsiTime, VelocTime, DynOut, OutGrids)

    "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()
Beispiel #4
0
def Solve_F90_steps(XBINPUT, XBOPTS):
    """@brief Nonlinear dynamic structural solver with time-steps controlled
    in Python but solved using F90 routines at each time-step."""

    "Check correct solution code"
    assert XBOPTS.Solution.value == 312, ('NonlinearDynamic (F90) requested' +\
                                              ' with wrong solution code')

    "Initialise variables for static analysis"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT, XBOPTS)

    "Change solution code to NonlinearStatic"
    XBOPTS.Solution.value = 112

    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')

    "Solve static"
    BeamLib.Cbeam3_Solv_NonlinearStatic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
                            PosIni, PsiIni, XBNODE, NumDof,\
                            PosDefor, PsiDefor)

    "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)

    "Change solution code to NonlinearDynamic"
    XBOPTS.Solution.value = 312

    "Initialise variables for dynamic analysis"
    Time, NumSteps, ForceTime, ForcedVel, ForcedVelDot,\
    PosDotDef, PsiDotDef,\
    OutGrids, PosPsiTime, VelocTime, DynOut\
        = BeamInit.Dynamic(XBINPUT,XBOPTS)

    "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

    "Save time-dependant variables with only one time step (2 time-steps)"
    TimeElements = Time[0:2].copy('F')
    Ftime = ForceTime[0:2].copy('F')
    Fvel = ForcedVel[0:2, :].copy('F')
    FvelDot = ForcedVelDot[0:2, :].copy('F')

    "Create Output vectors, one for each time-step"
    PosPsiTimeStep = PosPsiTime[0:2, :].copy('F')
    VelocTimeStep = VelocTime[0:2, :].copy('F')
    DynOutStep = DynOut[0:2 * NumNodes_tot.value, :].copy('F')

    "Initialise and approximate initial 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')

    "Start Time-loop"
    for TimeStep in range(0, NumSteps.value):

        "Update 2-step time arrays"
        TimeElements = Time[TimeStep:TimeStep + 2]
        Ftime = ForceTime[TimeStep:TimeStep + 2]
        Fvel[:, :] = ForcedVel[TimeStep:TimeStep + 2, :]
        FvelDot[:, :] = ForcedVelDot[TimeStep:TimeStep + 2, :]

        "Solve dynamic step using f90 solver"
        BeamLib.Cbeam3_Solv_NonlinearDynamicAccel(XBINPUT, XBOPTS, NumNodes_tot,\
                XBELEM,PosIni, PsiIni, XBNODE, NumDof,\
                PosDefor, PsiDefor,\
                ct.c_int(1), TimeElements, Ftime, Fvel, FvelDot,\
                PosDotDef, PsiDotDef,\
                PosDotDotDef, PsiDotDotDef,\
                PosPsiTimeStep, VelocTimeStep, DynOutStep, OutGrids)

        "Append output information"
        PosPsiTime[TimeStep + 1, :] = PosPsiTimeStep[1, :]
        VelocTime[TimeStep + 1, :] = VelocTimeStep[1, :]
        DynOut[(TimeStep)*NumNodes_tot.value:(TimeStep+1)*NumNodes_tot.value-1,\
                : ] = DynOutStep[NumNodes_tot.value:2*NumNodes_tot.value-1]

    "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()
Beispiel #5
0
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 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
Beispiel #7
0
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')
Beispiel #8
0
def Static(XBINPUT,XBOPTS, moduleName = None):
    """@brief Initialise everything needed for Static beam simulation."""

    # Declare variables that are not dependent on NumNodes_tot.
    XBELEM      = DerivedTypes.Xbelem(XBINPUT.NumElems,Settings.MaxElNod)
    NumDof      = ct.c_int()
    PsiIni      = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),
                           dtype=ct.c_double, order='F')
        
    # Load customised inputs
    if moduleName != None:
        Input = __import__(moduleName)
    else:
        Input = __import__('Input')
        
    # Check inputs.
    XBINPUT, XBOPTS = Input.Setup(XBINPUT, XBOPTS)
        
    # Set-up element properties
    NumNodes_tot, XBELEM = Input.Elem(XBINPUT, XBOPTS, XBELEM)
    # Set-up nodal properties.
    PosIni, PhiNodes, BoundConds = \
        Input.Node(XBINPUT, XBOPTS, NumNodes_tot, XBELEM)
    # Compute initial (undeformed) geometry.
    BeamLib.f_xbeam_undef_geom( 
        ct.byref(ct.c_int(XBINPUT.NumElems)), 
        ct.byref(NumNodes_tot), 
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)),
        PosIni.ctypes.data_as(ct.POINTER(ct.c_double)), 
        PhiNodes.ctypes.data_as(ct.POINTER(ct.c_double)), 
        PsiIni.ctypes.data_as(ct.POINTER(ct.c_double)),
        ct.byref(XBOPTS.FollowerForce),
        ct.byref(XBOPTS.FollowerForceRig),
        ct.byref(XBOPTS.PrintInfo),
        ct.byref(XBOPTS.OutInBframe),
        ct.byref(XBOPTS.OutInaframe),
        ct.byref(XBOPTS.ElemProj),
        ct.byref(XBOPTS.MaxIterations),
        ct.byref(XBOPTS.NumLoadSteps),
        ct.byref(XBOPTS.NumGauss),
        ct.byref(XBOPTS.Solution),
        ct.byref(XBOPTS.DeltaCurved),
        ct.byref(XBOPTS.MinDelta),
        ct.byref(XBOPTS.NewmarkDamp) )
    # Write to undeformed geometry to file.
    WriteMode = 'w'
    if XBOPTS.PrintInfo==True:
        sys.stdout.write('Writing file (mode: %s) %s' %(WriteMode,
                         Settings.OutputDir + Settings.OutputFileRoot + 
                         '_SOL' + XBOPTS.Solution.value + '_und.dat\n'))
    
    BeamIO.WriteUndefGeometry(XBINPUT.NumElems,NumNodes_tot.value,XBELEM,
                              PosIni,PsiIni,
                              Settings.OutputDir + Settings.OutputFileRoot +
                              '_INIT' , WriteMode)
    if XBOPTS.PrintInfo==True:
        sys.stdout.write('Identify nodal degrees of freedom ... ')
    # Initialize nodal data derived type.
    XBNODE = DerivedTypes.Xbnode(NumNodes_tot.value)
    BeamLib.f_xbeam_undef_dofs( 
        ct.byref(ct.c_int(XBINPUT.NumElems)), 
        ct.byref(NumNodes_tot), 
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        BoundConds.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Fdof.ctypes.data_as(ct.POINTER(ct.c_int)), 
        ct.byref(NumDof) )
    if XBOPTS.PrintInfo==True:
        sys.stdout.write('done\n')
    
    return XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni,\
            XBNODE, NumDof
Beispiel #9
0
def Solve_F90_steps(XBINPUT,XBOPTS):
    """@brief Nonlinear static structural solver using f90 solve routine called
    once per load-step."""
    
    "Check correct solution code"
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic (F90) requested' +\
                                              ' with wrong solution code')
    
    "Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
    
    
    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')
    
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case (using .f90 routine at' +\
                         ' each load-step) ... \n')
    
    
    
    "Initialise load increments and set F90 load-step to 1"
    LoadSteps = XBOPTS.NumLoadSteps.value
    LoadIncrement = XBINPUT.ForceStatic/LoadSteps
    XBOPTS.NumLoadSteps.value = 1
    
    
    "Start loading loop"
    for step in range(1,LoadSteps+1):
        "Current load to be applied"
        XBINPUT.ForceStatic = step*LoadIncrement
        
        "Print load step"
        if XBOPTS.PrintInfo.value == True:
            print('     Python-based outer load step %d' %(step))
          
        "Solve with one load step"
        BeamLib.Cbeam3_Solv_NonlinearStatic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
                                PosIni, PsiIni, XBNODE, NumDof,\
                                PosDefor, PsiDefor)
        
        
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')
    
     
    "Write deformed configuration to file"
    ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
    if XBOPTS.PrintInfo.value==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.value==True:
        sys.stdout.write('done\n')
    WriteMode = 'a'
    
    BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \
                       PosDefor, PsiDefor, ofile, WriteMode)
    
    
    "Print deformed configuration"
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('--------------------------------------\n')
        sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
        sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(' ')
            for inodj in range(3):
                sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
            sys.stdout.write('\n')
        sys.stdout.write('--------------------------------------\n')
        
    
    "Return solution as optional output argument"
    return PosDefor, PsiDefor
Beispiel #10
0
def Solve_Py(XBINPUT,XBOPTS):
    """Nonlinear static solver using Python to solve residual
    equation. Assembly of matrices is carried out with Fortran subroutines."""
    
    "Check correct solution code"
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic requested' +\
                                              ' with wrong solution code')
    
    "Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
    
    
    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')
    
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case in Python ... \n')

    
    "Initialise structural eqn tensors"
    KglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); ks = ct.c_int()
    FglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); fs = ct.c_int()
    
    DeltaS  = np.zeros(NumDof.value, ct.c_double, 'F')
    Qglobal = np.zeros(NumDof.value, ct.c_double, 'F')
    x       = np.zeros(NumDof.value, ct.c_double, 'F')
    dxdt    = np.zeros(NumDof.value, ct.c_double, 'F')
    
    
    "Load Step tensors"
    iForceStep     = np.zeros((NumNodes_tot.value,6), ct.c_double, 'F')
    iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    
    "Start Load Loop"
    for iLoadStep in range(XBOPTS.NumLoadSteps.value):
        
        "Reset convergence parameters"
        Iter = 0
        ResLog10 = 0.0
        
        iForceStep = XBINPUT.ForceStatic*float( (iLoadStep+1) /                \
                                                XBOPTS.NumLoadSteps.value)
        if XBOPTS.PrintInfo.value == True:
            sys.stdout.write('  iLoad: %-10d\n' %(iLoadStep+1))
            sys.stdout.write('   SubIter DeltaF     DeltaX     ResLog10\n')
            
        "Newton Iteration"
        while( (ResLog10 > np.log10(XBOPTS.MinDelta.value)) \
             & (Iter < XBOPTS.MaxIterations.value) ):
            
            "Increment iteration counter"
            Iter += 1
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('   %-7d ' %(Iter))
                
            
            "Set structural eqn tensors to zero"
            KglobalFull[:,:] = 0.0; ks = ct.c_int()
            FglobalFull[:,:] = 0.0; fs = ct.c_int()
            Qglobal[:] = 0.0
            
            
            "Assemble matrices for static problem"
            BeamLib.Cbeam3_Asbly_Static(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                        PosIni, PsiIni, PosDefor, PsiDefor,\
                        iForceStep, NumDof,\
                        ks, KglobalFull, fs, FglobalFull, Qglobal,\
                        XBOPTS)
            
            
            "Get state vector from current deformation"
            PosDot = np.zeros((NumNodes_tot.value,3), ct.c_double, 'F') #R
            PsiDot = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),\
                               ct.c_double, 'F') #Psi
            
            BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE,\
                          PosDefor, PsiDefor, PosDot, PsiDot,
                          x, dxdt)
            
            
            "Get forces on unconstrained nodes"
            BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                              ct.byref(ct.c_int(6)),\
                              iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              ct.byref(NumDof),\
                              iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
            
            "Calculate \Delta RHS"
            Qglobal = Qglobal - np.dot(FglobalFull, iForceStep_Dof)
            
            
            "Calculate \Delta State Vector"    
            DeltaS = - np.dot(np.linalg.inv(KglobalFull), Qglobal)
                
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('%-10.4e %-10.4e ' \
                                 % (max(abs(Qglobal)),max(abs(DeltaS))))
            
            
            "Update Solution"
            BeamLib.Cbeam3_Solv_Update_Static(XBINPUT, NumNodes_tot, XBELEM,\
                                              XBNODE, NumDof, DeltaS,\
                                              PosIni, PsiIni, PosDefor,PsiDefor)
            
            
            "Residual at first iteration"
            if(Iter == 1):
                Res0_Qglobal = max(abs(Qglobal)) + 1.e-16
                Res0_DeltaX  = max(abs(DeltaS)) + 1.e-16

            "Update residual and compute log10"
            Res_Qglobal = max(abs(Qglobal)) + 1.e-16
            Res_DeltaX  = max(abs(DeltaS)) + 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('%8.4f\n' %(ResLog10))
                
            "Stop the solution"
            if(ResLog10 > 10.):
                sys.stderr.write(' STOP\n')
                sys.stderr.write(' The max residual is %e\n' %(ResLog10))
                exit(1)
            
        "END Newton Loop"
    "END Load Loop"


    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')
    
     
    "Write deformed configuration to file"
    ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
    if XBOPTS.PrintInfo.value==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.value==True:
        sys.stdout.write('done\n')
    WriteMode = 'a'
    
    BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \
                       PosDefor, PsiDefor, ofile, WriteMode)
    
    
    "Print deformed configuration"
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('--------------------------------------\n')
        sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
        sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(' ')
            for inodj in range(3):
                sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
            sys.stdout.write('\n')
        sys.stdout.write('--------------------------------------\n')
        
    
    "Return solution as optional output argument"
    return PosDefor, PsiDefor
def assemble_GEBM_tensors(   iStep,
                             NumDof, Nconstr, NumNodes_tot,
                             XBELEM, XBNODE,
                             XBINPUT, VMINPUT, XBOPTS, 
                             AELAOPTS, VMOPTS,  
                             tmpVrel,     
                             PosIni, PsiIni,          
                             PosDefor, PsiDefor,  
                             PosDotDef, PsiDotDef,
                             PosDotDotDef, PsiDotDotDef,
                             Force, #?
                             MssFull, CssFull, KssFull, FstrucFull,
                             Msr, Csr,
                             Force_Dof, Qstruc,
                             MrsFull, CrsFull, KrsFull, FrigidFull,
                             Mrr, Crr, Qrigid,
                             Q, dQdt, dQddt, 
                             Force_All,
                             Msys, Csys, Ksys, Asys, Qsys ):
    
    
    Nsys = NumDof.value+10+Nconstr
    
    
    # utilities:
    # (dummy variables to store unused output from assembly functions)
    ms, mr = ct.c_int(), ct.c_int()
    cs, cr = ct.c_int(), ct.c_int()
    ks, kr = ct.c_int(), ct.c_int()
    fs, fr = ct.c_int(), ct.c_int()
       
    #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[:,:],Csys[:,:],Ksys[:,:],Asys[:,:],Qsys[:] = 0.0,0.0,0.0,0.0,0.0
     
    #rigid-body velocities and orientation from state vector
    Quat = dQdt[NumDof.value+6:NumDof.value+10].copy('F')
    Quat = Quat/np.linalg.norm(Quat)
    Cao  = xbl.Rot(Quat)                       # input for xbeam assembly
    Cqr = np.zeros((4,6), ct.c_double, 'F')    # dummy: output for xbeam assembly
    Cqq = np.zeros((4,4), ct.c_double, 'F')    # dummy: output for xbeam assembly
    
    #Update matrices and loads for structural dynamic analysis
    tmpQuat=Quat.copy('F')
    BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,     # in
                         PosIni, PsiIni, PosDefor, PsiDefor,                # in
                         PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,  # in
                         Force, tmpVrel, 0*tmpVrel,                         # in
                         NumDof, Settings.DimMat,                           # out
                         ms, MssFull, Msr,                                  # out
                         cs, CssFull, Csr,                                  # out
                         ks, KssFull, fs, FstrucFull,Qstruc,                # out
                         XBOPTS, Cao)                                       # in
    
    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,      # in
                         PosIni, PsiIni, PosDefor, PsiDefor,                # in
                         PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,  # in
                         tmpVrel, 0*tmpVrel, tmpQuat,                       # in
                         NumDof, Settings.DimMat,                           # out
                         mr, MrsFull, Mrr,                                  # out
                         cr, CrsFull, Crr, Cqr, Cqq,                        # out
                         kr, KrsFull, fr, FrigidFull, Qrigid,               # out
                         XBOPTS, Cao)                                       # in

    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)) )

    #Assemble discrete system matrices with linearised quaternion equations  
    Unit4 = np.zeros((4,4), ct.c_double, 'F')
    for i in range(4): Unit4[i,i] = 1.0     
    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+10,NumDof.value+6:NumDof.value+10] = 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+10,NumDof.value:NumDof.value+6] = Cqr.copy('F')
    Csys[NumDof.value+6:NumDof.value+10,NumDof.value+6:NumDof.value+10] = 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[iStep,:,:] ),
                                    (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead[iStep,:,:] ),
                                    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:NumDof.value+10] = np.dot(Cqq,dQdt[NumDof.value+6:NumDof.value+10])

    Qsys += np.dot(Msys,dQddt)
    
    # include damping
    if XBINPUT.str_damping_model == 'prop':
        Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                XBINPUT.str_damping_param['beta']  * KssFull
        Csys[:NumDof.value,:NumDof.value] += Cdamp
        Qsys[:NumDof.value] += np.dot(Cdamp, dQdt[:NumDof.value])


    #---------------------------------------------------------------------------------- special BCs
    
    iiblock=[]
    if XBNODE.Sflag.any():
        
        # find perpendicular vectors to axis of rotation
        XBINPUT.rvers=XBINPUT.rvers/np.linalg.norm(XBINPUT.rvers)
        nvec=0
        for ii in range(3):
            svers = np.zeros((3,))
            if XBINPUT.rvers[ii]<1e-9: 
                svers[ii]=1.0
                break
            #else:
            #    raise NameError('Develop robust way to find perpendicular vectors')
        if np.all(np.abs(svers)<1e-9): raise NameError('Develop robust way to find perpendicular vectors')
        tvers=np.cross(XBINPUT.rvers,svers)
        #print('rvers:'+3*' %.3f'%tuple(XBINPUT.rvers))
        #print('svers:'+3*' %.3f'%tuple(svers))
        #print('tvers:'+3*' %.3f'%tuple(tvers))
        #1/0
        # block translations (redundant:)
        for ii in range(3):
            if XBINPUT.EnforceTraVel_FoRA[ii] == True: iiblock.append(NumDof.value+ii)
        ##### block rotations
        #iirotfree=[] # free rotational dof 
        #for ii in range(3):
        #    if XBINPUT.EnforceAngVel_FoRA[ii] is True: 
        #        iiblock.append(NumDof.value+3+ii)
        #    else: iirotfree.append(NumDof.value+3+ii)
        #iirotfree=[NumDof.value+3+ii for ii in range(3)] # to add damping
    
    
        # augment system
        AugMat = 0.0*Msys # improve efficiency: preallocate this matrix
        B = np.zeros((Nconstr,Nsys-Nconstr))
        
        B[0,NumDof.value+3:NumDof.value+6]=svers
        B[1,NumDof.value+3:NumDof.value+6]=tvers
        BT=B.transpose()
        #BTB=np.dot(BT,B)
        
        AugMat[:Nsys-Nconstr,:Nsys-Nconstr]=XBINPUT.AugLagr_p*np.dot(BT,B)
        AugMat[:Nsys-Nconstr,Nsys-Nconstr:]=XBINPUT.AugLagr_k*BT
        AugMat[Nsys-Nconstr:,:Nsys-Nconstr]=XBINPUT.AugLagr_k*B
        
    # block translational dof
    if len(iiblock)>0:
        Msys[iiblock,:] = 0.0
        Msys[:,iiblock] = 0.0 
        Msys[iiblock,iiblock] = 1.0
        Csys[iiblock,:] = 0.0
        Ksys[iiblock,:] = 0.0
        Qsys[iiblock]   = 0.0
        if XBINPUT.sph_joint_damping is not None:
            iirotfree=[NumDof.value+3+ii for ii in range(3)]
            Csys[iirotfree,iirotfree] += XBINPUT.sph_joint_damping
            Qsys[iirotfree] += XBINPUT.sph_joint_damping*dQdt[iirotfree]
            
    
    
    # Augment Mass Matrix
    Msys += AugMat
    #Csys += AugMat        


    return Msys, Csys, Ksys, Qsys
Beispiel #12
0
def Solve_Py(XBINPUT,XBOPTS,VMOPTS,VMINPUT,AELAOPTS):
        """Nonlinear static solver using Python to solve aeroelastic
        equation. Assembly of structural matrices is carried out with 
        Fortran subroutines. Aerodynamics solved using PyAero.UVLM."""
        
        assert XBOPTS.Solution.value == 112, ('NonlinearStatic requested' +
                                                  ' with wrong solution code')
        
        # Initialize beam.
        XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                    = BeamInit.Static(XBINPUT,XBOPTS)
        # Set initial conditions as undef config.
        PosDefor = PosIni.copy(order='F')
        PsiDefor = PsiIni.copy(order='F')
        if XBOPTS.PrintInfo.value==True:
            sys.stdout.write('Solve nonlinear static case in Python ... \n')
        # Initialise structural eqn tensors.
        KglobalFull = np.zeros((NumDof.value,NumDof.value),
                               ct.c_double, 'F'); ks = ct.c_int()
        FglobalFull = np.zeros((NumDof.value,NumDof.value),
                               ct.c_double, 'F'); fs = ct.c_int()
        DeltaS  = np.zeros(NumDof.value, ct.c_double, 'F')
        Qglobal = np.zeros(NumDof.value, ct.c_double, 'F')
        x       = np.zeros(NumDof.value, ct.c_double, 'F')
        dxdt    = np.zeros(NumDof.value, ct.c_double, 'F')
        # Beam Load Step tensors
        iForceStep     = np.zeros((NumNodes_tot.value,6), ct.c_double, 'F')
        iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
        
        # Initialze Aero.    
        Section = InitSection(VMOPTS,VMINPUT,AELAOPTS.ElasticAxis)
        # Declare memory for Aero grid and velocities.
        Zeta = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C')
        ZetaDot = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C')
        # Additional Aero solver variables.
        AeroForces = np.zeros((VMOPTS.M.value+1,VMOPTS.N.value+1,3),ct.c_double,'C')
        Gamma = np.zeros((VMOPTS.M.value,VMOPTS.N.value),ct.c_double,'C')
        # Init external velocities.
        Uext = InitSteadyExternalVels(VMOPTS,VMINPUT)
        # Create zero triads for motion of reference frame.
        VelA_A = np.zeros((3))
        OmegaA_A = np.zeros((3))
        # Create zero vectors for structural vars not used in static analysis.
        PosDotDef = np.zeros_like(PosDefor, ct.c_double, 'F')
        PsiDotDef = np.zeros_like(PsiDefor, ct.c_double, 'F')      
        
        # Define tecplot stuff.
        FileName = Settings.OutputDir + Settings.OutputFileRoot + 'AeroGrid.dat'
        Variables = ['X', 'Y', 'Z','Gamma']        
        FileObject = PostProcess.WriteAeroTecHeader(FileName, 
                                                    'Default',
                                                    Variables)
        
        # Start Load Loop.
        for iLoadStep in range(XBOPTS.NumLoadSteps.value):
            # Reset convergence parameters and loads.
            Iter = 0
            ResLog10 = 0.0
            XBINPUT.ForceStatic[:,:] = 0.0
            AeroForces[:,:,:] = 0.0
            
            # Calculate aero loads.
            if hasattr(XBINPUT, 'ForcedVel'):
                CoincidentGrid(PosDefor,
                               PsiDefor,
                               Section,
                               XBINPUT.ForcedVel[0,:3],
                               XBINPUT.ForcedVel[0,3:],
                               PosDotDef,
                               PsiDotDef,
                               XBINPUT,
                               Zeta,
                               ZetaDot,
                               ctrlSurf = VMINPUT.ctrlSurf)
            else:
                CoincidentGrid(PosDefor, PsiDefor, Section, VelA_A, 
                               OmegaA_A, PosDotDef, PsiDotDef, XBINPUT,
                               Zeta, ZetaDot,
                               ctrlSurf = VMINPUT.ctrlSurf)
            
            if hasattr(XBINPUT,'ForcedVel'):
                ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta,XBINPUT.ForcedVel[0,:3])
            else:
                ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta)
            
            # Solve for AeroForces.
            UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Uext, ZetaStar, VMOPTS, 
                                   AeroForces, Gamma, GammaStar)
            
            AeroForces[:,:,:] = AELAOPTS.AirDensity*AeroForces[:,:,:]
            
            # Write solution to tecplot file.
            PostProcess.WriteUVLMtoTec(FileObject,
                                       Zeta,
                                       ZetaStar,
                                       Gamma,
                                       GammaStar,
                                       iLoadStep,
                                       XBOPTS.NumLoadSteps.value,
                                       iLoadStep*1.0,
                                       Text = True)
            
            # Map AeroForces to beam.
            CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces,
                                XBINPUT.ForceStatic)
            
            # Add gravity loads.
            AddGravityLoads(XBINPUT.ForceStatic,XBINPUT,XBELEM,AELAOPTS,
                            PsiDefor,VMINPUT.c)
            
            # Apply factor corresponding to force step.
            iForceStep = XBINPUT.ForceStatic*float( (iLoadStep+1) ) / \
                                                XBOPTS.NumLoadSteps.value
            
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('  iLoad: %-10d\n' %(iLoadStep+1))
                sys.stdout.write('   SubIter DeltaF     DeltaX     ResLog10\n')
                
            # Start Newton Iteration.
            while( (ResLog10 > np.log10(XBOPTS.MinDelta.value)) 
                 & (Iter < XBOPTS.MaxIterations.value) ):
                
                Iter += 1
                if XBOPTS.PrintInfo.value == True:
                    sys.stdout.write('   %-7d ' %(Iter))
                    
                # Set structural eqn tensors to zero
                KglobalFull[:,:] = 0.0; ks = ct.c_int()
                FglobalFull[:,:] = 0.0; fs = ct.c_int()
                Qglobal[:] = 0.0
                
                # Assemble matrices for static problem
                BeamLib.Cbeam3_Asbly_Static(XBINPUT,
                                            NumNodes_tot,
                                            XBELEM,
                                            XBNODE,
                                            PosIni,
                                            PsiIni,
                                            PosDefor,
                                            PsiDefor,
                                            iForceStep,
                                            NumDof,
                                            ks,
                                            KglobalFull,
                                            fs,
                                            FglobalFull,
                                            Qglobal,
                                            XBOPTS)
                
                # Get state vector from current deformation.
                PosDot = np.zeros((NumNodes_tot.value,3), ct.c_double, 'F')
                PsiDot = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),
                                  ct.c_double, 'F')
                
                BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot,
                                              NumDof,
                                              XBINPUT,
                                              XBNODE,
                                              PosDefor,
                                              PsiDefor,
                                              PosDot,
                                              PsiDot,
                                              x,
                                              dxdt)
                
                
                # Get forces on unconstrained nodes.
                BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),
                                  ct.byref(ct.c_int(6)),
                                  iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),
                                  ct.byref(NumDof),
                                  iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),
                                  XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
                
                # Calculate \Delta RHS.
                Qglobal = Qglobal - np.dot(FglobalFull, iForceStep_Dof)
                
                
                # Calculate \Delta State Vector. 
                DeltaS = - np.dot(np.linalg.inv(KglobalFull), Qglobal)
                    
                if XBOPTS.PrintInfo.value == True:
                    sys.stdout.write('%-10.4e %-10.4e ' 
                                     % (max(abs(Qglobal)),max(abs(DeltaS))))
                
                # Update Solution.
                BeamLib.Cbeam3_Solv_Update_Static(XBINPUT,
                                                  NumNodes_tot,
                                                  XBELEM,
                                                  XBNODE,
                                                  NumDof,
                                                  DeltaS,
                                                  PosIni,
                                                  PsiIni,
                                                  PosDefor,
                                                  PsiDefor)
                
                # Record residual at first iteration.
                if(Iter == 1):
                    Res0_Qglobal = max(abs(Qglobal)) + 1.e-16
                    Res0_DeltaX  = max(abs(DeltaS)) + 1.e-16
    
                # Update residual and compute log10
                Res_Qglobal = max(abs(Qglobal)) + 1.e-16
                Res_DeltaX  = max(abs(DeltaS)) + 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('%8.4f\n' %(ResLog10))
                    
                # Stop the solution.
                if(ResLog10 > 10.):
                    sys.stderr.write(' STOP\n')
                    sys.stderr.write(' The max residual is %e\n' %(ResLog10))
                    exit(1)
                elif Res_DeltaX < 1.e-14:
                    break
                
            # END Newton iteration
        # END Load step loop
    
    
        if XBOPTS.PrintInfo.value==True:
            sys.stdout.write(' ... done\n')
        
         
        # Write deformed configuration to file.
        ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
        if XBOPTS.PrintInfo.value==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.value==True:
            sys.stdout.write('done\n')
        WriteMode = 'a'
        
        BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, 
                           PosDefor, PsiDefor, ofile, WriteMode)
        
        # Print deformed configuration.
        if XBOPTS.PrintInfo.value==True:
            sys.stdout.write('--------------------------------------\n')
            sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
            sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
            for inodi in range(NumNodes_tot.value):
                sys.stdout.write(' ')
                for inodj in range(3):
                    sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
                sys.stdout.write('\n')
            sys.stdout.write('--------------------------------------\n')
            
        # Close Tecplot ascii FileObject.
        PostProcess.CloseAeroTecFile(FileObject)
            
        # Return solution
        return PosDefor, PsiDefor, Zeta, ZetaStar, Gamma, GammaStar, iForceStep
Beispiel #13
0
def Static(XBINPUT, XBOPTS, moduleName=None):
    """@brief Initialise everything needed for Static beam simulation."""

    # Declare variables that are not dependent on NumNodes_tot.
    XBELEM = DerivedTypes.Xbelem(XBINPUT.NumElems, Settings.MaxElNod)
    NumDof = ct.c_int()
    PsiIni = np.zeros((XBINPUT.NumElems, Settings.MaxElNod, 3),
                      dtype=ct.c_double,
                      order='F')

    # Check if custom Input module has been specified and import
    if moduleName != None:
        Input = __import__(moduleName)
    else:
        Input = __import__('Input')

    # Check inputs.
    XBINPUT, XBOPTS = Input.Setup(XBINPUT, XBOPTS)

    # Set-up element properties
    NumNodes_tot, XBELEM = Input.Elem(XBINPUT, XBOPTS, XBELEM)
    # Set-up nodal properties.
    PosIni, PhiNodes, BoundConds = \
        Input.Node(XBINPUT, XBOPTS, NumNodes_tot, XBELEM)
    # Compute initial (undeformed) geometry.
    BeamLib.f_xbeam_undef_geom(
        ct.byref(ct.c_int(XBINPUT.NumElems)), ct.byref(NumNodes_tot),
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)),
        PosIni.ctypes.data_as(ct.POINTER(ct.c_double)),
        PhiNodes.ctypes.data_as(ct.POINTER(ct.c_double)),
        PsiIni.ctypes.data_as(ct.POINTER(ct.c_double)),
        ct.byref(XBOPTS.FollowerForce), ct.byref(XBOPTS.FollowerForceRig),
        ct.byref(XBOPTS.PrintInfo), ct.byref(XBOPTS.OutInBframe),
        ct.byref(XBOPTS.OutInaframe), ct.byref(XBOPTS.ElemProj),
        ct.byref(XBOPTS.MaxIterations), ct.byref(XBOPTS.NumLoadSteps),
        ct.byref(XBOPTS.NumGauss), ct.byref(XBOPTS.Solution),
        ct.byref(XBOPTS.DeltaCurved), ct.byref(XBOPTS.MinDelta),
        ct.byref(XBOPTS.NewmarkDamp))
    # Write to undeformed geometry to file.
    WriteMode = 'w'
    if XBOPTS.PrintInfo == True:
        sys.stdout.write(
            'Writing file (mode: %s) %s' %
            (WriteMode, Settings.OutputDir + Settings.OutputFileRoot + '_SOL' +
             XBOPTS.Solution.value + '_und.dat\n'))

    BeamIO.WriteUndefGeometry(
        XBINPUT.NumElems, NumNodes_tot.value, XBELEM, PosIni, PsiIni,
        Settings.OutputDir + Settings.OutputFileRoot + '_INIT', WriteMode)
    if XBOPTS.PrintInfo == True:
        sys.stdout.write('Identify nodal degrees of freedom ... ')
    # Initialize nodal data derived type.
    XBNODE = DerivedTypes.Xbnode(NumNodes_tot.value)
    BeamLib.f_xbeam_undef_dofs(
        ct.byref(ct.c_int(XBINPUT.NumElems)), ct.byref(NumNodes_tot),
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)),
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)),
        BoundConds.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBNODE.Master.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)),
        XBNODE.Fdof.ctypes.data_as(ct.POINTER(ct.c_int)), ct.byref(NumDof))
    if XBOPTS.PrintInfo == True:
        sys.stdout.write('done\n')

    return XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni,\
            XBNODE, NumDof
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.
    
    kwords:
    @param writeDict OrderedDict of of outputs to write.
    @param mpcCont Instance of PyMPC.MPC class.
    """
        
    # 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 = False)
    
    # Open output file for writing
    if 'writeDict' in kwords and Settings.WriteOut == True:
        fp = OpenOutFile(kwords['writeDict'], XBOPTS, Settings)
        
    # Write initial outputs to file.
    if 'writeDict' in kwords and Settings.WriteOut == True:
        WriteToOutFile(kwords['writeDict'],
                       fp,
                       Time[0],
                       PosDefor,
                       PsiDefor,
                       PosIni,
                       PsiIni,
                       XBELEM,
                       ctrlSurf,
                       kwords['mpcCont'])
    # 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 = xbl.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 and kwords['mpcCont'] != None:
                    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 - OriginA_G[:],
                                           ZetaStar - OriginA_G[:],
                                           Gamma,
                                           GammaStar,
                                           TimeStep = iStep,
                                           NumTimeSteps = XBOPTS.NumLoadSteps.value,
                                           Time = Time[iStep],
                                           Text = False)

            # 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*xbl.QuadSkew(ForcedVel[iStep+1,3:])*dt)
        Quat = np.dot(Temp, np.dot(Unit4 - 0.25*xbl.QuadSkew(ForcedVel[iStep,3:])*dt, Quat))
        Quat = Quat/np.linalg.norm(Quat)
        Cao  = xbl.Rot(Quat) # transformation matrix at iStep+1
        
        if AELAOPTS.Tight == True:
            # CRV at iStep+1
            PsiA_G = xbl.quat2psi(Quat)
            # Origin at iStep+1
            OriginA_G[:] = OriginA_G[:] + ForcedVel[iStep,:3]*dt
            
            GammaSav = Gamma.copy(order = 'C')
            
        # 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,
                           kwords['mpcCont'])
        # END if write.
        
        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 assemble_GEBM_tensors(   iStep,
                             NumDof, NumNodes_tot,
                             XBELEM, XBNODE,
                             XBINPUT, VMINPUT, XBOPTS, 
                             AELAOPTS, VMOPTS,  
                             tmpVrel,     
                             PosIni, PsiIni,          
                             PosDefor, PsiDefor,  
                             PosDotDef, PsiDotDef,
                             PosDotDotDef, PsiDotDotDef,
                             Force, #?
                             MssFull, CssFull, KssFull, FstrucFull,
                             Msr, Csr,
                             Force_Dof, Qstruc,
                             MrsFull, CrsFull, KrsFull, FrigidFull,
                             Mrr, Crr, Qrigid,
                             Q, dQdt, dQddt, 
                             Force_All,
                             Msys, Csys, Ksys, Asys, Qsys ):
    
    # utilities:
    # (dummy variables to store unused output from assembly functions)
    ms, mr = ct.c_int(), ct.c_int()
    cs, cr = ct.c_int(), ct.c_int()
    ks, kr = ct.c_int(), ct.c_int()
    fs, fr = ct.c_int(), ct.c_int()
       
    #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[:,:],Csys[:,:],Ksys[:,:],Asys[:,:],Qsys[:] = 0.0,0.0,0.0,0.0,0.0
     
    #rigid-body velocities and orientation from state vector
    Quat = dQdt[NumDof.value+6:].copy('F')
    Quat = Quat/np.linalg.norm(Quat)
    Cao  = xbl.Rot(Quat)                       # input for xbeam assembly
    Cqr = np.zeros((4,6), ct.c_double, 'F')    # dummy: output for xbeam assembly
    Cqq = np.zeros((4,4), ct.c_double, 'F')    # dummy: output for xbeam assembly
    
    #Update matrices and loads for structural dynamic analysis
    tmpQuat=Quat.copy('F')
    BeamLib.Cbeam3_Asbly_Dynamic(XBINPUT, NumNodes_tot, XBELEM, XBNODE,     # in
                         PosIni, PsiIni, PosDefor, PsiDefor,                # in
                         PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,  # in
                         Force, tmpVrel, 0*tmpVrel,                         # in
                         NumDof, Settings.DimMat,                           # out
                         ms, MssFull, Msr,                                  # out
                         cs, CssFull, Csr,                                  # out
                         ks, KssFull, fs, FstrucFull,Qstruc,                # out
                         XBOPTS, Cao)                                       # in
    
    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,      # in
                         PosIni, PsiIni, PosDefor, PsiDefor,                # in
                         PosDotDef, PsiDotDef, PosDotDotDef, PsiDotDotDef,  # in
                         tmpVrel, 0*tmpVrel, tmpQuat,                       # in
                         NumDof, Settings.DimMat,                           # out
                         mr, MrsFull, Mrr,                                  # out
                         cr, CrsFull, Crr, Cqr, Cqq,                        # out
                         kr, KrsFull, fr, FrigidFull, Qrigid,               # out
                         XBOPTS, Cao)                                       # in

    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)) )

    #Assemble discrete system matrices with linearised quaternion equations  
    Unit4 = np.zeros((4,4), ct.c_double, 'F')
    for i in range(4): Unit4[i,i] = 1.0     
    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[iStep,:,:] ),
                                    (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead[iStep,:,:] ),
                                    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)
    
    # include damping
    if XBINPUT.str_damping_model == 'prop':
        Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                XBINPUT.str_damping_param['beta']  * KssFull
        Csys[:NumDof.value,:NumDof.value] += Cdamp
        Qsys[:NumDof.value] += np.dot(Cdamp, dQdt[:NumDof.value])


    #---------------------------------------------------------------------------------- special BCs
    
    iiblock=[]
    if XBNODE.Sflag.any():
        # block translations (redundant:)
        for ii in range(3):
            if XBINPUT.EnforceTraVel_FoRA[ii] == True: iiblock.append(NumDof.value+ii)
        # block rotations
        iirotfree=[] # free rotational dof 
        for ii in range(3):
            if XBINPUT.EnforceAngVel_FoRA[ii] is True: iiblock.append(NumDof.value+3+ii)
            else: iirotfree.append(NumDof.value+3+ii)
    
    if len(iiblock)>0:
        Msys[iiblock,:] = 0.0
        Msys[:,iiblock] = 0.0 
        Msys[iiblock,iiblock] = 1.0
        Csys[iiblock,:] = 0.0
        Ksys[iiblock,:] = 0.0
        Qsys[iiblock]   = 0.0
        if XBINPUT.sph_joint_damping is not None:
            Csys[iirotfree,iirotfree] += XBINPUT.sph_joint_damping
            Qsys[iirotfree] += XBINPUT.sph_joint_damping*dQdt[iirotfree]

    return Msys, Csys, Ksys, Qsys
def Solve_Py(XBINPUT,XBOPTS,VMOPTS,VMINPUT,AELAOPTS,**kwords):
    """Nonlinear static solver using Python to solve aeroelastic
    equation. Assembly of structural matrices is carried out with 
    Fortran subroutines. Aerodynamics solved using PyAero.UVLM."""
    
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic requested' +
                                              ' with wrong solution code')

    # I/O options
    XBOUT=DerivedTypes.Xboutput()
    SaveDict=Settings.SaveDict
    if 'SaveDict' in kwords: SaveDict=kwords['SaveDict']
    if SaveDict['Format']=='h5':
        Settings.WriteOut=False
        Settings.PlotTec=False
        OutList=[AELAOPTS, VMINPUT, VMOPTS, XBOPTS, XBINPUT, XBOUT]
        #if VMINPUT.ctrlSurf!=None:
        #    for cc in range(len(VMINPUT.ctrlSurf)):
        #        OutList.append(VMINPUT.ctrlSurf[cc])
            
    XBOUT.cputime.append(time.clock()) # time.processor_time more appropriate 

    # Initialize beam.
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
    # Set initial conditions as undef config.
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case in Python ... \n')
    # Initialise structural eqn tensors.
    KglobalFull = np.zeros((NumDof.value,NumDof.value),
                           ct.c_double, 'F'); ks = ct.c_int()
    FglobalFull = np.zeros((NumDof.value,NumDof.value),
                           ct.c_double, 'F'); fs = ct.c_int()
    DeltaS  = np.zeros(NumDof.value, ct.c_double, 'F')
    Qglobal = np.zeros(NumDof.value, ct.c_double, 'F')
    x       = np.zeros(NumDof.value, ct.c_double, 'F')
    dxdt    = np.zeros(NumDof.value, ct.c_double, 'F')
    # Beam Load Step tensors
    Force = np.zeros((XBINPUT.NumNodesTot,6),ct.c_double,'F')
    ForceAero = np.zeros((XBINPUT.NumNodesTot,6),ct.c_double,'F')
    iForceStep     = np.zeros((NumNodes_tot.value,6), ct.c_double, 'F')
    iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    
    # Initialze Aero.    
    Section = InitSection(VMOPTS,VMINPUT,AELAOPTS.ElasticAxis)
    # Declare memory for Aero grid and velocities.
    Zeta = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C')
    ZetaDot = np.zeros((Section.shape[0],PosDefor.shape[0],3),ct.c_double,'C')
    # Additional Aero solver variables.
    AeroForces = np.zeros((VMOPTS.M.value+1,VMOPTS.N.value+1,3),ct.c_double,'C')
    Gamma = np.zeros((VMOPTS.M.value,VMOPTS.N.value),ct.c_double,'C')
    # Init external velocities.
    Uext = InitSteadyExternalVels(VMOPTS,VMINPUT)
    # Create zero triads for motion of reference frame.
    VelA_A = np.zeros((3))
    OmegaA_A = np.zeros((3))
    # Create zero vectors for structural vars not used in static analysis.
    PosDotDef = np.zeros_like(PosDefor, ct.c_double, 'F')
    PsiDotDef = np.zeros_like(PsiDefor, ct.c_double, 'F')      
    
    # Define tecplot stuff.
    FileName = Settings.OutputDir + Settings.OutputFileRoot + 'AeroGrid.dat'
    Variables = ['X', 'Y', 'Z','Gamma']        
    FileObject = PostProcess.WriteAeroTecHeader(FileName, 
                                                'Default',
                                                Variables)
    
    # Start Load Loop.
    for iLoadStep in range(XBOPTS.NumLoadSteps.value + XBOPTS.MaxIterations.value):
        # The loads are applied at increements. When 
        #     iLoadStep=XBOPTS.NumLoadSteps.value
        # further XBOPTS.MaxIterations.value iterations are run to the 
        # aerodynamic loads to settle.
        # @warning: the number of for loops should be larger then 
        #           XBOPTS.NumLoadSteps.value, or full loading conditions will
        #           not be reached


        # Reset convergence parameters and loads.
        Iter = 0
        ResLog10 = 0.0
        Force[:,:] = 0.0
        AeroForces[:,:,:] = 0.0
        oldPos = PosDefor.copy(order='F')
        oldPsi = PsiDefor.copy(order='F')    
                
        # Calculate aero loads.
        if hasattr(XBINPUT, 'ForcedVel'):
            CoincidentGrid(PosDefor, PsiDefor,
                           Section,
                           XBINPUT.ForcedVel[0,:3],
                           XBINPUT.ForcedVel[0,3:],
                           PosDotDef, PsiDotDef,
                           XBINPUT,
                           Zeta, ZetaDot,
                           np.zeros(3), XBINPUT.PsiA_G, # call from Couple_NlnFlightDyn 
                           ctrlSurf = VMINPUT.ctrlSurf)
        else:
            CoincidentGrid(PosDefor, PsiDefor, Section, VelA_A, 
                           OmegaA_A, PosDotDef, PsiDotDef, XBINPUT,
                           Zeta, ZetaDot,
                           np.zeros(3), XBINPUT.PsiA_G, # call from Couple_NlnFlightDyn 
                           ctrlSurf = VMINPUT.ctrlSurf)
        
        if hasattr(XBINPUT,'ForcedVel'):
            ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta,
                                                 XBINPUT.ForcedVel[0,:3])
        else:
            ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta)
        

        #>>>>>>>> HEAD
        # Solve for AeroForces.
        UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Uext, ZetaStar, VMOPTS, 
                               AeroForces, Gamma, GammaStar)
        # ========   
        # # Define AICs here for debgugging - Rob 16/08/2016
        # AIC = np.zeros((VMOPTS.M.value*VMOPTS.N.value,
        #                 VMOPTS.M.value*VMOPTS.N.value),
        #                 ct.c_double,'C')
        # BIC = np.zeros((VMOPTS.M.value*VMOPTS.N.value,
        #                 VMOPTS.M.value*VMOPTS.N.value),
        #                 ct.c_double,'C')
        
        # # Solve for AeroForces.
        # UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Uext, ZetaStar, VMOPTS, 
        #                        AeroForces, Gamma, GammaStar, AIC, BIC)
        # <<<<<<<< rob 
        
        AeroForces[:,:,:] = AELAOPTS.AirDensity*AeroForces[:,:,:]
        
        # Write solution to tecplot file.
        if Settings.WriteOut==True and Settings.PlotTec==True:
            PostProcess.WriteUVLMtoTec(FileObject,
                                       Zeta,
                                       ZetaStar,
                                       Gamma,
                                       GammaStar,
                                       iLoadStep,
                                       XBOPTS.NumLoadSteps.value,
                                       iLoadStep*1.0,
                                       Text = True)

        # Map AeroForces to beam.
        CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces,
                            Force, PsiA_G=XBINPUT.PsiA_G)
            
        # Add gravity loads.
        # FoR A orientation is given by XBINPUT.PsiA_G
        AddGravityLoads(Force,XBINPUT,XBELEM,AELAOPTS, PsiDefor,VMINPUT.c, 
                                   PsiA_G=XBINPUT.PsiA_G, FollowerForceRig=True)

        # Apply factor corresponding to force step.
        if iLoadStep < XBOPTS.NumLoadSteps.value:
            iForceStep = (Force + XBINPUT.ForceStatic)*float( (iLoadStep+1) )/\
                                                XBOPTS.NumLoadSteps.value
        else:
            # continue at full loading until equilibrium 
            iForceStep = Force + XBINPUT.ForceStatic

        if XBOPTS.PrintInfo.value == True:
            sys.stdout.write('  iLoad: %-10d\n' %(iLoadStep+1))
            sys.stdout.write('   SubIter DeltaF     DeltaX     ResLog10\n')

        # Start Newton Iteration.
        while( (ResLog10 > np.log10(XBOPTS.MinDelta.value)) 
             & (Iter < XBOPTS.MaxIterations.value) ):

            Iter += 1
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('   %-7d ' %(Iter))
                
            # Set structural eqn tensors to zero
            KglobalFull[:,:] = 0.0; ks = ct.c_int()
            FglobalFull[:,:] = 0.0; fs = ct.c_int()
            Qglobal[:] = 0.0
            
            # Assemble matrices for static problem
            BeamLib.Cbeam3_Asbly_Static(XBINPUT,
                                        NumNodes_tot,
                                        XBELEM,
                                        XBNODE,
                                        PosIni,
                                        PsiIni,
                                        PosDefor,
                                        PsiDefor,
                                        iForceStep,
                                        NumDof,
                                        ks,
                                        KglobalFull,
                                        fs,
                                        FglobalFull,
                                        Qglobal,
                                        XBOPTS)
            
            # Get state vector from current deformation.
            PosDot = np.zeros((NumNodes_tot.value,3), ct.c_double, 'F')
            PsiDot = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),
                              ct.c_double, 'F')
            
            BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot,
                                          NumDof,
                                          XBINPUT,
                                          XBNODE,
                                          PosDefor,
                                          PsiDefor,
                                          PosDot,
                                          PsiDot,
                                          x,
                                          dxdt)
                    
            # Get forces on unconstrained nodes.
            BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),
                              ct.byref(ct.c_int(6)),
                              iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),
                              ct.byref(NumDof),
                              iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),
                              XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )

            # Calculate \Delta RHS.
            Qglobal = Qglobal - np.dot(FglobalFull, iForceStep_Dof)
            
            # Calculate \Delta State Vector. 
            DeltaS = - np.dot(np.linalg.inv(KglobalFull), Qglobal)
                
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('%-10.4e %-10.4e ' 
                                 % (max(abs(Qglobal)),max(abs(DeltaS))))
            
            # Update Solution.
            BeamLib.Cbeam3_Solv_Update_Static(XBINPUT,
                                              NumNodes_tot,
                                              XBELEM,
                                              XBNODE,
                                              NumDof,
                                              DeltaS,
                                              PosIni,
                                              PsiIni,
                                              PosDefor,
                                              PsiDefor)
            
            # Record residual at first iteration.
            if(Iter == 1):
                Res0_Qglobal = max(abs(Qglobal)) + 1.e-16
                Res0_DeltaX  = max(abs(DeltaS)) + 1.e-16

            # Update residual and compute log10
            Res_Qglobal = max(abs(Qglobal)) + 1.e-16
            Res_DeltaX  = max(abs(DeltaS)) + 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('%8.4f\n' %(ResLog10))
            
            # Stop the solution.                
            if(ResLog10 > 10.):
                sys.stderr.write(' STOP\n')
                sys.stderr.write(' The max residual is %e\n' %(ResLog10))
                exit(1)
            elif Res_DeltaX < 1.e-14:
                break
        # END Newton iteration

        # Isolate Aerodynamic force
        CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces,
                            ForceAero, PsiA_G=XBINPUT.PsiA_G)

        # After incremental loading continue until equilibrium reached
        if iLoadStep >= XBOPTS.NumLoadSteps.value:
            Pos_error = PosDefor - oldPos
            Psi_error = PsiDefor - oldPsi
            if( (np.linalg.norm(Pos_error)<=XBOPTS.MinDelta) & \
                (np.linalg.norm(Psi_error)<=XBOPTS.MinDelta) ):
                break
    # END Load step loop

    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')    
    
    if SaveDict['Format']!='h5':

        # Write deformed configuration to file.
        ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
        if XBOPTS.PrintInfo.value==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.value==True:
            sys.stdout.write('done\n')
        WriteMode = 'a'
 
        # 
        BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, 
                       PosDefor, PsiDefor, ofile, WriteMode)
    
        # Print deformed configuration.
        if XBOPTS.PrintInfo.value==True:
            sys.stdout.write('--------------------------------------\n')
            sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
            sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
            for inodi in range(NumNodes_tot.value):
                sys.stdout.write(' ')
                for inodj in range(3):
                    sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
                sys.stdout.write('\n')
            sys.stdout.write('--------------------------------------\n')
      
        # Close Tecplot ascii FileObject.
        PostProcess.CloseAeroTecFile(FileObject)

    # Drop output
    XBOUT.drop( PosIni=PosIni, PsiIni=PsiIni, 
                PosDeforStatic=PosDefor, PsiDeforStatic=PsiDefor, 
                ZetaStatic=Zeta, ZetaStarStatic=ZetaStar, 
                GammaStatic=Gamma, GammaStarStatic=GammaStar,
                ForceStaticTotal=iForceStep, ForceAeroStatic=ForceAero,
                #iForceStepDebug=iForceStep,
                Uext=Uext )

    PyLibs.io.save.h5file( SaveDict['OutputDir'],
                           SaveDict['OutputFileRoot']+'.h5',
                          *OutList)

    #>>>>>>> HEAD
    #=======
    if True:
        # print and save deformed wing total force coefficients (may include 
        # gravity and other applied static loads). Coefficients in wind-axes.
        cF = np.zeros((3))
        for i in range(VMOPTS.M.value+1):
            for j in range(VMOPTS.N.value+1):
                cF += AeroForces[i,j,:] 
        
        Calpha=Psi2TransMat(np.array([VMINPUT.alpha, 0.0, 0.0]))
        cF=np.dot(Calpha,cF)
        cF=cF/( 0.5*AELAOPTS.AirDensity*np.linalg.norm(VMINPUT.U_infty)**2.0 \
               *VMINPUT.c*XBINPUT.BeamLength)
        print("Reference condition total force coefficients: {}".format(cF))
        fileName = Settings.OutputDir + Settings.OutputFileRoot + 'refCf'
        savemat(fileName,{'cF':cF})
    #<<<<<<< rob 

    # Return solution
    return XBOUT
Beispiel #17
0
def Solve_Py(XBINPUT,XBOPTS, moduleName = None):
    """Nonlinear static solver using Python to solve residual
    equation. Assembly of matrices is carried out with Fortran subroutines."""
    
    "Check correct solution code"
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic requested' +\
                                              ' with wrong solution code')
    
    "Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS, moduleName)
    
    
    "Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')
    
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case in Python ... \n')

    "Initialise rotation operators"
    Unit = np.zeros((3,3), ct.c_double, 'F')
    for i in range(3):
        Unit[i,i] = 1.0
        
    Cao = Unit.copy('F')
    
    "Initialise structural eqn tensors"
    KglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); ks = ct.c_int()
    KglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); 
    FglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); fs = ct.c_int()    
    
    DeltaS  = np.zeros(NumDof.value, ct.c_double, 'F')
    Qglobal = np.zeros(NumDof.value, ct.c_double, 'F')
    x       = np.zeros(NumDof.value, ct.c_double, 'F')
    dxdt    = np.zeros(NumDof.value, ct.c_double, 'F')
    
    
    "Load Step tensors"
    iForceStep     = np.zeros((NumNodes_tot.value,6), ct.c_double, 'F')
    iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    
    "Start Load Loop"
    for iLoadStep in range(XBOPTS.NumLoadSteps.value):
        
        "Reset convergence parameters"
        Iter = 0
        ResLog10 = 1.0
        
        "General load case"
        iForceStep = XBINPUT.ForceStatic*float( (iLoadStep+1) /                \
                                                XBOPTS.NumLoadSteps.value)
        
        if XBOPTS.PrintInfo.value == True:
            sys.stdout.write('  iLoad: %-10d\n' %(iLoadStep+1))
            sys.stdout.write('   SubIter DeltaF     DeltaX     ResLog10\n')
            

        "Newton Iteration"
        while( (ResLog10 > XBOPTS.MinDelta.value) \
             & (Iter < XBOPTS.MaxIterations.value) ):
            
            "Increment iteration counter"
            Iter += 1
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('   %-7d ' %(Iter))
                
            
            "Set structural eqn tensors to zero"
            KglobalFull[:,:] = 0.0; ks = ct.c_int()
            KglobalFull_foll[:,:] = 0.0; 
            FglobalFull[:,:] = 0.0; fs = ct.c_int()
            Qglobal[:] = 0.0
            
            
            "Assemble matrices for static problem"
            BeamLib.Cbeam3_Asbly_Static(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                        PosIni, PsiIni, PosDefor, PsiDefor,\
                        iForceStep, NumDof,\
                        ks, KglobalFull, fs, FglobalFull, Qglobal,\
                        XBOPTS)
            
            
            "Get state vector from current deformation"
            PosDot = np.zeros((NumNodes_tot.value,3), ct.c_double, 'F') #R
            PsiDot = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),\
                               ct.c_double, 'F') #Psi
            
            BeamLib.Cbeam_Solv_Disp2State(NumNodes_tot, NumDof, XBINPUT, XBNODE,\
                          PosDefor, PsiDefor, PosDot, PsiDot,
                          x, dxdt)
            
            
            "Get forces on unconstrained nodes"
            BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                              ct.byref(ct.c_int(6)),\
                              iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              ct.byref(NumDof),\
                              iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
            
            
            "Calculate \Delta RHS"
            Qglobal = Qglobal - np.dot(FglobalFull, iForceStep_Dof)
            
            
            "Separate assembly of follower and dead loads"
            Qforces, KglobalFull_foll = \
                        XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \
                                              PosIni, PsiIni, PosDefor, PsiDefor, \
                                              XBINPUT.ForceStatic_foll,XBINPUT.ForceStatic_dead, \
                                              Cao, float((iLoadStep+1)/XBOPTS.NumLoadSteps.value))[:2]
                        
            KglobalFull += KglobalFull_foll
            Qglobal -= Qforces
            
            "Calculate \Delta State Vector"    
            DeltaS = - np.dot(np.linalg.inv(KglobalFull), Qglobal)
                
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('%-10.4e %-10.4e ' \
                                 % (max(abs(Qglobal)),max(abs(DeltaS))))
            
            
            "Update Solution"
            BeamLib.Cbeam3_Solv_Update_Static(XBINPUT, NumNodes_tot, XBELEM,\
                                              XBNODE, NumDof, DeltaS,\
                                              PosIni, PsiIni, PosDefor,PsiDefor)

                                    
            "Residual at first iteration"
            if(Iter == 1):
                Res0_Qglobal = max(max(abs(Qglobal)),1)
                Res0_DeltaX  = max(max(abs(DeltaS)),1)

            "Update residual and compute log10"
            Res_Qglobal = max(abs(Qglobal))
            Res_DeltaX  = max(abs(DeltaS))

            ResLog10 = max(Res_Qglobal/Res0_Qglobal, Res_DeltaX/Res0_DeltaX)
           
                        
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('%8.4f\n' %(ResLog10))
        
            
            "Stop the solution"
            if(ResLog10 > 1.e10):
                sys.stderr.write(' STOP\n')
                sys.stderr.write(' The max residual is %e\n' %(ResLog10))
                exit(1)
            
        "END Newton Loop"
    "END Load Loop"


    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')
    
     
    "Write deformed configuration to file"
    ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
    if XBOPTS.PrintInfo.value==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.value==True:
        sys.stdout.write('done\n')
    WriteMode = 'a'
    
    BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \
                       PosDefor, PsiDefor, ofile, WriteMode)
    
    
    "Print deformed configuration"
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('--------------------------------------\n')
        sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
        sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(' ')
            for inodj in range(3):
                sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
            sys.stdout.write('\n')
        sys.stdout.write('--------------------------------------\n')
        
    
    "Return solution as optional output argument"
    return PosDefor, PsiDefor
Beispiel #18
0
def LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \
                 PosIni, PsiIni, PosDefor, PsiDefor, \
                 Force_foll, Force_dead, CAG, iFactor):
    """@brief Assemble separate follower and dead loads.
    @param XBINPUT Xbeam inputs.
    @param XBELEM Xbeam element information.
    @param XBNODE Xbeam nodal information.
    @param XBOPTS Xbeam simulation options.
    @param NumDof numbers of DoF in the problem.
    @param PosInI nodal position at reference configuration.
    @param PsiIni nodal CRV at reference configuration.
    @param PosDefor nodal position at reference configuration.
    @param PsiDefor nodal CRV at reference configuration.
    @param Force_foll Matrix of applied nodal follower forces.
    @param Force_dead Matrix of applied nodal dead forces.
    @param CAG transformation matrix from inertial (G) to body-fixed frame (A).
    @param iFactor factor to account for optional load stepping.
    """
    
    # Initialise function variables
    NumNodes_tot=ct.c_int((XBINPUT.NumNodesElem - 1)*XBINPUT.NumElems + 1)
    
    KglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); ksf = ct.c_int()
                            
    FglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); fsf = ct.c_int()
                            
    FglobalFull_dead = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); fsd = ct.c_int()
                            
    Force_foll_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    Force_dead_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    
    
    # Assemble separate force coefficient matrices
    BeamLib.Cbeam3_Asbly_Fglobal(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                        PosIni, PsiIni, PosDefor, PsiDefor,\
                        Force_foll*iFactor,NumDof,\
                        ksf, KglobalFull_foll, fsf, FglobalFull_foll,\
                        fsd, FglobalFull_dead, CAG)
    
    # Get follower and dead forces on constrained nodes
    BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                      ct.byref(ct.c_int(6)),\
                      Force_foll.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      ct.byref(NumDof),\
                      Force_foll_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
    BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                      ct.byref(ct.c_int(6)),\
                      Force_dead.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      ct.byref(NumDof),\
                      Force_dead_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                      XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
    
    # Project follower and dead forces
    Qforces = np.dot(FglobalFull_foll, Force_foll_Dof*iFactor) + np.dot(FglobalFull_dead, Force_dead_Dof*iFactor)  

    #--------------------------------------
    # Repeat in case of rigid-body dynamics
    if XBOPTS.Solution.value == 912:
        FrigidFull_foll = np.zeros((6,NumDof.value+6), ct.c_double, 'F'); frf = ct.c_int()
        FrigidFull_dead = np.zeros((6,NumDof.value+6), ct.c_double, 'F'); frd = ct.c_int()
        
        Force_foll_All = np.zeros(NumDof.value+6, ct.c_double, 'F')
        Force_dead_All = np.zeros(NumDof.value+6, ct.c_double, 'F')
        
        # Assemble separate force coefficient matrices
        BeamLib.Xbeam_Asbly_Frigid(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                                     PosIni, PsiIni, PosDefor, PsiDefor,\
                                     NumDof, frf, FrigidFull_foll,\
                                     frd, FrigidFull_dead, CAG)
    
        # Get follower and dead forces on unconstrained nodes
        BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\
                                   ct.byref(ct.c_int(6)),\
                                   Force_foll.ctypes.data_as(ct.POINTER(ct.c_double)),\
                                   ct.byref(ct.c_int(NumDof.value+6)),\
                                   Force_foll_All.ctypes.data_as(ct.POINTER(ct.c_double)) )
        BeamLib.f_fem_m2v_nofilter(ct.byref(NumNodes_tot),\
                                   ct.byref(ct.c_int(6)),\
                                   Force_dead.ctypes.data_as(ct.POINTER(ct.c_double)),\
                                   ct.byref(ct.c_int(NumDof.value+6)),\
                                   Force_dead_All.ctypes.data_as(ct.POINTER(ct.c_double)) )
        
        # Project follower and dead forces
        Qrigid = np.dot(FrigidFull_foll,Force_foll_All*iFactor) + np.dot(FrigidFull_dead,Force_dead_All*iFactor) 
    
    else:
        Qrigid = 0.0

    #------------------
    # Return everything
    return Qforces, KglobalFull_foll, Qrigid
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')
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')
    
    # I/O management
    XBOUT=DerivedTypes.Xboutput()  
    SaveDict=Settings.SaveDict
    if 'SaveDict' in kwords: SaveDict=kwords['SaveDict']
    if SaveDict['Format']=='h5':
        Settings.WriteOut=False
        Settings.PlotTec=False
        OutList=[AELAOPTS, VMINPUT, VMOPTS, XBOPTS, XBINPUT, XBOUT]
        #if VMINPUT.ctrlSurf!=None:
        #    for cc in range(len(VMINPUT.ctrlSurf)):
        #        OutList.append(VMINPUT.ctrlSurf[cc])
        if SaveDict['SaveWake'] is True:
            dirwake=SaveDict['OutputDir']+'wake'+SaveDict['OutputFileRoot']+'/'
            os.system('mkdir -p %s' %dirwake)
            XBOUT.dirwake=dirwake  
            
    XBOUT.cputime.append(time.clock()) # time.processor_time more appropriate but equivalent
    # for debugging
    XBOUT.ForceDofList=[]
    XBOUT.ForceRigidList=[]

    # Initialise static beam data.
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
    
    # special BCs
    SphFlag=False
    if XBNODE.Sflag.any(): SphFlag=True
    
    # Debugging Flags

    if 'HardCodeAero' in kwords: HardCodeAero=kwords['HardCodeAero']
    SaveExtraVariables = False  

    
    #------------------------- Initial Displacement: ImpStart vs Static Solution
    
    # 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')


    if SaveDict['Format']!='h5': 
        write_SOL912_def(XBOPTS,XBINPUT,XBELEM,NumNodes_tot,PosDefor,PsiDefor,SaveDict)
    else:
        XBOUT.PosDeforStatic=PosDefor.copy()
        XBOUT.PsiDeforStatic=PsiDefor.copy()
        XBOUT.ForceTotStatic = Force.copy()
        
    
    
    #------------------------------------------------ Initialise Dynamic Problem
    
    # 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
    
    # sm I/O
    ### why forced velocity with Sol912 ???
    ### If forced velocities are prescribed, then is Sol312        
    XBOUT.PosDefor=PosDefor                # ...SOL912_def.dat
    XBOUT.PsiDefor=PsiDefor   
    XBOUT.ForceTime_force=ForceTime        # ...SOL912_force.dat
    XBOUT.Vrel_force=Vrel
    XBOUT.VrelDot_force=VrelDot   
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear dynamic case in Python ... \n')
    
    
    
    #------------------------------------------------------ Initialise Variables
    #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')
    

    
    #---------------------------------------------------- Start Dynamic Solution
    
    #Initialise rotation operators. TODO: include initial AOA here
    currVrel=Vrel[0,:].copy('F')
    
    
    # Initialise attitude:
    Quat =  xbl.psi2quat(XBINPUT.PsiA_G)
    #Quat=   XBINPUT.quat0
    
    #### sm debug
    XBOUT.Quat0=Quat
    XBOUT.currVel0=currVrel
    
    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')
    Force += (XBINPUT.ForceDyn[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:])
       
    # special BCs
    if SphFlag:
        # block translations
        iiblock = [ ii for ii in range(NumDof.value,NumDof.value+3) ]
        # block rotations
        iifree=[] # free rotational dof 
        for ii in range(3):
            if XBINPUT.EnforceAngVel_FoRA[ii] is True:
                iiblock.append(NumDof.value+3+ii)
            else:
                iifree.append(NumDof.value+3+ii)

        # block dof
        Msys[iiblock,:] = 0.0
        Msys[iiblock,iiblock] = 1.0
        Qsys[iiblock] = 0.0
        
        # add damp at the spherical joints
        if XBINPUT.sph_joint_damping is not None:
            Qsys[iifree]+= XBINPUT.sph_joint_damping*dQdt[iifree]
            
    # add structural damping term
    if XBINPUT.str_damping_model is not None:
        Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                XBINPUT.str_damping_param['beta']  * KssFull
        Qsys[:NumDof.value] += np.dot( Cdamp, dQdt[:NumDof.value] )                  
        pass
        
    #store initial matrices for eigenvalues analysis
    XBOUT.MssFull0 = MssFull.copy()
    XBOUT.CssFull0 = CssFull.copy()
    XBOUT.KssFull0 = KssFull.copy()

    # Initial Accel.
    ###dQddt[:] = np.dot(np.linalg.inv(Msys), -Qsys)
    dQddt[:] = np.linalg.solve(Msys,-Qsys)
    
    XBOUT.dQddt0=dQddt.copy()
    
    #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 Aerodynamic Force
    # 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_a = np.zeros(3,ct.c_double,'C')
    PsiA_G = XBINPUT.PsiA_G.copy() #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_a, PsiA_G,
                       VMINPUT.ctrlSurf)
        # init wake grid and gamma matrix.
        ZetaStar, GammaStar = InitSteadyWake(VMOPTS,VMINPUT,Zeta,currVrel[:3])
        
    # sm save
    #XBOUT.Zeta0 = Zeta.copy('C')
    #XBOUT.ZetaStar0 = ZetaStar.copy('C')
    #XBOUT.ZetaStarList.append(np.float32( ZetaStar.copy('C') ))
    
    # Define TecPlot stuff
    if Settings.PlotTec==True:
        FileObject=write_TecPlot(Zeta, ZetaStar, Gamma, GammaStar, NumSteps.value, 0, Time[0], SaveDict)
    
    if 'writeDict' in kwords and Settings.WriteOut == True:
        fp= write_SOL912_out(Time[0], PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, kwords['writeDict'], SaveDict)
            
    
    # sm write class
    XBOUT.QuatList.append(Quat.copy())
    XBOUT.CRVList.append(PsiA_G)
    XBOUT.PosIni=PosIni
    XBOUT.PsiIni=PsiIni    
    
    XBOUT.AsysListStart=[]
    XBOUT.AsysListEnd=[]
    XBOUT.MsysList=[]
    XBOUT.CsysList=[]
    XBOUT.KsysList=[]
    

    #---------------------------------------------------------------- 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 displacements 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)
        
            
        #------------------------------------------------------- Aero Force Loop
        # 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.
            # sm: origin position projected in FoR A
            currVrel=Vrel[iStep-1,:].copy('F')
            OriginA_a[:] = OriginA_a[:] + currVrel[:3]*dt #sm: OriginA_a initialised to zero
            
            # Update control surface deflection.
            if VMINPUT.ctrlSurf != None:
                # open-loop control
                for cc in range(len(VMINPUT.ctrlSurf)):
                    VMINPUT.ctrlSurf[cc].update(Time[iStep],iStep=iStep)
            
            # Generate surface grid.
            currVrel=Vrel[iStep,:].copy('F')
            CoincidentGrid(PosDefor, PsiDefor, Section, currVrel[:3], 
                           currVrel[3:], PosDotDef, PsiDotDef, XBINPUT,
                           Zeta, ZetaDot, OriginA_a, 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:
                FileObject=write_TecPlot(Zeta, ZetaStar, Gamma, GammaStar, NumSteps.value, iStep, 
                                         Time[iStep], SaveDict,FileObject=FileObject)

            # map AeroForces to beam.
            CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces,
                                Force, PsiA_G)

            ForceAero = Force.copy('C')  
            
            # 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')
            Force += (XBINPUT.ForceStatic + XBINPUT.ForceDyn[iStep+1,:,:]).copy('F')
                      
            # sm: here to avoid crash at first time-step
            XBOUT.ForceAeroList.append(ForceAero.copy('C')) 
         
        
        #END if iStep > 0        
            
        # sm: save aero data
        if ( SaveDict['SaveWake']==True and 
                 iStep%SaveDict['SaveWakeFreq'] == 0 ):
            nfile=iStep//SaveDict['SaveWakeFreq']
            hdwake=h5py.File(dirwake+'%.4d.h5'%nfile,'w')
            hdwake['iStep']=iStep
            hdwake['Zeta']= np.float32(Zeta.copy('C'))
            hdwake['ZetaStar']= np.float32(ZetaStar.copy('C'))            
            hdwake.close()
            #XBOUT.ZetaList.append( np.float32(Zeta.copy('C')) )
            #XBOUT.ZetaStarList.append( np.float32(ZetaStar.copy('C')) )
            #XBOUT.GammaStarList.append(GammaStar.copy('C'))
            #XBOUT.GammaList.append(Gamma.copy('C')) 
                      
          
        #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 displacements 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)
            
            # include damping
            if XBINPUT.str_damping_model == 'prop':
                Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                        XBINPUT.str_damping_param['beta']  * KssFull
                Csys[:NumDof.value,:NumDof.value] += Cdamp
                Qsys[:NumDof.value] += np.dot(Cdamp, dQdt[:NumDof.value])
                                

            # special BCs
            if SphFlag:
                Msys[iiblock,:] = 0.0
                Msys[iiblock,iiblock] = 1.0
                Csys[iiblock,:] = 0.0
                Ksys[iiblock,:] = 0.0
                Qsys[iiblock]   = 0.0
                if XBINPUT.sph_joint_damping is not None:
                    Csys[iifree,iifree] += XBINPUT.sph_joint_damping
                    Qsys[iifree] += XBINPUT.sph_joint_damping*dQdt[iifree]
          
            #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)
            DQ[:] = np.linalg.solve(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))

            if SaveExtraVariables is True:
                if Iter == 1:
                    XBOUT.AsysListStart.append(Asys.copy())
                if ( (ResLog10 < XBOPTS.MinDelta.value) or (Iter >= XBOPTS.MaxIterations.value) ):
                    XBOUT.AsysListEnd.append(Asys.copy())
                    XBOUT.MsysList.append(Msys.copy())
                    XBOUT.CsysList.append(Csys.copy())
                    XBOUT.KsysList.append(Ksys.copy())
        # END Netwon-Raphson.
        
        
        # sm debug:
        # save forcing terms:
        XBOUT.ForceDofList.append( np.dot(FstrucFull, Force_Dof).copy() )
        XBOUT.ForceRigidList.append( np.dot(FrigidFull, Force_All).copy() )
              
        #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'))
        
        
        if 'writeDict' in kwords and Settings.WriteOut == True:
            fp= write_SOL912_out(Time[iStep+1], PosDefor, PsiDefor, PosIni, PsiIni, XBELEM, 
                                 kwords['writeDict'], SaveDict, FileObject=fp)
                    
        # 'Rollup' due to external velocities. TODO: Must add gusts here!
        ZetaStar[:,:] = ZetaStar[:,:] + VMINPUT.U_infty*dt
        
        # sm: append outputs
        XBOUT.QuatList.append(Quat.copy())
        XBOUT.CRVList.append(PsiA_G.copy())
 
        # sm I/O: FoR A velocities/accelerations
        XBOUT.Time=Time                     # ...dyn.dat
        #XBOUT.PosPsiTime = PosPsiTime       
        
        XBOUT.DynOut=DynOut                 # ...shape.dat
        
        XBOUT.Vrel=Vrel                     # ...rigid.dat
        XBOUT.VrelDot=VrelDot
        #XBOUT.PosPsiTime=PosPsiTime          
        
        XBOUT.PsiList.append(PsiDefor.copy())   
        
        XBOUT.cputime.append( time.clock() - XBOUT.cputime[0] )
        
        if SaveDict['SaveProgress']:
            iisave=np.arange(1,NumSteps.value,np.ceil(NumSteps.value/SaveDict['NumSavePoints']))
            if any(iisave==iStep):
                PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5', 
                                      *OutList)
        
    # END Time loop
    
    
    if SaveDict['Format'] != 'h5': 
        write_SOL912_final(Time, PosPsiTime, NumNodes_tot, DynOut, Vrel, VrelDot, SaveDict) 
        
    # 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

    # sm I/O: FoR A velocities/accelerations

    XBOUT.Time=Time                     # ...dyn.dat
    XBOUT.PosPsiTime = PosPsiTime       
    
    XBOUT.DynOut=DynOut                 # ...sgape.dat
    
    XBOUT.Vrel=Vrel                     # ...rigid.dat
    XBOUT.VrelDot=VrelDot
    XBOUT.PosPsiTime=PosPsiTime   
    
    if  SaveDict['SaveWake'] is True:
        #XBOUT.dirwake=dirwake  
        XBOUT.NTwake=NumSteps.value//SaveDict['SaveWakeFreq']
    
    #saveh5(SaveDict, AELAOPTS, VMINPUT, VMOPTS, XBOPTS, XBINPUT, XBOUT )
    PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5', *OutList)
    
    return XBOUT
def Solve_F90(XBINPUT, XBOPTS):
    """@brief Nonlinear dynamic structural solver using f90 solve routine."""

    #Check correct solution code
    assert XBOPTS.Solution.value == 912, ('NonlinearDynamic (F90) requested' +\
                                              ' with wrong solution code')

    #Initialise variables for static analysis
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT, XBOPTS)

    #Change solution code to NonlinearStatic
    XBOPTS.Solution.value = 112

    #Set initial conditions as undef config
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')

    #Solve static
    BeamLib.Cbeam3_Solv_NonlinearStatic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
                            PosIni, PsiIni, XBNODE, NumDof,\
                            PosDefor, PsiDefor)

    #Change solution code back to NonlinearDynamic
    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, ForcedVel, ForcedVelDot,\
    PosDotDef, PsiDotDef,\
    OutGrids, PosPsiTime, VelocTime, DynOut\
        = BeamInit.Dynamic(XBINPUT,XBOPTS, NumNodes_tot)
    # Delete unused variables.
    del PosPsiTime, VelocTime

    #Initialise quaternions for rigid-body dynamics
    Quat = np.zeros(4, ct.c_double, 'F')
    Quat[0] = 1.0

    #Write _force file
    ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL912_force.dat'
    fp = open(ofile, 'w')
    BeamIO.Write_force_File(fp, Time, ForceTime, ForcedVel, ForcedVelDot)
    fp.close()

    #Solve dynamic using f90 solver
    BeamLib.Xbeam_Solv_FreeNonlinDynamic(XBINPUT, XBOPTS, NumNodes_tot, XBELEM,\
            PosIni, PsiIni, XBNODE, NumDof,\
            PosDefor, PsiDefor, Quat,\
            NumSteps, Time, ForceTime, ForcedVel, ForcedVelDot,\
            PosDotDef, PsiDotDef, DynOut, OutGrids)

    #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, ForcedVel, ForcedVelDot)
    fp.close()
def Solve_Py(XBINPUT,XBOPTS, moduleName = None, SaveDict=Settings.SaveDict):
    """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')
    
    # I/O management
    Settings.SaveDict=SaveDict # overwrite for compatibility with 'dat' format output
    XBOUT=DerivedTypes.Xboutput()
    XBOUT.cputime.append(time.clock())  
    if SaveDict['Format']=='h5':
        Settings.WriteOut=False
        Settings.PlotTec=False
        OutList=[XBOPTS, XBINPUT, XBOUT] 

    #"Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS, moduleName)
    
    # Solve static solution
    if XBOPTS.ImpStart==True:
        PosDefor = PosIni.copy(order='F')
        PsiDefor = PsiIni.copy(order='F')
        XBOUT.ForceStaticTotal = XBINPUT.ForceStatic.copy('F')
        AddGravityLoads(XBOUT.ForceStaticTotal,XBINPUT,XBELEM,
                        AELAOPTS=None, # allows defining inertial/elastic axis
                        PsiDefor=PsiDefor,
                        chord = 0.0, # used to define inertial/elastic axis
                        PsiA_G=XBINPUT.PsiA_G,
                        FollowerForceRig=XBOPTS.FollowerForceRig.value)
    else:
        XBOPTS.Solution.value = 112 
        XBSTA = Solve_Py_Static(XBINPUT, XBOPTS, moduleName, SaveDict=SaveDict)
        XBOPTS.Solution.value = 312
        # Define Pos/Psi as fortran array (crash otherwise)
        PosDefor=XBSTA.PosDeforStatic.copy(order='F')
        PsiDefor=XBSTA.PsiDeforStatic.copy(order='F')
        XBOUT.PosDeforStatic=XBSTA.PosDeforStatic
        XBOUT.PsiDeforStatic=XBSTA.PsiDeforStatic
        XBOUT.ForceStaticTotal=XBSTA.ForceStaticTotal # includes gravity loads
        del XBSTA

    #Write deformed configuration to file
    if SaveDict['Format']=='dat':
        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)
    else:
        XBOUT.drop( PosIni=PosIni, PsiIni=PsiIni, PosDeforStatic=PosDefor.copy(), 
                    PsiDeforStatic=PsiDefor.copy())
        PyLibs.io.save.h5file(SaveDict['OutputDir'],SaveDict['OutputFileRoot']+'.h5',
                              *OutList)
    
    
    #"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"
    if SaveDict['Format']=='dat':
        ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL312_force.dat'
        fp = open(ofile,'w')
        BeamIO.Write_force_File(fp, Time, ForceTime, ForcedVel, ForcedVelDot)
        fp.close() 

    # sm write class
    XBOUT.Time=Time
    XBOUT.Vrel=ForcedVel
    XBOUT.VrelDot=ForcedVelDot

    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
    Quat =  psi2quat(XBINPUT.PsiA_G)
    XBOUT.QuatList.append(Quat)
    
    #"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"
    # Note: rank of Force increased after removing ForceTime
    Force = (XBOUT.ForceStaticTotal + XBINPUT.ForceDyn[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)


    #store initial matrices for eigenvalues analysis
    XBOUT.M0 = MglobalFull.copy()
    XBOUT.C0 = CglobalFull.copy()
    XBOUT.K0 = KglobalFull.copy()
       
    #"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, \
                                    ### sm increased rank of ForceDyn_*
                                    #(XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \
                                    #(XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \
                                    (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll[0,:,:]), \
                                    (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead[0,:,:]), \
                                    Cao, 1)[0]
                         
    Qglobal -= Qforces

    #"Initial Accel"
    dXddt[:] = np.dot(np.linalg.inv(MglobalFull), -Qglobal)
    #embed()
    #dXddt[iiblock]=0.0
    
    #"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 calculation: moved in while as depending on PsiDefor
        #Force=(XBINPUT.ForceStatic+XBINPUT.ForceDyn*ForceTime[iStep+1]).copy('F')
        #Force=(XBINPUT.ForceStatic+XBINPUT.ForceDyn[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 force

            Force = (XBINPUT.ForceStatic+XBINPUT.ForceDyn[iStep+1,:,:]).copy('F')
            AddGravityLoads(Force, XBINPUT, XBELEM,
                        AELAOPTS=None, # allows defining inertial/elastic axis
                        PsiDefor=PsiDefor,
                        chord = 0.0, # used to define inertial/elastic axis
                        PsiA_G=XbeamLib.quat2psi(Quat),
                        FollowerForceRig=XBOPTS.FollowerForceRig.value)  

            #"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)) )
            

            Q1 =  np.dot(MglobalFull, dXddt)
            Q2 = np.dot(Mvel,ForcedVelDot[iStep+1,:])
            Q3 = - np.dot(FglobalFull, Force_Dof)

            #"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
    
        # sm I/O: FoR A velocities/accelerations
        XBOUT.QuatList.append(Quat)
        XBOUT.DynOut=DynOut
        XBOUT.PsiList.append(PsiDefor.copy())   
        XBOUT.cputime.append( time.clock() - XBOUT.cputime[0] )

        if SaveDict['SaveProgress'] and SaveDict['Format']=='h5':
            iisave=np.arange(1,NumSteps.value,np.ceil(
                                      NumSteps.value/SaveDict['NumSavePoints']))
            if any(iisave==iStep):
                PyLibs.io.save.h5file(SaveDict['OutputDir'], 
                                      SaveDict['OutputFileRoot']+'.h5',*OutList)
    
    #"END Time loop"


    if SaveDict['Format'] == 'dat': 
        #"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()
    else:
        XBINPUT.ForceDyn = XBINPUT.ForceDyn + XBINPUT.ForceDyn_foll +  XBINPUT.ForceDyn_dead
        PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5', 
                              *OutList)

    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')


    return XBOUT
Beispiel #23
0
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
Beispiel #24
0
def Static(XBINPUT,XBOPTS, moduleName = None):
    """@brief Initialise everything needed for Static beam simulation."""

    # Declare variables that are not dependent on NumNodes_tot.
    XBELEM      = DerivedTypes.Xbelem(XBINPUT.NumElems,Settings.MaxElNod)
    NumDof      = ct.c_int()
    PsiIni      = np.zeros((XBINPUT.NumElems,Settings.MaxElNod,3),
                           dtype=ct.c_double, order='F')
        
    # Load customised inputs
    if moduleName != None:
        Input = __import__(moduleName)
    else:
        Input = __import__('Input')
        
    # Check inputs.
    XBINPUT, XBOPTS = Input.Setup(XBINPUT, XBOPTS)
        
    # Set-up element properties
    NumNodes_tot, XBELEM = Input.Elem(XBINPUT, XBOPTS, XBELEM)
    # Set-up nodal properties.
    PosIni, PhiNodes, BoundConds = \
        Input.Node(XBINPUT, XBOPTS, NumNodes_tot, XBELEM)
    # Compute initial (undeformed) geometry.
    BeamLib.f_xbeam_undef_geom( 
        ct.byref(ct.c_int(XBINPUT.NumElems)), 
        ct.byref(NumNodes_tot), 
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)),
        PosIni.ctypes.data_as(ct.POINTER(ct.c_double)), 
        PhiNodes.ctypes.data_as(ct.POINTER(ct.c_double)), 
        PsiIni.ctypes.data_as(ct.POINTER(ct.c_double)),
        ct.byref(XBOPTS.FollowerForce),
        ct.byref(XBOPTS.FollowerForceRig),
        ct.byref(XBOPTS.PrintInfo),
        ct.byref(XBOPTS.OutInBframe),
        ct.byref(XBOPTS.OutInaframe),
        ct.byref(XBOPTS.ElemProj),
        ct.byref(XBOPTS.MaxIterations),
        ct.byref(XBOPTS.NumLoadSteps),
        ct.byref(XBOPTS.NumGauss),
        ct.byref(XBOPTS.Solution),
        ct.byref(XBOPTS.DeltaCurved),
        ct.byref(XBOPTS.MinDelta),
        ct.byref(XBOPTS.NewmarkDamp) )
    # Write to undeformed geometry to file.
    WriteMode = 'w'
    if XBOPTS.PrintInfo==True:
        sys.stdout.write('Writing file (mode: %s) %s' %(WriteMode,
                         Settings.OutputDir + Settings.OutputFileRoot + 
                         '_SOL' + XBOPTS.Solution.value + '_und.dat\n'))
    
    BeamIO.WriteUndefGeometry(XBINPUT.NumElems,NumNodes_tot.value,XBELEM,
                              PosIni,PsiIni,
                              Settings.OutputDir + Settings.OutputFileRoot +
                              '_INIT' , WriteMode)
    if XBOPTS.PrintInfo==True:
        sys.stdout.write('Identify nodal degrees of freedom ... ')
    # Initialize nodal data derived type.
    XBNODE = DerivedTypes.Xbnode(NumNodes_tot.value)

    BeamLib.f_xbeam_undef_dofs( 
        ct.byref(ct.c_int(XBINPUT.NumElems)), 
        ct.byref(NumNodes_tot), 
        XBELEM.NumNodes.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.MemNo.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Conn.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBELEM.Length.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.PreCurv.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Psi.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Vector.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Mass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.Stiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.InvStiff.ctypes.data_as(ct.POINTER(ct.c_double)), 
        XBELEM.RBMass.ctypes.data_as(ct.POINTER(ct.c_double)), 
        BoundConds.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Master.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)), 
        XBNODE.Fdof.ctypes.data_as(ct.POINTER(ct.c_int)), 
        ct.byref(NumDof),
        XBNODE.Sflag.ctypes.data_as(ct.POINTER(ct.c_int)) )

    if XBOPTS.PrintInfo==True:
        sys.stdout.write('done\n')
    
    # Add RBMasses
    # - this is added only to the master nodes (XBNODE.Master), otherwise
    # is double accounted for.
    Master = XBNODE.Master.reshape((len(XBNODE.Master)//2,2))
    for RBM in XBINPUT.RBMass:
        # Find elements associated to the node
        #  - node numbering starts from 1 in XBELEM.Conn
        #  - numbering of ee and ll starts from 1
        #  - len(ccvec)>1 if the node is at the edge of more then 1 element
        ccvec = np.where(XBELEM.Conn == RBM['node']+1)[0]
        for cc in ccvec:
            # Check if node is a master
            ee = cc//3 + 1  # element to which node belongs
            ll =  cc%3 + 1  # local position in element
            if ll in Master[Master[:,0]==ee,1]:
                XBELEM.RBMass[cc,:,:] = RBM['Mass']

    return XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni,\
            XBNODE, NumDof
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')
Beispiel #26
0
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, moduleName = None, SaveDict=Settings.SaveDict):
    """Nonlinear static solver using Python to solve residual
    equation. Assembly of matrices is carried out with Fortran subroutines."""
    
    #"Check correct solution code"
    assert XBOPTS.Solution.value == 112, ('NonlinearStatic requested' +\
                                              ' with wrong solution code')
    
    # I/O management
    Settings.SaveDict=SaveDict # overwrite for compatibility with 'dat' format output
    XBOUT=DerivedTypes.Xboutput()
    OutList=[XBOPTS, XBINPUT, XBOUT]

    #"Initialise beam"
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS, moduleName)

    # debug
    XBOUT.xbnode=XBNODE
    XBOUT.xbelem=XBELEM
    
    #"Set initial conditions as undef config"
    PosDefor = PosIni.copy(order='F')
    PsiDefor = PsiIni.copy(order='F')

    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('Solve nonlinear static case in Python ... \n')

    # Determine orientatin of FoR A w.r.t. G (for gravity loads)
    Quat = psi2quat(XBINPUT.PsiA_G)
    Cao = Rot(Quat)
    
    #"Initialise structural eqn tensors"
    KglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); ks = ct.c_int()
    KglobalFull_foll = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); 
    FglobalFull = np.zeros((NumDof.value,NumDof.value),\
                            ct.c_double, 'F'); fs = ct.c_int()    
    
    DeltaS  = np.zeros(NumDof.value, ct.c_double, 'F')
    Qglobal = np.zeros(NumDof.value, ct.c_double, 'F')
    x       = np.zeros(NumDof.value, ct.c_double, 'F')
    dxdt    = np.zeros(NumDof.value, ct.c_double, 'F')
    
    # Add Gravity
    XBOUT.ForceStaticTotal = XBINPUT.ForceStatic.copy('F')
    AddGravityLoads(XBOUT.ForceStaticTotal,XBINPUT,XBELEM,
                    AELAOPTS=None, # allows defining inertial/elastic axis
                    PsiDefor=PsiDefor,
                    chord = 0.0, # used to define inertial/elastic axis
                    PsiA_G=XBINPUT.PsiA_G,
                    FollowerForceRig=XBOPTS.FollowerForceRig.value)

    #"Load Step tensors"
    iForceStep     = np.zeros((NumNodes_tot.value,6), ct.c_double, 'F')
    iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, 'F')
    
    rpar=1.1 # amplification factor
    IncrPercent=np.array([ rpar**(ii) for ii in range(XBOPTS.NumLoadSteps.value) ])
    step00=1./np.sum(IncrPercent)
    IncrPercent=IncrPercent*step00
    assert np.sum(IncrPercent)-1.0<1e-10, 'Algorithm for determining step-'\
       'size of boundary displacements failed. Try reducing the number of steps'
        

    #"Start Load Loop"
    # NumLoadsSteps doubled if forced displacements are enforced in the solution:
    # - the first NumLoadsSteps increase the applied load
    # - the last NumLoadsSteps increase the applied displacements at the boundaries
    if len(XBINPUT.ForcedDisp)==0: 
        Nsteps = XBOPTS.NumLoadSteps.value
    else: 
        #NumDispSteps=XBOPTS.NumLoadSteps.value
        #Nsteps = NumDispSteps+XBOPTS.NumLoadSteps.value
        Nsteps = 2* XBOPTS.NumLoadSteps.value
        # At each iLoadStep>=XBOPTS.NumLoadSteps.value, an amount of 
        # displacement, determined through the IncrPercent factor, is enforced
        # at the boundary. IncrPercent can be arbitrary but should also be such
        # that the sum of each IncrPercent=1. 
        # The initial steps should be very small, so as to avoid the solution
        # to jump to a different branch.
        # Different models are possible:
        ## equally distributed: requires a lot of loads steps
        #IncrPercent=1./NumDispSteps*np.ones((NumDispSteps,))
        ## exponential distribution: guarantees very small initial steps but 
        # reduces the total number of steps
        #rpar=1.3 # amplification factor
        #IncrPercent=np.array([ rpar**(ii) for ii in range(NumDispSteps) ])
        #step00=1./np.sum(IncrPercent)
        #IncrPercent=IncrPercent*step00

    XBOUT.CondK=[]
    #for iLoadStep in range(XBOPTS.NumLoadSteps.value):
    for iLoadStep in range(Nsteps):
        
        #"Reset convergence parameters"
        Iter = 0
        ResLog10 = 1.0

        if iLoadStep<XBOPTS.NumLoadSteps.value:
            # Gradually increase the static load
            IncrHere=IncrPercent[iLoadStep]
            iForceStep=iForceStep+IncrHere*XBOUT.ForceStaticTotal

            # mid node
            midNode=int((XBINPUT.NumNodesTot-1)/2)
            #print('R0\tR1\tdR\tRguess\tIncOld\tIncHere')
            #print(6*'%.4f\t'\
            #         %( PosDefor_0[midNode,2], 
            #            PosDefor_1[midNode,2], 
            #           (PosDefor_1-PosDefor_0)[midNode,2],
            #            PosDefor[midNode,2],
            #            IncrPercent[iLoadStep-1],
            #            IncrHere))

        else: #if iLoadStep>=XBOPTS.NumLoadSteps.value:
            # Enforce displacement at the boundary
            IncrHere=IncrPercent[iLoadStep-XBOPTS.NumLoadSteps.value]
            # update displacements (also initial guess)
            print('IncrPercent (displacements are cumulative)=%f' %IncrHere)
            PosDefor = ForceDisplacement(PosIni,PosDefor,XBINPUT.ForcedDisp,
                                               XBINPUT.PsiA_G,factor=IncrHere)
        # debug forced displacements
        #import matplotlib.pyplot as plt 
        #plt.plot(PosIni[:,0],PosIni[:,2],'k',marker='s')
        #plt.plot(PosDefor[:,0],PosDefor[:,2],'r',marker='o')
        #plt.show()
        if XBOPTS.PrintInfo.value == True:
            sys.stdout.write('  iLoad: %-10d\n' %(iLoadStep+1))
            sys.stdout.write('   SubIter DeltaF     DeltaX     ResLog10\n')

        #"Newton Iteration"
        while( (ResLog10 > XBOPTS.MinDelta.value) \
             & (Iter < XBOPTS.MaxIterations.value) ):
            
            #"Increment iteration counter"
            Iter += 1
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('   %-7d ' %(Iter))

            #"Set structural eqn tensors to zero"
            KglobalFull[:,:] = 0.0; ks = ct.c_int()
            KglobalFull_foll[:,:] = 0.0; 
            FglobalFull[:,:] = 0.0; fs = ct.c_int()
            Qglobal[:] = 0.0

            #"Assemble matrices for static problem"
            BeamLib.Cbeam3_Asbly_Static(XBINPUT, NumNodes_tot, XBELEM, XBNODE,\
                        PosIni, PsiIni, PosDefor, PsiDefor,\
                        iForceStep, NumDof,\
                        ks, KglobalFull, fs, FglobalFull, Qglobal,\
                        XBOPTS, Cao)

            #"Get forces on unconstrained nodes"
            BeamLib.f_fem_m2v(ct.byref(NumNodes_tot),\
                              ct.byref(ct.c_int(6)),\
                              iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              ct.byref(NumDof),\
                              iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),\
                              XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)) )
            
            #"Separate assembly of follower and dead loads"
            Qforces, KglobalFull_foll = \
                XbeamLib.LoadAssembly(XBINPUT, XBELEM, XBNODE, XBOPTS, NumDof, \
                                      PosIni, PsiIni, PosDefor, PsiDefor, \
                                      XBINPUT.ForceStatic_foll,XBINPUT.ForceStatic_dead, \
                                      Cao, float((iLoadStep+1)/XBOPTS.NumLoadSteps.value))[:2]
                        
            KglobalFull += KglobalFull_foll
            Qglobal = Qglobal -Qforces -np.dot(FglobalFull,iForceStep_Dof)

            if iLoadStep==0 and Iter==1:
                XBOUT.K0=KglobalFull.copy()
                XBOUT.Q0=Qglobal.copy()

            #"Calculate \Delta State Vector"
            DeltaS = - np.linalg.solve(KglobalFull, Qglobal)
            # @warning: on very stiff problem, using a matrix inversion is more 
            # robust then using np.linalg.solve. A least-squares solver can also
            # be used.
            #if np.linalg.cond(KglobalFull)>1e20:
            #    Sol=np.linalg.lstsq(KglobalFull, -Qglobal)
            #    DeltaS=Sol[0]
            #    Krank=Sol[2]
            #    sing_val=Sol[3]
            #    if Krank<NumDof.value:
            #        raise NameError('Singular matrix')
            #else:
            #    #DeltaS = - np.dot(np.linalg.inv(KglobalFull), Qglobal)
            #    DeltaS = - np.linalg.solve(KglobalFull, Qglobal)

            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write(2*'%-10.4e '%(max(abs(Qglobal)),
                                                              max(abs(DeltaS))))
            #Residual at first iteration
            if(Iter == 1):
                Res0_Qglobal = max(max(abs(Qglobal)),1)
                Res0_DeltaX  = max(max(abs(DeltaS)),1)
            #Update residual and compute log10
            Res_Qglobal = max(abs(Qglobal))
            Res_DeltaX  = max(abs(DeltaS))

            ResLog10 = max(Res_Qglobal/Res0_Qglobal, Res_DeltaX/Res0_DeltaX)
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write('%8.4f\n' %(ResLog10))

            # Update Solution (PosDefor/PsiDefor) for next iter
            BeamLib.Cbeam3_Solv_Update_Static(XBINPUT, NumNodes_tot, XBELEM,\
                                              XBNODE, NumDof, DeltaS,\
                                              PosIni, PsiIni, PosDefor,PsiDefor)

            '''
            #### Floor constrained
            Cao=RotCRV(XBINPUT.PsiA_G)
            for nn in range(NumNodes_tot.value):
                posG=np.dot(Cao,PosDefor[nn,:])
                if posG[2]<0.0:
                    posG[2]=0.0
                    PosDefor[nn,:]=np.dot(Cao.T,posG)
            '''

            #"Stop the solution"
            if(ResLog10 > 1.e10):
                sys.stderr.write(' STOP\n')
                sys.stderr.write(' The max residual is %e\n' %(ResLog10))
                return XBOUT
                exit(1)
            
        #"END Newton Loop"
    #"END Load Loop"

    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')
   
    # Prepare output
    XBOUT.drop( PosIni=PosIni,PsiIni=PsiIni,PosDeforStatic=PosDefor.copy(), 
                PsiDeforStatic=PsiDefor.copy(), K=KglobalFull.copy())

    #"Print deformed configuration"
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write('--------------------------------------\n')
        sys.stdout.write('NONLINEAR STATIC SOLUTION\n')
        sys.stdout.write('%10s %10s %10s\n' %('X','Y','Z'))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(' ')
            for inodj in range(3):
                sys.stdout.write('%12.5e' %(PosDefor[inodi,inodj]))
            sys.stdout.write('\n')
        sys.stdout.write('--------------------------------------\n')

    if SaveDict['Format']=='dat':     
        #"Write deformed configuration to file"
        ofile = Settings.OutputDir + Settings.OutputFileRoot + '_SOL112_def.dat'
        if XBOPTS.PrintInfo.value==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.value==True:
            sys.stdout.write('done\n')
        WriteMode = 'a'
    
        BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, \
                           PosDefor, PsiDefor, ofile, WriteMode)
    else:
        PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5',
                              *OutList)
    
    #"Return solution as optional output argument"
    return XBOUT #PosDefor, PsiDefor
Beispiel #28
0
def Solve_Py(XBINPUT, XBOPTS, VMOPTS, VMINPUT, AELAOPTS):
    """Nonlinear static solver using Python to solve aeroelastic
        equation. Assembly of structural matrices is carried out with 
        Fortran subroutines. Aerodynamics solved using PyAero.UVLM."""

    assert XBOPTS.Solution.value == 112, "NonlinearStatic requested" + " with wrong solution code"

    # Initialize beam.
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof = BeamInit.Static(XBINPUT, XBOPTS)
    # Set initial conditions as undef config.
    PosDefor = PosIni.copy(order="F")
    PsiDefor = PsiIni.copy(order="F")
    if XBOPTS.PrintInfo.value == True:
        sys.stdout.write("Solve nonlinear static case in Python ... \n")
    # Initialise structural eqn tensors.
    KglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, "F")
    ks = ct.c_int()
    FglobalFull = np.zeros((NumDof.value, NumDof.value), ct.c_double, "F")
    fs = ct.c_int()
    DeltaS = np.zeros(NumDof.value, ct.c_double, "F")
    Qglobal = np.zeros(NumDof.value, ct.c_double, "F")
    x = np.zeros(NumDof.value, ct.c_double, "F")
    dxdt = np.zeros(NumDof.value, ct.c_double, "F")
    # Beam Load Step tensors
    iForceStep = np.zeros((NumNodes_tot.value, 6), ct.c_double, "F")
    iForceStep_Dof = np.zeros(NumDof.value, ct.c_double, "F")

    # Initialze Aero.
    Section = InitSection(VMOPTS, VMINPUT, AELAOPTS.ElasticAxis)
    # Declare memory for Aero grid and velocities.
    Zeta = np.zeros((Section.shape[0], PosDefor.shape[0], 3), ct.c_double, "C")
    ZetaDot = np.zeros((Section.shape[0], PosDefor.shape[0], 3), ct.c_double, "C")
    # Additional Aero solver variables.
    AeroForces = np.zeros((VMOPTS.M.value + 1, VMOPTS.N.value + 1, 3), ct.c_double, "C")
    Gamma = np.zeros((VMOPTS.M.value, VMOPTS.N.value), ct.c_double, "C")
    # Init external velocities.
    Uext = InitSteadyExternalVels(VMOPTS, VMINPUT)
    # Create zero triads for motion of reference frame.
    VelA_A = np.zeros((3))
    OmegaA_A = np.zeros((3))
    # Create zero vectors for structural vars not used in static analysis.
    PosDotDef = np.zeros_like(PosDefor, ct.c_double, "F")
    PsiDotDef = np.zeros_like(PsiDefor, ct.c_double, "F")

    # Define tecplot stuff.
    FileName = Settings.OutputDir + Settings.OutputFileRoot + "AeroGrid.dat"
    Variables = ["X", "Y", "Z", "Gamma"]
    FileObject = PostProcess.WriteAeroTecHeader(FileName, "Default", Variables)

    # Start Load Loop.
    for iLoadStep in range(XBOPTS.NumLoadSteps.value):
        # Reset convergence parameters and loads.
        Iter = 0
        ResLog10 = 0.0
        XBINPUT.ForceStatic[:, :] = 0.0
        AeroForces[:, :, :] = 0.0

        # Calculate aero loads.
        if hasattr(XBINPUT, "ForcedVel"):
            CoincidentGrid(
                PosDefor,
                PsiDefor,
                Section,
                XBINPUT.ForcedVel[0, :3],
                XBINPUT.ForcedVel[0, 3:],
                PosDotDef,
                PsiDotDef,
                XBINPUT,
                Zeta,
                ZetaDot,
                ctrlSurf=VMINPUT.ctrlSurf,
            )
        else:
            CoincidentGrid(
                PosDefor,
                PsiDefor,
                Section,
                VelA_A,
                OmegaA_A,
                PosDotDef,
                PsiDotDef,
                XBINPUT,
                Zeta,
                ZetaDot,
                ctrlSurf=VMINPUT.ctrlSurf,
            )

        if hasattr(XBINPUT, "ForcedVel"):
            ZetaStar, GammaStar = InitSteadyWake(VMOPTS, VMINPUT, Zeta, XBINPUT.ForcedVel[0, :3])
        else:
            ZetaStar, GammaStar = InitSteadyWake(VMOPTS, VMINPUT, Zeta)

        # Solve for AeroForces.
        UVLMLib.Cpp_Solver_VLM(Zeta, ZetaDot, Uext, ZetaStar, VMOPTS, AeroForces, Gamma, GammaStar)

        AeroForces[:, :, :] = AELAOPTS.AirDensity * AeroForces[:, :, :]

        # Write solution to tecplot file.
        PostProcess.WriteUVLMtoTec(
            FileObject,
            Zeta,
            ZetaStar,
            Gamma,
            GammaStar,
            iLoadStep,
            XBOPTS.NumLoadSteps.value,
            iLoadStep * 1.0,
            Text=True,
        )

        # Map AeroForces to beam.
        CoincidentGridForce(XBINPUT, PsiDefor, Section, AeroForces, XBINPUT.ForceStatic)

        # Add gravity loads.
        AddGravityLoads(XBINPUT.ForceStatic, XBINPUT, XBELEM, AELAOPTS, PsiDefor, VMINPUT.c)

        # Apply factor corresponding to force step.
        iForceStep = XBINPUT.ForceStatic * float((iLoadStep + 1)) / XBOPTS.NumLoadSteps.value

        if XBOPTS.PrintInfo.value == True:
            sys.stdout.write("  iLoad: %-10d\n" % (iLoadStep + 1))
            sys.stdout.write("   SubIter DeltaF     DeltaX     ResLog10\n")

        # Start Newton Iteration.
        while (ResLog10 > np.log10(XBOPTS.MinDelta.value)) & (Iter < XBOPTS.MaxIterations.value):

            Iter += 1
            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write("   %-7d " % (Iter))

            # Set structural eqn tensors to zero
            KglobalFull[:, :] = 0.0
            ks = ct.c_int()
            FglobalFull[:, :] = 0.0
            fs = ct.c_int()
            Qglobal[:] = 0.0

            # Assemble matrices for static problem
            BeamLib.Cbeam3_Asbly_Static(
                XBINPUT,
                NumNodes_tot,
                XBELEM,
                XBNODE,
                PosIni,
                PsiIni,
                PosDefor,
                PsiDefor,
                iForceStep,
                NumDof,
                ks,
                KglobalFull,
                fs,
                FglobalFull,
                Qglobal,
                XBOPTS,
            )

            # Get state vector from current deformation.
            PosDot = np.zeros((NumNodes_tot.value, 3), ct.c_double, "F")
            PsiDot = np.zeros((XBINPUT.NumElems, Settings.MaxElNod, 3), ct.c_double, "F")

            BeamLib.Cbeam_Solv_Disp2State(
                NumNodes_tot, NumDof, XBINPUT, XBNODE, PosDefor, PsiDefor, PosDot, PsiDot, x, dxdt
            )

            # Get forces on unconstrained nodes.
            BeamLib.f_fem_m2v(
                ct.byref(NumNodes_tot),
                ct.byref(ct.c_int(6)),
                iForceStep.ctypes.data_as(ct.POINTER(ct.c_double)),
                ct.byref(NumDof),
                iForceStep_Dof.ctypes.data_as(ct.POINTER(ct.c_double)),
                XBNODE.Vdof.ctypes.data_as(ct.POINTER(ct.c_int)),
            )

            # Calculate \Delta RHS.
            Qglobal = Qglobal - np.dot(FglobalFull, iForceStep_Dof)

            # Calculate \Delta State Vector.
            DeltaS = -np.dot(np.linalg.inv(KglobalFull), Qglobal)

            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write("%-10.4e %-10.4e " % (max(abs(Qglobal)), max(abs(DeltaS))))

            # Update Solution.
            BeamLib.Cbeam3_Solv_Update_Static(
                XBINPUT, NumNodes_tot, XBELEM, XBNODE, NumDof, DeltaS, PosIni, PsiIni, PosDefor, PsiDefor
            )

            # Record residual at first iteration.
            if Iter == 1:
                Res0_Qglobal = max(abs(Qglobal)) + 1.0e-16
                Res0_DeltaX = max(abs(DeltaS)) + 1.0e-16

            # Update residual and compute log10
            Res_Qglobal = max(abs(Qglobal)) + 1.0e-16
            Res_DeltaX = max(abs(DeltaS)) + 1.0e-16
            ResLog10 = max([np.log10(Res_Qglobal / Res0_Qglobal), np.log10(Res_DeltaX / Res0_DeltaX)])

            if XBOPTS.PrintInfo.value == True:
                sys.stdout.write("%8.4f\n" % (ResLog10))

            # Stop the solution.
            if ResLog10 > 10.0:
                sys.stderr.write(" STOP\n")
                sys.stderr.write(" The max residual is %e\n" % (ResLog10))
                exit(1)
            elif Res_DeltaX < 1.0e-14:
                break

        # END Newton iteration
    # END Load step loop

    if XBOPTS.PrintInfo.value == True:
        sys.stdout.write(" ... done\n")

    # Write deformed configuration to file.
    ofile = Settings.OutputDir + Settings.OutputFileRoot + "_SOL112_def.dat"
    if XBOPTS.PrintInfo.value == 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.value == True:
        sys.stdout.write("done\n")
    WriteMode = "a"

    BeamIO.OutputElems(XBINPUT.NumElems, NumNodes_tot.value, XBELEM, PosDefor, PsiDefor, ofile, WriteMode)

    # Print deformed configuration.
    if XBOPTS.PrintInfo.value == True:
        sys.stdout.write("--------------------------------------\n")
        sys.stdout.write("NONLINEAR STATIC SOLUTION\n")
        sys.stdout.write("%10s %10s %10s\n" % ("X", "Y", "Z"))
        for inodi in range(NumNodes_tot.value):
            sys.stdout.write(" ")
            for inodj in range(3):
                sys.stdout.write("%12.5e" % (PosDefor[inodi, inodj]))
            sys.stdout.write("\n")
        sys.stdout.write("--------------------------------------\n")

    # Close Tecplot ascii FileObject.
    PostProcess.CloseAeroTecFile(FileObject)

    # Return solution
    return PosDefor, PsiDefor, Zeta, ZetaStar, Gamma, GammaStar, iForceStep
def Solve_Py(XBINPUT,XBOPTS,SaveDict=Settings.SaveDict):

    """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')
    
    # I/O management
    Settings.SaveDict=SaveDict # overwrite for compatibility with 'dat' format output
    XBOUT=DerivedTypes.Xboutput()  
    XBOUT.cputime.append(time.clock())
    if SaveDict['Format']=='h5':
        Settings.WriteOut=False
        Settings.PlotTec=False
        OutList=[XBOPTS, XBINPUT, XBOUT]
            
    #Initialise beam
    XBINPUT, XBOPTS, NumNodes_tot, XBELEM, PosIni, PsiIni, XBNODE, NumDof \
                = BeamInit.Static(XBINPUT,XBOPTS)
     
    # Solve static
    # Note: gravity loads added according to XBINPUT.PsiA_G into Solve_Py_Static
    if XBOPTS.ImpStart==True:
        PosDefor = PosIni.copy(order='F')
        PsiDefor = PsiIni.copy(order='F')
        XBOUT.ForceStaticTotal = XBINPUT.ForceStatic.copy('F')
        AddGravityLoads(XBOUT.ForceStaticTotal,XBINPUT,XBELEM,
                        AELAOPTS=None, # allows defining inertial/elastic axis
                        PsiDefor=PsiDefor,
                        chord=0.0, # used to define inertial/elastic axis
                        PsiA_G=XBINPUT.PsiA_G,
                        FollowerForceRig=XBOPTS.FollowerForceRig.value)
    else:
        if XBNODE.Sflag.any():
            raise NameError('Static solution with spherical joint not '
                                                 'implemented and/or possible!')
        XBOPTS.Solution.value = 112
        XBSTA = Solve_Py_Static(XBINPUT, XBOPTS, SaveDict=SaveDict)
        XBOPTS.Solution.value = 912
        PosDefor=XBSTA.PosDeforStatic.copy(order='F')
        PsiDefor=XBSTA.PsiDeforStatic.copy(order='F')
        XBOUT.PosDeforStatic  =XBSTA.PosDeforStatic
        XBOUT.PsiDeforStatic  =XBSTA.PsiDeforStatic
        XBOUT.ForceStaticTotal=XBSTA.ForceStaticTotal # includes gravity
        del XBSTA


    if SaveDict['Format']=='dat': 
        PyLibs.io.dat.write_SOL912_def(XBOPTS,XBINPUT,XBELEM,
                                NumNodes_tot,PosDefor,PsiDefor,SaveDict)
    
    ## sm I/O
    #XBOUT.PosDefor=PosDefor
    #XBOUT.PsiDefor=PsiDefor

    #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
    if SaveDict['Format']=='dat': 
        PyLibs.io.dat.write_SOL912_force(XBOPTS,XBINPUT,XBELEM,
                                         Time, ForceTime, Vrel, VrelDot)
    
    # sm I/O
    ### why forced velocity with Sol912 ???
    ### If forced velocities are prescribed, then is Sol312
    XBOUT.Time_force=Time               # ...SOL912_force.dat
    XBOUT.ForceTime_force=ForceTime
    XBOUT.Vrel_force=Vrel
    XBOUT.VrelDot_force=VrelDot  
    # debugging 
    XBOUT.KsysList=[]
    XBOUT.MsysList=[]
    
    #---------------------------------------------------- Start Dynamic Solution

    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 =  xbl.psi2quat(XBINPUT.PsiA_G)
    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
    # Gravity needs including for correctly computing the accelerations at the 
    # 1st time-step
    # Note: rank of Force increased after removing ForceTime
    #Force = (XBINPUT.ForceStatic + XBINPUT.ForceDyn*ForceTime[0]).copy('F')
    #Force = (XBINPUT.ForceStatic + XBINPUT.ForceDyn[0,:,:]).copy('F')
    Force = (XBOUT.ForceStaticTotal + XBINPUT.ForceDyn[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, \
                                    ### sm increased rank of ForceDyn_*
                                    #(XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \
                                    #(XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \
                                    (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll[0,:,:]), \
                                    (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead[0,:,:]), \
                                    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:])
    
    # -------------------------------------------------------------------   
    # special BCs
    iiblock=[]
    if XBNODE.Sflag.any():
        # block translations (redundant:)
        for ii in range(3):
            if XBINPUT.EnforceTraVel_FoRA[ii] == True: iiblock.append(NumDof.value+ii)
        # block rotations
        iirotfree=[] # free rotational dof 
        for ii in range(3):
            if XBINPUT.EnforceAngVel_FoRA[ii] is True: iiblock.append(NumDof.value+3+ii)
            else: iirotfree.append(NumDof.value+3+ii)

    if len(iiblock)>0:
        Msys[iiblock,:] = 0.0
        Msys[:,iiblock] = 0.0 
        Msys[iiblock,iiblock] = 1.0
        Csys[iiblock,:] = 0.0
        Ksys[iiblock,:] = 0.0
        Qsys[iiblock]   = 0.0
        if XBINPUT.sph_joint_damping is not None:
            Csys[iirotfree,iirotfree] += XBINPUT.sph_joint_damping
            Qsys[iirotfree] += XBINPUT.sph_joint_damping*dQdt[iirotfree]
    #from IPython import embed; embed()     
    # add structural damping term
    if XBINPUT.str_damping_model is not None:
        Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                XBINPUT.str_damping_param['beta']  * KssFull
        Qsys[:NumDof.value] += np.dot( Cdamp, dQdt[:NumDof.value] )                  
        pass
     
    # -------------------------------------------------------------------  
    
    #store initial matrices for eigenvalues analysis
    XBOUT.Msys0 = Msys.copy()
    XBOUT.Csys0 = Csys.copy()
    XBOUT.Ksys0 = Ksys.copy()
    XBOUT.Qsys0 = Qsys.copy()

    #Initial Accel
    #dQddt[:] = np.dot(np.linalg.inv(Msys), -Qsys)
    #dQddt[:] = np.linalg.solve(Msys,-Qsys) # most correct but inaccurate / Msys ill-conditioned
    # solve only rigid body dynamics
    dQddt[:NumDof.value]=0.0
    dQddt[NumDof.value:]=np.linalg.solve( Msys[NumDof.value:,NumDof.value:],
                                                          -Qsys[NumDof.value:] ) 


    XBOUT.dQddt0=dQddt.copy()
    
    #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
    
    # sm write class
    XBOUT.QuatList.append(Quat)
    XBOUT.PosIni=PosIni
    XBOUT.PsiIni=PsiIni 
   
    XBOUT.AsysListStart=[]
    XBOUT.AsysListEnd=[]
    XBOUT.MsysList=[]
    XBOUT.CsysList=[]
    XBOUT.KsysList=[]
    
    #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
        ### Corrector
        #dQddt[:] = 0.0 # initial guess for acceleration at next time-step is zero
        Q += beta*dQddt*dt**2
        dQdt += gamma*dQddt*dt

        #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 displacements 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)


            ### sm: removed ForceTime and increased rank of ForceDyn 
            # Note: do not add gravity load here!
            #Force at current time-step
            #Force = (XBINPUT.ForceStatic+XBINPUT.ForceDyn*ForceTime[iStep+1]).copy('F')
            Force = (XBINPUT.ForceStatic+XBINPUT.ForceDyn[iStep+1,:,:]).copy('F')

            # Add gravity loads (accounting for new orientation)
            AddGravityLoads(Force, XBINPUT, XBELEM,
                        AELAOPTS=None, # allows defining inertial/elastic axis
                        PsiDefor=PsiDefor,
                        chord = 0.0, # used to define inertial/elastic axis
                        PsiA_G=xbl.quat2psi(Quat),
                        FollowerForceRig=XBOPTS.FollowerForceRig.value)

            #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, \
                                            ### sm: increased rank of ForceDyn_*
                                            #(XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll*tmpForceTime), \
                                            #(XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead*tmpForceTime), \
                                            (XBINPUT.ForceStatic_foll + XBINPUT.ForceDyn_foll[iStep+1,:,:] ), \
                                            (XBINPUT.ForceStatic_dead + XBINPUT.ForceDyn_dead[iStep+1,:,:] ), \
                                            Cao,1)
                                   
            Qstruc -= tmpQforces      
            Qrigid -= tmpQrigid
    
            #Compute residual
            Qstruc += -np.dot(FstrucFull, Force_Dof)
            Qrigid += -np.dot(FrigidFull, Force_All)
            
            # final of last iter
            XBOUT.Qstruc=Qstruc.copy()
            XBOUT.Qrigid=Qrigid.copy()
            # final of initial iter
            if iStep==0:
                XBOUT.Qstruc0=Qstruc.copy()
                XBOUT.Qrigid0=Qrigid.copy()
  
            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)

            # include damping
            if XBINPUT.str_damping_model == 'prop':
                Cdamp = XBINPUT.str_damping_param['alpha'] * MssFull + \
                        XBINPUT.str_damping_param['beta']  * KssFull
                Csys[:NumDof.value,:NumDof.value] += Cdamp
                Qsys[:NumDof.value] += np.dot(Cdamp, dQdt[:NumDof.value])
                                
            # special BCs
            # if  SphFlag:
            if len(iiblock)>0: # allow to enforce only attitude while keeping velocity free
                Msys[iiblock,:] = 0.0
                Msys[iiblock,iiblock] = 1.0
                Csys[iiblock,:] = 0.0
                Ksys[iiblock,:] = 0.0
                Qsys[iiblock]   = 0.0
                if XBINPUT.sph_joint_damping is not None:
                    Csys[iirotfree,iirotfree] += XBINPUT.sph_joint_damping
                    Qsys[iirotfree] += XBINPUT.sph_joint_damping*dQdt[iirotfree]

                #XBOUT.Msys=Msys.copy('F')
                #XBOUT.Qsys=Qsys.copy('F')
                #XBOUT.Csys=Csys.copy('F')
                #XBOUT.Ksys=Ksys.copy('F')
  
            #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 Newton-Raphson
        
        # debug
        #XBOUT.ForceRigidList.append( np.dot(FrigidFull, Force_All).copy() )
        
        #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'))
    
        # sm: append outputs
        XBOUT.QuatList.append(Quat.copy())
 
        # sm I/O: FoR A velocities/accelerations
        XBOUT.Time=Time                     # ...dyn.dat
        #XBOUT.PosPsiTime = PosPsiTime       
        
        XBOUT.DynOut=DynOut                 # ...shape.dat
        
        XBOUT.Vrel=Vrel                     # ...rigid.dat
        XBOUT.VrelDot=VrelDot
        #XBOUT.PosPsiTime=PosPsiTime          
 
        XBOUT.PsiList.append(PsiDefor.copy())   

        XBOUT.cputime.append( time.clock() - XBOUT.cputime[0] )

        if SaveDict['SaveProgress']:
            iisave=np.arange(1,NumSteps.value,np.ceil(NumSteps.value/SaveDict['NumSavePoints']))
            if any(iisave==iStep):
                PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5', 
                                      *OutList)
    
    #END Time loop

    if SaveDict['Format'] == 'dat': 
        PyLibs.io.dat.write_SOL912_final(Time, PosPsiTime, 
                                         NumNodes_tot, DynOut, Vrel, VrelDot, SaveDict) 
    
    if XBOPTS.PrintInfo.value==True:
        sys.stdout.write(' ... done\n')

    # sm I/O: FoR A velocities/accelerations
    XBOUT.Time=Time                     # ...dyn.dat
    XBOUT.PosPsiTime = PosPsiTime       
    
    XBOUT.DynOut=DynOut                 # ...shape.dat
    
    XBOUT.Vrel=Vrel                     # ...rigid.dat
    XBOUT.VrelDot=VrelDot
    XBOUT.PosPsiTime=PosPsiTime    
    
    # save h5 
    XBINPUT.ForceDyn = XBINPUT.ForceDyn + XBINPUT.ForceDyn_foll + XBINPUT.ForceDyn_dead
    del(XBINPUT.ForceDyn_dead)
    del(XBINPUT.ForceDyn_foll)
    PyLibs.io.save.h5file(SaveDict['OutputDir'], SaveDict['OutputFileRoot']+'.h5', *OutList)
     
    
    return XBOUT