예제 #1
0
def bar1():
    print '---------------------------------------------------------------'
    print 'Clamped bar loaded at the right end with unit displacement'
    print '[00]-[01]-[02]-[03]-[04]-[05]-[06]-[07]-[08]-[09]-[10]'
    print 'u[0] = 0, u[10] = 1'
    K = SysMtxAssembly()
    dof_map, mtx_arr = get_bar_mtx_array(shape=10)
    K.add_mtx_array(dof_map_arr=dof_map, mtx_arr=mtx_arr)
    K.register_constraint(a=0,  u_a=0.)  # clamped end
    K.register_constraint(a=10, u_a=1.)
    K.register_constraint(a=10, u_a=1.)
    K_dense = DenseMtx(assemb=K)
    R = zeros(K.n_dofs)
    print 'K\n', K_dense
    print 'R\n', R
    print 'K_arrays'
    for i, sys_mtx_array in enumerate(K.sys_mtx_arrays):
        print 'i\n', sys_mtx_array.mtx_arr
    K.apply_constraints(R)
    K_dense = DenseMtx(assemb=K)
    print 'K\n', K_dense
    print 'R\n', R
    print 'K_arrays'
    for i, sys_mtx_array in enumerate(K.sys_mtx_arrays):
        print 'i\n', sys_mtx_array.mtx_arr
    print 'u =',  K.solve(R)
    print
예제 #2
0
def bar1():
    print '---------------------------------------------------------------'
    print 'Clamped bar loaded at the right end with unit displacement'
    print '[00]-[01]-[02]-[03]-[04]-[05]-[06]-[07]-[08]-[09]-[10]'
    print 'u[0] = 0, u[10] = 1'
    K = SysMtxAssembly()
    dof_map, mtx_arr = get_bar_mtx_array( shape = 10 )
    K.add_mtx_array( dof_map_arr = dof_map, mtx_arr = mtx_arr )
    K.register_constraint( a = 0,  u_a = 0. ) # clamped end
    K.register_constraint( a = 10, u_a = 1. )
    K.register_constraint( a = 10, u_a = 1. )
    DenseMtx( assemb = K ).configure_traits()
    R = zeros( K.n_dofs )
    print 'u =',  K.solve( R )
    print
예제 #3
0
def bar8():
    print '---------------------------------------------------------------'
    print 'Single clamped element with two constraints'
    print 'within a single element'
    print '[0]-[1]'
    print 'u[0] = u[1], u[1] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 1 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    # add constraints
    K.register_constraint( a = 0, alpha = [1], ix_a = [1] )
    K.register_constraint( a = 1, u_a = 1. ) # clamped end
    # add load
    R = zeros( K.n_dofs )
    print 'u =', K.solve( R )
    print
예제 #4
0
    def setup( self ):

        # Put the spatial domain into the spatial context
        #
        self.sctx = sctx = self.sdomain.new_scontext()
        self.sctx.sdomain = self.sdomain

        # Let the boundary conditions setup themselves within the
        # spatial context
        #
        # TODO - the setup needs the link to the algorithm and to the
        # time-steppers as well.!
        #
        self.bcond_mngr.setup( sctx )

        # Let the response variables setup themselves within the
        # spatial context
        #
        self.rtrace_mngr.setup( self.sdomain )

        # Set up the system matrix
        #
        self.K = SysMtxAssembly()

        # Register the essential boundary conditions in the system matrix
        #
        self.bcond_mngr.apply_essential( self.K )

        self.tse_integ.apply_constraints( self.K )

        # Prepare the global update flag
        sctx.update_state_on = False

        if self.u_processor:
            if self.u_processor.sd == None:
                self.u_processor.sd = self.sdomain

        # add-on parameters to be passed to every inner loop
        # they are provided by u_processors 
        #
        self.kw = {}
        self.args = []
예제 #5
0
    def _get_stiffness( self, editor, object ):
        tstepper = editor.object.tstepper


        if isinstance( object, TStepper ):
            U_k = tstepper.U_k
            d_U = tstepper.d_U
            K, R = tstepper.eval( 'predictor', U_k, d_U, 0, 0 )
            print 'constraints'
            K.print_constraints()
            K.apply_constraints( R )
        else:

            dots = object.dots
            U_k = tstepper.U_k
            d_U = tstepper.d_U
            sctx = tstepper.sctx

            F_int, K_mtx = dots.get_corr_pred( sctx, U_k, d_U, 0, 0 )

            # put the matrix into the system assembly
            #
            K = SysMtxAssembly()
            if isinstance( K_mtx, ndarray ):
                K.add_mtx( K_mtx )
            elif isinstance( K_mtx, SysMtxArray ):
                K.sys_mtx_arrays.append( K_mtx )
            elif isinstance( K_mtx, list ):
                K.sys_mtx_arrays = K_mtx
            elif isinstance( K_mtx, SysMtxAssembly ):
                K.sys_mtx_arrays = K_mtx.sys_mtx_arrays

            n_dofs = tstepper.sdomain.n_dofs
            K.add_mtx( zeros( ( 1, 1 ), dtype = 'float_' ), array( [n_dofs - 1], dtype = 'int_' ) )

        return K
예제 #6
0
def bar4():
    print '---------------------------------------------------------------'
    print 'Clamped bar 3 domains, each with 2 elems (displ at right end)'
    print '[0]-[1]-[2] [3]-[4]-[5] [6]-[7]-[8]'
    print 'u[0] = 0, u[2] = u[3], u[5] = u[6], u[8] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 2 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    dof_map2, mtx_arr2 = get_bar_mtx_array( shape = 2 )
    K.add_mtx_array( dof_map_arr = dof_map2+3, mtx_arr = mtx_arr2 )
    dof_map3, mtx_arr3 = get_bar_mtx_array( shape = 2 )
    K.add_mtx_array( dof_map_arr = dof_map3+6, mtx_arr = mtx_arr3 )
    # add constraints
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 2, alpha = [1], ix_a = [3] )
    K.register_constraint( a = 5, alpha = [1], ix_a = [6] )
    K.register_constraint( a = 8, u_a = 1. ) # loaded end
    # add load
    R = zeros( K.n_dofs )
    print 'u =', K.solve( R )
    print
예제 #7
0
def bar2():
    print '---------------------------------------------------------------'
    print 'Clamped bar composed of two linked bars loaded at the right end'
    print '[00]-[01]-[02]-[03]-[04]-[05]-[06]-[07]-[08]-[09]-[10]'
    print '[11]-[12]-[13]-[14]-[15]-[16]-[17]-[18]-[19]-[20]-[21]'
    print 'u[0] = 0, u[5] = u[16], R[-1] = R[21] = 10'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 10 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    dof_map2, mtx_arr2 = get_bar_mtx_array( shape = 10 )
    # Note the dof map of the second br must be adjusted to start 
    # the DOF enumeration with 3
    n_dofs1 = K.n_dofs
    K.add_mtx_array( dof_map_arr = dof_map2+n_dofs1, mtx_arr = mtx_arr2 )
    # add constraints
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 5, alpha = [1], ix_a = [16] )
    # add load
    R = zeros( K.n_dofs )
    R[-1] = 10
    print 'u =', K.solve( R )
    print
    print '---------------------------------------------------------------'
    print 'Clamped bar composed of two linked bars control displ at right'
    print 'u[0] = 0, u[5] = u[16], u[21] = 1'
    # Remove the load and put a unit displacement at the right end
    # Note, the load is irrelevant in this case and will be rewritten
    #
    K.register_constraint( a = 21, u_a = 1 )
    print 'u =', K.solve( R )
    print
예제 #8
0
def bar6():
    print '---------------------------------------------------------------'
    print 'Clamped bar with 4 elements. Elements 2-4 are reinforced '
    print 'with another bar with 1 element linked proportianally'
    print '[0]-[1]-[2]-[3]-[4]'
    print '      [5]-[6]'
    print 'u[0] = 0, u[1] = u[5], u[3] = u[7], u[4] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 4 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    dof_map2, mtx_arr2 = get_bar_mtx_array( shape = 1 )
    K.add_mtx_array( dof_map_arr = dof_map2+5, mtx_arr = mtx_arr2 )
    # add constraints
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 5, alpha = [0.5,0.5], ix_a = [1,2] )
    K.register_constraint( a = 6, alpha = [0.5,0.5], ix_a = [2,3] )
    K.register_constraint( a = 4, u_a = 1. ) # loaded end
    # add load
    R = zeros( K.n_dofs )
    print 'u =', K.solve( R )
    print
