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
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
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
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 _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
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
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
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
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
def _get_K(self): return SysMtxAssembly()
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', )
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
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
# 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
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
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))
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)
# [ 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 #=========================================================================
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
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
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
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
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
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
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
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
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
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()
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
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', )