class SimCrackLoc( IBVModel ): '''Model assembling the components for studying the restrained crack localization. ''' shape = Int( 1, desc = 'Number of finite elements', ps_levsls = ( 10, 40, 4 ) ) length = Float( 1, desc = 'Length of the simulated region' ) #------------------------------------------------------- # Material model for the matrix #------------------------------------------------------- mats_m = Instance( MATS1DCrackLoc ) def _mats_m_default( self ): E_m = 1 mats_m = MATS1DCrackLoc( E = E_m, R = 0.5, epsilon_0 = 0.001, epsilon_f = 0.01 ) return mats_m mats_f = Instance( MATS1DElastic ) def _mats_f_default( self ): E_f = 0 mats_f = MATS1DElastic( E = E_f ) return mats_f mats_b = Instance( MATS1DElastic ) def _mats_b_default( self ): E_b = 0 mats_b = MATS1DElastic( E = E_b ) return mats_b mats = Instance( MATS1D5Bond ) def _mats_default( self ): # Material model construction mats = MATS1D5Bond( mats_phase1 = self.mats_m, mats_phase2 = self.mats_f, mats_ifslip = self.mats_b, mats_ifopen = MATS1DElastic( E = 0 ) # elastic function of open - inactive ) return mats #------------------------------------------------------- # Finite element type #------------------------------------------------------- fets = Instance( FETSEval ) def _fets_default( self ): #return FETS1D52L8ULRH( mats_eval = self.mats ) return FETS1D52L4ULRH( mats_eval = self.mats ) #return FETS1D2L3U( mats_eval = self.mats ) run = Button @on_trait_change( 'run' ) def peval( self ): '''Evaluation procedure. ''' #mv = MATS1DDamageView( model = mats_eval ) #mv.configure_traits() self.mats_m.reset_state() # Discretization # length = self.length domain = FEGrid( coord_min = ( 0., length / 5. ), coord_max = ( length, 0. ), shape = ( self.shape, 1 ), fets_eval = self.fets ) right_dofs = domain[-1, -1, -1, :].dofs[0, :, 0] print 'concrete_dofs', id( domain ), domain[:, 0, :, 0].dofs # Response tracers self.stress_strain = RTraceGraph( name = 'Fi,right over u_right (iteration)' , var_y = 'F_int', idx_y = right_dofs[0], var_x = 'U_k', idx_x = right_dofs[0] ) self.eps_m_field = RTraceDomainListField( name = 'eps_m' , position = 'int_pnts', var = 'eps1', idx = 0, warp = True ) self.eps_f_field = RTraceDomainListField( name = 'eps_f' , position = 'int_pnts', var = 'eps2', idx = 0, warp = True ) # Response tracers self.sig_m_field = RTraceDomainListField( name = 'sig_m' , position = 'int_pnts', var = 'mats_phase1_sig_app', idx = 0 ) self.sig_f_field = RTraceDomainListField( name = 'sig_f' , position = 'int_pnts', var = 'mats_phase2_sig_app', idx = 0 ) self.omega_m_field = RTraceDomainListField( name = 'omega_m' , position = 'int_pnts', var = 'mats_phase1_omega', idx = 0, warp = True ) # damage_onset_displ = self.mats_m.epsilon_0 * self.length go_behind = 1.5 finish_displ = go_behind * damage_onset_displ n_steps = 20 step_size = ( finish_displ - damage_onset_displ ) / n_steps tmax = 1 + n_steps def ls( t ): if t <= 1: return t else: return 1.0 + ( t - 1.0 ) / n_steps * ( go_behind - 1 ) ts = TSCrackLoc( dof_resultants = True, on_update = self.plot, sdomain = domain, bcond_list = [# define the left clamping BCSlice( var = 'u', value = 0., dims = [0], slice = domain[ 0, 0, 0, :] ), # loading at the right edge BCSlice( var = 'f', value = 1, dims = [0], slice = domain[-1, -1, -1, 0], time_function = ls ), # BCSlice(var='u', value = finish_displ, dims = [0], slice = domain[-1,-1,-1, 0], # time_function = ls ), # fix horizontal displacement in the top layer BCSlice( var = 'u', value = 0., dims = [0], slice = domain[:, -1, :, -1] ), # fix the vertical displacement all over the domain BCSlice( var = 'u', value = 0., dims = [1], slice = domain[ :, :, :, :] ) ], rtrace_list = [ self.stress_strain, self.eps_m_field, self.eps_f_field, self.sig_m_field, self.sig_f_field, self.omega_m_field ] ) # Add the time-loop control tloop = TLoop( tstepper = ts, KMAX = 200, debug = True, tolerance = 1e-5, tline = TLine( min = 0.0, step = 1.0, max = 1.0 ) ) print ts.rte_dict.keys() U = tloop.eval() self.plot() return array( [ U[right_dofs[-1]] ], dtype = 'float_' ) #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_ld = Instance( Figure ) def _figure_ld_default( self ): figure = Figure( facecolor = 'white' ) figure.add_axes( [0.12, 0.13, 0.85, 0.74] ) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_eps = Instance( Figure ) def _figure_eps_default( self ): figure = Figure( facecolor = 'white' ) figure.add_axes( [0.12, 0.13, 0.85, 0.74] ) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_omega = Instance( Figure ) def _figure_omega_default( self ): figure = Figure( facecolor = 'white' ) figure.add_axes( [0.12, 0.13, 0.85, 0.74] ) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_sig = Instance( Figure ) def _figure_sig_default( self ): figure = Figure( facecolor = 'white' ) figure.add_axes( [0.12, 0.13, 0.85, 0.74] ) return figure def plot( self ): self.stress_strain.refresh() t = self.stress_strain.trace ax = self.figure_ld.axes[0] ax.clear() ax.plot( t.xdata, t.ydata, linewidth = 2, color = 'blue' ) ax = self.figure_eps.axes[0] ax.clear() ef = self.eps_m_field.subfields[0] xdata = ef.vtk_X[:, 0] ydata = ef.field_arr[:, 0] idata = argsort( xdata ) ax.plot( xdata[idata], ydata[idata], linewidth = 2, color = 'grey' ) ef = self.eps_f_field.subfields[0] xdata = ef.vtk_X[:, 0] ydata = ef.field_arr[:, 0] idata = argsort( xdata ) ax.plot( xdata[idata], ydata[idata], linewidth = 2, color = 'red' ) ax.legend( ['eps_m', 'eps_f'] ) ax = self.figure_sig.axes[0] ax.clear() ef = self.sig_m_field.subfields[0] xdata = ef.vtk_X[:, 0] ydata = ef.field_arr[:, 0] idata = argsort( xdata ) ax.plot( xdata[idata], ydata[idata], linewidth = 2, color = 'grey' ) ef = self.sig_f_field.subfields[0] xdata = ef.vtk_X[:, 0] ydata = ef.field_arr[:, 0] idata = argsort( xdata ) ax.plot( xdata[idata], ydata[idata], linewidth = 2, color = 'red' ) ax.legend( ['sig_m', 'sig_f'] ) ax = self.figure_omega.axes[0] ax.clear() ef = self.omega_m_field.subfields[0] xdata = ef.vtk_X[:, 0] ydata = ef.field_arr[:, 0] idata = argsort( xdata ) ax.plot( xdata[idata], ydata[idata], linewidth = 2, color = 'brown' ) ax.legend( ['omega_m'] ) self.data_changed = True def get_sim_outputs( self ): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut( name = 'right end displacement', unit = 'm' ) ] data_changed = Event toolbar = ToolBar( Action( name = "Run", tooltip = 'Start computation', image = ImageResource( 'kt-start' ), action = "start_study" ), Action( name = "Pause", tooltip = 'Pause computation', image = ImageResource( 'kt-pause' ), action = "pause_study" ), Action( name = "Stop", tooltip = 'Stop computation', image = ImageResource( 'kt-stop' ), action = "stop_study" ), image_size = ( 32, 32 ), show_tool_names = False, show_divider = True, name = 'view_toolbar' ), traits_view = View( HSplit( VSplit( Item( 'run', show_label = False ), VGroup( Item( 'shape' ), Item( 'n_sjteps' ), Item( 'length' ), label = 'parameters', id = 'crackloc.viewmodel.factor.geometry', dock = 'tab', scrollable = True, ), VGroup( Item( 'mats_m@', show_label = False ), label = 'Matrix', dock = 'tab', id = 'crackloc.viewmodel.factor.matrix', scrollable = True ), VGroup( Item( 'mats_f@', show_label = False ), label = 'Fiber', dock = 'tab', id = 'crackloc.viewmodel.factor.fiber', scrollable = True ), VGroup( Item( 'mats_b@', show_label = False ), label = 'Bond', dock = 'tab', id = 'crackloc.viewmodel.factor.bond', scrollable = True ), id = 'crackloc.viewmodel.left', label = 'studied factors', layout = 'tabbed', dock = 'tab', ), VSplit( VGroup( Item( 'figure_ld', editor = MPLFigureEditor(), resizable = True, show_label = False ), label = 'stress-strain', id = 'crackloc.viewmode.figure_ld_window', dock = 'tab', ), VGroup( Item( 'figure_eps', editor = MPLFigureEditor(), resizable = True, show_label = False ), label = 'strains profile', id = 'crackloc.viewmode.figure_eps_window', dock = 'tab', ), VGroup( Item( 'figure_sig', editor = MPLFigureEditor(), resizable = True, show_label = False ), label = 'stress profile', id = 'crackloc.viewmode.figure_sig_window', dock = 'tab', ), VGroup( Item( 'figure_omega', editor = MPLFigureEditor(), resizable = True, show_label = False ), label = 'omega profile', id = 'crackloc.viewmode.figure_omega_window', dock = 'tab', ), id = 'crackloc.viewmodel.right', ), id = 'crackloc.viewmodel.splitter', ), title = 'SimVisage Component: Crack localization', id = 'crackloc.viewmodel', dock = 'tab', resizable = True, height = 0.8, width = 0.8, buttons = [OKButton] )
class SimBT4PT(IBVModel): '''Simulation: four point bending test. ''' input_change = Event @on_trait_change('+input,ccs_unit_cell.input_change') def _set_input_change(self): self.input_change = True implements(ISimModel) #----------------- # discretization: #----------------- # specify weather the elastomer is to be modeled or if the load is # introduced as line load # elstmr_flag = Bool(True) # discretization in x-direction (longitudinal): # outer_zone_shape_x = Int(6, input=True, ps_levels=(4, 12, 3)) # discretization in x-direction (longitudinal): # load_zone_shape_x = Int(2, input=True, ps_levels=(1, 4, 1)) # middle part discretization in x-direction (longitudinal): # mid_zone_shape_x = Int(3, input=True, ps_levels=(1, 4, 1)) # discretization in y-direction (width): # shape_y = Int(2, input=True, ps_levels=(1, 4, 2)) # discretization in z-direction: # shape_z = Int(2, input=True, ps_levels=(1, 3, 3)) #----------------- # geometry: #----------------- # # edge length of the bending specimen (beam) (entire length without symmetry) # length = Float(1.50, input=True) elstmr_length = Float(0.05, input=True) mid_zone_length = Float(0.50, input=True) elstmr_thickness = Float(0.005, input=True) width = Float(0.20, input=True) thickness = Float(0.06, input=True) #----------------- # derived geometric parameters #----------------- # # half the length of the elastomer (load introduction # with included symmetry) # sym_specmn_length = Property def _get_sym_specmn_length(self): return self.length / 2. sym_mid_zone_specmn_length = Property def _get_sym_mid_zone_specmn_length(self): return self.mid_zone_length / 2. # half the length of the elastomer (load introduction # with included symmetry # sym_elstmr_length = Property def _get_sym_elstmr_length(self): return self.elstmr_length / 2. # half the specimen width # sym_width = Property def _get_sym_width(self): return self.width / 2. #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # age of the plate at the time of testing # NOTE: that the same phi-function is used independent of age. This assumes a # an afine/proportional damage evolution for different ages. # age = Int(28, input=True) # time stepping params # tstep = Float(0.05, auto_set=False, enter_set=True, input=True) tmax = Float(1.0, auto_set=False, enter_set=True, input=True) tolerance = Float(0.001, auto_set=False, enter_set=True, input=True) # specify type of 'linalg.norm' # default value 'None' sets norm to 2-norm, # i.e "norm = sqrt(sum(x_i**2)) # # set 'ord=np.inf' to switch norm to # "norm = max(abs(x_i))" # ord = Enum(np.inf, None) n_mp = Int(30, input=True) # @todo: for mats_eval the information of the unit cell should be used # in order to use the same number of microplanes and model version etc... # specmn_mats = Property(Instance(MATS2D5MicroplaneDamage), depends_on='input_change') @cached_property def _get_specmn_mats(self): return MATS2D5MicroplaneDamage( E=self.E_c, # E=self.E_m, nu=self.nu, # corresponding to settings in "MatsCalib" n_mp=self.n_mp, symmetrization='sum-type', model_version='compliance', phi_fn=self.phi_fn) if elstmr_flag: elstmr_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_elstmr_mats(self): # specify a small elastomer stiffness (approximation) E_elast = self.E_c / 10. print 'effective elastomer E_modulus', E_elast return MATS3DElastic(E=E_elast, nu=0.4) #----------------- # fets: #----------------- # specify element shrink factor in plot of fe-model # vtk_r = Float(0.95) # use quadratic serendipity elements # specmn_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_specmn_fets(self): fets = FETS2D58H20U(mats_eval=self.specmn_mats) fets.vtk_r *= self.vtk_r return fets # use quadratic serendipity elements # elstmr_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_elstmr_fets(self): fets = FETS2D58H20U(mats_eval=self.elstmr_mats) fets.vtk_r *= self.vtk_r return fets fe_domain = Property(depends_on='+ps_levels, +input') @cached_property def _get_fe_domain(self): return FEDomain() #=========================================================================== # fe level #=========================================================================== outer_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_outer_zone_specmn_fe_level(self): return FERefinementGrid(name='outer zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) load_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_load_zone_specmn_fe_level(self): return FERefinementGrid(name='load zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) mid_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_mid_zone_specmn_fe_level(self): return FERefinementGrid(name='mid zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) elstmr_fe_level = Property(depends_on='+ps_levels, +input') def _get_elstmr_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.elstmr_fets, domain=self.fe_domain) #=========================================================================== # Grid definition #=========================================================================== mid_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_mid_zone_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(0., 0., 0.), coord_max=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, self.sym_width, self.thickness), shape=(self.mid_zone_shape_x, self.shape_y, self.shape_z), level=self.mid_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid load_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_load_zone_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, self.sym_width, self.thickness), shape=(self.load_zone_shape_x, self.shape_y, self.shape_z), level=self.load_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid # if elstmr_flag: elstmr_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_grid(self): fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, 0., self.thickness), coord_max=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, self.sym_width, self.thickness + self.elstmr_thickness), level=self.elstmr_fe_level, shape=(self.load_zone_shape_x, self.shape_y, 1), fets_eval=self.elstmr_fets) return fe_grid outer_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_outer_zone_specmn_fe_grid(self): # only a quarter of the plate is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_specmn_length, self.sym_width, self.thickness), shape=(self.outer_zone_shape_x, self.shape_y, self.shape_z), level=self.outer_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid #=========================================================================== # Boundary conditions #=========================================================================== w_max = Float(-0.030, input=True) # [m] w_max = Float(-0.030, input=True) # [m] bc_list = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_list(self): mid_zone_specimen = self.mid_zone_specmn_fe_grid load_zone_specimen = self.load_zone_specmn_fe_grid outer_zone_specimen = self.outer_zone_specmn_fe_grid if self.elstmr_flag: elastomer = self.elstmr_fe_grid #-------------------------------------------------------------- # boundary conditions for the symmetry #-------------------------------------------------------------- # symmetry in the xz-plane # (Note: the x-axis corresponds to the axis of symmetry along the longitudinal axis of the beam) # bc_outer_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=outer_zone_specimen[:, 0, :, :, 0, :]) bc_load_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=load_zone_specimen[:, 0, :, :, 0, :]) bc_mid_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=mid_zone_specimen[:, 0, :, :, 0, :]) if self.elstmr_flag: bc_el_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=elastomer[:, 0, :, :, 0, :]) # symmetry in the yz-plane # bc_mid_zone_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=mid_zone_specimen[0, :, :, 0, :, :]) #-------------------------------------------------------------- # boundary conditions for the support #-------------------------------------------------------------- bc_support_0y0 = BCSlice(var='u', value=0., dims=[2], slice=outer_zone_specimen[-1, :, 0, -1, :, 0]) #-------------------------------------------------------------- # connect all grids #-------------------------------------------------------------- link_loadzn_outerzn = BCDofGroup( var='u', value=0., dims=[0, 1, 2], get_dof_method=load_zone_specimen.get_right_dofs, get_link_dof_method=outer_zone_specimen.get_left_dofs, link_coeffs=[1.]) link_midzn_loadzn = BCDofGroup( var='u', value=0., dims=[0, 1, 2], get_dof_method=mid_zone_specimen.get_right_dofs, get_link_dof_method=load_zone_specimen.get_left_dofs, link_coeffs=[1.]) if self.elstmr_flag: link_elstmr_loadzn_z = BCDofGroup( var='u', value=0., dims=[2], get_dof_method=elastomer.get_back_dofs, get_link_dof_method=load_zone_specimen.get_front_dofs, link_coeffs=[1.]) # hold elastomer in a single point in order to avoid kinematic movement yielding singular K_mtx # bc_elstmr_fix = BCSlice(var='u', value=0., dims=[0], slice=elastomer[0, 0, 0, 0, 0, 0]) #-------------------------------------------------------------- # loading #-------------------------------------------------------------- # w_max = center displacement: w_max = self.w_max if self.elstmr_flag: # apply displacement at all top nodes of the elastomer (surface load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=elastomer[:, :, -1, :, :, -1]) else: bc_w = BCSlice( var='u', value=w_max, dims=[2], # slice is only exactly in the center of the loading zone for 'load_zone_shape_x' = 2 # center line of the load zone slice=load_zone_specimen[0, :, -1, -1, :, -1]) # f_max = 0.010 / 4. / self.sym_width # bc_line_f = BCSlice(var = 'f', value = f_max, dims = [2], # # slice is only valid for 'load_zone_shape_x' = 2 # # center line of the load zone # slice = load_zone_specimen[0, :, -1, -1, :, -1]) bc_list = [ bc_outer_zone_symplane_xz, bc_load_zone_symplane_xz, bc_mid_zone_symplane_xz, bc_mid_zone_symplane_yz, # link_midzn_loadzn, link_loadzn_outerzn, bc_support_0y0, # bc_w, ] if self.elstmr_flag: bc_list += [bc_el_symplane_xz, link_elstmr_loadzn_z, bc_elstmr_fix] return bc_list #---------------------- # tloop #---------------------- tloop = Property(depends_on='input_change') @cached_property def _get_tloop(self): #-------------------------------------------------------------- # ts #-------------------------------------------------------------- mid_zone_spec = self.mid_zone_specmn_fe_grid load_zone_spec = self.load_zone_specmn_fe_grid outer_zone_spec = self.outer_zone_specmn_fe_grid if self.elstmr_flag: # ELSTRMR TOP SURFACE # dofs at elastomer top surface (used to integrate the force) # elastomer = self.elstmr_fe_grid elstmr_top_dofs_z = elastomer[:, :, -1, :, :, -1].dofs[:, :, 2].flatten() load_dofs_z = np.unique(elstmr_top_dofs_z) print 'load_dofs_z', load_dofs_z else: # LINE LOAD TOP OF LOAD ZONE # dofs at center line of the specmn load zone (used to integrate the force) # note slice index in x-direction is only valid for load_zone_shape_x = 2 ! # load_zone_spec_topline_dofs_z = load_zone_spec[ 0, :, -1, -1, :, -1].dofs[:, :, 2].flatten() load_dofs_z = np.unique(load_zone_spec_topline_dofs_z) print 'load_dofs_z', load_dofs_z # SUPPRT LINE # dofs at support line of the specmn (used to integrate the force) # outer_zone_spec_supprtline_dofs_z = outer_zone_spec[ -1, :, 0, -1, :, 0].dofs[:, :, 2].flatten() supprt_dofs_z = np.unique(outer_zone_spec_supprtline_dofs_z) print 'supprt_dofs_z', supprt_dofs_z # CENTER DOF (used for tracing of the displacement) # center_bottom_dof = mid_zone_spec[0, 0, 0, 0, 0, 0].dofs[0, 0, 2] print 'center_bottom_dof', center_bottom_dof # THIRDPOINT DOF (used for tracing of the displacement) # dofs at center middle of the laod zone at the bottom side # # NOTE: slice index in x-direction is only valid for load_zone_shape_x = 2 ! thirdpoint_bottom_dof = load_zone_spec[0, 0, 0, -1, 0, 0].dofs[0, 0, 2] print 'thirdpoint_bottom_dof', thirdpoint_bottom_dof # force-displacement-diagram (CENTER) # self.f_w_diagram_center = RTraceGraph( name='displacement_elasttop (center) - force', var_x='U_k', idx_x=center_bottom_dof, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') # force-displacement-diagram_supprt (SUPPRT) # self.f_w_diagram_supprt = RTraceGraph( name='displacement_supprtline (center) - force', var_x='U_k', idx_x=center_bottom_dof, var_y='F_int', idx_y_arr=supprt_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='4000. * y') # force-displacement-diagram (THIRDPOINT) # self.f_w_diagram_thirdpoint = RTraceGraph( name='displacement_elasttop (thirdpoint) - force', var_x='U_k', idx_x=thirdpoint_bottom_dof, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') ts = TS( sdomain=self.fe_domain, bcond_list=self.bc_list, rtrace_list=[ self.f_w_diagram_center, self.f_w_diagram_thirdpoint, self.f_w_diagram_supprt, RTraceDomainListField(name='Displacement', var='u', idx=0, warp=True), # RTraceDomainListField(name = 'Stress' , # var = 'sig_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Strain' , # var = 'eps_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Damage' , # var = 'omega_mtx', idx = 0, warp = True, # record_on = 'update'), RTraceDomainListField(name='max_omega_i', warp=True, var='max_omega_i', idx=0, record_on='update'), # RTraceDomainListField(name = 'IStress' , # position = 'int_pnts', # var = 'sig_app', idx = 0, # record_on = 'update'), # RTraceDomainListField(name = 'IStrain' , # position = 'int_pnts', # var = 'eps_app', idx = 0, # record_on = 'update'), ]) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=50, tolerance=self.tolerance, RESETMAX=0, tline=TLine(min=0.0, step=self.tstep, max=self.tmax), ord=self.ord) return tloop def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() self.f_w_diagram_center.refresh() F_max = max(self.f_w_diagram_center.trace.ydata) u_center_top_z = U[self.center_top_dofs][0, 0, 2] return array([u_center_top_z, F_max], dtype='float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='u_center_top_z', unit='m'), SimOut(name='F_max', unit='kN') ]
class SimBT3PT(IBVModel): '''Simulation: Bending Test Three Point ''' input_change = Event @on_trait_change('+input,ccs_unit_cell.input_change') def _set_input_change(self): self.input_change = True implements(ISimModel) #----------------- # discretization: #----------------- # # discretization in x-direction (longitudinal): shape_x = Int(10, input=True, ps_levels=(4, 12, 3)) # discretization in x-direction (longitudinal): mid_shape_x = Int(1, input=True, ps_levels=(1, 4, 1)) # discretization in y-direction (width): shape_y = Int(3, input=True, ps_levels=(1, 4, 2)) # discretization in z-direction: shape_z = Int(2, input=True, ps_levels=(1, 3, 3)) # support discretization in xy-direction: # NOTE: chose '2' for 2x2-grid or '4' for 4x4-grid # shape_supprt_x = Int(2, input=True, ps_levels=(2, 4, 2)) #----------------- # geometry: #----------------- # # edge length of the bending specimen (beam) (entire length without symmetry) length = Float(1.15, input=True) elstmr_length = Float(0.05, input=True) elstmr_thickness = Float(0.005, input=True, enter_set=True, auto_set=False) width = Float(0.20, input=True) thickness = Float(0.06, input=True, ps_levels=(0.054, 0.060, 0.066)) # thickness of the tappered support # thickness_supprt = Float(0.02, input=True) #----------------- # derived geometric parameters #----------------- # # half the length of the elastomer (load introduction # with included symmetry) # sym_specmn_length = Property def _get_sym_specmn_length(self): return self.length / 2. # half the length of the elastomer (load introduction # with included symmetry # sym_elstmr_length = Property def _get_sym_elstmr_length(self): return (self.elstmr_length) / 2. # half the specimen width # sym_width = Property def _get_sym_width(self): return self.width / 2. #----------------- # specify the refinement of the idealization #----------------- # specify weather elastomer is to be modeled for load introduction # elstmr_flag = True # specify weather steel support is to be modeled # supprt_flag = False #----------------- # 'geo_transform' #----------------- # geometry transformation for four sided tappered support with reduced stiffness towards the edges (if modeled) # geo_supprt = Property(Instance(GeoSUPPRT), depends_on='+ps_levels, +input') @cached_property def _get_geo_supprt(self): # element length within the range of the slab without the area of the # load introduction plate # elem_size = self.sym_specmn_length / self.shape_x width_supprt = self.shape_supprt_x * elem_size print 'width_supprt = ', width_supprt return GeoSUPPRT(thickness_supprt=self.thickness_supprt, width_supprt=width_supprt, xyoffset=0., zoffset= -self.thickness_supprt) #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # age of the specimen at the time of testing # determines the E-modulus and nu based on the time dependent function stored # in 'CCSUniteCell' if params are not explicitly set # age = Int(28, input=True) # composite E-modulus / Poisson's ratio # NOTE 1: value are retrieved from the database # same values are used for calibration (as taken from tensile test) and for slab test # NOTE 2: alternatively the same phi-function could be used independent of age. This would assume # an afine/proportional damage evolution for different ages, i.e. a different E would be # used in the simulation of the slab test and within the calibration procedure. # time stepping params # tstep = Float(0.05, auto_set=False, enter_set=True, input=True) tmax = Float(1.0, auto_set=False, enter_set=True, input=True) tolerance = Float(0.001, auto_set=False, enter_set=True, input=True) # specify type of 'linalg.norm' # default value 'None' sets norm to 2-norm, # i.e "norm = sqrt(sum(x_i**2)) # # set 'ord=np.inf' to switch norm to # "norm = max(abs(x_i))" # ord = Enum(None, np.inf) # number of microplanes # n_mp = Int(30., auto_set=False, enter_set=True, input=True) #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # @todo: for mats_eval the information of the unit cell should be used # in order to use the same number of microplanes and model version etc... # specmn_mats = Property(Instance(MATS2D5MicroplaneDamage), depends_on='input_change') @cached_property def _get_specmn_mats(self): return MATS2D5MicroplaneDamage( E=self.E_c, # E=self.E_m, # relevant for compressive behavior/used for calibration of phi_fn nu=self.nu, # corresponding to settings in "MatsCalib" n_mp=30, symmetrization='sum-type', model_version='compliance', phi_fn=self.phi_fn) E_elstmr = Float(3000., input=True) elstmr_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_elstmr_mats(self): E_elstmr = self.E_elstmr print 'effective elastomer E_modulus', E_elstmr return MATS3DElastic(E=E_elstmr, nu=0.4) # E-modulus and nu of steel support E_s = Float(210000., auto_set=False, enter_set=True, input=True) nu_s = Float(0.20, auto_set=False, enter_set=True, input=True) supprt_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_supprt_mats(self): return MATS3DElastic(E=self.E_s, nu=self.nu_s) #----------------- # fets: #----------------- # specify element shrink factor in plot of fe-model # vtk_r = Float(0.95) # use quadratic serendipity elements # NOTE: 2D5 elements behave linear elastic in out of plane direction! # specmn_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_specmn_fets(self): fets = FETS2D58H20U(mats_eval=self.specmn_mats) fets.vtk_r *= self.vtk_r return fets # use quadratic serendipity elements # elstmr_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_elstmr_fets(self): fets = FETS2D58H20U(mats_eval=self.elstmr_mats) fets.vtk_r *= self.vtk_r return fets supprt_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_supprt_fets(self): # linear-elastic behavior quadratic serendipity elements fets = FETS3D8H20U(mats_eval=self.supprt_mats) fets.vtk_r *= self.vtk_r return fets #----------------- # fe_grid: #----------------- fe_domain = Property(depends_on='+ps_levels, +input') @cached_property def _get_fe_domain(self): return FEDomain() mid_specmn_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_mid_specmn_fe_level(self): return FERefinementGrid(name='middle specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) mid_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_mid_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(0., 0., 0.), coord_max=(self.sym_elstmr_length, self.width / 2, self.thickness), shape=(self.mid_shape_x, self.shape_y, self.shape_z), level=self.mid_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid specmn_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_specmn_fe_level(self): return FERefinementGrid(name='specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_specmn_length, self.sym_width, self.thickness), shape=(self.shape_x, self.shape_y, self.shape_z), level=self.specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid # if elstmr_flag: elstmr_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.elstmr_fets, domain=self.fe_domain) elstmr_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_grid(self): x_max = self.sym_elstmr_length y_max = self.width / 2. z_max = self.thickness + self.elstmr_thickness fe_grid = FEGrid(coord_min=(0, 0, self.thickness), coord_max=(x_max, y_max, z_max), level=self.elstmr_fe_level, shape=(self.mid_shape_x, self.shape_y, 1), fets_eval=self.elstmr_fets) return fe_grid if supprt_flag: supprt_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_supprt_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.supprt_fets, domain=self.fe_domain) supprt_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_supprt_fe_grid(self): return FEGrid(coord_min=(0, 0, 0), coord_max=(1, 1, 1), level=self.supprt_fe_level, # use shape (2,2) in order to place support in the center of the steel support # corresponding to 4 elements of the slab mesh # shape=(self.shape_supprt_x, self.shape_supprt_x, 1), geo_transform=self.geo_supprt, fets_eval=self.supprt_fets) #=========================================================================== # Boundary conditions #=========================================================================== # w_max = center displacement: # w_max = Float(-0.010, input=True) # [m] bc_list = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_list(self): specmn = self.specmn_fe_grid mid_specmn = self.mid_specmn_fe_grid if self.elstmr_flag: elstmr = self.elstmr_fe_grid #-------------------------------------------------------------- # boundary conditions for the symmetry #-------------------------------------------------------------- # the x-axis corresponds to the axis of symmetry along the longitudinal axis of the beam: bc_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=specmn[:, 0, :, :, 0, :]) bc_mid_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=mid_specmn[:, 0, :, :, 0, :]) bc_mid_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=mid_specmn[0, :, :, 0, :, :]) if self.elstmr_flag: bc_el_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=elstmr[:, 0, :, :, 0, :]) bc_el_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=elstmr[0, :, :, 0, :, :]) #-------------------------------------------------------------- # boundary conditions for the support #-------------------------------------------------------------- bc_support_0y0 = BCSlice(var='u', value=0., dims=[2], slice=specmn[-1, :, 0, -1, :, 0]) #-------------------------------------------------------------- # link domains #-------------------------------------------------------------- link_msp_sp = BCDofGroup(var='u', value=0., dims=[0, 1, 2], get_dof_method=mid_specmn.get_right_dofs, get_link_dof_method=specmn.get_left_dofs, link_coeffs=[1.]) # link_msp_sp_xyz = BCSlice(var = 'u', value = 0., dims = [0, 1, 2], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [0, 1, 2], # link_coeffs = [1.]) # link_msp_sp_y = BCSlice(var = 'u', value = 0., dims = [1], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [1], # link_coeffs = [1.]) # # link_msp_sp_z = BCSlice(var = 'u', value = 0., dims = [2], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [2], # link_coeffs = [1.]) # link_msp_sp = [ link_msp_sp_xyz ] if self.elstmr_flag: link_el_sp = BCDofGroup(var='u', value=0., dims=[2], get_dof_method=elstmr.get_back_dofs, get_link_dof_method=mid_specmn.get_front_dofs, link_coeffs=[1.]) #-------------------------------------------------------------- # loading #-------------------------------------------------------------- w_max = self.w_max # f_max = -0.010 / 0.10 # [MN/m] if self.elstmr_flag: # apply displacement at all top node (surface load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=elstmr[:, :, -1, :, :, -1]) # apply a single force at the center of the beam (system origin at top of the elastomer # and us elastomer-domain as load distribution plate with a high stiffness (e.g. steel) # F_max = -0.010 #[MN] # bc_F = BCSlice(var = 'f', value = F_max, dims = [2], # slice = elstmr[0, 0, -1, 0, 0, -1]) else: # center top nodes (line load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=mid_specmn[0, :, -1, 0, :, -1]) # NOTE: the entire symmetry axis (yz)-plane is moved downwards # in order to avoid large indentations at the top nodes # # bc_center_w = BCSlice( var = 'w', value = w_max, dims = [2], slice = mid_specmn[0, :, :, 0, :, :] ) bc_list = [bc_symplane_xz, bc_mid_symplane_xz, bc_mid_symplane_yz, bc_support_0y0, link_msp_sp, bc_w ] if self.elstmr_flag: bc_list_elstmr = [ link_el_sp, bc_el_symplane_xz, bc_el_symplane_yz ] bc_list += bc_list_elstmr return bc_list tloop = Property(depends_on='input_change') @cached_property def _get_tloop(self): #-------------------------------------------------------------- # ts #-------------------------------------------------------------- specmn = self.specmn_fe_grid mid_specmn = self.mid_specmn_fe_grid if self.supprt_flag: supprt = self.supprt_fe_grid supprt_dofs_z = np.unique(supprt[self.shape_supprt_x / 2, self.shape_y / 2, 0, 0, 0, 0].dofs[:, :, 2].flatten()) else: supprt_dofs_z = np.unique(specmn[-1, :, 0, -1, :, 0].dofs[:, :, 2].flatten()) print 'supprt_dofs_z (unique)', supprt_dofs_z if self.elstmr_flag: elstmr = self.elstmr_fe_grid load_dofs_z = np.unique(elstmr[:, :, -1, :, :, -1].dofs[:, :, 2].flatten()) else: # center_top_line_dofs # load_dofs_z = np.unique(mid_specmn[0, :, -1, 0, :, -1].dofs[:, :, 2].flatten()) print 'load_dofs_z used for integration of force: ', load_dofs_z # center top z-dof # center_top_dof_z = mid_specmn[0, 0, 0, 0, 0, 0].dofs[0, 0, 2] print 'center_top_dof used for displacement tracing: ', center_top_dof_z # force-displacement-diagram (LOAD) # (surface load on the elstmr or line load at specimen center) # self.f_w_diagram_center = RTraceGraph(name='displacement (center) - reaction 2', var_x='U_k' , idx_x=center_top_dof_z, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') # force-displacement-diagram (SUPPORT) # (dofs at support line of the specmn used to integrate the force) # self.f_w_diagram_supprt = RTraceGraph(name='displacement (center) - reaction 2', var_x='U_k' , idx_x=center_top_dof_z, var_y='F_int', idx_y_arr=supprt_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='4000. * y') ts = TS( sdomain=self.fe_domain, bcond_list=self.bc_list, rtrace_list=[ self.f_w_diagram_center, self.f_w_diagram_supprt, RTraceDomainListField(name='Displacement' , var='u', idx=0, warp=True), # RTraceDomainListField(name = 'Stress' , # var = 'sig_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Strain' , # var = 'eps_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Damage' , # var = 'omega_mtx', idx = 0, warp = True, # record_on = 'update'), RTraceDomainListField(name='max_omega_i', warp=True, var='max_omega_i', idx=0, record_on='update'), # RTraceDomainListField(name = 'IStress' , # position = 'int_pnts', # var = 'sig_app', idx = 0, # record_on = 'update'), # RTraceDomainListField(name = 'IStrain' , # position = 'int_pnts', # var = 'eps_app', idx = 0, # record_on = 'update') ]) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=50, tolerance=self.tolerance, RESETMAX=0, tline=TLine(min=0.0, step=self.tstep, max=self.tmax) ) return tloop #-------------------------------------------------------------- # prepare pstudy #-------------------------------------------------------------- def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() self.f_w_diagram_center.refresh() F_max = max(self.f_w_diagram_center.trace.ydata) u_center_top_z = U[ self.center_top_dofs ][0, 0, 2] return array([ u_center_top_z, F_max ], dtype='float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='u_center_top_z', unit='m'), SimOut(name='F_max', unit='kN') ]
RESETMAX = 0, tolerance = 0.01, # 5e-4, debug = False, tline = TLine( min = 0.0, step = 0.02, max = 1.0 ) ) from pickle import dump, load do = 'show_last_result' # do = 'eval' if do == 'eval': print 'dof_number', upper_left_corner U = tloop.eval() #print 'U', U[ upper_left_corner ] f_w_diagram.refresh() file = open( 'f_w_diagram.pickle', 'w' ) dump( f_w_diagram.trace, file ) file.close() if do == 'show_last_result': from promod.exdb.ex_run import ExRun import pylab as p # f_w_diagram.trace.mpl_plot( p, color = 'red' ) file = open( 'f_w_diagram.pickle', 'r' ) trace = load( file ) p.plot( trace.xdata, trace.ydata, color = 'blue' ) file = open( 'f_w_diagram_TT11-10a.pickle', 'r' )
def eval( self ): elem_length = self.length / float( self.shape ) flaw_radius = self.flaw_radius mats = MATS1DElasticWithFlaw( E = 10., flaw_position = self.flaw_position, flaw_radius = flaw_radius, reduction_factor = self.reduction_factor ) #fets_eval = FETS1D2L( mats_eval = mats ) fets_eval = FETS1D2L3U( mats_eval = mats ) domain = FEGrid( coord_max = ( self.length, 0., 0. ), shape = ( self.shape, ), fets_eval = fets_eval ) avg_processor = RTNonlocalAvg( avg_fn = QuarticAF( radius = self.avg_radius, correction = True ) ) eps_app = RTraceDomainListField( name = 'Strain' , position = 'int_pnts', var = 'eps_app', warp = False ) damage = RTraceDomainListField( name = 'Damage' , position = 'int_pnts', var = 'omega', warp = False ) disp = RTraceDomainListField( name = 'Displacement' , position = 'int_pnts', var = 'u', warp = False ) sig_app = RTraceDomainListField( name = 'Stress' , position = 'int_pnts', var = 'sig_app' ) right_dof = domain[-1, -1].dofs[0, 0, 0] rt_fu = RTraceGraph( name = 'Fi,right over u_right (iteration)' , var_y = 'F_int', idx_y = right_dof, var_x = 'U_k', idx_x = right_dof ) ts = TS( u_processor = avg_processor, dof_resultants = True, sdomain = domain, # conversion to list (square brackets) is only necessary for slicing of # single dofs, e.g "get_left_dofs()[0,1]" # bcond_list = [ BCDof(var='u', dof = 0, value = 0.) ] + # [ BCDof(var='u', dof = 2, value = 0.001 ) ]+ # [ ) ], bcond_list = [BCDof( var = 'u', dof = 0, value = 0. ), # BCDof(var='u', dof = 1, link_dofs = [2], link_coeffs = [0.5], # value = 0. ), # BCDof(var='u', dof = 2, link_dofs = [3], link_coeffs = [1.], # value = 0. ), BCDof( var = 'u', dof = right_dof, value = 0.01, ) ], rtrace_list = [ rt_fu, eps_app, # damage, sig_app, disp, ] ) # Add the time-loop control tloop = TLoop( tstepper = ts, KMAX = 100, tolerance = 1e-5, verbose_iteration = False, tline = TLine( min = 0.0, step = 1.0, max = 1.0 ) ) U = tloop.eval() p.subplot( 221 ) rt_fu.refresh() rt_fu.trace.plot( p ) eps = eps_app.subfields[0] xdata = eps.vtk_X[:, 0] ydata = eps.field_arr[:, 0, 0] idata = argsort( xdata ) p.subplot( 222 ) p.plot( xdata[idata], ydata[idata], 'o-' ) disp = disp.subfields[0] xdata = disp.vtk_X[:, 0] ydata = disp.field_arr[:, 0] idata = argsort( xdata ) p.subplot( 223 ) p.plot( xdata[idata], ydata[idata], 'o-' ) sig = sig_app.subfields[0] xdata = sig.vtk_X[:, 0] ydata = sig.field_arr[:, 0, 0] idata = argsort( xdata ) p.subplot( 224 ) p.plot( xdata[idata], ydata[idata], 'o-' )
class SimCrackLoc(IBVModel): '''Model assembling the components for studying the restrained crack localization. ''' geo_transform = Instance(FlawCenteredGeoTransform) def _geo_transform_default(self): return FlawCenteredGeoTransform() shape = Int(10, desc='Number of finite elements', ps_levsls=(10, 40, 4), input=True) length = Float(1000, desc='Length of the simulated region', unit='mm', input=True) flaw_position = Float(500, input=True, unit='mm') flaw_radius = Float(100, input=True, unit='mm') reduction_factor = Float(0.9, input=True) elastic_fraction = Float(0.9, input=True) avg_radius = Float(400, input=True, unit='mm') # tensile strength of concrete f_m_t = Float(3.0, input=True, unit='MPa') epsilon_0 = Property(unit='-') def _get_epsilon_0(self): return self.f_m_t / self.E_m epsilon_f = Float(10, input=True, unit='-') h_m = Float(10, input=True, unit='mm') b_m = Float(8, input=True, unit='mm') A_m = Property(unit='m^2') def _get_A_m(self): return self.b_m * self.h_m E_m = Float(30.0e5, input=True, unit='MPa') E_f = Float(70.0e6, input=True, unit='MPa') A_f = Float(1.0, input=True, unit='mm^2') s_crit = Float(0.009, input=True, unit='mm') P_f = Property(depends_on='+input') @cached_property def _get_P_f(self): return sqrt(4 * self.A_f * pi) K_b = Property(depends_on='+input') @cached_property def _get_K_b(self): return self.T_max / self.s_crit tau_max = Float(0.0, input=True, unit='MPa') T_max = Property(depends_on='+input', unit='N/mm') @cached_property def _get_T_max(self): return self.tau_max * self.P_f rho = Property(depends_on='+input') @cached_property def _get_rho(self): return self.A_f / (self.A_f + self.A_m) #------------------------------------------------------- # Material model for the matrix #------------------------------------------------------- mats_m = Property(Instance(MATS1DDamageWithFlaw), depends_on='+input') @cached_property def _get_mats_m(self): mats_m = MATS1DDamageWithFlaw(E=self.E_m * self.A_m, flaw_position=self.flaw_position, flaw_radius=self.flaw_radius, reduction_factor=self.reduction_factor, epsilon_0=self.epsilon_0, epsilon_f=self.epsilon_f) return mats_m mats_f = Instance(MATS1DElastic) def _mats_f_default(self): mats_f = MATS1DElastic(E=self.E_f * self.A_f) return mats_f mats_b = Instance(MATS1DEval) def _mats_b_default(self): mats_b = MATS1DElastic(E=self.K_b) mats_b = MATS1DPlastic(E=self.K_b, sigma_y=self.T_max, K_bar=0., H_bar=0.) # plastic function of slip return mats_b mats_fb = Property(Instance(MATS1D5Bond), depends_on='+input') @cached_property def _get_mats_fb(self): # Material model construction return MATS1D5Bond( mats_phase1=MATS1DElastic(E=0), mats_phase2=self.mats_f, mats_ifslip=self.mats_b, mats_ifopen=MATS1DElastic( E=0) # elastic function of open - inactive ) #------------------------------------------------------- # Finite element type #------------------------------------------------------- fets_m = Property(depends_on='+input') @cached_property def _get_fets_m(self): fets_eval = FETS1D2L(mats_eval=self.mats_m) #fets_eval = FETS1D2L3U( mats_eval = self.mats_m ) return fets_eval fets_fb = Property(depends_on='+input') @cached_property def _get_fets_fb(self): return FETS1D52L4ULRH(mats_eval=self.mats_fb) #return FETS1D52L6ULRH( mats_eval = self.mats_fb ) #return FETS1D52L8ULRH( mats_eval = self.mats_fb ) #-------------------------------------------------------------------------------------- # Mesh integrator #-------------------------------------------------------------------------------------- fe_domain_structure = Property(depends_on='+input') @cached_property def _get_fe_domain_structure(self): '''Root of the domain hierarchy ''' elem_length = self.length / float(self.shape) fe_domain = FEDomain() fe_m_level = FERefinementGrid(name='matrix domain', domain=fe_domain, fets_eval=self.fets_m) fe_grid_m = FEGrid(name='matrix grid', coord_max=(self.length, ), shape=(self.shape, ), level=fe_m_level, fets_eval=self.fets_m, geo_transform=self.geo_transform) fe_fb_level = FERefinementGrid(name='fiber bond domain', domain=fe_domain, fets_eval=self.fets_fb) fe_grid_fb = FEGrid(coord_min=(0., length / 5.), coord_max=(length, 0.), shape=(self.shape, 1), level=fe_fb_level, fets_eval=self.fets_fb, geo_transform=self.geo_transform) return fe_domain, fe_grid_m, fe_grid_fb, fe_m_level, fe_fb_level fe_domain = Property def _get_fe_domain(self): return self.fe_domain_structure[0] fe_grid_m = Property def _get_fe_grid_m(self): return self.fe_domain_structure[1] fe_grid_fb = Property def _get_fe_grid_fb(self): return self.fe_domain_structure[2] fe_m_level = Property def _get_fe_m_level(self): return self.fe_domain_structure[3] fe_fb_level = Property def _get_fe_fb_level(self): return self.fe_domain_structure[4] #--------------------------------------------------------------------------- # Load scaling adapted to the elastic and inelastic regime #--------------------------------------------------------------------------- final_displ = Property(depends_on='+input') @cached_property def _get_final_displ(self): damage_onset_displ = self.mats_m.epsilon_0 * self.length return damage_onset_displ / self.elastic_fraction step_size = Property(depends_on='+input') @cached_property def _get_step_size(self): n_steps = self.n_steps return 1.0 / float(n_steps) time_function = Property(depends_on='+input') @cached_property def _get_time_function(self): '''Get the time function so that the elastic regime is skipped in a single step. ''' step_size = self.step_size elastic_value = self.elastic_fraction * 0.98 * self.reduction_factor inelastic_value = 1.0 - elastic_value def ls(t): if t <= step_size: return (elastic_value / step_size) * t else: return elastic_value + (t - step_size) * (inelastic_value) / ( 1 - step_size) return ls def plot_time_function(self, p): '''Plot the time function. ''' n_steps = self.n_steps mats = self.mats step_size = self.step_size ls_t = linspace(0, step_size * n_steps, n_steps + 1) ls_fn = frompyfunc(self.time_function, 1, 1) ls_v = ls_fn(ls_t) p.subplot(321) p.plot(ls_t, ls_v, 'ro-') final_epsilon = self.final_displ / self.length kappa = linspace(mats.epsilon_0, final_epsilon, 10) omega_fn = frompyfunc(lambda kappa: mats._get_omega(None, kappa), 1, 1) omega = omega_fn(kappa) kappa_scaled = (step_size + (1 - step_size) * (kappa - mats.epsilon_0) / (final_epsilon - mats.epsilon_0)) xdata = hstack([array([0.0], dtype=float), kappa_scaled]) ydata = hstack([array([0.0], dtype=float), omega]) p.plot(xdata, ydata, 'g') p.xlabel('regular time [-]') p.ylabel('scaled time [-]') run = Button @on_trait_change('run') def peval(self): '''Evaluation procedure. ''' #mv = MATS1DDamageView( model = mats_eval ) #mv.configure_traits() right_dof_m = self.fe_grid_m[-1, -1].dofs[0, 0, 0] right_dof_fb = self.fe_grid_fb[-1, -1, -1, -1].dofs[0, 0, 0] # Response tracers A = self.A_m + self.A_f self.sig_eps_m = RTraceGraph(name='F_u_m', var_y='F_int', idx_y=right_dof_m, var_x='U_k', idx_x=right_dof_m, transform_y='y / %g' % A) # Response tracers self.sig_eps_f = RTraceGraph(name='F_u_f', var_y='F_int', idx_y=right_dof_fb, var_x='U_k', idx_x=right_dof_fb, transform_y='y / %g' % A) self.eps_m_field = RTraceDomainListField(name='eps_m', position='int_pnts', var='eps_app', warp=False) self.eps_f_field = RTraceDomainListField(name='eps_f', position='int_pnts', var='mats_phase2_eps_app', warp=False) # Response tracers self.sig_m_field = RTraceDomainListField(name='sig_m', position='int_pnts', var='sig_app') self.sig_f_field = RTraceDomainListField(name='sig_f', position='int_pnts', var='mats_phase2_sig_app') self.omega_m_field = RTraceDomainListField(name='omega_m', position='int_pnts', var='omega', warp=False) self.shear_flow_field = RTraceDomainListField(name='shear flow', position='int_pnts', var='shear_flow', warp=False) self.slip_field = RTraceDomainListField(name='slip', position='int_pnts', var='slip', warp=False) avg_processor = None if self.avg_radius > 0.0: n_dofs = self.fe_domain.n_dofs avg_processor = RTUAvg(sd=self.fe_m_level, n_dofs=n_dofs, avg_fn=QuarticAF(radius=self.avg_radius)) ts = TStepper( u_processor=avg_processor, dof_resultants=True, sdomain=self.fe_domain, bcond_list=[ # define the left clamping BCSlice(var='u', value=0., dims=[0], slice=self.fe_grid_fb[0, 0, 0, :]), # BCSlice( var = 'u', value = 0., dims = [0], slice = self.fe_grid_m[ 0, 0 ] ), # loading at the right edge # BCSlice( var = 'f', value = 1, dims = [0], slice = domain[-1, -1, -1, 0], # time_function = ls ), BCSlice(var='u', value=self.final_displ, dims=[0], slice=self.fe_grid_fb[-1, -1, -1, :], time_function=self.time_function), # BCSlice( var = 'u', value = self.final_displ, dims = [0], slice = self.fe_grid_m[-1, -1], # time_function = self.time_function ), # fix horizontal displacement in the top layer # BCSlice( var = 'u', value = 0., dims = [0], slice = domain[:, -1, :, -1] ), # fix the vertical displacement all over the domain BCSlice(var='u', value=0., dims=[1], slice=self.fe_grid_fb[:, :, :, :]), # # Connect bond and matrix domains BCDofGroup(var='u', value=0., dims=[0], get_link_dof_method=self.fe_grid_fb.get_bottom_dofs, get_dof_method=self.fe_grid_m.get_all_dofs, link_coeffs=[1.]) ], rtrace_list=[ self.sig_eps_m, self.sig_eps_f, self.eps_m_field, self.eps_f_field, self.sig_m_field, self.sig_f_field, self.omega_m_field, self.shear_flow_field, self.slip_field, ]) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=300, tolerance=1e-5, debug=False, verbose_iteration=True, verbose_time=False, tline=TLine(min=0.0, step=self.step_size, max=1.0)) tloop.on_accept_time_step = self.plot U = tloop.eval() self.sig_eps_f.refresh() max_sig_m = max(self.sig_eps_m.trace.ydata) return array([U[right_dof_m], max_sig_m], dtype='float_') #-------------------------------------------------------------------------------------- # Tracers #-------------------------------------------------------------------------------------- def plot_sig_eps(self, p): p.set_xlabel('control displacement [mm]') p.set_ylabel('stress [MPa]') self.sig_eps_m.refresh() self.sig_eps_m.trace.plot(p, 'o-') self.sig_eps_f.refresh() self.sig_eps_f.trace.plot(p, 'o-') p.plot(self.sig_eps_m.trace.xdata, self.sig_eps_m.trace.ydata + self.sig_eps_f.trace.ydata, 'o-') def plot_eps(self, p): eps_m = self.eps_m_field.subfields[0] xdata = eps_m.vtk_X[:, 0] ydata = eps_m.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], 'o-') eps_f = self.eps_f_field.subfields[1] xdata = eps_f.vtk_X[:, 0] ydata = eps_f.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], 'o-') p.set_ylim(ymin=0) p.set_xlabel('bar axis [mm]') p.set_ylabel('strain [-]') def plot_omega(self, p): omega_m = self.omega_m_field.subfields[0] xdata = omega_m.vtk_X[:, 0] ydata = omega_m.field_arr[:] idata = argsort(xdata) p.fill(xdata[idata], ydata[idata], facecolor='gray', alpha=0.2) print 'max omega', max(ydata[idata]) p.set_ylim(ymin=0, ymax=1.0) p.set_xlabel('bar axis [mm]') p.set_ylabel('omega [-]') def plot_sig(self, p): sig_m = self.sig_m_field.subfields[0] xdata = sig_m.vtk_X[:, 0] ydata = sig_m.field_arr[:, 0, 0] idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], 'o-') sig_f = self.sig_f_field.subfields[1] xdata = sig_f.vtk_X[:, 0] ydata = sig_f.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], 'o-') xdata = sig_f.vtk_X[:, 0] ydata = sig_f.field_arr[:, 0, 0] + sig_m.field_arr[:, 0, 0] p.plot(xdata[idata], ydata[idata], 'ro-') p.set_ylim(ymin=0) # , ymax = 1.2 * ymax ) p.set_xlabel('bar axis [mm]') p.set_ylabel('stress [MPa]') def plot_shear_flow(self, p): shear_flow = self.shear_flow_field.subfields[1] xdata = shear_flow.vtk_X[:, 0] ydata = shear_flow.field_arr[:, 0] / self.P_f idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], 'o-') p.set_xlabel('bar axis [mm]') p.set_ylabel('shear flow [N/m]') def plot_slip(self, p): slip = self.slip_field.subfields[1] xdata = slip.vtk_X[:, 0] ydata = slip.field_arr[:, 0] idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], 'ro-') p.set_xlabel('bar axis [mm]') p.set_ylabel('slip [N/m]') def plot_tracers(self, p=p): p.subplot(221) self.plot_sig_eps(p) p.subplot(223) self.plot_eps(p) p.subplot(224) self.plot_sig(p) #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_ld = Instance(Figure) def _figure_ld_default(self): figure = Figure(facecolor='white') figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_eps = Instance(Figure) def _figure_eps_default(self): figure = Figure(facecolor='white') figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_shear_flow = Instance(Figure) def _figure_shear_flow_default(self): figure = Figure(facecolor='white') figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_sig = Instance(Figure) def _figure_sig_default(self): figure = Figure(facecolor='white') figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure #--------------------------------------------------------------- # PLOT OBJECT #------------------------------------------------------------------- figure_shear_flow = Instance(Figure) def _figure_shear_flow_default(self): figure = Figure(facecolor='white') figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure def plot(self): self.figure_ld.clear() ax = self.figure_ld.gca() self.plot_sig_eps(ax) self.figure_eps.clear() ax = self.figure_eps.gca() self.plot_eps(ax) ax2 = ax.twinx() self.plot_omega(ax2) self.figure_sig.clear() ax = self.figure_sig.gca() self.plot_sig(ax) ax2 = ax.twinx() self.plot_omega(ax2) self.figure_shear_flow.clear() ax = self.figure_shear_flow.gca() self.plot_shear_flow(ax) ax2 = ax.twinx() self.plot_slip(ax2) self.data_changed = True def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='right end displacement', unit='m'), SimOut(name='peak load', uni='MPa') ] data_changed = Event toolbar = ToolBar(Action(name="Run", tooltip='Start computation', image=ImageResource('kt-start'), action="start_study"), Action(name="Pause", tooltip='Pause computation', image=ImageResource('kt-pause'), action="pause_study"), Action(name="Stop", tooltip='Stop computation', image=ImageResource('kt-stop'), action="stop_study"), image_size=(32, 32), show_tool_names=False, show_divider=True, name='view_toolbar'), traits_view = View(HSplit( VSplit( Item('run', show_label=False), VGroup( Item('shape'), Item('n_steps'), Item('length'), label='parameters', id='crackloc.viewmodel.factor.geometry', dock='tab', scrollable=True, ), VGroup(Item('E_m'), Item('f_m_t'), Item('avg_radius'), Item('h_m'), Item('b_m'), Item('A_m', style='readonly'), Item('mats_m', show_label=False), label='Matrix', dock='tab', id='crackloc.viewmodel.factor.matrix', scrollable=True), VGroup(Item('E_f'), Item('A_f'), Item('mats_f', show_label=False), label='Fiber', dock='tab', id='crackloc.viewmodel.factor.fiber', scrollable=True), VGroup(Group( Item('tau_max'), Item('s_crit'), Item('P_f', style='readonly', show_label=True), Item('K_b', style='readonly', show_label=True), Item('T_max', style='readonly', show_label=True), ), Item('mats_b', show_label=False), label='Bond', dock='tab', id='crackloc.viewmodel.factor.bond', scrollable=True), VGroup(Item('rho', style='readonly', show_label=True), label='Composite', dock='tab', id='crackloc.viewmodel.factor.jcomposite', scrollable=True), id='crackloc.viewmodel.left', label='studied factors', layout='tabbed', dock='tab', ), VSplit( VGroup( Item('figure_ld', editor=MPLFigureEditor(), resizable=True, show_label=False), label='stress-strain', id='crackloc.viewmode.figure_ld_window', dock='tab', ), VGroup( Item('figure_eps', editor=MPLFigureEditor(), resizable=True, show_label=False), label='strains profile', id='crackloc.viewmode.figure_eps_window', dock='tab', ), VGroup( Item('figure_sig', editor=MPLFigureEditor(), resizable=True, show_label=False), label='stress profile', id='crackloc.viewmode.figure_sig_window', dock='tab', ), VGroup( Item('figure_shear_flow', editor=MPLFigureEditor(), resizable=True, show_label=False), label='bond shear and slip profiles', id='crackloc.viewmode.figure_shear_flow_window', dock='tab', ), id='crackloc.viewmodel.right', ), id='crackloc.viewmodel.splitter', ), title='SimVisage Component: Crack localization', id='crackloc.viewmodel', dock='tab', resizable=True, height=0.8, width=0.8, buttons=[OKButton])
class SimCrackLoc(IBVModel): """Model assembling the components for studying the restrained crack localization. """ geo_transform = Instance(FlawCenteredGeoTransform) def _geo_transform_default(self): return FlawCenteredGeoTransform() shape = Int(10, desc="Number of finite elements", ps_levsls=(10, 40, 4), input=True) length = Float(1000, desc="Length of the simulated region", unit="mm", input=True) flaw_position = Float(500, input=True, unit="mm") flaw_radius = Float(100, input=True, unit="mm") reduction_factor = Float(0.9, input=True) elastic_fraction = Float(0.9, input=True) avg_radius = Float(400, input=True, unit="mm") # tensile strength of concrete f_m_t = Float(3.0, input=True, unit="MPa") epsilon_0 = Property(unit="-") def _get_epsilon_0(self): return self.f_m_t / self.E_m epsilon_f = Float(10, input=True, unit="-") h_m = Float(10, input=True, unit="mm") b_m = Float(8, input=True, unit="mm") A_m = Property(unit="m^2") def _get_A_m(self): return self.b_m * self.h_m E_m = Float(30.0e5, input=True, unit="MPa") E_f = Float(70.0e6, input=True, unit="MPa") A_f = Float(1.0, input=True, unit="mm^2") s_crit = Float(0.009, input=True, unit="mm") P_f = Property(depends_on="+input") @cached_property def _get_P_f(self): return sqrt(4 * self.A_f * pi) K_b = Property(depends_on="+input") @cached_property def _get_K_b(self): return self.T_max / self.s_crit tau_max = Float(0.0, input=True, unit="MPa") T_max = Property(depends_on="+input", unit="N/mm") @cached_property def _get_T_max(self): return self.tau_max * self.P_f rho = Property(depends_on="+input") @cached_property def _get_rho(self): return self.A_f / (self.A_f + self.A_m) # ------------------------------------------------------- # Material model for the matrix # ------------------------------------------------------- mats_m = Property(Instance(MATS1DDamageWithFlaw), depends_on="+input") @cached_property def _get_mats_m(self): mats_m = MATS1DDamageWithFlaw( E=self.E_m * self.A_m, flaw_position=self.flaw_position, flaw_radius=self.flaw_radius, reduction_factor=self.reduction_factor, epsilon_0=self.epsilon_0, epsilon_f=self.epsilon_f, ) return mats_m mats_f = Instance(MATS1DElastic) def _mats_f_default(self): mats_f = MATS1DElastic(E=self.E_f * self.A_f) return mats_f mats_b = Instance(MATS1DEval) def _mats_b_default(self): mats_b = MATS1DElastic(E=self.K_b) mats_b = MATS1DPlastic(E=self.K_b, sigma_y=self.T_max, K_bar=0.0, H_bar=0.0) # plastic function of slip return mats_b mats_fb = Property(Instance(MATS1D5Bond), depends_on="+input") @cached_property def _get_mats_fb(self): # Material model construction return MATS1D5Bond( mats_phase1=MATS1DElastic(E=0), mats_phase2=self.mats_f, mats_ifslip=self.mats_b, mats_ifopen=MATS1DElastic(E=0), # elastic function of open - inactive ) # ------------------------------------------------------- # Finite element type # ------------------------------------------------------- fets_m = Property(depends_on="+input") @cached_property def _get_fets_m(self): fets_eval = FETS1D2L(mats_eval=self.mats_m) # fets_eval = FETS1D2L3U( mats_eval = self.mats_m ) return fets_eval fets_fb = Property(depends_on="+input") @cached_property def _get_fets_fb(self): return FETS1D52L4ULRH(mats_eval=self.mats_fb) # return FETS1D52L6ULRH( mats_eval = self.mats_fb ) # return FETS1D52L8ULRH( mats_eval = self.mats_fb ) # -------------------------------------------------------------------------------------- # Mesh integrator # -------------------------------------------------------------------------------------- fe_domain_structure = Property(depends_on="+input") @cached_property def _get_fe_domain_structure(self): """Root of the domain hierarchy """ elem_length = self.length / float(self.shape) fe_domain = FEDomain() fe_m_level = FERefinementGrid(name="matrix domain", domain=fe_domain, fets_eval=self.fets_m) fe_grid_m = FEGrid( name="matrix grid", coord_max=(self.length,), shape=(self.shape,), level=fe_m_level, fets_eval=self.fets_m, geo_transform=self.geo_transform, ) fe_fb_level = FERefinementGrid(name="fiber bond domain", domain=fe_domain, fets_eval=self.fets_fb) fe_grid_fb = FEGrid( coord_min=(0.0, length / 5.0), coord_max=(length, 0.0), shape=(self.shape, 1), level=fe_fb_level, fets_eval=self.fets_fb, geo_transform=self.geo_transform, ) return fe_domain, fe_grid_m, fe_grid_fb, fe_m_level, fe_fb_level fe_domain = Property def _get_fe_domain(self): return self.fe_domain_structure[0] fe_grid_m = Property def _get_fe_grid_m(self): return self.fe_domain_structure[1] fe_grid_fb = Property def _get_fe_grid_fb(self): return self.fe_domain_structure[2] fe_m_level = Property def _get_fe_m_level(self): return self.fe_domain_structure[3] fe_fb_level = Property def _get_fe_fb_level(self): return self.fe_domain_structure[4] # --------------------------------------------------------------------------- # Load scaling adapted to the elastic and inelastic regime # --------------------------------------------------------------------------- final_displ = Property(depends_on="+input") @cached_property def _get_final_displ(self): damage_onset_displ = self.mats_m.epsilon_0 * self.length return damage_onset_displ / self.elastic_fraction step_size = Property(depends_on="+input") @cached_property def _get_step_size(self): n_steps = self.n_steps return 1.0 / float(n_steps) time_function = Property(depends_on="+input") @cached_property def _get_time_function(self): """Get the time function so that the elastic regime is skipped in a single step. """ step_size = self.step_size elastic_value = self.elastic_fraction * 0.98 * self.reduction_factor inelastic_value = 1.0 - elastic_value def ls(t): if t <= step_size: return (elastic_value / step_size) * t else: return elastic_value + (t - step_size) * (inelastic_value) / (1 - step_size) return ls def plot_time_function(self, p): """Plot the time function. """ n_steps = self.n_steps mats = self.mats step_size = self.step_size ls_t = linspace(0, step_size * n_steps, n_steps + 1) ls_fn = frompyfunc(self.time_function, 1, 1) ls_v = ls_fn(ls_t) p.subplot(321) p.plot(ls_t, ls_v, "ro-") final_epsilon = self.final_displ / self.length kappa = linspace(mats.epsilon_0, final_epsilon, 10) omega_fn = frompyfunc(lambda kappa: mats._get_omega(None, kappa), 1, 1) omega = omega_fn(kappa) kappa_scaled = step_size + (1 - step_size) * (kappa - mats.epsilon_0) / (final_epsilon - mats.epsilon_0) xdata = hstack([array([0.0], dtype=float), kappa_scaled]) ydata = hstack([array([0.0], dtype=float), omega]) p.plot(xdata, ydata, "g") p.xlabel("regular time [-]") p.ylabel("scaled time [-]") run = Button @on_trait_change("run") def peval(self): """Evaluation procedure. """ # mv = MATS1DDamageView( model = mats_eval ) # mv.configure_traits() right_dof_m = self.fe_grid_m[-1, -1].dofs[0, 0, 0] right_dof_fb = self.fe_grid_fb[-1, -1, -1, -1].dofs[0, 0, 0] # Response tracers A = self.A_m + self.A_f self.sig_eps_m = RTraceGraph( name="F_u_m", var_y="F_int", idx_y=right_dof_m, var_x="U_k", idx_x=right_dof_m, transform_y="y / %g" % A ) # Response tracers self.sig_eps_f = RTraceGraph( name="F_u_f", var_y="F_int", idx_y=right_dof_fb, var_x="U_k", idx_x=right_dof_fb, transform_y="y / %g" % A ) self.eps_m_field = RTraceDomainListField(name="eps_m", position="int_pnts", var="eps_app", warp=False) self.eps_f_field = RTraceDomainListField( name="eps_f", position="int_pnts", var="mats_phase2_eps_app", warp=False ) # Response tracers self.sig_m_field = RTraceDomainListField(name="sig_m", position="int_pnts", var="sig_app") self.sig_f_field = RTraceDomainListField(name="sig_f", position="int_pnts", var="mats_phase2_sig_app") self.omega_m_field = RTraceDomainListField(name="omega_m", position="int_pnts", var="omega", warp=False) self.shear_flow_field = RTraceDomainListField( name="shear flow", position="int_pnts", var="shear_flow", warp=False ) self.slip_field = RTraceDomainListField(name="slip", position="int_pnts", var="slip", warp=False) avg_processor = None if self.avg_radius > 0.0: n_dofs = self.fe_domain.n_dofs avg_processor = RTUAvg(sd=self.fe_m_level, n_dofs=n_dofs, avg_fn=QuarticAF(radius=self.avg_radius)) ts = TStepper( u_processor=avg_processor, dof_resultants=True, sdomain=self.fe_domain, bcond_list=[ # define the left clamping BCSlice(var="u", value=0.0, dims=[0], slice=self.fe_grid_fb[0, 0, 0, :]), # BCSlice( var = 'u', value = 0., dims = [0], slice = self.fe_grid_m[ 0, 0 ] ), # loading at the right edge # BCSlice( var = 'f', value = 1, dims = [0], slice = domain[-1, -1, -1, 0], # time_function = ls ), BCSlice( var="u", value=self.final_displ, dims=[0], slice=self.fe_grid_fb[-1, -1, -1, :], time_function=self.time_function, ), # BCSlice( var = 'u', value = self.final_displ, dims = [0], slice = self.fe_grid_m[-1, -1], # time_function = self.time_function ), # fix horizontal displacement in the top layer # BCSlice( var = 'u', value = 0., dims = [0], slice = domain[:, -1, :, -1] ), # fix the vertical displacement all over the domain BCSlice(var="u", value=0.0, dims=[1], slice=self.fe_grid_fb[:, :, :, :]), # # Connect bond and matrix domains BCDofGroup( var="u", value=0.0, dims=[0], get_link_dof_method=self.fe_grid_fb.get_bottom_dofs, get_dof_method=self.fe_grid_m.get_all_dofs, link_coeffs=[1.0], ), ], rtrace_list=[ self.sig_eps_m, self.sig_eps_f, self.eps_m_field, self.eps_f_field, self.sig_m_field, self.sig_f_field, self.omega_m_field, self.shear_flow_field, self.slip_field, ], ) # Add the time-loop control tloop = TLoop( tstepper=ts, KMAX=300, tolerance=1e-5, debug=False, verbose_iteration=True, verbose_time=False, tline=TLine(min=0.0, step=self.step_size, max=1.0), ) tloop.on_accept_time_step = self.plot U = tloop.eval() self.sig_eps_f.refresh() max_sig_m = max(self.sig_eps_m.trace.ydata) return array([U[right_dof_m], max_sig_m], dtype="float_") # -------------------------------------------------------------------------------------- # Tracers # -------------------------------------------------------------------------------------- def plot_sig_eps(self, p): p.set_xlabel("control displacement [mm]") p.set_ylabel("stress [MPa]") self.sig_eps_m.refresh() self.sig_eps_m.trace.plot(p, "o-") self.sig_eps_f.refresh() self.sig_eps_f.trace.plot(p, "o-") p.plot(self.sig_eps_m.trace.xdata, self.sig_eps_m.trace.ydata + self.sig_eps_f.trace.ydata, "o-") def plot_eps(self, p): eps_m = self.eps_m_field.subfields[0] xdata = eps_m.vtk_X[:, 0] ydata = eps_m.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], "o-") eps_f = self.eps_f_field.subfields[1] xdata = eps_f.vtk_X[:, 0] ydata = eps_f.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], "o-") p.set_ylim(ymin=0) p.set_xlabel("bar axis [mm]") p.set_ylabel("strain [-]") def plot_omega(self, p): omega_m = self.omega_m_field.subfields[0] xdata = omega_m.vtk_X[:, 0] ydata = omega_m.field_arr[:] idata = argsort(xdata) p.fill(xdata[idata], ydata[idata], facecolor="gray", alpha=0.2) print "max omega", max(ydata[idata]) p.set_ylim(ymin=0, ymax=1.0) p.set_xlabel("bar axis [mm]") p.set_ylabel("omega [-]") def plot_sig(self, p): sig_m = self.sig_m_field.subfields[0] xdata = sig_m.vtk_X[:, 0] ydata = sig_m.field_arr[:, 0, 0] idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], "o-") sig_f = self.sig_f_field.subfields[1] xdata = sig_f.vtk_X[:, 0] ydata = sig_f.field_arr[:, 0, 0] idata = argsort(xdata) p.plot(xdata[idata], ydata[idata], "o-") xdata = sig_f.vtk_X[:, 0] ydata = sig_f.field_arr[:, 0, 0] + sig_m.field_arr[:, 0, 0] p.plot(xdata[idata], ydata[idata], "ro-") p.set_ylim(ymin=0) # , ymax = 1.2 * ymax ) p.set_xlabel("bar axis [mm]") p.set_ylabel("stress [MPa]") def plot_shear_flow(self, p): shear_flow = self.shear_flow_field.subfields[1] xdata = shear_flow.vtk_X[:, 0] ydata = shear_flow.field_arr[:, 0] / self.P_f idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], "o-") p.set_xlabel("bar axis [mm]") p.set_ylabel("shear flow [N/m]") def plot_slip(self, p): slip = self.slip_field.subfields[1] xdata = slip.vtk_X[:, 0] ydata = slip.field_arr[:, 0] idata = argsort(xdata) ymax = max(ydata) p.plot(xdata[idata], ydata[idata], "ro-") p.set_xlabel("bar axis [mm]") p.set_ylabel("slip [N/m]") def plot_tracers(self, p=p): p.subplot(221) self.plot_sig_eps(p) p.subplot(223) self.plot_eps(p) p.subplot(224) self.plot_sig(p) # --------------------------------------------------------------- # PLOT OBJECT # ------------------------------------------------------------------- figure_ld = Instance(Figure) def _figure_ld_default(self): figure = Figure(facecolor="white") figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure # --------------------------------------------------------------- # PLOT OBJECT # ------------------------------------------------------------------- figure_eps = Instance(Figure) def _figure_eps_default(self): figure = Figure(facecolor="white") figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure # --------------------------------------------------------------- # PLOT OBJECT # ------------------------------------------------------------------- figure_shear_flow = Instance(Figure) def _figure_shear_flow_default(self): figure = Figure(facecolor="white") figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure # --------------------------------------------------------------- # PLOT OBJECT # ------------------------------------------------------------------- figure_sig = Instance(Figure) def _figure_sig_default(self): figure = Figure(facecolor="white") figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure # --------------------------------------------------------------- # PLOT OBJECT # ------------------------------------------------------------------- figure_shear_flow = Instance(Figure) def _figure_shear_flow_default(self): figure = Figure(facecolor="white") figure.add_axes([0.12, 0.13, 0.85, 0.74]) return figure def plot(self): self.figure_ld.clear() ax = self.figure_ld.gca() self.plot_sig_eps(ax) self.figure_eps.clear() ax = self.figure_eps.gca() self.plot_eps(ax) ax2 = ax.twinx() self.plot_omega(ax2) self.figure_sig.clear() ax = self.figure_sig.gca() self.plot_sig(ax) ax2 = ax.twinx() self.plot_omega(ax2) self.figure_shear_flow.clear() ax = self.figure_shear_flow.gca() self.plot_shear_flow(ax) ax2 = ax.twinx() self.plot_slip(ax2) self.data_changed = True def get_sim_outputs(self): """ Specifies the results and their order returned by the model evaluation. """ return [SimOut(name="right end displacement", unit="m"), SimOut(name="peak load", uni="MPa")] data_changed = Event toolbar = ( ToolBar( Action(name="Run", tooltip="Start computation", image=ImageResource("kt-start"), action="start_study"), Action(name="Pause", tooltip="Pause computation", image=ImageResource("kt-pause"), action="pause_study"), Action(name="Stop", tooltip="Stop computation", image=ImageResource("kt-stop"), action="stop_study"), image_size=(32, 32), show_tool_names=False, show_divider=True, name="view_toolbar", ), ) traits_view = View( HSplit( VSplit( Item("run", show_label=False), VGroup( Item("shape"), Item("n_steps"), Item("length"), label="parameters", id="crackloc.viewmodel.factor.geometry", dock="tab", scrollable=True, ), VGroup( Item("E_m"), Item("f_m_t"), Item("avg_radius"), Item("h_m"), Item("b_m"), Item("A_m", style="readonly"), Item("mats_m", show_label=False), label="Matrix", dock="tab", id="crackloc.viewmodel.factor.matrix", scrollable=True, ), VGroup( Item("E_f"), Item("A_f"), Item("mats_f", show_label=False), label="Fiber", dock="tab", id="crackloc.viewmodel.factor.fiber", scrollable=True, ), VGroup( Group( Item("tau_max"), Item("s_crit"), Item("P_f", style="readonly", show_label=True), Item("K_b", style="readonly", show_label=True), Item("T_max", style="readonly", show_label=True), ), Item("mats_b", show_label=False), label="Bond", dock="tab", id="crackloc.viewmodel.factor.bond", scrollable=True, ), VGroup( Item("rho", style="readonly", show_label=True), label="Composite", dock="tab", id="crackloc.viewmodel.factor.jcomposite", scrollable=True, ), id="crackloc.viewmodel.left", label="studied factors", layout="tabbed", dock="tab", ), VSplit( VGroup( Item("figure_ld", editor=MPLFigureEditor(), resizable=True, show_label=False), label="stress-strain", id="crackloc.viewmode.figure_ld_window", dock="tab", ), VGroup( Item("figure_eps", editor=MPLFigureEditor(), resizable=True, show_label=False), label="strains profile", id="crackloc.viewmode.figure_eps_window", dock="tab", ), VGroup( Item("figure_sig", editor=MPLFigureEditor(), resizable=True, show_label=False), label="stress profile", id="crackloc.viewmode.figure_sig_window", dock="tab", ), VGroup( Item("figure_shear_flow", editor=MPLFigureEditor(), resizable=True, show_label=False), label="bond shear and slip profiles", id="crackloc.viewmode.figure_shear_flow_window", dock="tab", ), id="crackloc.viewmodel.right", ), id="crackloc.viewmodel.splitter", ), title="SimVisage Component: Crack localization", id="crackloc.viewmodel", dock="tab", resizable=True, height=0.8, width=0.8, buttons=[OKButton], )
def eval(self): elem_length = self.length / float(self.shape) flaw_radius = self.flaw_radius mats = MATS1DElasticWithFlaw(E=10., flaw_position=self.flaw_position, flaw_radius=flaw_radius, reduction_factor=self.reduction_factor) #fets_eval = FETS1D2L( mats_eval = mats ) fets_eval = FETS1D2L3U(mats_eval=mats) domain = FEGrid(coord_max=(self.length, 0., 0.), shape=(self.shape,), fets_eval=fets_eval) avg_processor = RTNonlocalAvg(avg_fn=QuarticAF(radius=self.avg_radius, correction=True)) eps_app = RTraceDomainListField(name='Strain' , position='int_pnts', var='eps_app', warp=False) damage = RTraceDomainListField(name='Damage' , position='int_pnts', var='omega', warp=False) disp = RTraceDomainListField(name='Displacement' , position='int_pnts', var='u', warp=False) sig_app = RTraceDomainListField(name='Stress' , position='int_pnts', var='sig_app') right_dof = domain[-1, -1].dofs[0, 0, 0] rt_fu = RTraceGraph(name='Fi,right over u_right (iteration)' , var_y='F_int', idx_y=right_dof, var_x='U_k', idx_x=right_dof) ts = TS(u_processor=avg_processor, dof_resultants=True, sdomain=domain, # conversion to list (square brackets) is only necessary for slicing of # single dofs, e.g "get_left_dofs()[0,1]" # bcond_list = [ BCDof(var='u', dof = 0, value = 0.) ] + # [ BCDof(var='u', dof = 2, value = 0.001 ) ]+ # [ ) ], bcond_list=[BCDof(var='u', dof=0, value=0.), # BCDof(var='u', dof = 1, link_dofs = [2], link_coeffs = [0.5], # value = 0. ), # BCDof(var='u', dof = 2, link_dofs = [3], link_coeffs = [1.], # value = 0. ), BCDof(var='u', dof=right_dof, value=0.01, ) ], rtrace_list=[ rt_fu, eps_app, # damage, sig_app, disp, ] ) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=100, tolerance=1e-5, verbose_iteration=False, tline=TLine(min=0.0, step=1.0, max=1.0)) U = tloop.eval() p.subplot(221) rt_fu.refresh() rt_fu.trace.plot(p) eps = eps_app.subfields[0] xdata = eps.vtk_X[:, 0] ydata = eps.field_arr[:, 0, 0] idata = argsort(xdata) p.subplot(222) p.plot(xdata[idata], ydata[idata], 'o-') disp = disp.subfields[0] xdata = disp.vtk_X[:, 0] ydata = disp.field_arr[:, 0] idata = argsort(xdata) p.subplot(223) p.plot(xdata[idata], ydata[idata], 'o-') sig = sig_app.subfields[0] xdata = sig.vtk_X[:, 0] ydata = sig.field_arr[:, 0, 0] idata = argsort(xdata) p.subplot(224) p.plot(xdata[idata], ydata[idata], 'o-')
class SimBT4PT(IBVModel): '''Simulation: four point bending test. ''' input_change = Event @on_trait_change('+input,ccs_unit_cell.input_change') def _set_input_change(self): self.input_change = True implements(ISimModel) #----------------- # discretization: #----------------- # specify weather the elastomer is to be modeled or if the load is # introduced as line load # elstmr_flag = Bool(True) # discretization in x-direction (longitudinal): # outer_zone_shape_x = Int(6, input=True, ps_levels=(4, 12, 3)) # discretization in x-direction (longitudinal): # load_zone_shape_x = Int(2, input=True, ps_levels=(1, 4, 1)) # middle part discretization in x-direction (longitudinal): # mid_zone_shape_x = Int(3, input=True, ps_levels=(1, 4, 1)) # discretization in y-direction (width): # shape_y = Int(2, input=True, ps_levels=(1, 4, 2)) # discretization in z-direction: # shape_z = Int(2, input=True, ps_levels=(1, 3, 3)) #----------------- # geometry: #----------------- # # edge length of the bending specimen (beam) (entire length without symmetry) # length = Float(1.50, input=True) elstmr_length = Float(0.05, input=True) mid_zone_length = Float(0.50, input=True) elstmr_thickness = Float(0.005, input=True) width = Float(0.20, input=True) thickness = Float(0.06, input=True) #----------------- # derived geometric parameters #----------------- # # half the length of the elastomer (load introduction # with included symmetry) # sym_specmn_length = Property def _get_sym_specmn_length(self): return self.length / 2. sym_mid_zone_specmn_length = Property def _get_sym_mid_zone_specmn_length(self): return self.mid_zone_length / 2. # half the length of the elastomer (load introduction # with included symmetry # sym_elstmr_length = Property def _get_sym_elstmr_length(self): return self.elstmr_length / 2. # half the specimen width # sym_width = Property def _get_sym_width(self): return self.width / 2. #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # age of the plate at the time of testing # NOTE: that the same phi-function is used independent of age. This assumes a # an afine/proportional damage evolution for different ages. # age = Int(28, input=True) # time stepping params # tstep = Float(0.05, auto_set=False, enter_set=True, input=True) tmax = Float(1.0, auto_set=False, enter_set=True, input=True) tolerance = Float(0.001, auto_set=False, enter_set=True, input=True) # specify type of 'linalg.norm' # default value 'None' sets norm to 2-norm, # i.e "norm = sqrt(sum(x_i**2)) # # set 'ord=np.inf' to switch norm to # "norm = max(abs(x_i))" # ord = Enum(np.inf, None) n_mp = Int(30, input=True) # @todo: for mats_eval the information of the unit cell should be used # in order to use the same number of microplanes and model version etc... # specmn_mats = Property(Instance(MATS2D5MicroplaneDamage), depends_on='input_change') @cached_property def _get_specmn_mats(self): return MATS2D5MicroplaneDamage( E=self.E_c, # E=self.E_m, nu=self.nu, # corresponding to settings in "MatsCalib" n_mp=self.n_mp, symmetrization='sum-type', model_version='compliance', phi_fn=self.phi_fn) if elstmr_flag: elstmr_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_elstmr_mats(self): # specify a small elastomer stiffness (approximation) E_elast = self.E_c / 10. print 'effective elastomer E_modulus', E_elast return MATS3DElastic(E=E_elast, nu=0.4) #----------------- # fets: #----------------- # specify element shrink factor in plot of fe-model # vtk_r = Float(0.95) # use quadratic serendipity elements # specmn_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_specmn_fets(self): fets = FETS2D58H20U(mats_eval=self.specmn_mats) fets.vtk_r *= self.vtk_r return fets # use quadratic serendipity elements # elstmr_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_elstmr_fets(self): fets = FETS2D58H20U(mats_eval=self.elstmr_mats) fets.vtk_r *= self.vtk_r return fets fe_domain = Property(depends_on='+ps_levels, +input') @cached_property def _get_fe_domain(self): return FEDomain() #=========================================================================== # fe level #=========================================================================== outer_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_outer_zone_specmn_fe_level(self): return FERefinementGrid(name='outer zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) load_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_load_zone_specmn_fe_level(self): return FERefinementGrid(name='load zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) mid_zone_specmn_fe_level = Property(depends_on='+ps_levels, +input') def _get_mid_zone_specmn_fe_level(self): return FERefinementGrid(name='mid zone specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) elstmr_fe_level = Property(depends_on='+ps_levels, +input') def _get_elstmr_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.elstmr_fets, domain=self.fe_domain) #=========================================================================== # Grid definition #=========================================================================== mid_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_mid_zone_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(0., 0., 0.), coord_max=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, self.sym_width, self.thickness), shape=(self.mid_zone_shape_x, self.shape_y, self.shape_z), level=self.mid_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid load_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_load_zone_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, self.sym_width, self.thickness), shape=(self.load_zone_shape_x, self.shape_y, self.shape_z), level=self.load_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid # if elstmr_flag: elstmr_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_grid(self): fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length - self.sym_elstmr_length, 0., self.thickness), coord_max=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, self.sym_width, self.thickness + self.elstmr_thickness), level=self.elstmr_fe_level, shape=(self.load_zone_shape_x, self.shape_y, 1), fets_eval=self.elstmr_fets) return fe_grid outer_zone_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_outer_zone_specmn_fe_grid(self): # only a quarter of the plate is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_mid_zone_specmn_length + self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_specmn_length, self.sym_width, self.thickness), shape=(self.outer_zone_shape_x, self.shape_y, self.shape_z), level=self.outer_zone_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid #=========================================================================== # Boundary conditions #=========================================================================== w_max = Float(-0.030, input=True) # [m] w_max = Float(-0.030, input=True) # [m] bc_list = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_list(self): mid_zone_specimen = self.mid_zone_specmn_fe_grid load_zone_specimen = self.load_zone_specmn_fe_grid outer_zone_specimen = self.outer_zone_specmn_fe_grid if self.elstmr_flag: elastomer = self.elstmr_fe_grid #-------------------------------------------------------------- # boundary conditions for the symmetry #-------------------------------------------------------------- # symmetry in the xz-plane # (Note: the x-axis corresponds to the axis of symmetry along the longitudinal axis of the beam) # bc_outer_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=outer_zone_specimen[:, 0, :, :, 0, :]) bc_load_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=load_zone_specimen[:, 0, :, :, 0, :]) bc_mid_zone_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=mid_zone_specimen[:, 0, :, :, 0, :]) if self.elstmr_flag: bc_el_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=elastomer[:, 0, :, :, 0, :]) # symmetry in the yz-plane # bc_mid_zone_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=mid_zone_specimen[0, :, :, 0, :, :]) #-------------------------------------------------------------- # boundary conditions for the support #-------------------------------------------------------------- bc_support_0y0 = BCSlice(var='u', value=0., dims=[2], slice=outer_zone_specimen[-1, :, 0, -1, :, 0]) #-------------------------------------------------------------- # connect all grids #-------------------------------------------------------------- link_loadzn_outerzn = BCDofGroup(var='u', value=0., dims=[0, 1, 2], get_dof_method=load_zone_specimen.get_right_dofs, get_link_dof_method=outer_zone_specimen.get_left_dofs, link_coeffs=[1.]) link_midzn_loadzn = BCDofGroup(var='u', value=0., dims=[0, 1, 2], get_dof_method=mid_zone_specimen.get_right_dofs, get_link_dof_method=load_zone_specimen.get_left_dofs, link_coeffs=[1.]) if self.elstmr_flag: link_elstmr_loadzn_z = BCDofGroup(var='u', value=0., dims=[2], get_dof_method=elastomer.get_back_dofs, get_link_dof_method=load_zone_specimen.get_front_dofs, link_coeffs=[1.]) # hold elastomer in a single point in order to avoid kinematic movement yielding singular K_mtx # bc_elstmr_fix = BCSlice(var='u', value=0., dims=[0], slice=elastomer[0, 0, 0, 0, 0, 0]) #-------------------------------------------------------------- # loading #-------------------------------------------------------------- # w_max = center displacement: w_max = self.w_max if self.elstmr_flag: # apply displacement at all top nodes of the elastomer (surface load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=elastomer[:, :, -1, :, :, -1]) else: bc_w = BCSlice(var='u', value=w_max, dims=[2], # slice is only exactly in the center of the loading zone for 'load_zone_shape_x' = 2 # center line of the load zone slice=load_zone_specimen[0, :, -1, -1, :, -1]) # f_max = 0.010 / 4. / self.sym_width # bc_line_f = BCSlice(var = 'f', value = f_max, dims = [2], # # slice is only valid for 'load_zone_shape_x' = 2 # # center line of the load zone # slice = load_zone_specimen[0, :, -1, -1, :, -1]) bc_list = [bc_outer_zone_symplane_xz, bc_load_zone_symplane_xz, bc_mid_zone_symplane_xz, bc_mid_zone_symplane_yz, # link_midzn_loadzn, link_loadzn_outerzn, bc_support_0y0, # bc_w, ] if self.elstmr_flag: bc_list += [ bc_el_symplane_xz, link_elstmr_loadzn_z, bc_elstmr_fix ] return bc_list #---------------------- # tloop #---------------------- tloop = Property(depends_on='input_change') @cached_property def _get_tloop(self): #-------------------------------------------------------------- # ts #-------------------------------------------------------------- mid_zone_spec = self.mid_zone_specmn_fe_grid load_zone_spec = self.load_zone_specmn_fe_grid outer_zone_spec = self.outer_zone_specmn_fe_grid if self.elstmr_flag: # ELSTRMR TOP SURFACE # dofs at elastomer top surface (used to integrate the force) # elastomer = self.elstmr_fe_grid elstmr_top_dofs_z = elastomer[:, :, -1, :, :, -1].dofs[:, :, 2].flatten() load_dofs_z = np.unique(elstmr_top_dofs_z) print 'load_dofs_z', load_dofs_z else: # LINE LOAD TOP OF LOAD ZONE # dofs at center line of the specmn load zone (used to integrate the force) # note slice index in x-direction is only valid for load_zone_shape_x = 2 ! # load_zone_spec_topline_dofs_z = load_zone_spec[0, :, -1, -1, :, -1].dofs[:, :, 2].flatten() load_dofs_z = np.unique(load_zone_spec_topline_dofs_z) print 'load_dofs_z', load_dofs_z # SUPPRT LINE # dofs at support line of the specmn (used to integrate the force) # outer_zone_spec_supprtline_dofs_z = outer_zone_spec[-1, :, 0, -1, :, 0].dofs[:, :, 2].flatten() supprt_dofs_z = np.unique(outer_zone_spec_supprtline_dofs_z) print 'supprt_dofs_z', supprt_dofs_z # CENTER DOF (used for tracing of the displacement) # center_bottom_dof = mid_zone_spec[0, 0, 0, 0, 0, 0].dofs[0, 0, 2] print 'center_bottom_dof', center_bottom_dof # THIRDPOINT DOF (used for tracing of the displacement) # dofs at center middle of the laod zone at the bottom side # # NOTE: slice index in x-direction is only valid for load_zone_shape_x = 2 ! thirdpoint_bottom_dof = load_zone_spec[0, 0, 0, -1, 0, 0].dofs[0, 0, 2] print 'thirdpoint_bottom_dof', thirdpoint_bottom_dof # force-displacement-diagram (CENTER) # self.f_w_diagram_center = RTraceGraph(name='displacement_elasttop (center) - force', var_x='U_k' , idx_x=center_bottom_dof, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') # force-displacement-diagram_supprt (SUPPRT) # self.f_w_diagram_supprt = RTraceGraph(name='displacement_supprtline (center) - force', var_x='U_k' , idx_x=center_bottom_dof, var_y='F_int', idx_y_arr=supprt_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='4000. * y') # force-displacement-diagram (THIRDPOINT) # self.f_w_diagram_thirdpoint = RTraceGraph(name='displacement_elasttop (thirdpoint) - force', var_x='U_k' , idx_x=thirdpoint_bottom_dof, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') ts = TS( sdomain=self.fe_domain, bcond_list=self.bc_list, rtrace_list=[ self.f_w_diagram_center, self.f_w_diagram_thirdpoint, self.f_w_diagram_supprt, RTraceDomainListField(name='Displacement' , var='u', idx=0, warp=True), # RTraceDomainListField(name = 'Stress' , # var = 'sig_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Strain' , # var = 'eps_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Damage' , # var = 'omega_mtx', idx = 0, warp = True, # record_on = 'update'), RTraceDomainListField(name='max_omega_i', warp=True, var='max_omega_i', idx=0, record_on='update'), # RTraceDomainListField(name = 'IStress' , # position = 'int_pnts', # var = 'sig_app', idx = 0, # record_on = 'update'), # RTraceDomainListField(name = 'IStrain' , # position = 'int_pnts', # var = 'eps_app', idx = 0, # record_on = 'update'), ] ) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=50, tolerance=self.tolerance, RESETMAX=0, tline=TLine(min=0.0, step=self.tstep, max=self.tmax), ord=self.ord ) return tloop def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() self.f_w_diagram_center.refresh() F_max = max(self.f_w_diagram_center.trace.ydata) u_center_top_z = U[ self.center_top_dofs ][0, 0, 2] return array([ u_center_top_z, F_max ], dtype='float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='u_center_top_z', unit='m'), SimOut(name='F_max', unit='kN') ]
class SimBT3PT(IBVModel): '''Simulation: Bending Test Three Point ''' input_change = Event @on_trait_change('+input,ccs_unit_cell.input_change') def _set_input_change(self): self.input_change = True implements(ISimModel) #----------------- # discretization: #----------------- # # discretization in x-direction (longitudinal): shape_x = Int(10, input=True, ps_levels=(4, 12, 3)) # discretization in x-direction (longitudinal): mid_shape_x = Int(1, input=True, ps_levels=(1, 4, 1)) # discretization in y-direction (width): shape_y = Int(3, input=True, ps_levels=(1, 4, 2)) # discretization in z-direction: shape_z = Int(2, input=True, ps_levels=(1, 3, 3)) # support discretization in xy-direction: # NOTE: chose '2' for 2x2-grid or '4' for 4x4-grid # shape_supprt_x = Int(2, input=True, ps_levels=(2, 4, 2)) #----------------- # geometry: #----------------- # # edge length of the bending specimen (beam) (entire length without symmetry) length = Float(1.15, input=True) elstmr_length = Float(0.05, input=True) elstmr_thickness = Float(0.005, input=True, enter_set=True, auto_set=False) width = Float(0.20, input=True) thickness = Float(0.06, input=True, ps_levels=(0.054, 0.060, 0.066)) # thickness of the tappered support # thickness_supprt = Float(0.02, input=True) #----------------- # derived geometric parameters #----------------- # # half the length of the elastomer (load introduction # with included symmetry) # sym_specmn_length = Property def _get_sym_specmn_length(self): return self.length / 2. # half the length of the elastomer (load introduction # with included symmetry # sym_elstmr_length = Property def _get_sym_elstmr_length(self): return (self.elstmr_length) / 2. # half the specimen width # sym_width = Property def _get_sym_width(self): return self.width / 2. #----------------- # specify the refinement of the idealization #----------------- # specify weather elastomer is to be modeled for load introduction # elstmr_flag = True # specify weather steel support is to be modeled # supprt_flag = False #----------------- # 'geo_transform' #----------------- # geometry transformation for four sided tappered support with reduced stiffness towards the edges (if modeled) # geo_supprt = Property(Instance(GeoSUPPRT), depends_on='+ps_levels, +input') @cached_property def _get_geo_supprt(self): # element length within the range of the slab without the area of the # load introduction plate # elem_size = self.sym_specmn_length / self.shape_x width_supprt = self.shape_supprt_x * elem_size print 'width_supprt = ', width_supprt return GeoSUPPRT(thickness_supprt=self.thickness_supprt, width_supprt=width_supprt, xyoffset=0., zoffset=-self.thickness_supprt) #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # age of the specimen at the time of testing # determines the E-modulus and nu based on the time dependent function stored # in 'CCSUniteCell' if params are not explicitly set # age = Int(28, input=True) # composite E-modulus / Poisson's ratio # NOTE 1: value are retrieved from the database # same values are used for calibration (as taken from tensile test) and for slab test # NOTE 2: alternatively the same phi-function could be used independent of age. This would assume # an afine/proportional damage evolution for different ages, i.e. a different E would be # used in the simulation of the slab test and within the calibration procedure. # time stepping params # tstep = Float(0.05, auto_set=False, enter_set=True, input=True) tmax = Float(1.0, auto_set=False, enter_set=True, input=True) tolerance = Float(0.001, auto_set=False, enter_set=True, input=True) # specify type of 'linalg.norm' # default value 'None' sets norm to 2-norm, # i.e "norm = sqrt(sum(x_i**2)) # # set 'ord=np.inf' to switch norm to # "norm = max(abs(x_i))" # ord = Enum(None, np.inf) # number of microplanes # n_mp = Int(30., auto_set=False, enter_set=True, input=True) #---------------------------------------------------------------------------------- # mats_eval #---------------------------------------------------------------------------------- # @todo: for mats_eval the information of the unit cell should be used # in order to use the same number of microplanes and model version etc... # specmn_mats = Property(Instance(MATS2D5MicroplaneDamage), depends_on='input_change') @cached_property def _get_specmn_mats(self): return MATS2D5MicroplaneDamage( E=self.E_c, # E=self.E_m, # relevant for compressive behavior/used for calibration of phi_fn nu=self.nu, # corresponding to settings in "MatsCalib" n_mp=30, symmetrization='sum-type', model_version='compliance', phi_fn=self.phi_fn) E_elstmr = Float(3000., input=True) elstmr_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_elstmr_mats(self): E_elstmr = self.E_elstmr print 'effective elastomer E_modulus', E_elstmr return MATS3DElastic(E=E_elstmr, nu=0.4) # E-modulus and nu of steel support E_s = Float(210000., auto_set=False, enter_set=True, input=True) nu_s = Float(0.20, auto_set=False, enter_set=True, input=True) supprt_mats = Property(Instance(MATS3DElastic), depends_on='input_change') @cached_property def _get_supprt_mats(self): return MATS3DElastic(E=self.E_s, nu=self.nu_s) #----------------- # fets: #----------------- # specify element shrink factor in plot of fe-model # vtk_r = Float(0.95) # use quadratic serendipity elements # NOTE: 2D5 elements behave linear elastic in out of plane direction! # specmn_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_specmn_fets(self): fets = FETS2D58H20U(mats_eval=self.specmn_mats) fets.vtk_r *= self.vtk_r return fets # use quadratic serendipity elements # elstmr_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_elstmr_fets(self): fets = FETS2D58H20U(mats_eval=self.elstmr_mats) fets.vtk_r *= self.vtk_r return fets supprt_fets = Property(Instance(FETSEval), depends_on='input_change') @cached_property def _get_supprt_fets(self): # linear-elastic behavior quadratic serendipity elements fets = FETS3D8H20U(mats_eval=self.supprt_mats) fets.vtk_r *= self.vtk_r return fets #----------------- # fe_grid: #----------------- fe_domain = Property(depends_on='+ps_levels, +input') @cached_property def _get_fe_domain(self): return FEDomain() mid_specmn_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_mid_specmn_fe_level(self): return FERefinementGrid(name='middle specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) mid_specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_mid_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(0., 0., 0.), coord_max=(self.sym_elstmr_length, self.width / 2, self.thickness), shape=(self.mid_shape_x, self.shape_y, self.shape_z), level=self.mid_specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid specmn_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_specmn_fe_level(self): return FERefinementGrid(name='specimen patch', fets_eval=self.specmn_fets, domain=self.fe_domain) specmn_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_specmn_fe_grid(self): # only a quarter of the beam is simulated due to symmetry: fe_grid = FEGrid(coord_min=(self.sym_elstmr_length, 0., 0.), coord_max=(self.sym_specmn_length, self.sym_width, self.thickness), shape=(self.shape_x, self.shape_y, self.shape_z), level=self.specmn_fe_level, fets_eval=self.specmn_fets) return fe_grid # if elstmr_flag: elstmr_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.elstmr_fets, domain=self.fe_domain) elstmr_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_elstmr_fe_grid(self): x_max = self.sym_elstmr_length y_max = self.width / 2. z_max = self.thickness + self.elstmr_thickness fe_grid = FEGrid(coord_min=(0, 0, self.thickness), coord_max=(x_max, y_max, z_max), level=self.elstmr_fe_level, shape=(self.mid_shape_x, self.shape_y, 1), fets_eval=self.elstmr_fets) return fe_grid if supprt_flag: supprt_fe_level = Property(depends_on='+ps_levels, +input') @cached_property def _get_supprt_fe_level(self): return FERefinementGrid(name='elastomer patch', fets_eval=self.supprt_fets, domain=self.fe_domain) supprt_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_supprt_fe_grid(self): return FEGrid( coord_min=(0, 0, 0), coord_max=(1, 1, 1), level=self.supprt_fe_level, # use shape (2,2) in order to place support in the center of the steel support # corresponding to 4 elements of the slab mesh # shape=(self.shape_supprt_x, self.shape_supprt_x, 1), geo_transform=self.geo_supprt, fets_eval=self.supprt_fets) #=========================================================================== # Boundary conditions #=========================================================================== # w_max = center displacement: # w_max = Float(-0.010, input=True) # [m] bc_list = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_list(self): specmn = self.specmn_fe_grid mid_specmn = self.mid_specmn_fe_grid if self.elstmr_flag: elstmr = self.elstmr_fe_grid #-------------------------------------------------------------- # boundary conditions for the symmetry #-------------------------------------------------------------- # the x-axis corresponds to the axis of symmetry along the longitudinal axis of the beam: bc_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=specmn[:, 0, :, :, 0, :]) bc_mid_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=mid_specmn[:, 0, :, :, 0, :]) bc_mid_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=mid_specmn[0, :, :, 0, :, :]) if self.elstmr_flag: bc_el_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=elstmr[:, 0, :, :, 0, :]) bc_el_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=elstmr[0, :, :, 0, :, :]) #-------------------------------------------------------------- # boundary conditions for the support #-------------------------------------------------------------- bc_support_0y0 = BCSlice(var='u', value=0., dims=[2], slice=specmn[-1, :, 0, -1, :, 0]) #-------------------------------------------------------------- # link domains #-------------------------------------------------------------- link_msp_sp = BCDofGroup(var='u', value=0., dims=[0, 1, 2], get_dof_method=mid_specmn.get_right_dofs, get_link_dof_method=specmn.get_left_dofs, link_coeffs=[1.]) # link_msp_sp_xyz = BCSlice(var = 'u', value = 0., dims = [0, 1, 2], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [0, 1, 2], # link_coeffs = [1.]) # link_msp_sp_y = BCSlice(var = 'u', value = 0., dims = [1], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [1], # link_coeffs = [1.]) # # link_msp_sp_z = BCSlice(var = 'u', value = 0., dims = [2], # slice = specmn[0, :, :, 0, :, :], # link_slice = mid_specmn[-1 :, :, -1, :, :], # link_dims = [2], # link_coeffs = [1.]) # link_msp_sp = [ link_msp_sp_xyz ] if self.elstmr_flag: link_el_sp = BCDofGroup( var='u', value=0., dims=[2], get_dof_method=elstmr.get_back_dofs, get_link_dof_method=mid_specmn.get_front_dofs, link_coeffs=[1.]) #-------------------------------------------------------------- # loading #-------------------------------------------------------------- w_max = self.w_max # f_max = -0.010 / 0.10 # [MN/m] if self.elstmr_flag: # apply displacement at all top node (surface load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=elstmr[:, :, -1, :, :, -1]) # apply a single force at the center of the beam (system origin at top of the elastomer # and us elastomer-domain as load distribution plate with a high stiffness (e.g. steel) # F_max = -0.010 #[MN] # bc_F = BCSlice(var = 'f', value = F_max, dims = [2], # slice = elstmr[0, 0, -1, 0, 0, -1]) else: # center top nodes (line load) # bc_w = BCSlice(var='u', value=w_max, dims=[2], slice=mid_specmn[0, :, -1, 0, :, -1]) # NOTE: the entire symmetry axis (yz)-plane is moved downwards # in order to avoid large indentations at the top nodes # # bc_center_w = BCSlice( var = 'w', value = w_max, dims = [2], slice = mid_specmn[0, :, :, 0, :, :] ) bc_list = [ bc_symplane_xz, bc_mid_symplane_xz, bc_mid_symplane_yz, bc_support_0y0, link_msp_sp, bc_w ] if self.elstmr_flag: bc_list_elstmr = [link_el_sp, bc_el_symplane_xz, bc_el_symplane_yz] bc_list += bc_list_elstmr return bc_list tloop = Property(depends_on='input_change') @cached_property def _get_tloop(self): #-------------------------------------------------------------- # ts #-------------------------------------------------------------- specmn = self.specmn_fe_grid mid_specmn = self.mid_specmn_fe_grid if self.supprt_flag: supprt = self.supprt_fe_grid supprt_dofs_z = np.unique(supprt[self.shape_supprt_x / 2, self.shape_y / 2, 0, 0, 0, 0].dofs[:, :, 2].flatten()) else: supprt_dofs_z = np.unique(specmn[-1, :, 0, -1, :, 0].dofs[:, :, 2].flatten()) print 'supprt_dofs_z (unique)', supprt_dofs_z if self.elstmr_flag: elstmr = self.elstmr_fe_grid load_dofs_z = np.unique(elstmr[:, :, -1, :, :, -1].dofs[:, :, 2].flatten()) else: # center_top_line_dofs # load_dofs_z = np.unique(mid_specmn[0, :, -1, 0, :, -1].dofs[:, :, 2].flatten()) print 'load_dofs_z used for integration of force: ', load_dofs_z # center top z-dof # center_top_dof_z = mid_specmn[0, 0, 0, 0, 0, 0].dofs[0, 0, 2] print 'center_top_dof used for displacement tracing: ', center_top_dof_z # force-displacement-diagram (LOAD) # (surface load on the elstmr or line load at specimen center) # self.f_w_diagram_center = RTraceGraph( name='displacement (center) - reaction 2', var_x='U_k', idx_x=center_top_dof_z, var_y='F_int', idx_y_arr=load_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='-4000. * y') # force-displacement-diagram (SUPPORT) # (dofs at support line of the specmn used to integrate the force) # self.f_w_diagram_supprt = RTraceGraph( name='displacement (center) - reaction 2', var_x='U_k', idx_x=center_top_dof_z, var_y='F_int', idx_y_arr=supprt_dofs_z, record_on='update', transform_x='-x * 1000', # %g * x' % ( fabs( w_max ),), # due to symmetry the total force sums up from four parts of the beam (2 symmetry axis): # transform_y='4000. * y') ts = TS( sdomain=self.fe_domain, bcond_list=self.bc_list, rtrace_list=[ self.f_w_diagram_center, self.f_w_diagram_supprt, RTraceDomainListField(name='Displacement', var='u', idx=0, warp=True), # RTraceDomainListField(name = 'Stress' , # var = 'sig_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Strain' , # var = 'eps_app', idx = 0, warp = True, # record_on = 'update'), # RTraceDomainListField(name = 'Damage' , # var = 'omega_mtx', idx = 0, warp = True, # record_on = 'update'), RTraceDomainListField(name='max_omega_i', warp=True, var='max_omega_i', idx=0, record_on='update'), # RTraceDomainListField(name = 'IStress' , # position = 'int_pnts', # var = 'sig_app', idx = 0, # record_on = 'update'), # RTraceDomainListField(name = 'IStrain' , # position = 'int_pnts', # var = 'eps_app', idx = 0, # record_on = 'update') ]) # Add the time-loop control tloop = TLoop(tstepper=ts, KMAX=50, tolerance=self.tolerance, RESETMAX=0, tline=TLine(min=0.0, step=self.tstep, max=self.tmax)) return tloop #-------------------------------------------------------------- # prepare pstudy #-------------------------------------------------------------- def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() self.f_w_diagram_center.refresh() F_max = max(self.f_w_diagram_center.trace.ydata) u_center_top_z = U[self.center_top_dofs][0, 0, 2] return array([u_center_top_z, F_max], dtype='float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='u_center_top_z', unit='m'), SimOut(name='F_max', unit='kN') ]