예제 #9
0
class TStepper(HasTraits):

    '''Time stepper object for non-linear Newton-Raphson solver.
    '''

    mats_eval = Property(Instance(MATSEval))
    '''Finite element formulation object.
    '''
    @cached_property
    def _get_mats_eval(self):
        return MATSEval()

    fets_eval = Property(Instance(FETS1D52ULRH))
    '''Finite element formulation object.
    '''
    @cached_property
    def _get_fets_eval(self):
        return FETS1D52ULRH()

    A = Property
    '''array containing the A_m, L_b, A_f
    '''
    @cached_property
    def _get_A(self):
        return np.array([self.fets_eval.A_m, self.fets_eval.L_b, self.fets_eval.A_f])

    # specimen length
    L_x = Float(18.4)

    # Number of elements
    n_e_x = Int(10)

    domain = Property(Instance(FEGrid), depends_on='L_x, n_e_x')
    '''Diescretization object.
    '''
    @cached_property
    def _get_domain(self):
        # Element definition
        domain = FEGrid(coord_max=(self.L_x,),
                        shape=(self.n_e_x,),
                        fets_eval=self.fets_eval)
        return domain

    bc_list = List(Instance(BCDof))

    J_mtx = Property(depends_on='L_x, n_e_x')
    '''Array of Jacobian matrices.
    '''
    @cached_property
    def _get_J_mtx(self):
        fets_eval = self.fets_eval
        domain = self.domain
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)
        # [ n_e, n_geo_r, n_dim_geo ]
        elem_x_map = domain.elem_X_map
        # [ n_e, n_ip, n_dim_geo, n_dim_geo ]
        J_mtx = np.einsum('ind,enf->eidf', dNr_geo, elem_x_map)
        return J_mtx

    J_det = Property(depends_on='L_x, n_e_x')
    '''Array of Jacobi determinants.
    '''
    @cached_property
    def _get_J_det(self):
        return np.linalg.det(self.J_mtx)

    B = Property(depends_on='L_x, n_e_x')
    '''The B matrix
    '''
    @cached_property
    def _get_B(self):
        '''Calculate and assemble the system stiffness matrix.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain

        n_s = mats_eval.n_s

        n_dof_r = fets_eval.n_dof_r
        n_nodal_dofs = fets_eval.n_nodal_dofs

        n_ip = fets_eval.n_gp
        n_e = domain.n_active_elems
        #[ d, i]
        r_ip = fets_eval.ip_coords[:, :-2].T
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)

        J_inv = np.linalg.inv(self.J_mtx)

        # shape function for the unknowns
        # [ d, n, i]
        Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None,:])
        dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1])

        # [ i, n, d ]
        Nr = np.einsum('dni->ind', Nr)
        dNr = np.einsum('dni->ind', dNr)
        Nx = Nr
        # [ n_e, n_ip, n_dof_r, n_dim_dof ]
        dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

        B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_nodal_dofs), dtype='f')
        B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0]
        B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0]
        B_factors = np.array([-1, 1], dtype='float_')
        B[:, :,:, B_N_n_rows, B_N_n_cols] = (B_factors[None, None,:] *
                                              Nx[:, :, N_idx])
        B[:, :,:, B_dN_n_rows, B_dN_n_cols] = dNx[:,:,:, dN_idx]

        return B

    def apply_essential_bc(self):
        '''Insert initial boundary conditions at the start up of the calculation.. 
        '''
        self.K = SysMtxAssembly()
        for bc in self.bc_list:
            bc.apply_essential(self.K)

    def apply_bc(self, step_flag, K_mtx, F_ext, t_n, t_n1):
        '''Apply boundary conditions for the current load increement
        '''
        for bc in self.bc_list:
            bc.apply(step_flag, None, K_mtx, F_ext, t_n, t_n1)

    def get_corr_pred(self, step_flag, d_U, eps, sig, t_n, t_n1):
        '''Function calculationg the residuum and tangent operator.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain
        elem_dof_map = domain.elem_dof_map
        n_e = domain.n_active_elems
        n_dof_r, n_dim_dof = self.fets_eval.dof_r.shape
        n_nodal_dofs = self.fets_eval.n_nodal_dofs
        n_el_dofs = n_dof_r * n_nodal_dofs
        # [ i ]
        w_ip = fets_eval.ip_weights

        d_u_e = d_U[elem_dof_map]
        #[n_e, n_dof_r, n_dim_dof]
        d_u_n = d_u_e.reshape(n_e, n_dof_r, n_nodal_dofs)
        #[n_e, n_ip, n_s]
        d_eps = np.einsum('einsd,end->eis', self.B, d_u_n)

        # update strain
        eps += d_eps
#         if np.any(sig) == np.nan:
#             sys.exit()

        # material response state variables at integration point
        sig, D = mats_eval.get_corr_pred(eps, d_eps, sig, t_n, t_n1)

        # system matrix
        self.K.reset_mtx()
        Ke = np.einsum('i,s,einsd,eist,eimtf,ei->endmf',
                       w_ip, self.A, self.B, D, self.B, self.J_det)

        self.K.add_mtx_array(
            Ke.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map)

        # internal forces
        # [n_e, n_n, n_dim_dof]
        Fe_int = np.einsum('i,s,eis,einsd,ei->end',
                           w_ip, self.A, sig, self.B, self.J_det)
        F_int = -np.bincount(elem_dof_map.flatten(), weights=Fe_int.flatten())
        self.apply_bc(step_flag, self.K, F_int, t_n, t_n1)
        return F_int, self.K, eps, sig
예제 #10
0
 def _get_K(self):
     return SysMtxAssembly()
예제 #11
0
class TStepper(IBVResource):
    """
    The TStepper is a spatially bounded TStepperEval.

    The binding is done by associating the time-stepper with a spatial
    object. In fact, any TStepper, not only the top-level one must
    be associated with spatial object. For the chained
    sub-time-steppers, this association is done using a parameter sctx
    (see the specification above). This parameters stands for spatial
    context. Note, the distinction between the spatial object and
    spatial context. While the spatial object represents a single
    spatial entity (one of domain, element, layer or material point)
    the spatial context represents a complex reference within the
    spatial object In particular, spatial context of a particular
    material point is represented as tuple containing tuple of
    references to [domain, element, layer, integration cell, material
    point].
    """

    # service specifiers - used to link the service to this object
    service_class = 'ibvpy.plugins.tstepper_service.TStepperService'
    service_attrib = 'tstepper'

    # Integration terms involved in the evaluation of the
    # incremetal spatial integrals.
    # Must be either an instance of ts_eval or a tuple specifying a pair
    # ( ts_eval, sdomain ) or a list of tuples with the pairs
    # [ ( ts_eval, sdomain ), ( ts_eval, sdomain ), ... ]
    #

    # Sub-time-stepper or integrator.
    #
    tse = Instance(ITStepperEval)

    tse_integ = Property(depends_on='tse,_sdomain, _sdomain.changed_structure')

    @cached_property
    def _get_tse_integ(self):
        if self.tse:
            self.tse.tstepper = self
            return self.tse
        else:
            self.sdomain.dots.tstepper = self
            return self.sdomain.dots

    # Spatial domain to bind the time-stepper to.
    # For convenience automatically convert the plain list to FEDomainList
    #
    _sdomain = Instance(ISDomain)
    sdomain = Property(Instance(ISDomain))

    def _set_sdomain(self, value):
        if isinstance(value, FEGrid):
            # construct FERefinementGrid and FEDomain
            self._sdomain = FEDomain()
            fe_rgrid = FERefinementGrid(domain=self._sdomain,
                                        fets_eval=value.fets_eval)
            value.level = fe_rgrid
        elif isinstance(value, FERefinementGrid):
            # construct FEDomain
            self._sdomain = FEDomain()
            value.domain = self._sdomain
        elif isinstance(value, list):
            self._sdomain = FEDomain()
            for d in value:
                if isinstance(d, FEGrid):
                    fe_rgrid = FERefinementGrid(domain=self._sdomain,
                                                fets_eval=d.fets_eval)
                    d.level = fe_rgrid
                elif isinstance(d, FESubDomain):
                    d.domain = self._sdomain
                else:
                    raise TypeError, 'The list can contain only FEGrid or FERefinementGrid'
        else:
            self._sdomain = value

    def _get_sdomain(self):
        if self._sdomain == None:
            self._sdomain = SDomain()
        return self._sdomain

    subdomains = Property()

    def _get_subdomains(self):
        if self.sdomain == None:
            return []
        return self.sdomain.subdomains

    xdomains = Property()

    def _get_xdomains(self):
        if self.sdomain == None:
            return []
        return self.sdomain.xdomains

    def redraw(self):
        self.sdomain.redraw()

    sctx = Instance(SContext)

    # Boundary condition manager
    #
    bcond_mngr = Instance(BCondMngr)

    def _bcond_mngr_default(self):
        return BCondMngr()

    # Convenience constructor
    #
    # This property provides the possibility to write
    # tstepper.bcond_list = [BCDof(var='u',dof=5,value=0, ... ]
    # The result gets propageted to the BCondMngr
    #
    bcond_list = Property(List)

    def _get_bcond_list(self):
        return self.bcond_mngr.bcond_list

    def _set_bcond_list(self, bcond_list):
        self.bcond_mngr.bcond_list = bcond_list

    # Response variable manager
    #
    rtrace_mngr = Instance(RTraceMngr)

    def _rtrace_mngr_default(self):
        return RTraceMngr(tstepper=self)

    # Convenience constructor
    #
    # This property provides the possibility to write
    # tstepper.bcond_list = [RVDof(var='u',dof=5,value=0, ... ]
    # The result gets propageted to the RTraceMngr
    #
    rtrace_list = Property(List)

    def _get_rtrace_list(self):
        return self.rtrace_mngr.rtrace_list

    def _set_rtrace_list(self, rtrace_list):
        self.rtrace_mngr.rtrace_list = rtrace_list

    # Possibility to add a callable for derived
    # variables of the control variable.
    u_processor = Callable

    # Backward reference to the time-loop in order to accommadate the
    # response-trace-evaluators from the top level. These include
    # bookkeeping data like memory usage or solving time per selected
    # type of operation.
    #
    tloop = WeakRef

    dir = Property

    def _get_dir(self):
        return self.tloop.dir

    dof_resultants = Bool(True)

    rte_dict = Property(Dict, depends_on='tse')

    @cached_property
    def _get_rte_dict(self):
        '''
        Gather all the currently applicable evaluators from the sub-ts
        and from the time-loop.

        Note the control data (time-loop data) is available within the
        model to construct flexible views (tracers) on the model.
        '''
        _rte_dict = {}

        def _get_F_int(sctx, U_k, *args, **kw):
            return self.F_int

        if self.dof_resultants:
            _rte_dict['F_int'] = _get_F_int  # lambda sctx, U_k: self.F_int
            _rte_dict['F_ext'] = lambda sctx, U_k, *args, **kw: self.F_ext

        _rte_dict.update(self.tse_integ.rte_dict)
        if self.tloop:
            _rte_dict.update(self.tloop.rte_dict)
        return _rte_dict

    def new_cntl_var(self):
        return self.tse_integ.new_cntl_var()

    def new_resp_var(self):
        return self.tse_integ.new_resp_var()

    U_k = Property(depends_on='_sdomain.changed_structure')

    @cached_property
    def _get_U_k(self):
        '''
         Setup the primary state variables on demand
        '''
        U_k = self.new_cntl_var()
        return U_k

    d_U = Property(depends_on='_sdomain.changed_structure')

    @cached_property
    def _get_d_U(self):
        '''
         Current increment of the displacement variable
        '''
        return self.new_cntl_var()

    F_ext = Property(depends_on='_sdomain.changed_structure')

    @cached_property
    def _get_F_ext(self):
        '''
         Return the response variable to be used when assembling the
         boundary conditions. Should the bcond_mngr take care of this?
         That's the source object, isn't it? BCondMngr is the bounded
         version of the conditions, it could supply the matrix
         autonomously.

        '''
        return self.new_resp_var()

    # missing - setup of the time-stepper itself. reacting to changes
    # in the sub time-steppers. bcond_list and rtrace_list must be reset once
    # a change has been performed either in a spatial domain or in
    # tse.
    #
    def setup(self):

        # Put the spatial domain into the spatial context
        #
        self.sctx = sctx = self.sdomain.new_scontext()
        self.sctx.sdomain = self.sdomain

        # Let the boundary conditions setup themselves within the
        # spatial context
        #
        # TODO - the setup needs the link to the algorithm and to the
        # time-steppers as well.!
        #
        self.bcond_mngr.setup(sctx)

        # Let the response variables setup themselves within the
        # spatial context
        #
        self.rtrace_mngr.setup(self.sdomain)

        # Set up the system matrix
        #
        self.K = SysMtxAssembly()

        # Register the essential boundary conditions in the system matrix
        #
        self.bcond_mngr.apply_essential(self.K)

        self.tse_integ.apply_constraints(self.K)

        # Prepare the global update flag
        sctx.update_state_on = False

        if self.u_processor:
            if self.u_processor.sd == None:
                self.u_processor.sd = self.sdomain

        # add-on parameters to be passed to every inner loop
        # they are provided by u_processors
        #
        self.kw = {}
        self.args = []

    def eval(self, step_flag, U_k, d_U, t_n, t_n1):
        '''Get the tangential operator (system matrix) and residuum
        associated with the current time step.

        @param step_flag: indicator of the predictor | corrector step
           it is needed for proper handling of constraint equations
           (essential boundary conditions and explicit links between
           the variables.

        @param U_k: current value of the control variable (including
            the value of the last increment d_U

        @param d_U: increment of the control variable
            U[k] = U[k-1] + d_U

        @param t_n: value of the time control parameters in the
            last equilibrated step.

        @param t_n1: value of the target time in the current
            time step.
        '''

        # Reset the system matrix (constraints are preserved)
        #
        self.K.reset_mtx()

        # Put the spatial domain into the spatial context
        #
        sctx = self.sctx

        if self.u_processor:
            self.args, self.kw = self.u_processor(U_k)

        # Let the time sub-stepper evaluate its contribution.
        #
        F_int, K_mtx = self.tse_integ.get_corr_pred(sctx, U_k, d_U, t_n, t_n1,
                                                    *self.args, **self.kw)

        # Promote the system matrix to the SysMtxAssembly
        # Supported representation of the system matrix is
        # float, ndarray, SysMtxArray and SysMtxAssembly
        #
        # @todo use coerce in order to hide this conversions.
        # or is adapter concept of traits a possibility?
        #
        if isinstance(K_mtx, np.ndarray):
            self.K.add_mtx(K_mtx)
        elif isinstance(K_mtx, SysMtxArray):
            self.K.sys_mtx_arrays.append(K_mtx)
        elif isinstance(K_mtx, list):
            self.K.sys_mtx_arrays = K_mtx
        elif isinstance(K_mtx, SysMtxAssembly):
            self.K.sys_mtx_arrays = K_mtx.sys_mtx_arrays

        norm_F_int = np.linalg.norm(F_int)
        # Switch off the global update flag
        #
        sctx.update_state_on = False

        # Apply the boundary conditions
        #
        if self.dof_resultants == True:

            # The code below is somewhat wasteful as it explicitly constructs the
            # array of external forces and then makes the subraction
            # F_ext - F_int to obtain the residual forces. As a result, there two unnecessary
            # intermediate instances of the vector of the size [n_dofs].
            #
            # The two arrays are needed only for postprocessing, if there is a need
            # to get the reaction forces at particular DOF stored in F_int
            # or when the imposed time-dependent loading in a DOF should be visualized
            # for verification, the two vectors must be defined as
            # attributes of the tstepper objects. For this reasons, the more demanding
            # implementation is used here.
            #
            # Remember F_int
            #
            self.F_int = F_int

            # Prepare F_ext by zeroing it
            #
            self.F_ext[:] = 0.0

            # Assemble boundary conditions in K and self.F_ext
            #
            self.bcond_mngr.apply(step_flag, sctx, self.K, self.F_ext, t_n,
                                  t_n1)

            # Return the system matrix assembly K and the residuum
            #
            return self.K, self.F_ext - self.F_int, norm_F_int

        else:

            # On the other hand, the time-loop only requires the residuum
            # which can be obtained without an additional
            # memory consumption by issuing an in-place switch of the sign
            #
            F_int *= -1  # in-place sign change of the internal forces
            #
            # Then F_int can be used as the target for the boundary conditions
            #
            self.bcond_mngr.apply(step_flag, sctx, self.K, F_int, t_n, t_n1)

            #
            # The subtraction F_ext - F_int has then been performed implicitly
            # and the residuum can be returned by issuing
            #
            return self.K, F_int, norm_F_int

    def update_state(self, U):
        '''
        spatial context represents a stack with the top object
         representing the current level.
        @param U:
        '''
        #sctx = ( self.sdomain, )
        self.sctx.update_state_on = True
        #self.tse_integ.update_state( sctx, U )

    def register_mv_pipelines(self, e):
        '''Register the visualization pipelines in mayavi engine
        '''
        self.tse_integ.register_mv_pipelines(e)
        scene = e.new_scene()
        scene.name = 'Spatial domain'
        self.sdomain.register_mv_pipelines(e)
        self.rtrace_mngr.register_mv_pipelines(e)

    traits_view = View(
        Group(Item('sdomain', style='custom', show_label=False),
              label='Discretization'),
        Group(Item('tse', style='simple', show_label=False),
              label='Integrator'),
        Group(Item('bcond_mngr', style='custom', show_label=False),
              label='Boundary conditions'),
        Group(Item('dof_resultants'), label='Options'),
        resizable=True,
        height=0.8,
        width=0.8,
        buttons=[OKButton, CancelButton],
        kind='subpanel',
    )
예제 #12
0
파일: tstepper.py 프로젝트: simvisage/bmcs
class TStepper(HasTraits):

    '''Time stepper object for non-linear Newton-Raphson solver.
    '''

    mats_eval = Instance(IMATSEval)
    '''Finite element formulation object.
    '''

    def _mats_eval_default(self):
        return MATSBondSlipFatigue()

    fets_eval = Instance(IFETSEval)
    '''Finite element formulation object.
    '''

    def _fets_eval_default(self):
        return FETS1D52ULRHFatigue()

    A = Property()
    '''array containing the A_m, L_b, A_f
    '''

    def _get_A(self):
        return np.array([self.fets_eval.A_m, self.fets_eval.P_b, self.fets_eval.A_f])

    sdomain = Instance(FEGrid)
    #=========================================================================
    # index maps
    #=========================================================================

    dof_ECid = Property(depends_on='+input')
    '''For a given element, layer, node number and dimension
    return the dof number
    '''
    @cached_property
    def _get_dof_ECid(self):
        dof_EiCd = self.sdomain.dof_grid.cell_dof_map[..., np.newaxis]
        return np.einsum('EiCd->ECid', dof_EiCd)

    I_Ei = Property(depends_on='+input')
    '''For a given element and its node number return the global index
    of the node'''
    @cached_property
    def _get_I_Ei(self):
        return self.sdomain.geo_grid.cell_grid.cell_node_map

    dof_E = Property(depends_on='+input')
    '''Get ordered array of degrees of freedom corresponding to each element.
    '''
    @cached_property
    def _get_dof_E(self):
        return self.dof_ECid.reshape(-1, self.fets_eval.n_e_dofs)

    dof_ICd = Property(depends_on='+input')
    '''Get degrees of freedom
    '''
    @cached_property
    def _get_dof_ICd(self):
        return self.sdomain.dof_grid.dofs

    dofs = Property(depends_on='_input')
    '''Get degrees of freedom flat'''
    @cached_property
    def _get_dofs(self):
        return self.dof_ICd.flatten()
    #=========================================================================
    # Coordinate arrays
    #=========================================================================

    X_Id = Property(depends_on='+input')
    'Coordinate of the node `I` in dimension `d`'
    @cached_property
    def _get_X_Id(self):
        return self.sdomain.geo_grid.cell_grid.point_x_arr

    X_Eid = Property(depends_on='+input')
    'Coordinate of the node `i` in  element `E` in dimension `d`'
    @cached_property
    def _get_X_Eid(self):
        return self.X_Id[self.I_Ei, :]

    X_Emd = Property(depends_on='+input')
    'Coordinate of the integration point `m` of an element `E` in dimension `d`'
    @cached_property
    def _get_X_Emd(self):
        N_mi_geo = self.fets_eval.N_mi_geo
        return np.einsum('mi,Eid->Emd', N_mi_geo, self.X_Eid)

    X_J = Property(depends_on='+input')
    '''Return ordered vector of nodal coordinates respecting the the order
    of the flattened array of elements, nodes and spatial dimensions.'''
    @cached_property
    def _get_X_J(self):
        return self.X_Eid.flatten()

    X_M = Property(depends_on='+input')
    '''Return ordered vector of global coordinates of integration points
    respecting the the order of the flattened array of elements, 
    nodes and spatial dimensions. Can be used for point-value visualization
    of response variables.'''
    @cached_property
    def _get_X_M(self):
        return self.X_Emd.flatten()

    #=========================================================================
    # cached time-independent terms
    #=========================================================================
    dN_Eimd = Property
    '''Shape function derivatives in every integration point
    '''

    def _get_dN_Eimd(self):
        return self.constant_terms[0]

    sN_Cim = Property
    '''Slip operator between the layers C = 0,1
    '''

    def _get_sN_Cim(self):
        return self.constant_terms[1]

    constant_terms = Property(depends_on='+input')
    '''Procedure calculating all constant terms of the finite element
    algorithm including the geometry mapping (Jacobi), shape 
    functions and the kinematics needed
    for the integration of stresses and stifnesses in every material point.
    '''
    @cached_property
    def _get_constant_terms(self):
        fet = self.fets_eval
        dN_mid_geo = fet.dN_mid_geo
        N_mi = fet.N_mi
        dN_mid = fet.dN_mid
        # Geometry approximation / Jacobi transformation
        J_Emde = np.einsum('mid,Eie->Emde', dN_mid_geo, self.X_Eid)
        J_inv_Emed = np.linalg.inv(J_Emde)
        # Quadratic forms
        dN_Eimd = np.einsum('mid,Eied->Eime', dN_mid, J_inv_Emed)
        sN_Cim = np.einsum('C,mi->Cim', SWITCH_C, N_mi)
        return dN_Eimd, sN_Cim

    # Boundary condition manager
    #
    bcond_mngr = Instance(BCondMngr)

    def _bcond_mngr_default(self):
        return BCondMngr()

    bcond_list = Property(List(BCDof))
    '''Convenience constructor
    This property provides the possibility to write
    tstepper.bcond_list = [BCDof(var='u',dof=5,value=0, ... ]
    The result gets propagated to the BCondMngr
    '''

    def _get_bcond_list(self):
        return self.bcond_mngr.bcond_list

    def _set_bcond_list(self, bcond_list):
        self.bcond_mngr.bcond_list = bcond_list

    J_mtx = Property(depends_on='L_x')
    '''Array of Jacobian matrices.
    '''
    @cached_property
    def _get_J_mtx(self):
        fets_eval = self.fets_eval
        domain = self.sdomain
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)
        # [ n_e, n_geo_r, n_dim_geo ]
        elem_x_map = domain.elem_X_map
        # [ n_e, n_ip, n_dim_geo, n_dim_geo ]
        J_mtx = np.einsum('ind,enf->eidf', dNr_geo, elem_x_map)
        return J_mtx

    J_det = Property(depends_on='L_x')
    '''Array of Jacobi determinants.
    '''
    @cached_property
    def _get_J_det(self):
        return np.linalg.det(self.J_mtx)

    B = Property(depends_on='L_x')
    '''The B matrix
    '''
    @cached_property
    def _get_B(self):
        '''Calculate and assemble the system stiffness matrix.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.sdomain

        n_s = mats_eval.n_s - 1

        n_dof_r = fets_eval.n_dof_r
        n_nodal_dofs = fets_eval.n_nodal_dofs

        n_ip = fets_eval.n_gp
        n_e = domain.n_active_elems
        #[ d, i]
        r_ip = fets_eval.ip_coords[:, :-2].T
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)

        J_inv = np.linalg.inv(self.J_mtx)

        # shape function for the unknowns
        # [ d, n, i]
        Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None, :])
        dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1])

        # [ i, n, d ]
        Nr = np.einsum('dni->ind', Nr)
        dNr = np.einsum('dni->ind', dNr)
        Nx = Nr
        # [ n_e, n_ip, n_dof_r, n_dim_dof ]
        dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

        B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_nodal_dofs), dtype='f')
        B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0]
        B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0]
        B_factors = np.array([-1, 1], dtype='float_')
        B[:, :, :, B_N_n_rows, B_N_n_cols] = (B_factors[None, None, :] *
                                              Nx[:, :, N_idx])
        B[:, :, :, B_dN_n_rows, B_dN_n_cols] = dNx[:, :, :, dN_idx]

        return B

    def apply_essential_bc(self):
        '''Insert initial boundary conditions at the start up of the calculation.. 
        '''
        self.K = SysMtxAssembly()
        for bc in self.bcond_list:
            bc.apply_essential(self.K)

    def apply_bc(self, step_flag, K_mtx, F_ext, t_n, t_n1):
        '''Apply boundary conditions for the current load increement
        '''
        for bc in self.bcond_list:
            bc.apply(step_flag, None, K_mtx, F_ext, t_n, t_n1)

    def get_corr_pred(self, step_flag, U, d_U, eps, sig,
                      t_n, t_n1, xs_pi, alpha, z, kappa, w):
        '''Function calculationg the residuum and tangent operator.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.sdomain
        elem_dof_map = domain.elem_dof_map

        n_e = domain.n_active_elems
        n_dof_r, n_dim_dof = self.fets_eval.dof_r.shape
        n_nodal_dofs = self.fets_eval.n_nodal_dofs
        n_el_dofs = n_dof_r * n_nodal_dofs
        # [ i ]
        w_ip = fets_eval.ip_weights

        d_u_e = d_U[elem_dof_map]
        #[n_e, n_dof_r, n_dim_dof]
        d_u_n = d_u_e.reshape(n_e, n_dof_r, n_nodal_dofs)
        #[n_e, n_ip, n_s]
        d_eps = np.einsum('Einsd,End->Eis', self.B, d_u_n)
        # print'B =', self.B
        # update strain
        eps += d_eps

        # material response state variables at integration point
        sig, D, xs_pi, alpha, z, w = mats_eval.get_corr_pred(
            eps, d_eps, sig, t_n, t_n1, xs_pi, alpha, z, kappa, w)
        # print'D=',D
        # system matrix
        self.K.reset_mtx()
        Ke = np.einsum('i,s,einsd,eist,eimtf,ei->endmf',
                       w_ip, self.A, self.B, D, self.B, self.J_det)

        self.K.add_mtx_array(
            Ke.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map)

        # internal forces
        # [n_e, n_n, n_dim_dof]
        Fe_int = np.einsum('i,s,eis,einsd,ei->end',
                           w_ip, self.A, sig, self.B, self.J_det)
        F_int = np.bincount(elem_dof_map.flatten(), weights=Fe_int.flatten())
        F_ext = np.zeros_like(F_int, dtype=np.float_)
        self.apply_bc(step_flag, self.K, F_ext, t_n, t_n1)

        return F_ext - F_int, F_int, self.K, eps, sig, xs_pi, alpha, z, w, D
예제 #13
0
class TStepper(HasTraits):
    '''Time stepper object for non-linear Newton-Raphson solver.
    '''

    mats_eval = Property(Instance(MATSEvalFatigue))
    '''Finite element formulation object.
    '''
    @cached_property
    def _get_mats_eval(self):
        return MATSEvalFatigue()

    fets_eval = Property(Instance(FETS1D52ULRHFatigue))
    '''Finite element formulation object.
    '''

    @cached_property
    def _get_fets_eval(self):
        return FETS1D52ULRHFatigue()

    A = Property()
    '''array containing the A_m, L_b, A_f
    '''

    def _get_A(self):
        return np.array(
            [self.fets_eval.A_m, self.fets_eval.P_b, self.fets_eval.A_f])

    # Number of elements
    n_e_x = 20
    # length
    L_x = Float(200)

    domain = Property(Instance(FEGrid), depends_on='L_x')
    '''Diescretization object.
    '''

    @cached_property
    def _get_domain(self):
        # Element definition
        domain = FEGrid(coord_max=(self.L_x, ),
                        shape=(self.n_e_x, ),
                        fets_eval=self.fets_eval)
        return domain

    # Boundary condition manager
    #
    bcond_mngr = Instance(BCondMngr)

    def _bcond_mngr_default(self):
        return BCondMngr()

    bcond_list = Property(List(BCDof))
    '''Convenience constructor
    This property provides the possibility to write
    tstepper.bcond_list = [BCDof(var='u',dof=5,value=0, ... ]
    The result gets propagated to the BCondMngr
    '''

    def _get_bcond_list(self):
        return self.bcond_mngr.bcond_list

    def _set_bcond_list(self, bcond_list):
        self.bcond_mngr.bcond_list = bcond_list

    J_mtx = Property(depends_on='L_x')
    '''Array of Jacobian matrices.
    '''

    @cached_property
    def _get_J_mtx(self):
        fets_eval = self.fets_eval
        domain = self.domain
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)
        # [ n_e, n_geo_r, n_dim_geo ]
        elem_x_map = domain.elem_X_map
        # [ n_e, n_ip, n_dim_geo, n_dim_geo ]
        J_mtx = np.einsum('ind,enf->eidf', dNr_geo, elem_x_map)
        return J_mtx

    J_det = Property(depends_on='L_x')
    '''Array of Jacobi determinants.
    '''

    @cached_property
    def _get_J_det(self):
        return np.linalg.det(self.J_mtx)

    B = Property(depends_on='L_x')
    '''The B matrix
    '''

    @cached_property
    def _get_B(self):
        '''Calculate and assemble the system stiffness matrix.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain

        n_s = mats_eval.n_s - 1

        n_dof_r = fets_eval.n_dof_r
        n_nodal_dofs = fets_eval.n_nodal_dofs

        n_ip = fets_eval.n_gp
        n_e = domain.n_active_elems
        #[ d, i]
        r_ip = fets_eval.ip_coords[:, :-2].T
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)

        J_inv = np.linalg.inv(self.J_mtx)

        # shape function for the unknowns
        # [ d, n, i]
        Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None, :])
        dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1])

        # [ i, n, d ]
        Nr = np.einsum('dni->ind', Nr)
        dNr = np.einsum('dni->ind', dNr)
        Nx = Nr
        # [ n_e, n_ip, n_dof_r, n_dim_dof ]
        dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

        B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_nodal_dofs), dtype='f')
        B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0]
        B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0]
        B_factors = np.array([-1, 1], dtype='float_')
        B[:, :, :, B_N_n_rows,
          B_N_n_cols] = (B_factors[None, None, :] * Nx[:, :, N_idx])
        B[:, :, :, B_dN_n_rows, B_dN_n_cols] = dNx[:, :, :, dN_idx]

        return B

    def apply_essential_bc(self):
        '''Insert initial boundary conditions at the start up of the calculation.. 
        '''
        self.K = SysMtxAssembly()
        for bc in self.bcond_list:
            bc.apply_essential(self.K)

    def apply_bc(self, step_flag, K_mtx, F_ext, t_n, t_n1):
        '''Apply boundary conditions for the current load increement
        '''
        for bc in self.bcond_list:
            bc.apply(step_flag, None, K_mtx, F_ext, t_n, t_n1)

    def get_corr_pred(self, step_flag, U, d_U, eps, sig, t_n, t_n1, xs_pi,
                      alpha, z, w):
        '''Function calculationg the residuum and tangent operator.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain
        elem_dof_map = domain.elem_dof_map

        n_e = domain.n_active_elems
        n_dof_r, n_dim_dof = self.fets_eval.dof_r.shape
        n_nodal_dofs = self.fets_eval.n_nodal_dofs
        n_el_dofs = n_dof_r * n_nodal_dofs
        # [ i ]
        w_ip = fets_eval.ip_weights

        d_u_e = d_U[elem_dof_map]
        #[n_e, n_dof_r, n_dim_dof]
        d_u_n = d_u_e.reshape(n_e, n_dof_r, n_nodal_dofs)
        #[n_e, n_ip, n_s]
        d_eps = np.einsum('einsd,end->eis', self.B, d_u_n)
        # print'B =', self.B
        # update strain
        eps += d_eps

        # material response state variables at integration point
        sig, D, xs_pi, alpha, z, w = mats_eval.get_corr_pred(
            eps, d_eps, sig, t_n, t_n1, xs_pi, alpha, z, w)
        # print'D=',D
        # system matrix
        self.K.reset_mtx()
        Ke = np.einsum('i,s,einsd,eist,eimtf,ei->endmf', w_ip, self.A, self.B,
                       D, self.B, self.J_det)

        self.K.add_mtx_array(Ke.reshape(-1, n_el_dofs, n_el_dofs),
                             elem_dof_map)

        # internal forces
        # [n_e, n_n, n_dim_dof]
        Fe_int = np.einsum('i,s,eis,einsd,ei->end', w_ip, self.A, sig, self.B,
                           self.J_det)
        F_int = np.bincount(elem_dof_map.flatten(), weights=Fe_int.flatten())
        F_ext = np.zeros_like(F_int, dtype=np.float_)
        self.apply_bc(step_flag, self.K, F_ext, t_n, t_n1)

        return F_ext - F_int, F_int, self.K, eps, sig, xs_pi, alpha, z, w, D
예제 #14
0
파일: febond.py 프로젝트: rosoba/rosoba
    # shape function for the unknowns are identical with the geomeetric
    # approximation
    dNr = dNr_geo

    # [ n_e, n_ip, n_dof_r, n_dim_dof ]
    dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

    B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_dim_dof), dtype='f')
    B_n_rows, B_n_cols, dNx_d = [0, 1, 2, 2], [0, 1, 0, 1], [0, 1, 1, 0]
    B[:, :, :, B_n_rows, B_n_cols] = dNx[:, :, :, dNx_d]

    #=========================================================================
    # System matrix
    #=========================================================================
    K = np.einsum('i,einsd,st,eimtf,ei->endmf', w_ip, B, D_el, B, J_det)
    K_mtx = SysMtxAssembly()
    K_mtx.add_mtx_array(K.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map)

    #=========================================================================
    # Load vector
    #=========================================================================

    R = np.zeros((n_dofs,), dtype='float_')
    R[[8, 10]] = 1.0
    K_mtx.register_constraint(a=0)
    K_mtx.register_constraint(a=1)
    K_mtx.register_constraint(a=2)
    u = K_mtx.solve(R)
    print 'u', u
예제 #15
0
Created on 05.02.2017

@author: Yingxiong
'''
from numpy import array, zeros, arange, array_equal, hstack, dot
from mathkit.matrix_la.sys_mtx_assembly import SysMtxAssembly


shape = 5

el_mtx = array([[10, -10],
                [-10, 10]], dtype='float_')

mtx_arr = array([el_mtx for i in range(shape)], dtype=float)
dof_map = array([arange(shape),
                 arange(shape) + 1], dtype=int).transpose()

K = SysMtxAssembly()

K.add_mtx_array(dof_map_arr=dof_map, mtx_arr=mtx_arr)
K.register_constraint(a=0,  u_a=0.)  # clamped end
K.register_constraint(a=4, u_a=2.)
K.register_constraint(a=2,  u_a=1.)

R = zeros(K.n_dofs)
R[-1] = 10.
print 'u =',  K.solve(R)

print R
print K.sys_mtx_arrays[0].mtx_arr
예제 #16
0
class TLoop(HasStrictTraits):

    ts = Instance(MATS3DExplore)

    tline = Instance(TLine, ())
    d_t = Float(0.005)
    t_max = Float(1.0)
    k_max = Int(50)
    tolerance = Float(1e-4)
    step_tolerance = Float(1e-8)

    t_record = List
    U_n = Array(dtype=np.float_)
    K = Array(dtype=np.float_)
    state = Array(dtype=np.float_)
    F_record = List
    U_record = List
    state_record = List

    K = Instance(SysMtxAssembly)

    paused = Bool(False)
    restart = Bool(True)

    def setup(self):
        n_comps = 6
        n_dofs = n_comps
        self.U_n = np.zeros((n_dofs, ))
        t_n = self.tline.val
        self.t_record = [t_n]
        self.U_record = [np.zeros_like(self.U_n)]
        self.F_record = [np.copy(self.U_n)]
        self.state_record = [np.copy(self.state)]
        # Set up the system matrix
        #
        self.K = SysMtxAssembly()
        self.ts.bcond_mngr.apply_essential(self.K)

    state_changed = Event
    state_arrays = Property(Dict(Str, Array), depends_on='state_changed')
    '''Dictionary of state arrays.
    The entry names and shapes are defined by the material
    model.
    '''

    @cached_property
    def _get_state_arrays(self):
        sa_shapes = self.ts.state_array_shapes
        print('state array generated', sa_shapes)
        return {
            name: np.zeros(mats_sa_shape, dtype=np.float_)[np.newaxis, ...]
            for name, mats_sa_shape in list(sa_shapes.items())
        }

    def init(self):
        if self.paused:
            self.paused = False
        if self.restart:
            self.tline.val = 0
            self.state_changed = True
            self.setup()
            self.restart = False

    def eval(self):
        # Reset the system matrix (constraints are preserved)
        #
        self.d_t = self.tline.step
        F_ext = np.zeros_like(self.U_n)
        t_n = self.tline.val
        t_n1 = t_n
        U_n = self.U_n
        while (t_n1 - self.tline.max) <= self.step_tolerance and \
                not (self.restart or self.paused):
            k = 0
            print('load factor', t_n1, end=' ')
            step_flag = 'predictor'
            U_k = np.copy(U_n)
            d_U_k = np.zeros_like(U_k)
            while k <= self.k_max and \
                    not (self.restart or self.paused):

                self.K.reset_mtx()

                K_mtx, F_int = self.ts.get_corr_pred(U_k, d_U_k, t_n, t_n1,
                                                     step_flag,
                                                     **self.state_arrays)

                self.K.add_mtx(K_mtx)

                # Prepare F_ext by zeroing it
                #
                F_ext[:] = 0.0

                # Assemble boundary conditions in K and self.F_ext
                #
                self.ts.bcond_mngr.apply(step_flag, None, self.K, F_ext, t_n,
                                         t_n1)

                # Return the system matrix assembly K and the residuum
                #
                R = F_ext - F_int

                self.K.apply_constraints(R)
                d_U_k, pos_def = self.K.solve()
                U_k += d_U_k
                if np.linalg.norm(R) < self.tolerance:
                    U_n[:] = U_k[:]
                    self.t_record.append(t_n1)
                    self.U_record.append(U_k)
                    self.F_record.append(F_int)
                    self.state_record.append(np.copy(self.state))
                    break
                k += 1
                step_flag = 'corrector'

            if k >= self.k_max:
                print(' ----------> no convergence')
                break
            else:
                print('(', k, ')')
            if self.restart or self.paused:
                print('interrupted iteration')
                break

            t_n = t_n1
            t_n1 = t_n + self.d_t
            self.tline.val = min(t_n, self.tline.max)
        return U_k

    def get_time_idx_arr(self, vot):
        '''Get the index corresponding to visual time
        '''
        x = self.t_record
        idx = np.array(np.arange(len(x)), dtype=np.float_)
        t_idx = np.interp(vot, x, idx)
        return np.array(t_idx + 0.5, np.int_)

    def get_time_idx(self, vot):
        return int(self.get_time_idx_arr(vot))
예제 #17
0
 def apply_essential_bc(self):
     '''Insert initial boundary conditions at the start up of the calculation.. 
     '''
     self.K = SysMtxAssembly()
     for bc in self.bc_list:
         bc.apply_essential(self.K)
예제 #18
0
    # [ n_e, n_ip, n_dof_r, n_dim_dof ]
    dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

    B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_dim_dof), dtype='f')
    B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0]
    B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0]
    B_factors = np.array([-1, 1], dtype='float_')
    B[:, :, :, B_N_n_rows,
      B_N_n_cols] = (B_factors[None, None, :] * Nx[:, :, N_idx])
    B[:, :, :, B_dN_n_rows, B_dN_n_cols] = dNx[:, :, :, dN_idx]

    #=========================================================================
    # System matrix
    #=========================================================================
    K = np.einsum('i,einsd,st,eimtf,ei->endmf', w_ip, B, D_el, B, J_det)
    K_mtx = SysMtxAssembly()
    K_mtx.add_mtx_array(K.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map)

    #=========================================================================
    # BC and solver
    #=========================================================================
    R = np.zeros((n_dofs, ), dtype='float_')
    #     R[2 * n_e_x + 1] = 1.0
    K_mtx.register_constraint(a=0)
    K_mtx.register_constraint(a=2 * n_e_x + 1, u_a=1.0)
    u = K_mtx.solve(R)
    print 'u', u

    #=========================================================================
    # strain and slip
    #=========================================================================
예제 #19
0
 def apply_essential_bc(self):
     '''Insert initial boundary conditions at the start up of the calculation.. 
     '''
     self.K = SysMtxAssembly()
     for bc in self.bc_list:
         bc.apply_essential(self.K)
예제 #20
0
def bar5():
    print '---------------------------------------------------------------'
    print 'Clamped bar with 4 elements. Elements 2-4 are reinforced '
    print 'with another bar with 3 elements'
    print '[0]-[1]-[2]-[3]-[4]'
    print '    [5]-[6]-[7]'
    print 'u[0] = 0, u[1] = u[5], u[3] = u[7], u[4] = 1'
    # assemble the matrix
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=4)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    dof_map2, mtx_arr2 = get_bar_mtx_array(shape=2)
    K.add_mtx_array(dof_map_arr=dof_map2 + 5, mtx_arr=mtx_arr2)
    # add constraints
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=1, alpha=[1], ix_a=[5])
    K.register_constraint(a=3, alpha=[1], ix_a=[7])
    K.register_constraint(a=4, u_a=1.)  # loaded end
    # add load
    R = zeros(K.n_dofs)
    print 'u =', K.solve(R)
    print
예제 #21
0
def bar5():
    print '---------------------------------------------------------------'
    print 'Clamped bar with 4 elements. Elements 2-4 are reinforced '
    print 'with another bar with 3 elements'
    print '[0]-[1]-[2]-[3]-[4]'
    print '    [5]-[6]-[7]'
    print 'u[0] = 0, u[1] = u[5], u[3] = u[7], u[4] = 1'
    # assemble the matrix
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 4 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    dof_map2, mtx_arr2 = get_bar_mtx_array( shape = 2 )
    K.add_mtx_array( dof_map_arr = dof_map2+5, mtx_arr = mtx_arr2 )
    # add constraints
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 1, alpha = [1], ix_a = [5] )
    K.register_constraint( a = 3, alpha = [1], ix_a = [7] )
    K.register_constraint( a = 4, u_a = 1. ) # loaded end
    # add load
    R = zeros( K.n_dofs )
    print 'u =', K.solve( R )
    print
예제 #22
0
def bar6():
    print '---------------------------------------------------------------'
    print 'Clamped bar with 4 elements. Elements 2-4 are reinforced '
    print 'with another bar with 1 element linked proportianally'
    print '[0]-[1]-[2]-[3]-[4]'
    print '      [5]-[6]'
    print 'u[0] = 0, u[1] = u[5], u[3] = u[7], u[4] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=4)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    dof_map2, mtx_arr2 = get_bar_mtx_array(shape=1)
    K.add_mtx_array(dof_map_arr=dof_map2 + 5, mtx_arr=mtx_arr2)
    # add constraints
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=5, alpha=[0.5, 0.5], ix_a=[1, 2])
    K.register_constraint(a=6, alpha=[0.5, 0.5], ix_a=[2, 3])
    K.register_constraint(a=4, u_a=1.)  # loaded end
    # add load
    R = zeros(K.n_dofs)
    print 'u =', K.solve(R)
    print
예제 #23
0
def bar7():
    print '---------------------------------------------------------------'
    print 'Two clamped beams link in parallel'
    print 'and loaded by force at right end'
    print '[5]-[6]-[7]-[8]-[9]'
    print '[0]-[1]-[2]-[3]-[4]'
    print 'u[5] = u[0], u[0] = 0, u[4] = 0.5 * u[9], R[4] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array( shape = 4 )
    K.add_mtx_array( dof_map_arr = dof_map1, mtx_arr = mtx_arr1 )
    dof_map2, mtx_arr2 = get_bar_mtx_array( shape = 4 )
    K.add_mtx_array( dof_map_arr = dof_map2+5, mtx_arr = mtx_arr2 )
    # add load
    R = zeros( K.n_dofs )
    # load at the coupled end nodes is doubled
    R[9] = 1
    R[4] = 1
    # add constraints
    K.register_constraint( a = 5, alpha = [1], ix_a = [0] )
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 4, alpha = [0.5], ix_a = [9] )
    # add load
    R = zeros( K.n_dofs )
    # load at the coupled end nodes is doubled
    R[9] = 1
    R[4] = 1
    print 'u =', K.solve( R )
    print
예제 #24
0
def bar7():
    print '---------------------------------------------------------------'
    print 'Two clamped beams link in parallel'
    print 'and loaded by force at right end'
    print '[5]-[6]-[7]-[8]-[9]'
    print '[0]-[1]-[2]-[3]-[4]'
    print 'u[5] = u[0], u[0] = 0, u[4] = 0.5 * u[9], R[4] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=4)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    dof_map2, mtx_arr2 = get_bar_mtx_array(shape=4)
    K.add_mtx_array(dof_map_arr=dof_map2 + 5, mtx_arr=mtx_arr2)
    # add load
    R = zeros(K.n_dofs)
    # load at the coupled end nodes is doubled
    R[9] = 1
    R[4] = 1
    # add constraints
    K.register_constraint(a=5, alpha=[1], ix_a=[0])
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=4, alpha=[0.5], ix_a=[9])
    # add load
    R = zeros(K.n_dofs)
    # load at the coupled end nodes is doubled
    R[9] = 1
    R[4] = 1
    print 'u =', K.solve(R)
    print
예제 #25
0
def bar9():
    print '---------------------------------------------------------------'
    print 'Single clamped element with two constraints'
    print 'within a single element'
    print '[0]-[1]-[2]-[3]'
    print 'u[0] = u[1], u[1] = 1'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=3)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    # add constraints
    K.register_constraint(a=0)
    K.register_constraint(a=3, u_a=1.)  # clamped end
    # add load
    R = zeros(K.n_dofs)
    print 'u =', K.solve(R)
    print
    K.register_constraint(a=1, alpha=[1], ix_a=[2])
    print 'u =', K.solve(R)
    print
예제 #26
0
def bar2():
    print '---------------------------------------------------------------'
    print 'Clamped bar composed of two linked bars loaded at the right end'
    print '[00]-[01]-[02]-[03]-[04]-[05]-[06]-[07]-[08]-[09]-[10]'
    print '[11]-[12]-[13]-[14]-[15]-[16]-[17]-[18]-[19]-[20]-[21]'
    print 'u[0] = 0, u[5] = u[16], R[-1] = R[21] = 10'
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=10)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    dof_map2, mtx_arr2 = get_bar_mtx_array(shape=10)
    # Note the dof map of the second br must be adjusted to start
    # the DOF enumeration with 3
    n_dofs1 = K.n_dofs
    K.add_mtx_array(dof_map_arr=dof_map2 + n_dofs1, mtx_arr=mtx_arr2)
    # add constraints
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=5, alpha=[1], ix_a=[16])
    # add load
    R = zeros(K.n_dofs)
    R[-1] = 10
    print 'u =', K.solve(R)
    print
    print '---------------------------------------------------------------'
    print 'Clamped bar composed of two linked bars control displ at right'
    print 'u[0] = 0, u[5] = u[16], u[21] = 1'
    # Remove the load and put a unit displacement at the right end
    # Note, the load is irrelevant in this case and will be rewritten
    #
    K.register_constraint(a=21, u_a=1)
    print 'u =', K.solve(R)
    print
예제 #27
0
def bar3():
    print '---------------------------------------------------------------'
    print 'Clamped bar with recursive constraints (load at right end)'
    print '[0]-[1]-[2]-[3]'    
    print 'u[1] = 0.5 * u[2], u[2] = 1 * u[3], R[3] = 11'
    K = SysMtxAssembly()
    dof_map, mtx_arr = get_bar_mtx_array( shape = 3 )
    K.add_mtx_array( dof_map_arr = dof_map, mtx_arr = mtx_arr )
    K.register_constraint( a = 0, u_a = 0. ) # clamped end
    K.register_constraint( a = 1, alpha = [0.5], ix_a = [2] )
    K.register_constraint( a = 2, alpha = [1.0], ix_a = [3] )
    R = zeros( K.n_dofs )
    R[3] = 1
    K.apply_constraints(R)
    print 'u =', K.solve( )
    print
    print '---------------------------------------------------------------'
    print 'Clamped bar with recursive constraints (displ at right end)'
    print 'u[1] = 0.5 * u[2], u[2] = 1.0 * u[3], u[3] = 1'
    K.register_constraint( a = 3, u_a = 1 )
    print 'u =', K.solve( R )
    print
예제 #28
0
def bar3():
    print '---------------------------------------------------------------'
    print 'Clamped bar with recursive constraints (load at right end)'
    print '[0]-[1]-[2]-[3]'
    print 'u[1] = 0.5 * u[2], u[2] = 1 * u[3], R[3] = 11'
    K = SysMtxAssembly()
    dof_map, mtx_arr = get_bar_mtx_array(shape=3)
    K.add_mtx_array(dof_map_arr=dof_map, mtx_arr=mtx_arr)
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=1, alpha=[0.5], ix_a=[2])
    K.register_constraint(a=2, alpha=[1.0], ix_a=[3])
    R = zeros(K.n_dofs)
    R[3] = 1
    K.apply_constraints(R)
    print 'u =', K.solve()
    print
    print '---------------------------------------------------------------'
    print 'Clamped bar with recursive constraints (displ at right end)'
    print 'u[1] = 0.5 * u[2], u[2] = 1.0 * u[3], u[3] = 1'
    K.register_constraint(a=3, u_a=1)
    print 'u =', K.solve(R)
    print
예제 #29
0
def bar4():
    print('---------------------------------------------------------------')
    print('Clamped bar 3 domains, each with 2 elems (displ at right end)')
    print('[0]-[1]-[2] [3]-[4]-[5] [6]-[7]-[8]')
    print('u[0] = 0, u[2] = u[3], u[5] = u[6], u[8] = 1')
    K = SysMtxAssembly()
    dof_map1, mtx_arr1 = get_bar_mtx_array(shape=2)
    K.add_mtx_array(dof_map_arr=dof_map1, mtx_arr=mtx_arr1)
    dof_map2, mtx_arr2 = get_bar_mtx_array(shape=2)
    K.add_mtx_array(dof_map_arr=dof_map2 + 3, mtx_arr=mtx_arr2)
    dof_map3, mtx_arr3 = get_bar_mtx_array(shape=2)
    K.add_mtx_array(dof_map_arr=dof_map3 + 6, mtx_arr=mtx_arr3)
    # add constraints
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=2, alpha=[1], ix_a=[3])
    K.register_constraint(a=5, alpha=[1], ix_a=[6])
    K.register_constraint(a=8, u_a=1.)  # loaded end
    # add load
    R = zeros(K.n_dofs)
    print('u =', K.solve(R))
    print()
예제 #30
0
class TStepper(HasTraits):

    '''Time stepper object for non-linear Newton-Raphson solver.
    '''

    mats_eval = Instance(MATSEval, arg=(), kw={})  # material model

    fets_eval = Instance(FETS1D52ULRH, arg=(), kw={})  # element formulation

    A = Property(depends_on='fets_eval.A_f, fets_eval.A_m, fets_eval.L_b')
    '''array containing the A_m, L_b, A_f
    '''
    @cached_property
    def _get_A(self):
        return np.array([self.fets_eval.A_m, self.fets_eval.L_b, self.fets_eval.A_f])

    # number of elements
    n_e_x = Float(20.)

    # specimen length
    L_x = Float(75.)

    domain = Property(depends_on='n_e_x, L_x')
    '''Diescretization object.
    '''
    @cached_property
    def _get_domain(self):
        # Element definition
        domain = FEGrid(coord_max=(self.L_x,),
                        shape=(self.n_e_x,),
                        fets_eval=self.fets_eval)
        return domain

    bc_list = List(Instance(BCDof))

    J_mtx = Property(depends_on='n_e_x, L_x')
    '''Array of Jacobian matrices.
    '''
    @cached_property
    def _get_J_mtx(self):
        fets_eval = self.fets_eval
        domain = self.domain
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)
        # [ n_e, n_geo_r, n_dim_geo ]
        elem_x_map = domain.elem_X_map
        # [ n_e, n_ip, n_dim_geo, n_dim_geo ]
        J_mtx = np.einsum('ind,enf->eidf', dNr_geo, elem_x_map)
        return J_mtx

    J_det = Property(depends_on='n_e_x, L_x')
    '''Array of Jacobi determinants.
    '''
    @cached_property
    def _get_J_det(self):
        return np.linalg.det(self.J_mtx)

    B = Property(depends_on='n_e_x, L_x')
    '''The B matrix
    '''
    @cached_property
    def _get_B(self):
        '''Calculate and assemble the system stiffness matrix.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain

        n_s = mats_eval.n_s

        n_dof_r = fets_eval.n_dof_r
        n_nodal_dofs = fets_eval.n_nodal_dofs

        n_ip = fets_eval.n_gp
        n_e = domain.n_active_elems
        #[ d, i]
        r_ip = fets_eval.ip_coords[:, :-2].T
        # [ d, n ]
        geo_r = fets_eval.geo_r.T
        # [ d, n, i ]
        dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5
        # [ i, n, d ]
        dNr_geo = np.einsum('dni->ind', dNr_geo)

        J_inv = np.linalg.inv(self.J_mtx)

        # shape function for the unknowns
        # [ d, n, i]
        Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None,:])
        dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1])

        # [ i, n, d ]
        Nr = np.einsum('dni->ind', Nr)
        dNr = np.einsum('dni->ind', dNr)
        Nx = Nr
        # [ n_e, n_ip, n_dof_r, n_dim_dof ]
        dNx = np.einsum('eidf,inf->eind', J_inv, dNr)

        B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_nodal_dofs), dtype='f')
        B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0]
        B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0]
        B_factors = np.array([-1, 1], dtype='float_')
        B[:, :,:, B_N_n_rows, B_N_n_cols] = (B_factors[None, None,:] *
                                              Nx[:, :, N_idx])
        B[:, :,:, B_dN_n_rows, B_dN_n_cols] = dNx[:,:,:, dN_idx]

        return B

    def apply_essential_bc(self):
        '''Insert initial boundary conditions at the start up of the calculation.. 
        '''
        self.K = SysMtxAssembly()
        for bc in self.bc_list:
            bc.apply_essential(self.K)

    def apply_bc(self, step_flag, K_mtx, F_ext, t_n, t_n1):
        '''Apply boundary conditions for the current load increement
        '''
        for bc in self.bc_list:
            bc.apply(step_flag, None, K_mtx, F_ext, t_n, t_n1)

    def get_corr_pred(self, step_flag, d_U, eps, sig, t_n, t_n1):
        '''Function calculationg the residuum and tangent operator.
        '''
        mats_eval = self.mats_eval
        fets_eval = self.fets_eval
        domain = self.domain
        elem_dof_map = domain.elem_dof_map
        n_e = domain.n_active_elems
        n_dof_r, n_dim_dof = self.fets_eval.dof_r.shape
        n_nodal_dofs = self.fets_eval.n_nodal_dofs
        n_el_dofs = n_dof_r * n_nodal_dofs
        # [ i ]
        w_ip = fets_eval.ip_weights

        d_u_e = d_U[elem_dof_map]
        #[n_e, n_dof_r, n_dim_dof]
        d_u_n = d_u_e.reshape(n_e, n_dof_r, n_nodal_dofs)
        #[n_e, n_ip, n_s]
        d_eps = np.einsum('einsd,end->eis', self.B, d_u_n)

        # update strain
        eps += d_eps

        # material response state variables at integration point
        sig, D = mats_eval.get_corr_pred(eps, d_eps, sig, t_n, t_n1)

        # system matrix
        self.K.reset_mtx()
        Ke = np.einsum('i,s,einsd,eist,eimtf,ei->endmf',
                       w_ip, self.A, self.B, D, self.B, self.J_det)

        self.K.add_mtx_array(
            Ke.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map)

        # internal forces
        # [n_e, n_n, n_dim_dof]
        Fe_int = np.einsum('i,s,eis,einsd,ei->end',
                           w_ip, self.A, sig, self.B, self.J_det)
        F_int = -np.bincount(elem_dof_map.flatten(), weights=Fe_int.flatten())
        self.apply_bc(step_flag, self.K, F_int, t_n, t_n1)
        return F_int, self.K, eps, sig
예제 #31
0
class TStepper( IBVResource ):
    """
    The TStepper is a spatially bounded TStepperEval.

    The binding is done by associating the time-stepper with a spatial
    object. In fact, any TStepper, not only the top-level one must
    be associated with spatial object. For the chained
    sub-time-steppers, this association is done using a parameter sctx
    (see the specification above). This parameters stands for spatial
    context. Note, the distinction between the spatial object and
    spatial context. While the spatial object represents a single
    spatial entity (one of domain, element, layer or material point)
    the spatial context represents a complex reference within the
    spatial object In particular, spatial context of a particular
    material point is represented as tuple containing tuple of
    references to [domain, element, layer, integration cell, material
    point].
    """

    # service specifiers - used to link the service to this object
    service_class = 'ibvpy.plugins.tstepper_service.TStepperService'
    service_attrib = 'tstepper'

    # Integration terms involved in the evaluation of the 
    # incremetal spatial integrals.
    # Must be either an instance of ts_eval or a tuple specifying a pair 
    # ( ts_eval, sdomain ) or a list of tuples with the pairs 
    # [ ( ts_eval, sdomain ), ( ts_eval, sdomain ), ... ]
    #

    # Sub-time-stepper or integrator.
    #
    tse = Instance( ITStepperEval )

    tse_integ = Property( depends_on = 'tse,_sdomain, _sdomain.changed_structure' )
    @cached_property
    def _get_tse_integ( self ):
        if self.tse:
            self.tse.tstepper = self
            return self.tse
        else:
            self.sdomain.dots.tstepper = self
            return self.sdomain.dots

    # Spatial domain to bind the time-stepper to.
    # For convenience automatically convert the plain list to FEDomainList
    #
    _sdomain = Instance( ISDomain )
    sdomain = Property( Instance( ISDomain ) )
    def _set_sdomain( self, value ):
        if isinstance( value, FEGrid ):
            # construct FERefinementGrid and FEDomain
            self._sdomain = FEDomain()
            fe_rgrid = FERefinementGrid( domain = self._sdomain,
                                         fets_eval = value.fets_eval )
            value.level = fe_rgrid
        elif isinstance( value, FERefinementGrid ):
            # construct FEDomain
            self._sdomain = FEDomain()
            value.domain = self._sdomain
        elif isinstance( value, list ):
            self._sdomain = FEDomain()
            for d in value:
                if isinstance( d, FEGrid ):
                    fe_rgrid = FERefinementGrid( domain = self._sdomain,
                                                 fets_eval = d.fets_eval )
                    d.level = fe_rgrid
                elif isinstance( d, FESubDomain ):
                    d.domain = self._sdomain
                else:
                    raise TypeError, 'The list can contain only FEGrid or FERefinementGrid'
        else:
            self._sdomain = value
    def _get_sdomain( self ):
        if self._sdomain == None:
            self._sdomain = SDomain()
        return self._sdomain

    subdomains = Property()
    def _get_subdomains( self ):
        if self.sdomain == None:
            return []
        return self.sdomain.subdomains

    xdomains = Property()
    def _get_xdomains( self ):
        if self.sdomain == None:
            return []
        return self.sdomain.xdomains

    def redraw( self ):
        self.sdomain.redraw()

    sctx = Instance( SContext )

    # Boundary condition manager
    #
    bcond_mngr = Instance( BCondMngr )
    def _bcond_mngr_default( self ):
        return BCondMngr()

    # Convenience constructor 
    #
    # This property provides the possibility to write
    # tstepper.bcond_list = [BCDof(var='u',dof=5,value=0, ... ]
    # The result gets propageted to the BCondMngr
    #
    bcond_list = Property( List )
    def _get_bcond_list( self ):
        return self.bcond_mngr.bcond_list
    def _set_bcond_list( self, bcond_list ):
        self.bcond_mngr.bcond_list = bcond_list

    # Response variable manager
    #
    rtrace_mngr = Instance( RTraceMngr )
    def _rtrace_mngr_default( self ):
        return RTraceMngr( tstepper = self )

    # Convenience constructor 
    #
    # This property provides the possibility to write
    # tstepper.bcond_list = [RVDof(var='u',dof=5,value=0, ... ]
    # The result gets propageted to the RTraceMngr
    #
    rtrace_list = Property( List )
    def _get_rtrace_list( self ):
        return self.rtrace_mngr.rtrace_list
    def _set_rtrace_list( self, rtrace_list ):
        self.rtrace_mngr.rtrace_list = rtrace_list

    # Possibility to add a callable for derived 
    # variables of the control variable. 
    u_processor = Callable

    # Backward reference to the time-loop in order to accommadate the
    # response-trace-evaluators from the top level. These include
    # bookkeeping data like memory usage or solving time per selected
    # type of operation.
    #
    tloop = WeakRef

    dir = Property
    def _get_dir( self ):
        return self.tloop.dir

    dof_resultants = Bool( True )

    rte_dict = Property( Dict, depends_on = 'tse' )
    @cached_property
    def _get_rte_dict( self ):
        '''
        Gather all the currently applicable evaluators from the sub-ts
        and from the time-loop.

        Note the control data (time-loop data) is available within the
        model to construct flexible views (tracers) on the model.
        '''
        _rte_dict = {}

        def _get_F_int( sctx, U_k, *args, **kw ):
            return self.F_int

        if self.dof_resultants:
            _rte_dict['F_int'] = _get_F_int # lambda sctx, U_k: self.F_int
            _rte_dict['F_ext'] = lambda sctx, U_k, *args, **kw: self.F_ext

        _rte_dict.update( self.tse_integ.rte_dict )
        if self.tloop:
            _rte_dict.update( self.tloop.rte_dict )
        return _rte_dict

    def new_cntl_var( self ):
        return self.tse_integ.new_cntl_var()

    def new_resp_var( self ):
        return self.tse_integ.new_resp_var()

    U_k = Property( depends_on = '_sdomain.changed_structure' )
    @cached_property
    def _get_U_k( self ):
        '''
         Setup the primary state variables on demand
        '''
        U_k = self.new_cntl_var()
        return U_k

    d_U = Property( depends_on = '_sdomain.changed_structure' )
    @cached_property
    def _get_d_U( self ):
        '''
         Current increment of the displacement variable 
        '''
        return self.new_cntl_var()

    F_ext = Property( depends_on = '_sdomain.changed_structure' )
    @cached_property
    def _get_F_ext( self ):
        '''
         Return the response variable to be used when assembling the
         boundary conditions. Should the bcond_mngr take care of this? 
         That's the source object, isn't it? BCondMngr is the bounded
         version of the conditions, it could supply the matrix
         autonomously.
        
        '''
        return self.new_resp_var()

    # missing - setup of the time-stepper itself. reacting to changes
    # in the sub time-steppers. bcond_list and rtrace_list must be reset once
    # a change has been performed either in a spatial domain or in
    # tse.
    #
    def setup( self ):

        # Put the spatial domain into the spatial context
        #
        self.sctx = sctx = self.sdomain.new_scontext()
        self.sctx.sdomain = self.sdomain

        # Let the boundary conditions setup themselves within the
        # spatial context
        #
        # TODO - the setup needs the link to the algorithm and to the
        # time-steppers as well.!
        #
        self.bcond_mngr.setup( sctx )

        # Let the response variables setup themselves within the
        # spatial context
        #
        self.rtrace_mngr.setup( self.sdomain )

        # Set up the system matrix
        #
        self.K = SysMtxAssembly()

        # Register the essential boundary conditions in the system matrix
        #
        self.bcond_mngr.apply_essential( self.K )

        self.tse_integ.apply_constraints( self.K )

        # Prepare the global update flag
        sctx.update_state_on = False

        if self.u_processor:
            if self.u_processor.sd == None:
                self.u_processor.sd = self.sdomain

        # add-on parameters to be passed to every inner loop
        # they are provided by u_processors 
        #
        self.kw = {}
        self.args = []


    def eval( self, step_flag, U_k, d_U, t_n, t_n1 ):
        '''Get the tangential operator (system matrix) and residuum
        associated with the current time step.
        
        @param step_flag: indicator of the predictor | corrector step
           it is needed for proper handling of constraint equations 
           (essential boundary conditions and explicit links between 
           the variables.
           
        @param U_k: current value of the control variable (including 
            the value of the last increment d_U
        
        @param d_U: increment of the control variable 
            U[k] = U[k-1] + d_U
            
        @param t_n: value of the time control parameters in the 
            last equilibrated step.
            
        @param t_n1: value of the target time in the current 
            time step.
        '''

        # Reset the system matrix (constraints are preserved)
        #
        self.K.reset_mtx()

        # Put the spatial domain into the spatial context
        #
        sctx = self.sctx

        if self.u_processor:
            self.args, self.kw = self.u_processor( U_k )

        # Let the time sub-stepper evaluate its contribution.
        #
        F_int, K_mtx = self.tse_integ.get_corr_pred( sctx, U_k, d_U, t_n, t_n1,
                                                     *self.args, **self.kw )

        # Promote the system matrix to the SysMtxAssembly
        # Supported representation of the system matrix is
        # float, ndarray, SysMtxArray and SysMtxAssembly
        #
        # @todo use coerce in order to hide this conversions.
        # or is adapter concept of traits a possibility?
        #
        if isinstance( K_mtx, ndarray ):
            self.K.add_mtx( K_mtx )
        elif isinstance( K_mtx, SysMtxArray ):
            self.K.sys_mtx_arrays.append( K_mtx )
        elif isinstance( K_mtx, list ):
            self.K.sys_mtx_arrays = K_mtx
        elif isinstance( K_mtx, SysMtxAssembly ):
            self.K.sys_mtx_arrays = K_mtx.sys_mtx_arrays

        # Switch off the global update flag
        #
        sctx.update_state_on = False

        # Apply the boundary conditions
        #
        if self.dof_resultants == True:

            # The code below is somewhat wasteful as it explicitly constructs the
            # array of external forces and then makes the subraction
            # F_ext - F_int to obtain the residual forces. As a result, there two unnecessary 
            # intermediate instances of the vector of the size [n_dofs].
            #
            # The two arrays are needed only for postprocessing, if there is a need
            # to get the reaction forces at particular DOF stored in F_int 
            # or when the imposed time-dependent loading in a DOF should be visualized 
            # for verification, the two vectors must be defined as 
            # attributes of the tstepper objects. For this reasons, the more demanding
            # implementation is used here. 
            #
            # Remember F_int
            #
            self.F_int = F_int

            # Prepare F_ext by zeroing it
            #
            self.F_ext[:] = 0.0

            # Assemble boundary conditions in K and self.F_ext 
            #
            self.bcond_mngr.apply( step_flag, sctx, self.K, self.F_ext, t_n, t_n1 )

            # Return the system matrix assembly K and the residuum
            #
            return self.K, self.F_ext - self.F_int

        else:

            # On the other hand, the time-loop only requires the residuum 
            # which can be obtained withoug an additional
            # memory consumption by issuing an in-place switch of the sign
            # 
            F_int *= -1 # in-place sign change of the internal forces
            # 
            # Then F_int can be used as the target for the boundary conditions
            #
            self.bcond_mngr.apply( step_flag, sctx, self.K, F_int, t_n, t_n1 )

            #
            # The subtraction F_ext - F_int has then been performed implicitly
            # and the residuum can be returned by issuing
            #
            return self.K, F_int

    def update_state( self, U ):
        '''
        spatial context represents a stack with the top object
         representing the current level.
        @param U: 
        '''
        #sctx = ( self.sdomain, )
        self.sctx.update_state_on = True
        #self.tse_integ.update_state( sctx, U )

    def register_mv_pipelines( self, e ):
        '''Register the visualization pipelines in mayavi engine
        '''
        self.tse_integ.register_mv_pipelines( e )
        scene = e.new_scene()
        scene.name = 'Spatial domain'
        self.sdomain.register_mv_pipelines( e )
        self.rtrace_mngr.register_mv_pipelines( e )

    traits_view = View( Group( Item( 'sdomain', style = 'custom', show_label = False ),
                               label = 'Discretization' ),
                        Group( Item( 'tse', style = 'simple', show_label = False ),
                               label = 'Integrator' ),
                        Group( Item( 'bcond_mngr', style = 'custom', show_label = False ),
                               label = 'Boundary conditions' ),
                        Group( Item( 'dof_resultants' ),
                               label = 'Options' ),
                        resizable = True,
                        height = 0.8,
                        width = 0.8,
                        buttons = [OKButton, CancelButton],
                        kind = 'subpanel',
                        )
예제 #32
0
def bar1():
    print '---------------------------------------------------------------'
    print 'Clamped bar loaded at the right end with unit displacement'
    print '[00]-[01]-[02]-[03]-[04]-[05]-[06]-[07]-[08]-[09]-[10]'
    print 'u[0] = 0, u[10] = 1'
    K = SysMtxAssembly()
    dof_map, mtx_arr = get_bar_mtx_array(shape=10)
    K.add_mtx_array(dof_map_arr=dof_map, mtx_arr=mtx_arr)
    K.register_constraint(a=0, u_a=0.)  # clamped end
    K.register_constraint(a=10, u_a=1.)
    K.register_constraint(a=10, u_a=1.)
    K_dense = DenseMtx(assemb=K)
    R = zeros(K.n_dofs)
    print 'K\n', K_dense
    print 'R\n', R
    print 'K_arrays'
    for i, sys_mtx_array in enumerate(K.sys_mtx_arrays):
        print 'i\n', sys_mtx_array.mtx_arr
    K.apply_constraints(R)
    K_dense = DenseMtx(assemb=K)
    print 'K\n', K_dense
    print 'R\n', R
    print 'K_arrays'
    for i, sys_mtx_array in enumerate(K.sys_mtx_arrays):
        print 'i\n', sys_mtx_array.mtx_arr
    print 'u =', K.solve(R)
    print