class CBShortFiber(RF): ''' Micromechanical response of a short fiber bridging a crack ''' implements(IRF) xi = Float(distr=['weibull_min', 'uniform']) E_f = Float(distr=['uniform', 'norm']) r = Float(distr=['uniform', 'norm']) le = Float(distr=['uniform']) tau = Float(distr=['norm', 'uniform', 'weibull_min']) snub = Float(distr=['uniform', 'norm']) phi = Float(distr=['sin2x', 'uniform']) w = Float C_code = '' def __call__(self, w, tau, r, E_f, le, phi, snub, xi, epsm_softening): T = 2. * tau / r # debonding stage ef0_deb = np.sqrt(T * w / E_f) # crack opening at which debonding is finished w0 = le**2 * T / E_f # pulling out stage - the fiber is pulled out from the # side with the shorter embedded length only ef0_pull = (le + w0 - w) * T / E_f ef0 = (ef0_deb * (w < w0) + ef0_pull * (w > w0)) * np.exp(phi * snub) + epsm_softening # include breaking strain ef0 = ef0 * (ef0 < xi) * (ef0 > 0.0) return ef0
class ConstantFrictionAndFreeLength(RF): ''' ''' implements(IRF) title = Str('pull-out with constant friction and free length ') tau = Float(8, auto_set=False, enter_set=True, distr=['uniform']) # free length l = Float(1, auto_set=False, enter_set=True, distr=['uniform', 'norm']) E = Float(70e9, auto_set=False, enter_set=True, distr=['uniform']) A = Float(5.30929158457e-10, auto_set=False, enter_set=True, distr=['uniform', 'weibull_min']) # waviness in strains slack = Float(0.1, auto_set=False, enter_set=True, distr=['uniform']) u = Float(auto_set=False, enter_set=True, ctrl_range=(0.0, 1.0, 10)) def __call__(self, u, tau, l, E, A, slack): return -l * (1 + slack) * tau * Heaviside(u - l * (slack)) + \ + sqrt((l * (1 + slack) * tau) ** 2 \ + 2 * E * A * (u - (l * slack)) * Heaviside(u - l * (slack)))
class fiber_tt_2p(RF): ur''' Response Function with two-parameters. ====================================== The function describes a linear dependency with a coefficient :math:`\lambda` up to the threshold :math:`\xi` and zero value beyond the threshold: .. math:: q( \varepsilon; \theta, \lambda ) = \lambda \varepsilon H( \xi - \varepsilon ) where the variables :math:`\lambda=` stiffness parameter and :math:`\xi=` breaking strain are considered random and normally distributed. The function :math:`H(\eta)` represents the Heaviside function with values 0 for :math:`\eta < 0` and 1 for :math:`\eta > 0`. ''' implements(IRF) title = Str('brittle filament') def __call__(self, e, la, xi): ''' Response function of a single fiber ''' return la * e * Heaviside(xi - e) cython_code = ''' # Computation of the q( ... ) function if eps < 0 or eps > xi: q = 0.0 else: q = la * eps ''' c_code = '''
class POClampedFiber(RF): ''' Pullout of fiber from a stiff matrix; stress criterion for debonding, fixed fiber end ''' implements(IRF) title = Str('pullout - clamped fiber with constant friction') image = Image('pics/cb_short_fiber.jpg', size=2) xi = Float(0.0179, auto_set=False, enter_set=True, input=True, distr=['weibull_min', 'uniform']) tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) l = Float(0.0, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='free length') D_f = Float(26e-3, auto_set=False, input=True, enter_set=True, distr=['uniform', 'weibull_min']) E_f = Float(72.0e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) theta = Float(0.01, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='slack') phi = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='bond quality') L = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length') u = Float(auto_set=False, enter_set=True, ctrl_range=(0.0, 0.02, 100)) x_label = Str('displacement [mm]') y_label = Str('force [N]') C_code = Str('') def __call__(self, u, tau, l, D_f, E_f, theta, xi, phi, L): A = pi * D_f ** 2 / 4. l = l * (1 + theta) u = u - theta * l T = tau * phi * D_f * pi q = (-l * T + sqrt(l ** 2 * T ** 2 + 2 * u * H(u) * E_f * A * T)) # displacement at which the debonding is finished u0 = L * T * (L + 2 * l) / 2 / E_f / A q = q * H(T * L - q) + (T * L + (u - u0) / (l + L) * A * E_f) * H(q - T * L) # ---------------------------------- q = q * H(A * E_f * xi - q) return q
class ConstantFrictionFreeLengthFiniteFiber(RF): ''' ''' implements(IRF) title = Str('pull-out with constant friction and free length ') tau = Float(0.5e6, auto_set=False, enter_set=True, distr=['uniform', 'weibull_min', 'norm']) # free length l = Float(0.01, auto_set=False, enter_set=True, distr=['uniform', 'norm']) E = Float(70e9, auto_set=False, enter_set=True, distr=['uniform']) A = Float(5.30929158457e-10, auto_set=False, enter_set=True, distr=['uniform', 'weibull_min']) # waviness in strains slack = Float(0.0, auto_set=False, enter_set=True, distr=['uniform']) # embedded length L = Float(0.03, auto_set=False, enter_set=True, distr=['uniform']) # breking stress sigma_fu = Float(1200.e6, auto_set=False, enter_set=True, distr=['uniform']) u = Float(auto_set=False, enter_set=True, ctrl_range=(0.0, 1.0, 10)) # def __call__(self, u, tau, l, E, A, slack, L, sigma_fu): ''' method for evaluating the u-F diagram for pullout with constant friction at the interface and free fiber length sticking out of the matrix ''' # defining tau as length dependent tau = tau * sqrt(4 * A * pi) # constitutive law for a pullout without free length q = ( -l * ( 1 + slack ) * tau * Heaviside( u / 2 - l * ( slack ) ) + \ + sqrt( ( l * ( 1 + slack ) * tau ) ** 2 * Heaviside( u / 2 - l * ( slack ) ) \ + 2 * E * A * ( u / 2 - ( l * slack ) ) * Heaviside( u / 2 - l * ( slack ) ) ) ) # deformation of the free length added continuous = q * Heaviside( L - l - q / tau )\ + ( L - l ) * tau * Heaviside( l + q / tau - L ) # a check whether the fiber has reached the breaking stress return continuous * Heaviside(sigma_fu - continuous / A)
class fiber_tt_2p(RF): '''Linear elastic, brittle filament. ''' implements(IRF) title = Str('brittle filament') def __call__(self, e, la, xi): ''' Response function of a single fiber ''' return la * e * Heaviside(xi - e) C_code = '''
class fiber_tt_5p_np(RF): ''' Response function of a single fiber ''' implements(IRF) title = Str('brittle filament') def __call__(self, eps, lambd, xi, E_mod, theta, A): ''' Implements the response function with arrays as variables. first extract the variable discretizations from the orthogonal grid. ''' eps_ = (eps - theta * (1 + lambd)) / ((1 + theta) * (1 + lambd)) eps_ *= Heaviside(eps_) eps_grid = eps_ * Heaviside(xi - eps_) q_grid = E_mod * A * eps_grid return q_grid
class fiber_tt_5p_ne(RF): ''' Response function of a single fiber ''' implements(IRF) title = Str('brittle filament') def __call__(self, eps, lambd, xi, E_mod, theta, A): ''' Implements the response function with arrays as variables. first extract the variable discretizations from the orthogonal grid. ''' eps_ = ne.evaluate( "((eps - theta * (1 + lambd)) / ((1 + theta) * (1 + lambd)))") tmp = Heaviside_ne(eps_) eps_ = ne.evaluate('eps_*tmp') tmp = Heaviside_ne(ne.evaluate("(xi - eps_)")) eps_grid = ne.evaluate("eps_ * tmp") q_grid = ne.evaluate("E_mod * A * eps_grid") return q_grid
class fiber_tt_2p(RF): '''Linear elastic, brittle filament. ''' implements(IRF) title = Str('brittle filament') def __call__(self, e, la, xi): ''' Response function of a single fiber ''' return la * e * Heaviside(xi - e) cython_code = ''' # Computation of the q( ... ) function if eps < 0 or eps > xi: q = 0.0 else: q = la * eps ''' weave_code = '''
class CodeGenLangDictC(HasStrictTraits): implements(ICodeGenLangDict) LD_BEGIN_EPS_LOOP_ACTIVE = 'for( int i_eps = 0; i_eps < %(i)i; i_eps++){\n' LD_END_EPS_LOOP_ACTIVE = '};\n' LD_ACCESS_EPS_IDX = '\tdouble eps = e_arr( i_eps );\n' LD_ACCESS_EPS_PTR = '\tdouble eps = *( e_arr + i_eps );\n' LD_ASSIGN_EPS = 'double eps = e;\n' LD_LINE_MACRO = '#line 100\n' LD_INIT_MU_Q = 'double mu_q=0;\n' LD_INIT_Q = 'double q=0;\n' LD_EVAL_MU_Q = '\tmu_q += q * dG;\n' LD_ADD_MU_Q = '\tmu_q += q;\n' LD_ASSIGN_MU_Q_IDX = 'mu_q_arr(i_eps) = mu_q;\n' LD_ASSIGN_MU_Q_PTR = '*(mu_q_arr + i_eps) = mu_q;\n' LD_RETURN_MU_Q = 'return_val = mu_q;' LD_INIT_THETA = 'double %s = %g;\n' LD_BEGIN_THETA_DEEP_LOOP = '%(t)sfor( int i_%(s)s = 0; i_%(s)s < %(i)i; i_%(s)s++){\n' LD_END_THETA_DEEP_LOOP = '};\n' LD_ACCESS_THETA_IDX = '\tdouble %s = %s_flat( i_%s );\n' LD_ACCESS_THETA_PTR = '\tdouble %s = *( %s_flat + i_%s );\n' LD_BEGIN_THETA_FLAT_LOOP = 'for( int i = 0; i < %i; i++){\n' LD_ACCESS_THETA_FLAT_IDX = '\tdouble %s = %s_flat( i);\n' LD_ACCESS_THETA_FLAT_PTR = '\tdouble %s = *( %s_flat + i );\n' LD_END_THETA_FLAT_LOOP = '};\n' LD_DECLARE_DG_IDX = '\tdouble dG = dG_grid(' LD_DECLARE_DG_PTR = '\tdouble dG = ' LD_ACCESS_DG_IDX = 'i_%s' LD_ACCESS_DG_PTR = '*( %s_dG + i_%s)' LD_ASSIGN_DG = 'double dG = %.100g;\n' LD_END_BRACE = ');\n'
class CodeGenLangDictCython(HasStrictTraits): implements(ICodeGenLangDict) LD_BEGIN_EPS_LOOP_ACTIVE = '\tcdef np.ndarray mu_q_arr = np.zeros_like( e_arr )\n\tfor i_eps from 0 <= i_eps < %(i)i:\n\t\teps = e_arr[i_eps]\n' LD_END_EPS_LOOP_ACTIVE = '\n' LD_ACCESS_EPS_IDX = '' LD_ACCESS_EPS_PTR = '' LD_ASSIGN_EPS = '' LD_LINE_MACRO = '' LD_INIT_MU_Q = '' LD_INIT_Q = '\tmu_q = 0\n' LD_EVAL_MU_Q = 'mu_q += q * dG\n' LD_ADD_MU_Q = 'mu_q += q\n' LD_ASSIGN_MU_Q_IDX = '\t\tmu_q_arr[i_eps] = mu_q\n\treturn mu_q_arr\n' LD_ASSIGN_MU_Q_PTR = '\t\tmu_q_arr[i_eps] = mu_q\n\treturn mu_q_arr\n' LD_RETURN_MU_Q = '\treturn mu_q' LD_INIT_THETA = '\t\t%s = %g\n' LD_BEGIN_THETA_DEEP_LOOP = '%(t)s\tfor i_%(s)s from 0 <= i_%(s)s <%(i)i:\n' LD_END_THETA_DEEP_LOOP = '\n' LD_ACCESS_THETA_IDX = '\t\t%s = %s_flat[ i_%s ]\n' LD_ACCESS_THETA_PTR = '\t\t%s = %s_flat[ i_%s ]\n' LD_BEGIN_THETA_FLAT_LOOP = '\tfor i from 0 <= i < %i:\n' LD_ACCESS_THETA_FLAT_IDX = '\t\t%s = %s_flat[i]\n' LD_ACCESS_THETA_FLAT_PTR = '\t\t%s = %s_flat[i]\n' LD_END_THETA_FLAT_LOOP = '\n' LD_DECLARE_DG_IDX = '%(t)s\tdG = dG_grid[' LD_DECLARE_DG_PTR = '%(t)s\tdG = ' LD_ACCESS_DG_IDX = 'i_%s' LD_ACCESS_DG_PTR = '%s_dG[i_%s]' LD_ASSIGN_DG = '\tdG = %.100g\n' LD_END_BRACE = ']\n'
class CBClampedFiber(RF): ''' Crack bridged by a short fiber with constant frictional interface to the matrix; clamped fiber end ''' implements(IRF) title = Str('crack bridge - clamped fiber with constant friction') image = Image('pics/cb_short_fiber.jpg') xi = Float(0.0179, auto_set=False, enter_set=True, input=True, distr=['weibull_min', 'uniform']) tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) l = Float(0.0, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='free length') D_f = Float(26e-3, auto_set=False, input=True, enter_set=True, distr=['uniform', 'weibull_min']) E_f = Float(72.0e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) theta = Float(0.01, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='slack') phi = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='bond quality') Ll = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - left') Lr = Float(.5, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - right') w = Float(auto_set=False, enter_set=True, input=True, desc='crack width', ctrl_range=(0, 0.01, 100)) x_label = Str('crack opening [mm]') y_label = Str('force [N]') C_code = Str('') # TODO: case where Lmin is zero - gives a double sided pullout # should be one sided though def __call__(self, w, tau, l, D_f, E_f, theta, xi, phi, Ll, Lr): A = pi * D_f**2 / 4. Lmin = minimum(Ll, Lr) Lmax = maximum(Ll, Lr) Lmin = maximum(Lmin - l / 2., 0) Lmax = maximum(Lmax - l / 2., 0) l = minimum(Lr + Ll, l) l = l * (1 + theta) w = w - theta * l T = tau * phi * D_f * pi # double sided debonding l0 = l / 2. q0 = (-l0 * T + sqrt((l0 * T)**2 + w * Heaviside(w) * E_f * A * T)) # displacement at which the debonding to the closer clamp is finished # the closer distance is min(L1,L2) w0 = Lmin * T * (Lmin + 2 * l0) / E_f / A # debonding from one side; the other side is clamped # equal to L1*T + one sided pullout with embedded length Lmax - Lmin and free length 2*L1 + l # force at w0 Q0 = Lmin * T l1 = 2 * Lmin + l q1 = (-(l1) * T + sqrt((l1 * T)**2 + 2 * (w - w0) * Heaviside(w - w0) * E_f * A * T)) + Q0 # displacement at debonding finished at both sides # equals a force of T*(larger clamp distance) # displacement, at which both sides are debonded w1 = w0 + (Lmax - Lmin) * T * ((Lmax - Lmin) + 2 * (l + 2 * Lmin)) / 2 / E_f / A # linear elastic response with stiffness EA/(clamp distance) q2 = E_f * A * (w - w1) / (Lmin + Lmax + l) + (Lmax) * T q0 = q0 * Heaviside(w0 - w) q1 = q1 * Heaviside(w - w0) * Heaviside(w1 - w) q2 = q2 * Heaviside(w - w1) q = q0 + q1 + q2 # include breaking strain q = q * Heaviside(A * E_f * xi - q) #return q0, q1, q2 * Heaviside( A * E_f * xi - q2 ), w0 + theta * l, w1 + theta * l return q
class ExpST(ExType): '''Experiment: Slab Test ''' # label = Str('slab test') implements(IExType) #-------------------------------------------------------------------- # register a change of the traits with metadata 'input' #-------------------------------------------------------------------- input_change = Event @on_trait_change('+input, ccs.input_change, +ironing_param') def _set_input_change(self): self.input_change = True #-------------------------------------------------------------------------------- # specify inputs: #-------------------------------------------------------------------------------- edge_length = Float(1.25, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) thickness = Float(0.06, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) # age of the concrete at the time of testing age = Int(28, unit='d', input=True, table_field=True, auto_set=False, enter_set=True) loading_rate = Float(2.0, unit='mm/min', input=True, table_field=True, auto_set=False, enter_set=True) #-------------------------------------------------------------------------- # composite cross section #-------------------------------------------------------------------------- ccs = Instance(CompositeCrossSection) def _ccs_default(self): '''default settings correspond to setup '7u_MAG-07-03_PZ-0708-1' ''' fabric_layout_key = '2D-05-11' # fabric_layout_key = 'MAG-07-03' # fabric_layout_key = '2D-02-06a' # concrete_mixture_key = 'PZ-0708-1' # concrete_mixture_key = 'FIL-10-09' # concrete_mixture_key = 'barrelshell' concrete_mixture_key = 'PZ-0708-1' # orientation_fn_key = 'all0' orientation_fn_key = '90_0' n_layers = 2 s_tex_z = 0.015 / (n_layers + 1) ccs = CompositeCrossSection ( fabric_layup_list=[ plain_concrete(0.035), # plain_concrete(s_tex_z * 0.5), FabricLayUp ( n_layers=n_layers, orientation_fn_key=orientation_fn_key, s_tex_z=s_tex_z, fabric_layout_key=fabric_layout_key ), # plain_concrete(s_tex_z * 0.5) plain_concrete(0.5) ], concrete_mixture_key=concrete_mixture_key ) return ccs #-------------------------------------------------------------------------- # Get properties of the composite #-------------------------------------------------------------------------- # E-modulus of the composite at the time of testing E_c = Property(Float, unit='MPa', depends_on='input_change', table_field=True) def _get_E_c(self): return self.ccs.get_E_c_time(self.age) # E-modulus of the composite after 28 days E_c28 = DelegatesTo('ccs', listenable=False) # reinforcement ration of the composite rho_c = DelegatesTo('ccs', listenable=False) #-------------------------------------------------------------------------------- # define processing #-------------------------------------------------------------------------------- # put this into the ironing procedure processor # jump_rtol = Float(0.1, auto_set=False, enter_set=True, ironing_param=True) data_array_ironed = Property(Array(float), depends_on='data_array, +ironing_param, +axis_selection') @cached_property def _get_data_array_ironed(self): '''remove the jumps in the displacement curves due to resetting the displacement gauges. ''' print '*** curve ironing activated ***' # each column from the data array corresponds to a measured parameter # e.g. displacement at a given point as function of time u = f(t)) # data_array_ironed = copy(self.data_array) for idx in range(self.data_array.shape[1]): # use ironing method only for columns of the displacement gauges. # if self.names_and_units[0][ idx ] != 'Kraft' and \ self.names_and_units[0][ idx ] != 'Bezugskanal' and \ self.names_and_units[0][ idx ] != 'Weg': # 1d-array corresponding to column in data_array data_arr = copy(data_array_ironed[:, idx]) # get the difference between each point and its successor jump_arr = data_arr[1:] - data_arr[0:-1] # get the range of the measured data data_arr_range = max(data_arr) - min(data_arr) # determine the relevant criteria for a jump # based on the data range and the specified tolerances: jump_crit = self.jump_rtol * data_arr_range # get the indexes in 'data_column' after which a # jump exceeds the defined tolerance criteria jump_idx = where(fabs(jump_arr) > jump_crit)[0] print 'number of jumps removed in data_arr_ironed for', self.names_and_units[0][ idx ], ': ', jump_idx.shape[0] # glue the curve at each jump together for jidx in jump_idx: # get the offsets at each jump of the curve shift = data_arr[jidx + 1] - data_arr[jidx] # shift all succeeding values by the calculated offset data_arr[jidx + 1:] -= shift data_array_ironed[:, idx] = data_arr[:] return data_array_ironed @on_trait_change('+ironing_param') def process_source_data(self): '''read in the measured data from file and assign attributes after array processing. NOTE: if center displacement gauge ('WA_M') is missing the measured displacement of the cylinder ('Weg') is used instead. A minor mistake is made depending on how much time passes before the cylinder has contact with the slab. ''' print '*** process source data ***' self._read_data_array() # curve ironing: # self.processed_data_array = self.data_array_ironed # set attributes: # self._set_array_attribs() if 'WA_M' not in self.factor_list: print '*** NOTE: Displacement gauge at center ("WA_M") missing. Cylinder displacement ("Weg") is used instead! ***' self.WA_M = self.Weg #-------------------------------------------------------------------------------- # plot templates #-------------------------------------------------------------------------------- plot_templates = {'force / deflection (all)' : '_plot_force_deflection', # 'force / average deflection (c; ce; e) interpolated' : '_plot_force_deflection_avg_interpolated', # 'force / deflection (center)' : '_plot_force_center_deflection', # 'smoothed force / deflection (center)' : '_plot_force_center_deflection_smoothed', # 'force / deflection (edges)' : '_plot_force_edge_deflection', # 'force / deflection (center-edges)' : '_plot_force_center_edge_deflection', # 'force / average deflection (edges)' : '_plot_force_edge_deflection_avg', # 'force / average deflection (center-edges)' : '_plot_force_center_edge_deflection_avg', # 'force / average deflection (c; ce; e)' : '_plot_force_deflection_avg', # 'force / deflection (center) interpolated' : '_plot_force_center_deflection_interpolated', # 'force / deflection (corner)' : '_plot_force_corner_deflection', # 'edge_deflection_avg (front/back) / edge_deflection_avg (left/right)' : '_plot_edge_deflection_edge_deflection_avg', } default_plot_template = 'force / deflection (center)' n_fit_window_fraction = Float(0.1) # get only the ascending branch of the response curve # max_force_idx = Property(Int) def _get_max_force_idx(self): '''get the index of the maximum force''' return argmax(-self.Kraft) f_asc = Property(Array) def _get_f_asc(self): '''get only the ascending branch of the response curve''' return -self.Kraft[:self.max_force_idx + 1] w_asc = Property(Array) def _get_w_asc(self): '''get only the ascending branch of the response curve''' return -self.WA_M[:self.max_force_idx + 1] n_points = Property(Int) def _get_n_points(self): return int(self.n_fit_window_fraction * len(self.w_asc)) f_smooth = Property() def _get_f_smooth(self): return smooth(self.f_asc, self.n_points, 'flat') w_smooth = Property() def _get_w_smooth(self): return smooth(self.w_asc, self.n_points, 'flat') def _plot_force_deflection(self, axes, offset_w=0., color='blue', linewidth=1.5, linestyle='-'): '''plot the F-w-diagramm for all displacement gauges including maschine displacement ''' xkey = 'deflection [mm]' ykey = 'force [kN]' max_force_idx = self.max_force_idx # max_force_idx = -2 f_asc = -self.Kraft[:max_force_idx + 1] print 'self.factor_list', self.factor_list header_string = '' for i in self.factor_list[2:]: header_string = header_string + i + '; ' T_arr = -getattr(self, i) T_asc = T_arr[:max_force_idx + 1] axes.plot(T_asc, f_asc, label=i) axes.legend() img_dir = os.path.join(simdb.exdata_dir, 'img_dir') # check if directory exist otherwise create # if os.path.isdir(img_dir) == False: os.makedirs(img_dir) test_series_dir = os.path.join(img_dir, 'slab_test_astark') # check if directory exist otherwise create # if os.path.isdir(test_series_dir) == False: os.makedirs(test_series_dir) out_file = os.path.join(test_series_dir, self.key) np.savetxt(out_file, self.processed_data_array, delimiter=';') # workaround for old numpy.savetxt version: f = open(out_file, 'r') temp = f.read() f.close() f = open(out_file, 'w') f.write(header_string) f.write(temp) f.close() def _plot_force_center_deflection(self, axes, offset_w=0., color='blue', linewidth=1.5, linestyle='-'): '''plot the F-w-diagramm for the center (c) deflection ''' xkey = 'deflection [mm]' ykey = 'force [kN]' xdata = -self.WA_M ydata = -self.Kraft xdata += offset_w # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) axes.plot(xdata, ydata, color=color, linewidth=linewidth, linestyle=linestyle) def _plot_force_corner_deflection(self, axes): '''plot the F-w-diagramm for the corner deflection (at the center of one of the supports) ''' xkey = 'deflection [mm]' ykey = 'force [kN]' xdata = -self.WA_Eck ydata = -self.Kraft # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) axes.plot(xdata, ydata # color = c, linewidth = w, linestyle = s ) def _plot_force_center_deflection_smoothed(self, axes): '''plot the F-w-diagramm for the center (c) deflection (smoothed curves) ''' axes.plot(self.w_smooth, self.f_smooth, color='blue', linewidth=1) # secant_stiffness_w10 = (f_smooth[10] - f_smooth[0]) / (w_smooth[10] - w_smooth[0]) # w0_lin = array([0.0, w_smooth[10] ], dtype = 'float_') # f0_lin = array([0.0, w_smooth[10] * secant_stiffness_w10 ], dtype = 'float_') # axes.plot( w0_lin, f0_lin, color = 'black' ) def _plot_force_center_deflection_interpolated(self, axes, linestyle='-', linewidth=1.5, plot_elastic_stiffness=True): '''plot the F-w-diagramm for the center (c) deflection (interpolated initial stiffness) ''' # get the index of the maximum stress max_force_idx = argmax(-self.Kraft) # get only the ascending branch of the response curve f_asc = -self.Kraft[:max_force_idx + 1] w_m = -self.WA_M[:max_force_idx + 1] # w_m -= 0.17 # axes.plot(w_m, f_asc, color = 'blue', linewidth = 1) # move the starting point of the center deflection curve to the point where the force starts # (remove offset in measured displacement where there is still no force measured) # idx_0 = np.where(f_asc > 0.0)[0][0] f_asc_cut = np.hstack([f_asc[ idx_0: ]]) w_m_cut = np.hstack([w_m[ idx_0: ]]) - w_m[ idx_0 ] # print 'f_asc_cut.shape', f_asc_cut.shape # fw_arr = np.hstack([f_asc_cut[:, None], w_m_cut[:, None]]) # print 'fw_arr.shape', fw_arr.shape # np.savetxt('ST-6c-2cm-TU_bs2_f-w_asc.csv', fw_arr, delimiter=';') axes.plot(w_m_cut, f_asc_cut, color='k', linewidth=linewidth, linestyle=linestyle) # composite E-modulus # E_c = self.E_c print 'E_c', E_c if self.thickness == 0.02 and self.edge_length == 0.80 and plot_elastic_stiffness == True: K_linear = E_c / 24900. * 1.056 # [MN/m]=[kN/mm] bending stiffness with respect to center force max_f = f_asc_cut[-1] w_linear = np.array([0., max_f / K_linear]) f_linear = np.array([0., max_f]) axes.plot(w_linear, f_linear, linewidth=linewidth, linestyle='--', color='k') if self.thickness == 0.03 and self.edge_length == 1.25 and plot_elastic_stiffness == True: K_linear = E_c / 24900. * 1.267 # [MN/m]=[kN/mm] bending stiffness with respect to center force max_f = f_asc_cut[-1] w_linear = np.array([0., max_f / K_linear]) f_linear = np.array([0., max_f]) axes.plot(w_linear, f_linear, linewidth=linewidth, linestyle='--', color='k') if self.thickness == 0.06 and self.edge_length == 1.25 and plot_elastic_stiffness == True: K_linear = E_c / 24900. * 10.14 # [MN/m]=[kN/mm] bending stiffness with respect to center force max_f = f_asc_cut[-1] w_linear = np.array([0., max_f / K_linear]) f_linear = np.array([0., max_f]) axes.plot(w_linear, f_linear, linewidth=linewidth, linestyle='--', color='k') def _plot_force_edge_deflection(self, axes): '''plot the F-w-diagramm for the edge (e) deflections ''' max_force_idx = self.max_force_idx f_asc = self.f_asc w_v_asc = -self.WA_V[:max_force_idx + 1] w_h_asc = -self.WA_H[:max_force_idx + 1] w_l_asc = -self.WA_L[:max_force_idx + 1] w_r_asc = -self.WA_R[:max_force_idx + 1] axes.plot(w_v_asc, f_asc, color='blue', linewidth=1) axes.plot(w_h_asc, f_asc, color='blue', linewidth=1) axes.plot(w_l_asc, f_asc, color='green', linewidth=1) axes.plot(w_r_asc, f_asc, color='green', linewidth=1) def _plot_force_edge_deflection_avg(self, axes): '''plot the average F-w-diagramm for the edge (e) deflections ''' max_force_idx = self.max_force_idx f_asc = self.f_asc w_v_asc = -self.WA_V[:max_force_idx + 1] w_h_asc = -self.WA_H[:max_force_idx + 1] w_l_asc = -self.WA_L[:max_force_idx + 1] w_r_asc = -self.WA_R[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_vh_asc = (w_v_asc + w_h_asc) / 2 w_lr_asc = (w_l_asc + w_r_asc) / 2 axes.plot(w_vh_asc, f_asc, color='blue', linewidth=1, label='w_vh') axes.plot(w_lr_asc, f_asc, color='blue', linewidth=1, label='w_lr') axes.legend() def _plot_edge_deflection_edge_deflection_avg(self, axes): '''plot the average edge (e) deflections for the front/back and left/right ''' max_force_idx = self.max_force_idx w_v_asc = -self.WA_V[:max_force_idx + 1] w_h_asc = -self.WA_H[:max_force_idx + 1] w_l_asc = -self.WA_L[:max_force_idx + 1] w_r_asc = -self.WA_R[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_vh_asc = (w_v_asc + w_h_asc) / 2 w_lr_asc = (w_l_asc + w_r_asc) / 2 axes.plot(w_vh_asc, w_lr_asc, color='blue', linewidth=1, label='w_vh / w_lr') axes.plot(np.array([0, w_vh_asc[-1]]), np.array([0, w_vh_asc[-1]]), color='k', linewidth=1, linestyle='-') axes.legend() def _plot_force_center_edge_deflection(self, axes): '''plot the F-w-diagramm for the center-edge (ce) deflections ''' max_force_idx = argmax(-self.Kraft) # get only the ascending branch of the response curve f_asc = -self.Kraft[:max_force_idx + 1] w_ml_asc = -self.WA_ML[:max_force_idx + 1] w_mr_asc = -self.WA_MR[:max_force_idx + 1] axes.plot(w_ml_asc, f_asc, color='blue', linewidth=1, label='w_ml') axes.plot(w_mr_asc, f_asc, color='blue', linewidth=1, label='w_mr') axes.legend() def _plot_force_center_edge_deflection_avg(self, axes): '''plot the average F-w-diagramm for the center-edge (ce) deflections ''' # get the index of the maximum stress max_force_idx = argmax(-self.Kraft) # get only the ascending branch of the response curve f_asc = -self.Kraft[:max_force_idx + 1] w_ml_asc = -self.WA_ML[:max_force_idx + 1] w_mr_asc = -self.WA_MR[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_mlmr_asc = (w_ml_asc + w_mr_asc) / 2 axes.plot(w_mlmr_asc, f_asc, color='blue', linewidth=1) def _plot_force_deflection_avg(self, axes): '''plot the average F-w-diagramms for the center(c), center-edge (ce) and edge(vh) and edge (lr) deflections ''' # get the index of the maximum stress max_force_idx = argmax(-self.Kraft) # get only the ascending branch of the response curve f_asc = -self.Kraft[:max_force_idx + 1] w_m = -self.WA_M[:max_force_idx + 1] axes.plot(w_m, f_asc, color='blue', linewidth=1) # ## center-edge deflection (ce) w_ml_asc = -self.WA_ML[:max_force_idx + 1] w_mr_asc = -self.WA_MR[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_mlmr_asc = (w_ml_asc + w_mr_asc) / 2 axes.plot(w_mlmr_asc, f_asc, color='red', linewidth=1) # ## edge deflections (e) w_v_asc = -self.WA_V[:max_force_idx + 1] w_h_asc = -self.WA_H[:max_force_idx + 1] w_l_asc = -self.WA_L[:max_force_idx + 1] w_r_asc = -self.WA_R[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_vh_asc = (w_v_asc + w_h_asc) / 2 w_lr_asc = (w_l_asc + w_r_asc) / 2 axes.plot(w_vh_asc, f_asc, color='green', linewidth=1, label='w_vh') axes.plot(w_lr_asc, f_asc, color='blue', linewidth=1, label='w_lr') # # set axis-labels # xkey = 'deflection [mm]' # ykey = 'force [kN]' # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) def _plot_force_deflection_avg_interpolated(self, axes, linewidth=1): '''plot the average F-w-diagrams for the center(c), center-edge (ce) and edge(vh) and edge (lr) deflections NOTE: center deflection curve is cut at its starting point in order to remove offset in the dispacement meassurement ''' # get the index of the maximum stress max_force_idx = argmax(-self.Kraft) # get only the ascending branch of the response curve f_asc = -self.Kraft[:max_force_idx + 1] w_m = -self.WA_M[:max_force_idx + 1] # w_m -= 0.17 # axes.plot(w_m, f_asc, color = 'blue', linewidth = 1) # move the starting point of the center deflection curve to the point where the force starts # (remove offset in measured displacement where there is still no force measured) # idx_0 = np.where(f_asc > 0.05)[0][0] f_asc_cut = f_asc[ idx_0: ] w_m_cut = w_m[ idx_0: ] - w_m[ idx_0 ] axes.plot(w_m_cut, f_asc_cut, color='k', linewidth=linewidth) # plot machine displacement (hydraulic cylinder) # # Weg_asc = -self.Weg[ :max_force_idx + 1 ] # axes.plot(Weg_asc, f_asc, color='k', linewidth=1.5) # ## center-edge deflection (ce) w_ml_asc = -self.WA_ML[:max_force_idx + 1] w_mr_asc = -self.WA_MR[:max_force_idx + 1] # get the average displacement values of the corresponding displacement gauges w_mlmr_asc = (w_ml_asc + w_mr_asc) / 2 # axes.plot(w_mlmr_asc, f_asc, color='red', linewidth=1) axes.plot(w_ml_asc, f_asc, color='k', linewidth=linewidth) axes.plot(w_mr_asc, f_asc, color='k', linewidth=linewidth) # ## edge deflections (e) w_v_asc = -self.WA_V[:max_force_idx + 1] w_h_asc = -self.WA_H[:max_force_idx + 1] w_l_asc = -self.WA_L[:max_force_idx + 1] w_r_asc = -self.WA_R[:max_force_idx + 1] axes.plot(w_v_asc, f_asc, color='grey', linewidth=linewidth) axes.plot(w_h_asc, f_asc, color='grey', linewidth=linewidth) axes.plot(w_l_asc, f_asc, color='k', linewidth=linewidth) axes.plot(w_r_asc, f_asc, color='k', linewidth=linewidth) # get the average displacement values of the corresponding displacement gauges # w_vh_asc = (w_v_asc + w_h_asc) / 2 # w_lr_asc = (w_l_asc + w_r_asc) / 2 # axes.plot(w_vh_asc, f_asc, color='green', linewidth=1, label='w_vh') # axes.plot(w_lr_asc, f_asc, color='blue', linewidth=1, label='w_lr') # save 'F-w-arr_m-mlmr-vh-lr' in directory "/simdb/simdata/exp_st" # simdata_dir = os.path.join(simdb.simdata_dir, 'exp_st') # if os.path.isdir(simdata_dir) == False: # os.makedirs(simdata_dir) # filename = os.path.join(simdata_dir, 'F-w-arr_m-mlmr-vh-lr_' + self.key + '.csv') # Fw_m_mlmr_vh_lr_arr = np.hstack([f_asc[:, None], w_m[:, None] - w_m[ idx_0 ], w_mlmr_asc[:, None], w_vh_asc[:, None], w_lr_asc[:, None]]) # print 'Fw_m_mlmr_vh_lr_arr' # np.savetxt(filename, Fw_m_mlmr_vh_lr_arr, delimiter=';') # print 'F-w-curves for center, middle, edges saved to file %s' % (filename) #-------------------------------------------------------------------------------- # view #-------------------------------------------------------------------------------- traits_view = View(VGroup( Group( Item('jump_rtol', format_str="%.4f"), label='curve_ironing' ), Group( Item('thickness', format_str="%.3f"), Item('edge_length', format_str="%.3f"), label='geometry' ), Group( Item('loading_rate'), Item('age'), label='loading rate and age' ), Group( Item('E_c', show_label=True, style='readonly', format_str="%.0f"), Item('ccs@', show_label=False), label='composite cross section' ) ), scrollable=True, resizable=True, height=0.8, width=0.6 )
class CBClampedRandXi(RF): ''' Crack bridged by a fiber with constant frictional interface to rigid; free fiber end; ''' implements(IRF) title = Str('crack bridge with rigid matrix') tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) r = Float(0.013, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='fiber radius') E_f = Float(72e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) m = Float(5., auto_set=False, enter_set=True, input=True, distr=['uniform']) sV0 = Float(3.e-3, auto_set=False, enter_set=True, input=True, distr=['uniform']) V_f = Float(0.0175, auto_set=False, enter_set=True, input=True, distr=['uniform']) lm = Float(np.inf, auto_set=False, enter_set=True, input=True, distr=['uniform']) w = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) x_label = Str('crack opening [mm]') y_label = Str('composite stress [MPa]') C_code = Str('') def __call__(self, w, tau, E_f, V_f, r, m, sV0): '''free and fixed fibers combined the failure probability of fixed fibers is evaluated by integrating only between -lm/2 and lm/2. Only intact fibers are considered (no pullout contribution)''' T = 2. * tau / r + 1e-10 k = np.sqrt(T / E_f) ef0cb = k * np.sqrt(w) s = ((T * (m + 1) * sV0**m) / (2. * E_f * pi * r**2))**(1. / (m + 1)) Gxi_deb = 1 - np.exp(-(ef0cb / s)**(m + 1)) if w == 1.00: a0 = ef0cb * E_f / T print np.sum(a0 > 500.) / float(np.sum(a0 > -0.1)) return ef0cb * (1 - Gxi_deb) * E_f * V_f * r**2
class SimDT(IBVModel): '''Simulation: Disk Test ''' implements(ISimModel) material_density_roof = Float(-22.4e-3) # [MN/m^3] #---------------------------------------------------- # elements #---------------------------------------------------- n_elems = Int(50, input=True) thickness = Float(1.0, input=True) vtk_r = Float(0.9, input=True) # fets used for roof # fets_disk = Instance((FETSEval), depends_on='+ps_levels, +input') def _fets_disk_default(self): # fe_quad_serendipity_roof is defined in base class # connected with material properties of the roof # mats = MATS2DElastic(E=3.1e5, nu=0.0, stress_state='plane_stress') fets = FETS2D4Q8U(mats_eval=mats) fets.vtk_r *= self.vtk_r return fets geo_disk = Property(depends_on='+ps_levels, +input') @cached_property def _get_geo_disk(self): # list of local origins of each roof defined in global coordinates # (for MRDetail two roofs are considered due to symmetry) # gt = GeoSquare2Circle(circle_center=[0.0, 0.0], circle_radius=1.0, square_edge=4.0) return gt #---------------------------------------------------- # fe-grids #---------------------------------------------------- fe_disk_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_fe_disk_grid(self): fe_grid = FEGrid(coord_min=(-1, -1), coord_max=(1, 1), geo_transform=self.geo_disk, shape=(self.n_elems, self.n_elems), fets_eval=self.fets_disk) return fe_grid bc_fixed = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_fixed(self): fe_disk_grid = self.fe_disk_grid extension = 0.1 alpha_45 = math.pi / 4.0 bc00 = BCDof(var='u', dof=fe_disk_grid[0, 0, 0, 0].dofs[0, 0, 1], value=-extension * math.cos(alpha_45)) bc00_link = BCDof(var='u', dof=fe_disk_grid[0, 0, 0, 0].dofs[0, 0, 0], link_dofs=[fe_disk_grid[0, 0, 0, 0].dofs[0, 0, 1]], link_coeffs=[1.0], value=0) bc10 = BCDof(var='u', dof=fe_disk_grid[-1, 0, -1, 0].dofs[0, 0, 1], value=-extension * math.cos(alpha_45)) bc10_link = BCDof(var='u', dof=fe_disk_grid[-1, 0, -1, 0].dofs[0, 0, 0], link_dofs=[fe_disk_grid[-1, 0, -1, 0].dofs[0, 0, 1]], link_coeffs=[-1.0], value=0) bc11 = BCDof(var='u', dof=fe_disk_grid[-1, -1, -1, -1].dofs[0, 0, 1], value=extension * math.cos(alpha_45)) bc11_link = BCDof( var='u', dof=fe_disk_grid[-1, -1, -1, -1].dofs[0, 0, 0], link_dofs=[fe_disk_grid[-1, -1, -1, -1].dofs[0, 0, 1]], link_coeffs=[1.0], value=0) bc01 = BCDof(var='u', dof=fe_disk_grid[0, -1, 0, -1].dofs[0, 0, 1], value=extension * math.cos(alpha_45)) bc01_link = BCDof(var='u', dof=fe_disk_grid[0, -1, 0, -1].dofs[0, 0, 0], link_dofs=[fe_disk_grid[0, -1, 0, -1].dofs[0, 0, 1]], link_coeffs=[-1.0], value=0) n_xy = self.n_elems / 2 bc_bottom = BCDof(var='u', dof=fe_disk_grid[n_xy, 0, 0, 0].dofs[0, 0, 1], value=-extension) bc_right = BCDof(var='u', dof=fe_disk_grid[-1, n_xy, -1, 0].dofs[0, 0, 0], value=extension) bc_top = BCDof(var='u', dof=fe_disk_grid[n_xy, -1, 0, -1].dofs[0, 0, 1], value=extension) bc_left = BCDof(var='u', dof=fe_disk_grid[0, n_xy, 0, 0].dofs[0, 0, 0], value=-extension) return [ bc_left, bc_right, bc_top, bc_bottom, bc00, bc00_link, bc10, bc10_link, bc11, bc11_link, bc01, bc01_link ] #---------------------------------------------------- # ps_study #---------------------------------------------------- def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' # DISPLACEMENT # U = self.tloop.eval() def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='$u_z$', unit='[mm]'), ] #---------------------------------------------------- # response tracer #---------------------------------------------------- rtrace_list = List def _rtrace_list_default(self): return [self.max_princ_stress, self.sig_app, self.u] max_princ_stress = Instance(RTraceDomainListField) def _max_princ_stress_default(self): return RTraceDomainListField( name='max principle stress', idx=0, var='max_principle_sig', warp=True, # position = 'int_pnts', record_on='update', ) sig_app = Property(Instance(RTraceDomainListField), depends_on='+ps_levels, +input') @cached_property def _get_sig_app(self): return RTraceDomainListField( name='sig_app', position='int_pnts', var='sig_app', record_on='update', ) u = Property(Instance(RTraceDomainListField), depends_on='+ps_levels, +input') @cached_property def _get_u(self): return RTraceDomainListField( name='displacement', var='u', warp=True, record_on='update', ) #---------------------------------------------------- # time loop #---------------------------------------------------- tline = Instance(TLine) def _tline_default(self): return TLine(min=0.0, step=1.0, max=1.0) tloop = Property(depends_on='+ps_levels, +input') @cached_property def _get_tloop(self): roof = self.fe_disk_grid # self.bc_roof_deadweight + \ bc_list = self.bc_fixed ts = TS(sdomain=[roof], dof_resultants=True, bcond_list=bc_list, rtrace_list=self.rtrace_list) # Add the time-loop control # tloop = TLoop(tstepper=ts, tolerance=1e-4, tline=self.tline) return tloop
class ExpEM(ExType): '''Experiment: Elastic Modulus Test ''' # label = Str('three point bending test') implements(IExType) file_ext = 'TRA' #-------------------------------------------------------------------- # register a change of the traits with metadata 'input' #-------------------------------------------------------------------- input_change = Event @on_trait_change('+input, ccs.input_change, +ironing_param') def _set_input_change(self): self.input_change = True #-------------------------------------------------------------------------------- # specify inputs: #-------------------------------------------------------------------------------- edge_length = Float(0.06, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) height = Float(0.12, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) gauge_length = Float(0.10, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) # age of the concrete at the time of testing age = Int(39, unit='d', input=True, table_field=True, auto_set=False, enter_set=True) loading_rate = Float(0.6, unit='MPa/s', input=True, table_field=True, auto_set=False, enter_set=True) #-------------------------------------------------------------------------- # composite cross section #-------------------------------------------------------------------------- ccs = Instance(CompositeCrossSection) def _ccs_default(self): '''default settings correspond to setup '7u_MAG-07-03_PZ-0708-1' ''' # fabric_layout_key = 'MAG-07-03' # fabric_layout_key = '2D-02-06a' fabric_layout_key = '2D-05-11' # concrete_mixture_key = 'PZ-0708-1' concrete_mixture_key = 'FIL-10-09' orientation_fn_key = 'all0' # orientation_fn_key = 'all90' # orientation_fn_key = '90_0' n_layers = 12 s_tex_z = 0.060 / (n_layers + 1) ccs = CompositeCrossSection(fabric_layup_list=[ plain_concrete(s_tex_z * 0.5), FabricLayUp(n_layers=n_layers, orientation_fn_key=orientation_fn_key, s_tex_z=s_tex_z, fabric_layout_key=fabric_layout_key), plain_concrete(s_tex_z * 0.5) ], concrete_mixture_key=concrete_mixture_key) return ccs #-------------------------------------------------------------------------- # Get properties of the composite #-------------------------------------------------------------------------- # E-modulus of the composite at the time of testing E_c = Property(Float, unit='MPa', depends_on='input_change', table_field=True) def _get_E_c(self): return self.ccs.get_E_c_time(self.age) # E-modulus of the composite after 28 days E_c28 = DelegatesTo('ccs', listenable=False) # reinforcement ration of the composite rho_c = DelegatesTo('ccs', listenable=False) #-------------------------------------------------------------------------------- # define processing #-------------------------------------------------------------------------------- def _read_data_array(self): ''' Read the experiment data. ''' print 'READ FILE' _data_array = np.loadtxt(self.data_file, delimiter=';', skiprows=2, usecols=(4, 5, 10, 17, 20)) self.data_array = _data_array names_and_units = Property @cached_property def _get_names_and_units(self): ''' Set the names and units of the measured data. ''' names = ['delta_u1', 'delta_u2', 'w', 'F', 'time'] units = ['mm', 'mm', 'mm', 'kN', 's'] return names, units #0#"Arbeit"; #1#"Dehn. abs"; #2#"Dehn. abs (2. Kanal)"; #3#"Dehnung"; #4#"Dehnung (1. Kanal)"; #5#"Dehnung (2. Kanal)"; #6#"Dehnung nominell"; #7#"DeltaE"; #8#"E1 korr"; #9#"E2 korr"; #10#"Kolbenweg"; #11#"Kolbenweg abs."; #12#"Lastrahmen"; #13#"LE-Kanal"; #14#"PrXXXfzeit"; #15#"Querdehnung"; #16#"S korr"; #17#"Standardkraft"; #18#"Vorlaufzeit"; #19#"Weg"; #20#"Zeit"; #21#"Zyklus" # #"Nm"; #"mm"; #"mm"; #"mm"; #"mm"; #"mm"; #"mm"; #"%"; #"mm"; #" "; #"mm"; #"mm"; #" "; #"mm"; #"s"; #"mm"; #" "; #"N"; #"s"; #"mm"; #"s"; #" " #-------------------------------------------------------------------------------- # plot templates #-------------------------------------------------------------------------------- plot_templates = { 'force / displacement': '_plot_force_displacement', 'stress / strain': '_plot_stress_strain', 'stress / time': '_plot_stress_time', } default_plot_template = 'force / displacement' def _plot_force_displacement(self, axes): xkey = 'deflection [mm]' ykey = 'force [kN]' xdata = self.w ydata = self.F / 1000. # convert from [N] to [kN] axes.set_xlabel('%s' % (xkey, )) axes.set_ylabel('%s' % (ykey, )) axes.plot(xdata, ydata # color = c, linewidth = w, linestyle = s ) def _plot_stress_strain(self, axes): sig = (self.F / 1000000.) / (self.edge_length**2 ) # convert from [N] to [MN] eps1 = (self.delta_u1 / 1000.) / self.gauge_length eps2 = (self.delta_u2 / 1000.) / self.gauge_length eps_m = (eps1 + eps2) / 2. axes.plot(eps_m, sig, color='blue', linewidth=2) def _plot_stress_time(self, axes): sig = (self.F / 1000000.) / (self.edge_length**2 ) # convert from [N] to [MN] axes.plot(self.time, sig, color='blue', linewidth=2) def _plot_displ_time(self, axes): axes.plot(self.time, self.displ, color='blue', linewidth=2) #-------------------------------------------------------------------------------- # view #-------------------------------------------------------------------------------- traits_view = View(VGroup( Group(Item('edge_length', format_str="%.3f"), Item('height', format_str="%.3f"), Item('gauge_length', format_str="%.3f"), label='geometry'), Group(Item('loading_rate'), Item('age'), label='loading rate and age'), Group(Item('E_c', show_label=True, style='readonly', format_str="%.0f"), Item('ccs@', show_label=False), label='composite cross section')), scrollable=True, resizable=True, height=0.8, width=0.6)
class SimTTBC(IBVModel): '''Simulation: Tensile Test Buttsrap Clamping ''' implements(ISimModel) #=========================================================================== # Speciment geometry and discretization parameters #=========================================================================== specimen_ne_x = Int(2, input=True, label='number of elements in x-direction') specimen_ne_y = Int(2, input=True, label='number of elements in y-direction') specimen_thickness = Float(0.01, input=True, label='thickness of the tensile specimen') specimen_length = Float(0.6, input=True, label='length of the tensile specimen') specimen_width = Float(0.1, input=True, label='length of the tensile specimen') #=========================================================================== # Buttstrap geometry and discretization parameters #=========================================================================== buttstrap_length = Int(0.2, input=True, label='length of the buttstrap') buttstrap_ne_y = Int( 4, input=True, label='number of elements in buttstrap in y-direction') buttstrap_ne_x = Int( 7, input=True, label='number of elements in buttstrap in x-direction') buttstrap_max_thickness = Float(0.04, input=True, label='maximum thickness of the buttstrap') buttstrap_min_thickness = Float( 0.04, input=True, label='mimnimum thickness of the buttstrsp') #=========================================================================== # Elastomer geometry #=========================================================================== elastomer_thickness = Float(0.002, input=True, label='thickness of the elastomer') elastomer_ne_y = Int( 1, input=True, label='number of elements in buttstrap in y-direction') elastomer_ne_x = Property def _get_elastomer_ne_x(self): return self.buttstrap_ne_x #=========================================================================== # Friction interface geometry #=========================================================================== friction_thickness = Float(0.005, input=True, label='thickness of the elastomer') friction_ne_y = Int(1, input=True, label='number of elements in buttstrap in y-direction') friction_ne_x = Property def _get_friction_ne_x(self): return self.buttstrap_ne_x vtk_r = Float(1.0, input=True) #=========================================================================== # Element types #=========================================================================== E_specimen = Int(31000.0, input=True, label='E Modulus of the specimen [MPa]') nu_specimen = Int(0.2, input=True, label='E Modulus of the specimen [-]') specimen_fets = Instance((FETSEval), depends_on='+ps_levels, +input') def _specimen_fets_default(self): # fe_quad_serendipity_roof is defined in base class # connected with material properties of the roof # E_eff = self.E_specimen * self.specimen_width mats = MATS2DElastic(E=E_eff, nu=self.nu_specimen, stress_state='plane_stress') fets = FETS2D4Q8U(mats_eval=mats) fets.vtk_r *= self.vtk_r return fets E_buttstrap = Int(210000.0, input=True, label='E Modulus of the buttstrap [MPa]') nu_buttstrap = Int(0.2, input=True, label='Poisson ratio of the buttstrap [-]') buttstrap_fets = Instance((FETSEval), depends_on='+ps_levels, +input') def _buttstrap_fets_default(self): # fe_quad_serendipity_roof is defined in base class # connected with material properties of the roof # E_eff = self.E_buttstrap * self.specimen_width mats = MATS2DElastic(E=E_eff, nu=self.nu_buttstrap, stress_state='plane_stress') fets = FETS2D4Q8U(mats_eval=mats) fets.vtk_r *= self.vtk_r return fets E_elastomer = Int(100.0, input=True, label='E Modulus of the elastomer [MPa]') nu_elastomer = Int(0.3, input=True, label='Poisson ratio of the elastomer [-]') elastomer_fets = Instance((FETSEval), depends_on='+ps_levels, +input') def _elastomer_fets_default(self): # fe_quad_serendipity_roof is defined in base class # connected with material properties of the roof # E_eff = self.E_buttstrap * self.specimen_width mats = MATS2DElastic(E=E_eff, nu=self.nu_elastomer, stress_state='plane_stress') fets = FETS2D4Q8U(mats_eval=mats) fets.vtk_r *= self.vtk_r return fets g = Float(400000, input=True, label='Shear stiffness between elastomer and specimen') t_max = Float(50, input=True, label='Frictional stress between elastomer and specimen') friction_fets = Instance((FETSEval), depends_on='+ps_levels, +input') def _friction_fets_default(self): width = self.specimen_width G = self.g * width T_max = self.t_max * width ifopen_law = MFnLineArray(ydata=[-1.0e+1, 0., 1.0e+10], xdata=[-1., 0., 1.]) mats_ifopen = MATS1DElastic(stress_strain_curve=ifopen_law) mats = MATS1D5Bond(mats_phase1=MATS1DElastic(E=0), mats_phase2=MATS1DElastic(E=0), mats_ifslip=MATS1DPlastic(E=G, sigma_y=T_max, K_bar=0., H_bar=0.), mats_ifopen=mats_ifopen) fets = FETS1D52L6ULRH(mats_eval=mats) #quadratic fets.vtk_r *= self.vtk_r return fets #=========================================================================== # Geometry transformation #=========================================================================== specimen_cl_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_specimen_cl_geo(self): def geo_transform(points): points[:, 0] *= self.buttstrap_length points[:, 1] *= self.specimen_thickness return points return geo_transform specimen_fl_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_specimen_fl_geo(self): def geo_transform(points): points[:, 0] *= self.specimen_length points[:, 0] += self.buttstrap_length points[:, 1] *= self.specimen_thickness return points return geo_transform buttstrap_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_buttstrap_geo(self): def geo_transform(points): x, y = points.T x *= self.buttstrap_length h_max = self.buttstrap_max_thickness h_min = self.buttstrap_min_thickness y *= (h_max - (h_max - h_min) / self.buttstrap_length * x) y_offset = (self.specimen_thickness + self.friction_thickness + self.elastomer_thickness) points[:, 0], points[:, 1] = x, y + y_offset return points return geo_transform buttstrap_clamp_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_buttstrap_clamp_geo(self): def geo_transform(points): x, y = points.T x *= self.buttstrap_length x -= self.buttstrap_length y *= self.buttstrap_max_thickness y_offset = (self.specimen_thickness + self.friction_thickness + self.elastomer_thickness) points[:, 0], points[:, 1] = x, y + y_offset return points return geo_transform elastomer_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_elastomer_geo(self): def geo_transform(points): x, y = points.T x *= self.buttstrap_length h = self.elastomer_thickness y *= h y_offset = self.specimen_thickness + self.friction_thickness points[:, 0], points[:, 1] = x, y + y_offset return points return geo_transform friction_geo = Property(depends_on='+ps_levels, +input') @cached_property def _get_friction_geo(self): def geo_transform(points): x, y = points.T x *= self.buttstrap_length h = self.friction_thickness y *= h y_offset = self.specimen_thickness points[:, 0], points[:, 1] = x, y + y_offset return points return geo_transform #=========================================================================== # FE-grids #=========================================================================== fe_domain = Property(depends_on='+ps_levels, +input') @cached_property def _get_fe_domain(self): return FEDomain() specimen_fl_fe_level = Property(depends_on='+ps_levels, +input') def _get_specimen_fl_fe_level(self): return FERefinementGrid(name='specimen free level', fets_eval=self.specimen_fets, domain=self.fe_domain) specimen_cl_fe_level = Property(depends_on='+ps_levels, +input') def _get_specimen_cl_fe_level(self): return FERefinementGrid(name='specimen clamped level', fets_eval=self.specimen_fets, domain=self.fe_domain) buttstrap_fe_level = Property(depends_on='+ps_levels, +input') def _get_buttstrap_fe_level(self): return FERefinementGrid(name='buttstrap level', fets_eval=self.buttstrap_fets, domain=self.fe_domain) buttstrap_clamp_fe_level = Property(depends_on='+ps_levels, +input') def _get_buttstrap_clamp_fe_level(self): return FERefinementGrid(name='buttstrap clamp level', fets_eval=self.buttstrap_fets, domain=self.fe_domain) elastomer_fe_level = Property(depends_on='+ps_levels, +input') def _get_elastomer_fe_level(self): return FERefinementGrid(name='elastomer level', fets_eval=self.elastomer_fets, domain=self.fe_domain) friction_fe_level = Property(depends_on='+ps_levels, +input') def _get_friction_fe_level(self): return FERefinementGrid(name='friction level', fets_eval=self.friction_fets, domain=self.fe_domain) specimen_fl_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_specimen_fl_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.specimen_fl_fe_level, geo_transform=self.specimen_fl_geo, shape=(self.specimen_ne_x, self.specimen_ne_y), fets_eval=self.specimen_fets) return fe_grid specimen_cl_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_specimen_cl_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.specimen_cl_fe_level, geo_transform=self.specimen_cl_geo, shape=(self.buttstrap_ne_x, self.specimen_ne_y), fets_eval=self.specimen_fets) return fe_grid buttstrap_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_buttstrap_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.buttstrap_fe_level, geo_transform=self.buttstrap_geo, shape=(self.buttstrap_ne_x, self.buttstrap_ne_y), fets_eval=self.buttstrap_fets) return fe_grid buttstrap_clamp_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_buttstrap_clamp_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.buttstrap_clamp_fe_level, geo_transform=self.buttstrap_clamp_geo, shape=(1, self.buttstrap_ne_y), fets_eval=self.buttstrap_fets) return fe_grid elastomer_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_elastomer_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.elastomer_fe_level, geo_transform=self.elastomer_geo, shape=(self.elastomer_ne_x, self.elastomer_ne_y), fets_eval=self.elastomer_fets) return fe_grid friction_fe_grid = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_friction_fe_grid(self): fe_grid = FEGrid(coord_min=(0, 0), coord_max=(1, 1), level=self.friction_fe_level, geo_transform=self.friction_geo, shape=(self.friction_ne_x, self.friction_ne_y), fets_eval=self.friction_fets) return fe_grid #=========================================================================== # Boundary conditions #=========================================================================== bc_list = Property(depends_on='+ps_levels, +input') @cached_property def _get_bc_list(self): sp_fl_grid = self.specimen_fl_fe_grid sp_cl_grid = self.specimen_cl_fe_grid bs_grid = self.buttstrap_fe_grid bs_clamp_grid = self.buttstrap_clamp_fe_grid el_grid = self.elastomer_fe_grid fl_grid = self.friction_fe_grid link_sp_fl_cl = BCDofGroup( var='u', value=0., dims=[0, 1], get_dof_method=sp_cl_grid.get_right_dofs, get_link_dof_method=sp_fl_grid.get_left_dofs, link_coeffs=[1.]) link_fl_sp = BCDofGroup(var='u', value=0., dims=[0, 1], get_dof_method=fl_grid.get_bottom_dofs, get_link_dof_method=sp_cl_grid.get_top_dofs, link_coeffs=[1.]) link_el_fl = BCDofGroup(var='u', value=0., dims=[0, 1], get_dof_method=el_grid.get_bottom_dofs, get_link_dof_method=fl_grid.get_top_dofs, link_coeffs=[1.]) link_bs_el = BCDofGroup(var='u', value=0., dims=[0, 1], get_dof_method=bs_grid.get_bottom_dofs, get_link_dof_method=el_grid.get_top_dofs, link_coeffs=[1.]) link_bs_clamp = BCDofGroup(var='u', value=0., dims=[0, 1], get_dof_method=bs_clamp_grid.get_right_dofs, get_link_dof_method=bs_grid.get_left_dofs, link_coeffs=[1.]) symx_bc = BCSlice(var='u', slice=sp_fl_grid[-1, :, -1, :], dims=[0], value=0) symy_fl_bc = BCSlice(var='u', slice=sp_fl_grid[:, 0, :, 0], dims=[1], value=0) symy_cl_bc = BCSlice(var='u', slice=sp_cl_grid[:, 0, :, 0], dims=[1], value=0) cntl_bc = BCSlice(var='u', slice=bs_clamp_grid[0, :, 0, :], dims=[0], value=-0.001) return [ symx_bc, symy_fl_bc, symy_cl_bc, cntl_bc, link_sp_fl_cl, link_bs_clamp, link_fl_sp, link_el_fl, link_bs_el ] #---------------------------------------------------- # ps_study #---------------------------------------------------- def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' # DISPLACEMENT # U = self.tloop.eval() def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='$u_z$', unit='[mm]'), ] #---------------------------------------------------- # response tracer #---------------------------------------------------- rtrace_list = List def _rtrace_list_default(self): return [self.max_princ_stress, self.sig_app, self.u] max_princ_stress = Instance(RTraceDomainListField) def _max_princ_stress_default(self): return RTraceDomainListField( name='max principle stress', idx=0, var='max_principle_sig', warp=True, # position = 'int_pnts', record_on='update', ) sig_app = Property(Instance(RTraceDomainListField), depends_on='+ps_levels, +input') @cached_property def _get_sig_app(self): return RTraceDomainListField( name='sig_app', position='int_pnts', var='sig_app', record_on='update', ) u = Property(Instance(RTraceDomainListField), depends_on='+ps_levels, +input') @cached_property def _get_u(self): return RTraceDomainListField( name='displacement', var='u', warp=True, record_on='update', ) #---------------------------------------------------- # time loop #---------------------------------------------------- tline = Instance(TLine) def _tline_default(self): return TLine(min=0.0, step=1.0, max=1.0) tloop = Property(depends_on='+ps_levels, +input') @cached_property def _get_tloop(self): ts = TS(sdomain=self.fe_domain, dof_resultants=True, bcond_list=self.bc_list, rtrace_list=self.rtrace_list) # Add the time-loop control # tloop = TLoop(tstepper=ts, tolerance=1e-4, tline=self.tline) return tloop
class POShortFiber(RF): ''' Pullout of fiber from a stiff matrix; stress criterion for debonding, free fiber end ''' implements(IRF) title = Str('pullout - short fiber with constant friction') image = Image('pics/cb_short_fiber.jpg') xi = Float(0.0179, auto_set=False, enter_set=True, input=True, distr=['weibull_min', 'uniform']) E_f = Float(200e+3, auto_set=False, enter_set=True, desc='filament stiffness [N/mm2]', distr=['uniform', 'norm'], scale=210e3, shape=0) D_f = Float(0.3, auto_set=False, enter_set=True, desc='filament diameter [mm]', distr=['uniform', 'norm'], scale=0.5, shape=0) le = Float(8.5, auto_set=False, enter_set=True, desc='embedded lentgh [mm]', distr=['uniform'], scale=8.5, shape=0) L_f = Float(17.0, auto_set=False, enter_set=True, desc='fiber length [mm]', distr=['uniform', 'norm'], scale=30, shape=0) tau = Float(1.76, auto_set=False, enter_set=True, desc='bond shear stress [N/mm2]', distr=['norm', 'uniform'], scale=1.76, shape=0.5) f = Float(0.03, auto_set=False, enter_set=True, desc='snubbing coefficient', distr=['uniform', 'norm'], scale=0.05, shape=0) phi = Float(0.0, auto_set=False, enter_set=True, desc='inclination angle', distr=['sin2x', 'sin_distr'], scale=1.0, shape=0) l = Float(0.0, auto_set=False, enter_set=True, distr=['uniform'], desc='free length') theta = Float(0.01, auto_set=False, enter_set=True, distr=['uniform', 'norm'], desc='slack') u = Float(ctrl_range=(0, 0.01, 100), auto_set=False, enter_set=True) x_label = Str('displacement [mm]', enter_set=True, auto_set=False) y_label = Str('force [N]', enter_set=True, auto_set=False) C_code = '' def __call__(self, u, tau, L_f, D_f, E_f, le, phi, f, l, theta, xi): l = l * (1 + theta) u = u - theta * l T = tau * pi * D_f E = E_f A = D_f**2 / 4. * pi # debonding stage q_deb = -l * T + sqrt((l * T)**2 + 2 * E * A * T * u * H(u)) # displacement at which debonding is finished u0 = le * T * (le + 2 * l) / 2 / E_f / A q_pull = le * T * ((u0 - u) / (le - u0) + 1) q = q_deb * H(le * T - q_deb) + q_pull * H(q_deb - le * T) # include inclination influence q = q * H(q) * e**(f * phi) # include breaking strain q = q * H(A * E_f * xi - q) return q def get_q_x(self, u, x, tau, L_f, D_f, E_f, z, phi, f, l, theta, xi): q = self.__call__(u, tau, L_f, D_f, E_f, z, phi, f, l, theta, xi) l = l * (1 + theta) T = tau * pi * D_f qfree = q qbond = q - T * (x - l) return qfree * H(l - x) + qbond * H(x - l) traits_view = View(Item('E_f', label='fiber E-mod'), Item('D_f', label='fiber diameter'), Item('f', label='snubbing coef.'), Item('phi', label='inclination angle'), Item('le', label='embedded length'), Item('tau', label='frictional coef.'), resizable=True, scrollable=True, height=0.8, width=0.8, buttons=[OKButton, CancelButton])
class MRone(MushRoofModel): implements(ISimModel) mushroof_part = 'one' #=============================================================================== # fe_grid #=============================================================================== n_elems_xy_quarter = Int(10, input=True) # , ps_levels = [4, 16, 5] ) n_elems_z = Int(1, input=True) # , ps_levels = [1, 2, 1] ) n_elems_col_z = Int(10, input=True, ps_levels=[5, 20, 3]) n_elems_col_xy = Int(2, input=True, ps_levels=[2, 4, 1]) shift_elems = True vtk_r = Float(1.00) # default roof fe_roof = Instance((FETSEval), depends_on='+ps_levels, +input') def _fe_roof_default(self): fets = self.fe_quad_serendipity_roof fets.vtk_r *= self.vtk_r return fets # default plate fe_plate = Instance((FETSEval), depends_on='+ps_levels, +input') def _fe_plate_default(self): fets = self.fe_quad_serendipity_plate fets.ngp_r = 3 fets.ngp_s = 3 fets.ngp_t = 3 fets.vtk_r *= self.vtk_r return fets # shell # hp_shell = Property(Instance(HPShell), depends_on='+ps_levels, +input') @cached_property def _get_hp_shell(self): return HPShell(length_xy_quarter=self.length_xy_quarter, length_z=self.length_z, n_elems_xy_quarter=self.n_elems_xy_quarter, n_elems_z=self.n_elems_z, scalefactor_delta_h=self.scalefactor_delta_h, const_reinf_layer_elem=self.const_reinf_layer_elem, width_top_col=self.width_top_col, mushroof_part=self.mushroof_part, shift_array=self.shift_array, X0=self.X0) # plate # plate = Property(Instance(GEOColumn), depends_on='+ps_levels, +input') @cached_property def _get_plate(self): return GEOColumn( width_top=self.width_top_col, width_bottom=self.width_top_col, X0=[3.5, 3.5, -self.t_plate], # - 0.25], h_col=self.t_plate) # column # # X0_column = Array( [ 4., 4., -3.] ) column = Property(Instance(GEOColumn), depends_on='+ps_levels, +input') @cached_property def _get_column(self): return GEOColumn( width_top_col=self.width_top_col, width_bottom_col=self.width_bottom_col, h_col=self.h_col - self.t_plate, # r_pipe = self.r_pipe, X0=[3.5, 3.5, -(self.h_col)]) # - 0.5] ) # default column fe_column = Instance((FETSEval), transient=True, depends_on='+ps_levels, +input') def _fe_column_default(self): fets = self.fe_quad_serendipity_column fets.vtk_r *= self.vtk_r return fets fe_grid_roof = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_fe_grid_roof(self): return FEGrid(coord_min=(0.0, 0.0, 0.0), coord_max=(1.0, 1.0, 1.0), geo_transform=self.hp_shell, shift_array=self.shift_array, shape=(self.n_elems_xy, self.n_elems_xy, self.n_elems_z), fets_eval=self.fe_roof) fe_grid_column = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_fe_grid_column(self): return FEGrid(coord_min=(0.0, 0.0, 0.0), coord_max=(1.0, 1.0, 1.0), geo_transform=self.column, shape=(self.n_elems_col_xy, self.n_elems_col_xy, self.n_elems_col_z), fets_eval=self.fe_column) fe_grid_plate = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_fe_grid_plate(self): return FEGrid(coord_min=(0.0, 0.0, 0.0), coord_max=(1.0, 1.0, 1.0), geo_transform=self.plate, shape=(self.n_elems_col_xy, self.n_elems_col_xy, 2), fets_eval=self.fe_plate) #=============================================================================== # ps_study #=============================================================================== def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() U_edge = U[self.edge_corner_1_dof][0, 0, 2] F_int = self.tloop.tstepper.F_int F_int_slice_x = F_int[self.edge_roof_right] F_int_slice_y = F_int[self.edge_roof_top] # bring dofs into right order for plot # F_hinge_in_order_x = self.sort_by_dofs(self.edge_roof_top, F_int_slice_x) F_hinge_in_order_y = self.sort_by_dofs(self.edge_roof_top, F_int_slice_y) F_hinge_x = append(F_hinge_in_order_x[:, :-1, 0], F_hinge_in_order_x[-1, -1, 0]) F_hinge_y = append(F_hinge_in_order_y[:, :-1, 1], F_hinge_in_order_y[-1, -1, 1]) F_hinge_y_sum = sum(F_hinge_y.flatten()) F_hinge_x_sum = sum(F_hinge_x.flatten()) # # self.visual_force_bar( F_hinge_x.flatten() # , y_label = "internal force x [MN]" # , Title = 'F_Hinge_x_shrinkage' ) # self.visual_force_bar( F_hinge_y.flatten() # , y_label = "internal force y [MN]" # , Title = 'F_Hinge_y_shrinkage' ) print "u_edge", U_edge print "n_elems_xy_col", self.n_elems_col_xy print "n_elems_z_col", self.n_elems_col_z print "n_elems_xy_quarter", self.n_elems_xy_quarter print "n_elems_z", self.n_elems_z return array( [ U_edge, # u_x_corner2, # F_hinge_y_sum] ) # u_z_corner2, # max_princ_stress ] ], dtype='float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='U', unit='m'), # SimOut( name = 'u_x_corner2', unit = 'm' ), # SimOut( name = 'N Gelenk', unit = 'MN' ), ] # SimOut( name = 'u_z_corner2', unit = 'm' ), # SimOut( name = 'maximum principle stress', unit = 'MPa' ) ] #=============================================================================== # response tracer #=============================================================================== rtrace_list = List def _rtrace_list_default(self): return [self.max_princ_stress, self.sig_app, self.u, self.f_dof] shift_array = Array(value=[ [0.45 / 2**0.5, 0.45 / 2**0.5, 1], ], input=True) #=============================================================================== # boundary conditions #=============================================================================== bc_plate_roof_link_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_bc_plate_roof_link_list(self): ''' links all plate corner nodes of each elements to the adjacent elements of the roof ''' roof = self.fe_grid_roof plate = self.fe_grid_plate bc_col_link_list = [] slice_1 = [ BCSlice(var='u', dims=[0, 1, 2], slice=roof[self.n_elems_xy_quarter - 1, self.n_elems_xy_quarter, 0, 0, 0, 0], link_slice=plate[0, 0, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_2 = [ BCSlice(var='u', dims=[0, 1, 2], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter - 1, 0, 0, 0, 0], link_slice=plate[-1, 0, -1, -1, 0, -1], link_coeffs=[1.0], value=0.) ] slice_3 = [ BCSlice(var='u', dims=[0, 1, 2], slice=roof[self.n_elems_xy_quarter + 1, self.n_elems_xy_quarter, 0, 0, 0, 0], link_slice=plate[-1, -1, -1, -1, -1, -1], link_coeffs=[1.0], value=0.) ] slice_4 = [ BCSlice(var='u', dims=[0, 1, 2], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter + 1, 0, 0, 0, 0], link_slice=plate[0, -1, -1, 0, -1, -1], link_coeffs=[1.0], value=0.) ] slice_5 = [ BCSlice(var='u', dims=[0, 1, 2], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter, 0, 0, 0, 0], link_slice=plate[self.n_elems_col_xy / 2.0, self.n_elems_col_xy / 2.0, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] bc_plate_roof_link_list = slice_1 + slice_2 + slice_3 + slice_4 + slice_5 return bc_plate_roof_link_list bc_roof_top_roof_low_link_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_bc_roof_top_roof_low_link_list(self): ''' links all plate corner nodes of each elements to the adjacent elements of the roof ''' roof = self.fe_grid_roof plate = self.fe_grid_plate bc_roof_top_roof_low_link_list = [] slice_1 = [ BCSlice(var='u', dims=[2], link_slice=roof[self.n_elems_xy_quarter - 1, self.n_elems_xy_quarter, 0, 0, 0, 0], slice=roof[self.n_elems_xy_quarter - 1, self.n_elems_xy_quarter, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_2 = [ BCSlice(var='u', dims=[2], link_slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter - 1, 0, 0, 0, 0], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter - 1, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_3 = [ BCSlice(var='u', dims=[2], link_slice=roof[self.n_elems_xy_quarter + 1, self.n_elems_xy_quarter, 0, 0, 0, 0], slice=roof[self.n_elems_xy_quarter + 1, self.n_elems_xy_quarter, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_4 = [ BCSlice(var='u', dims=[2], link_slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter + 1, 0, 0, 0, 0], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter + 1, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_5 = [ BCSlice(var='u', dims=[2], link_slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter, 0, 0, 0, 0], slice=roof[self.n_elems_xy_quarter, self.n_elems_xy_quarter, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] bc_roof_top_roof_low_link_list = slice_1 + slice_2 + slice_3 + slice_4 + slice_5 return bc_roof_top_roof_low_link_list bc_plate_column_link_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_bc_plate_column_link_list(self): ''' links all column nodes to plate nodes ''' column = self.fe_grid_column plate = self.fe_grid_plate slice_1 = [ BCSlice(var='u', dims=[0, 1, 2], slice=plate[:, :, 0, -1, -1, 0], link_slice=column[:, :, -1, -1, -1, -1], link_coeffs=[1.0], value=0.) ] slice_2 = [ BCSlice(var='u', dims=[0, 1, 2], slice=plate[:, :, 0, 0, 0, 0], link_slice=column[:, :, -1, 0, 0, -1], link_coeffs=[1.0], value=0.) ] slice_3 = [ BCSlice(var='u', dims=[0, 1, 2], slice=plate[:, :, 0, 0, -1, 0], link_slice=column[:, :, -1, 0, -1, -1], link_coeffs=[1.0], value=0.) ] slice_4 = [ BCSlice(var='u', dims=[0, 1, 2], slice=plate[:, :, 0, -1, 0, 0], link_slice=column[:, :, -1, -1, 0, -1], link_coeffs=[1.0], value=0.) ] return slice_1 + slice_2 + slice_3 + slice_4 # return [BCSlice( var = 'u' , dims = [0, 1, 2], # slice = plate[:,:,0,:,:, 0 ], # link_slice = column[ :,:,-1 ,:,:,-1], link_coeffs = [1.0], value = 0. )] link_edge_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_link_edge_list(self): ''' links all edge nodes to one node, for this node boundary conditions are applied, the complete force within the edge hinge can therefore be evaluated at one node ''' roof = self.fe_grid_roof dof_constraint_0 = [ BCSlice(var='u', dims=[1], slice=roof[:, -1, -1, :, -1, -1], value=0.0) ] dof_constraint_1 = [ BCSlice(var='u', dims=[0], slice=roof[-1, :, -1, -1, :, -1], value=0.0) ] link_edge_list = dof_constraint_0 + dof_constraint_1 return link_edge_list bc_col_clamped_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_bc_col_clamped_list(self): column = self.fe_grid_column constraint = [ BCSlice(var='u', dims=[0, 1, 2], slice=column[:, :, 0, :, :, 0], value=0.0) ] return constraint bc_col_hinge_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_bc_col_hinge_list(self): constraint = [] column = self.fe_grid_column for i in range(0, self.n_elems_col): dof_const = [ BCSlice(var='u', dims=[0, 1, 2], slice=column[i, 0, 0, 0, 0, 0], link_slice=column[-1 - i, -1, 0, -1, -1, 0], link_coeffs=[-1.0], value=0.0) ] constraint = constraint + dof_const for i in range(0, self.n_elems_col): dof_const = [ BCSlice(var='u', dims=[0, 1, 2], slice=column[0, -1 - i, 0, 0, -1, 0], link_slice=column[-1, i, 0, -1, 0, 0], link_coeffs=[-1.0], value=0.0) ] constraint = constraint + dof_const return constraint #=============================================================================== # loading cases for mr_one only symmetric #=============================================================================== lc_g_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_lc_g_list(self): # slices roof = self.fe_grid_roof column = self.fe_grid_column upper_surf = roof[:, :, -1, :, :, -1] bottom_edge_roof = roof[:, 0, -1, :, 0, -1] left_edge_roof = roof[0, :, -1, 0, :, -1] # loads in global z- direction material_density_roof = -22.4e-3 # [MN/m^3] material_density_column = -26e-3 # [MN/m^3] additional_surface_load = -0.20e-3 # [MN/m^2] additional_t_constr = -0.02 * 22.4e-3 edge_load = -0.35e-3 # [MN/m] return [ BCSlice(var='f', value=material_density_roof, dims=[2], integ_domain='global', slice=roof[:, :, :, :, :, :]), BCSlice(var='f', value=material_density_column, dims=[2], integ_domain='global', slice=column[:, :, :, :, :, :]), ] # BCSlice( var = 'f', value = additional_surface_load + additional_t_constr, # dims = [2], integ_domain = 'global', # slice = upper_surf ), # BCSlice( var = 'f', value = edge_load, dims = [2], # integ_domain = 'global', # slice = bottom_edge_roof ), # BCSlice( var = 'f', value = edge_load, dims = [2], # integ_domain = 'global', # slice = left_edge_roof )] lc_s_list = Property(List, depends_on='+ps_levels, +input') @cached_property def _get_lc_s_list(self): # slices roof = self.fe_grid_roof upper_surf = roof[:, :, -1, :, :, -1] # loads in global z- direction snow_load = -0.85e-3 return [ BCSlice(var='f', value=snow_load, dims=[2], integ_domain='global', slice=upper_surf) ] lc_shrink_list = Property(List, depends_on='+ps_levels, +input') def _get_lc_shrink_list(self): self.initial_strain_roof = True self.initial_strain_col = True self.t_up = -100 self.t_lo = -100 #=============================================================================== # time loop #=============================================================================== tloop = Property(depends_on='+ps_levels, +input') @cached_property def _get_tloop(self): roof = self.fe_grid_roof column = self.fe_grid_column plate = self.fe_grid_plate ts = TS( sdomain=[roof, plate, column], dof_resultants=True, bcond_list= # boundary conditions # self.bc_roof_top_roof_low_link_list + self.bc_plate_column_link_list + self.bc_plate_roof_link_list + self.link_edge_list + self.bc_col_clamped_list + # loading # self.lc_g_list, rtrace_list=self.rtrace_list) # Add the time-loop control tloop = TLoop(tstepper=ts, tolerance=1e-4, tline=self.tline) self.edge_corner_1_dof = roof[0, 0, 0, 0, 0, 0].dofs self.edge_corner_2_dof = roof[-1, 0, -1, -1, 0, -1].dofs self.dof = roof[-1, 0, -1, -1, 0, -1].dofs[0][0][0] self.edge_roof_top = roof[:, -1, -1, :, -1, -1].dofs self.edge_roof_right = roof[-1, :, -1, -1, :, -1].dofs return tloop
class fiber_tt_5p(RF): ur''' Response function of a single fiber =================================== Response of a fiber loaded in tension can be described by a linear function with the domain bounded from left and right by the Heaviside terms. .. math:: q\left(\varepsilon; E,A,\theta,\lambda,\xi\right) = E A \frac{{\varepsilon - \theta \left( {1 + \lambda } \right)}}{{\left( {1 + \theta } \right) \left( {1 + \lambda } \right)}} H\left[ {e - \theta \left( {1 + \lambda } \right)} \right] H\left[ {\xi - \frac{{e - \theta \left( {1 + \lambda } \right)}} {{\left( {1 + \theta } \right)\left( {1 + \lambda } \right)}}} \right] where the variables :math:`A=` cross-sectional area, :math:`E=` Young's modulus, :math:`\theta=` filament activation strain, :math:`\lambda=` ratio of extra (stretched) filament length to the nominal length and :math:`\xi=` breaking strain are considered random and normally distributed. The function :math:`H(\eta)` represents the Heaviside function with values 0 for :math:`\eta < 0` and 1 for :math:`\eta > 0`. ''' implements(IRF) title = Str('brittle filament') def __call__(self, eps, lambd, xi, E_mod, theta, A): ''' Implements the response function with arrays as variables. first extract the variable discretizations from the orthogonal grid. ''' # NOTE: as each variable is an array oriented in different direction # the algebraic expressions (-+*/) perform broadcasting,. i.e. performing # the operation for all combinations of values. Thus, the resulgin eps # is contains the value of local strain for any combination of # global strain, xi, theta and lambda # eps_ = (eps - theta * (1 + lambd)) / ((1 + theta) * (1 + lambd)) # cut off all the negative strains due to delayed activation # eps_ *= Heaviside(eps_) # broadcast eps also in the xi - dimension # (by multiplying with array containing ones with the same shape as xi ) # eps_grid = eps_ * Heaviside(xi - eps_) # cut off all the realizations with strain greater than the critical one. # # eps_grid[ eps_grid >= xi ] = 0 # transform it to the force # q_grid = E_mod * A * eps_grid return q_grid cython_code = ''' eps_ = ( eps - theta * ( 1 + lambd ) ) / ( ( 1 + theta ) * ( 1 + lambd ) ) # Computation of the q( ... ) function if eps_ < 0 or eps_ > xi: q = 0.0 else: q = E_mod * A * eps_ ''' weave_code = '''
class RFBase(RF): implements(IRF) title = Str('RFSumParams') a = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) b = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) c = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) d = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) ee = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) f = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) g = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) h = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) i = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1) k = Float(1, auto_set=False, enter_set=True, distr=['uniform'], loc=1.0, scale=0.1, shape=0.1)
class ExpSH(ExType): '''Experiment: Shell Test ''' # label = Str('slab test') implements(IExType) #-------------------------------------------------------------------- # register a change of the traits with metadata 'input' #-------------------------------------------------------------------- input_change = Event @on_trait_change('+input, ccs.input_change, +ironing_param') def _set_input_change(self): self.input_change = True #-------------------------------------------------------------------------------- # specify inputs: #-------------------------------------------------------------------------------- edge_length = Float(1.25, unit = 'm', input = True, table_field = True, auto_set = False, enter_set = True) thickness = Float(0.03, unit = 'm', input = True, table_field = True, auto_set = False, enter_set = True) # age of the concrete at the time of testing age = Int(28, unit = 'd', input = True, table_field = True, auto_set = False, enter_set = True) loading_rate = Float(0.50, unit = 'mm/min', input = True, table_field = True, auto_set = False, enter_set = True) #-------------------------------------------------------------------------- # composite cross section #-------------------------------------------------------------------------- ccs = Instance(CompositeCrossSection) def _ccs_default(self): '''default settings correspond to setup '7u_MAG-07-03_PZ-0708-1' ''' fabric_layout_key = 'MAG-07-03' # fabric_layout_key = '2D-02-06a' concrete_mixture_key = 'PZ-0708-1' # concrete_mixture_key = 'FIL-10-09' # orientation_fn_key = 'all0' orientation_fn_key = '90_0' n_layers = 10 s_tex_z = 0.030 / (n_layers + 1) ccs = CompositeCrossSection ( fabric_layup_list = [ plain_concrete(s_tex_z * 0.5), FabricLayUp ( n_layers = n_layers, orientation_fn_key = orientation_fn_key, s_tex_z = s_tex_z, fabric_layout_key = fabric_layout_key ), plain_concrete(s_tex_z * 0.5) ], concrete_mixture_key = concrete_mixture_key ) return ccs #-------------------------------------------------------------------------- # Get properties of the composite #-------------------------------------------------------------------------- # E-modulus of the composite at the time of testing E_c = Property(Float, unit = 'MPa', depends_on = 'input_change', table_field = True) def _get_E_c(self): return self.ccs.get_E_c_time(self.age) # E-modulus of the composite after 28 days E_c28 = DelegatesTo('ccs', listenable = False) # reinforcement ration of the composite rho_c = DelegatesTo('ccs', listenable = False) #-------------------------------------------------------------------------------- # define processing #-------------------------------------------------------------------------------- # put this into the ironing procedure processor # jump_rtol = Float(1, auto_set = False, enter_set = True, ironing_param = True) data_array_ironed = Property(Array(float), depends_on = 'data_array, +ironing_param, +axis_selection') @cached_property def _get_data_array_ironed(self): '''remove the jumps in the displacement curves due to resetting the displacement gauges. ''' print '*** curve ironing activated ***' # each column from the data array corresponds to a measured parameter # e.g. displacement at a given point as function of time u = f(t)) # data_array_ironed = copy(self.data_array) for idx in range(self.data_array.shape[1]): # use ironing method only for columns of the displacement gauges. # if self.names_and_units[0][ idx ] != 'Kraft' and \ self.names_and_units[0][ idx ] != 'Bezugskanal' and \ self.names_and_units[0][ idx ] != 'D_1o'and \ self.names_and_units[0][ idx ] != 'D_2o'and \ self.names_and_units[0][ idx ] != 'D_3o'and \ self.names_and_units[0][ idx ] != 'D_4o'and \ self.names_and_units[0][ idx ] != 'D_1u'and \ self.names_and_units[0][ idx ] != 'D_2u'and \ self.names_and_units[0][ idx ] != 'D_3u'and \ self.names_and_units[0][ idx ] != 'D_4u'and \ self.names_and_units[0][ idx ] != 'W30_vo'and \ self.names_and_units[0][ idx ] != 'W30_hi': # 1d-array corresponding to column in data_array data_arr = copy(data_array_ironed[:, idx]) # get the difference between each point and its successor jump_arr = data_arr[1:] - data_arr[0:-1] # get the range of the measured data data_arr_range = max(data_arr) - min(data_arr) # determine the relevant criteria for a jump # based on the data range and the specified tolerances: jump_crit = self.jump_rtol * data_arr_range # get the indexes in 'data_column' after which a # jump exceeds the defined tolerance criteria jump_idx = where(fabs(jump_arr) > jump_crit)[0] print 'number of jumps removed in data_arr_ironed for', self.names_and_units[0][ idx ], ': ', jump_idx.shape[0] # glue the curve at each jump together for jidx in jump_idx: # get the offsets at each jump of the curve shift = data_arr[jidx + 1] - data_arr[jidx] # shift all succeeding values by the calculated offset data_arr[jidx + 1:] -= shift data_array_ironed[:, idx] = data_arr[:] return data_array_ironed @on_trait_change('+ironing_param') def process_source_data(self): '''read in the measured data from file and assign attributes after array processing. NOTE: if center displacement gauge ('WA_M') is missing the measured displacement of the cylinder ('Weg') is used instead. A minor mistake is made depending on how much time passes before the cylinder has contact with the slab. ''' print '*** process source data ***' self._read_data_array() # curve ironing: # self.processed_data_array = self.data_array_ironed # set attributes: # self._set_array_attribs() #-------------------------------------------------------------------------------- # plot templates #-------------------------------------------------------------------------------- plot_templates = {'force / time' : '_plot_force_time', 'force / deflections center' : '_plot_force_deflection_center', 'force / smoothed deflections center' : '_plot_smoothed_force_deflection_center', 'force / deflections' : '_plot_force_deflections', 'force / eps_t' : '_plot_force_eps_t', 'force / eps_c' : '_plot_force_eps_c', 'force / w_elastomer' : '_plot_force_w_elastomer', 'time / deflections' : '_plot_time_deflections' } default_plot_template = 'force / time' def _plot_force_w_elastomer(self, axes): xkey = 'displacement [mm]' ykey = 'force [kN]' W_vl = self.W_vl W_vr = self.W_vr W_hl = self.W_hl W_hr = self.W_hr force = self.Kraft axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) axes.plot(W_vl, force) axes.plot(W_vr, force) axes.plot(W_hl, force) axes.plot(W_hr, force) def _plot_force_time(self, axes): xkey = 'time [s]' ykey = 'force [kN]' xdata = self.Bezugskanal ydata = self.Kraft axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) axes.plot(xdata, ydata # color = c, linewidth = w, linestyle = s ) def _plot_force_deflection_center(self, axes): xkey = 'deflection [mm]' ykey = 'force [kN]' xdata = -self.WD4 ydata = self.Kraft axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) axes.plot(xdata, ydata # color = c, linewidth = w, linestyle = s ) n_fit_window_fraction = Float(0.1) def _plot_smoothed_force_deflection_center(self, axes): xkey = 'deflection [mm]' ykey = 'force [kN]' # get the index of the maximum stress max_force_idx = argmax(self.Kraft) # get only the ascending branch of the response curve f_asc = self.Kraft[:max_force_idx + 1] w_asc = -self.WD4[:max_force_idx + 1] f_max = f_asc[-1] w_max = w_asc[-1] n_points = int(self.n_fit_window_fraction * len(w_asc)) f_smooth = smooth(f_asc, n_points, 'flat') w_smooth = smooth(w_asc, n_points, 'flat') axes.plot(w_smooth, f_smooth, color = 'blue', linewidth = 2) secant_stiffness_w10 = (f_smooth[10] - f_smooth[0]) / (w_smooth[10] - w_smooth[0]) w0_lin = array([0.0, w_smooth[10] ], dtype = 'float_') f0_lin = array([0.0, w_smooth[10] * secant_stiffness_w10 ], dtype = 'float_') axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) def _plot_force_deflections(self, axes): xkey = 'displacement [mm]' ykey = 'force [kN]' # get the index of the maximum stress max_force_idx = argmax(self.Kraft) # get only the ascending branch of the response curve f_asc = self.Kraft[:max_force_idx + 1] WD1 = -self.WD1[:max_force_idx + 1] WD2 = -self.WD2[:max_force_idx + 1] WD3 = -self.WD3[:max_force_idx + 1] WD4 = -self.WD4[:max_force_idx + 1] WD5 = -self.WD5[:max_force_idx + 1] WD6 = -self.WD6[:max_force_idx + 1] WD7 = -self.WD7[:max_force_idx + 1] axes.plot(WD1, f_asc, color = 'blue', linewidth = 1 ) axes.plot(WD2, f_asc, color = 'green', linewidth = 1 ) axes.plot(WD3, f_asc, color = 'red', linewidth = 1 ) axes.plot(WD4, f_asc, color = 'black', linewidth = 3 ) axes.plot(WD5, f_asc, color = 'red', linewidth = 1 ) axes.plot(WD6, f_asc, color = 'green', linewidth = 1 ) axes.plot(WD7, f_asc, color = 'blue', linewidth = 1 ) axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) def _plot_time_deflections(self, axes): xkey = 'time [s]' ykey = 'deflections [mm]' time = self.Bezugskanal WD1 = -self.WD1 WD2 = -self.WD2 WD3 = -self.WD3 WD4 = -self.WD4 WD5 = -self.WD5 WD6 = -self.WD6 WD7 = -self.WD7 axes.plot(time, WD1, color = 'blue', linewidth = 1 ) axes.plot(time, WD2, color = 'green', linewidth = 1 ) axes.plot(time, WD3, color = 'red', linewidth = 1 ) axes.plot(time, WD4, color = 'black', linewidth = 3 ) axes.plot(time, WD5, color = 'red', linewidth = 1 ) axes.plot(time, WD6, color = 'green', linewidth = 1 ) axes.plot(time, WD7, color = 'blue', linewidth = 1 ) axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) def _plot_force_eps_c(self, axes): xkey = 'force [kN]' ykey = 'strain [mm/m]' # get the index of the maximum stress max_force_idx = argmax(self.Kraft) # get only the ascending branch of the response curve f_asc = self.Kraft[:max_force_idx + 1] D_1u = -self.D_1u[:max_force_idx + 1] D_2u = -self.D_2u[:max_force_idx + 1] D_3u = -self.D_3u[:max_force_idx + 1] D_4u = -self.D_4u[:max_force_idx + 1] D_1o = -self.D_1o[:max_force_idx + 1] D_2o = -self.D_2o[:max_force_idx + 1] D_3o = -self.D_3o[:max_force_idx + 1] D_4o = -self.D_4o[:max_force_idx + 1] axes.plot(f_asc, D_1u, color = 'blue', linewidth = 1 ) axes.plot(f_asc, D_2u, color = 'blue', linewidth = 1 ) axes.plot(f_asc, D_3u, color = 'blue', linewidth = 1 ) axes.plot(f_asc, D_4u, color = 'blue', linewidth = 1 ) axes.plot(f_asc, D_1o, color = 'red', linewidth = 1 ) axes.plot(f_asc, D_2o, color = 'red', linewidth = 1 ) axes.plot(f_asc, D_3o, color = 'red', linewidth = 1 ) axes.plot(f_asc, D_4o, color = 'red', linewidth = 1 ) axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) def _plot_force_eps_t(self, axes): xkey = 'strain [mm/m]' ykey = 'force [kN]' # get the index of the maximum stress max_force_idx = argmax(self.Kraft) # get only the ascending branch of the response curve f_asc = self.Kraft[:max_force_idx + 1] eps_vo = -self.W30_vo[:max_force_idx + 1] / 300. eps_hi = -self.W30_hi[:max_force_idx + 1] / 300. axes.plot(eps_vo, f_asc, color = 'blue', linewidth = 1 ) axes.plot(eps_hi, f_asc, color = 'green', linewidth = 1 ) axes.set_xlabel('%s' % (xkey,)) axes.set_ylabel('%s' % (ykey,)) #-------------------------------------------------------------------------------- # view #-------------------------------------------------------------------------------- traits_view = View(VGroup( Group( Item('jump_rtol', format_str = "%.4f"), label = 'curve_ironing' ), Group( Item('thickness', format_str = "%.3f"), Item('edge_length', format_str = "%.3f"), label = 'geometry' ), Group( Item('loading_rate'), Item('age'), label = 'loading rate and age' ), Group( Item('E_c', show_label = True, style = 'readonly', format_str = "%.0f"), Item('ccs@', show_label = False), label = 'composite cross section' ) ), scrollable = True, resizable = True, height = 0.8, width = 0.6 )
class CBEMClampedFiber(RF): ''' Crack bridged by a short fiber with constant frictional interface to the elastic matrix; clamped fiber end ''' implements(IRF) title = Str('crack bridge - clamped fiber with constant friction') xi = Float(0.0179, auto_set=False, enter_set=True, input=True, distr=['weibull_min', 'uniform']) tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) l = Float(0.0, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='free length') A_r = Float(0.89, auto_set=False, input=True, enter_set=True, distr=['uniform', 'weibull_min'], desc='CS area of a the reinforcement') E_r = Float(72.0e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) E_m = Float(30.0e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) A_m = Float(50.0, auto_set=False, enter_set=True, input=True, distr=['uniform']) theta = Float(0.01, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='slack') phi = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='bond quality') Ll = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - left') Lr = Float(.5, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - right') Nf = Float(1., auto_set=False, enter_set=True, input=True, desc='number of parallel fibers', distr=['uniform']) w = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) Kr = Property(depends_on='A_r, E_r') @cached_property def _get_Kr(self): return self.A_r * self.E_r Km = Property(depends_on='A_r, E_m') @cached_property def _get_Km(self): return self.A_m * self.E_m Kc = Property(depends_on='A_r, E_r, E_m') @cached_property def _get_Kc(self): return self.Kr + self.Km x_label = Str('crack opening [mm]') y_label = Str('force [N]') C_code = Str('') def crackbridge(self, w, l, T, Kr, Km, Lmin, Lmax): c = (2 * l * (Km + Kr) + Kr * (Lmin + Lmax)) P0 = (T * (Km + Kr)) / (2. * Km**2) * (sqrt(c**2 + 4 * w * Kr * Km**2 / T) - c) return P0 def pullout(self, u, l, T, Kr, Km, L): c = l * (Km + Kr) + L * Kr P1 = (T * (Km + Kr)) / Km**2 * (sqrt(c**2 + 2 * u * Kr * Km**2 / T) - c) return P1 def linel(self, u, l, T, Kr, Km, L): P2 = (T * L**2 + 2 * u * Kr) / 2. / (L + l) return P2 def __call__(self, w, tau, l, A_r, E_f, E_m, A_m, theta, xi, phi, Ll, Lr, Nf): # cross sectional area of a single fiber Lmin = minimum(Ll, Lr) Lmax = maximum(Ll, Lr) Lmin = maximum(Lmin - l / 2., 0) Lmax = maximum(Lmax - l / 2., 0) l = minimum(l / 2., Lr) + minimum(l / 2., Ll) l = l * (1 + theta) w = w - theta * l w = H(w) * w D = sqrt(A_r * Nf / pi) * 2 T = tau * phi * D * pi Km = A_m * E_m Kr = A_r * E_f # double sided debonding l0 = l / 2. q0 = self.crackbridge(w, l0, T, Kr, Km, Lmin, Lmax) # displacement at which the debonding to the closer clamp is finished # the closer distance is min(L1,L2) w0 = T * Lmin * ((2 * l0 + Lmin) * (Kr + Km) + Kr * Lmax) / (Km * Kr) # force at w0 Q0 = Lmin * T * (Km + Kr) / (Km) # print Q0 # debonding from one side; the other side is clamped # equal to a one sided pullout with embedded length Lmax - Lmin and free length 2*Lmin + l l1 = 2 * Lmin + l L1 = Lmax - Lmin q1 = self.pullout(w - w0, l1, T, Kr, Km, L1) + Q0 # debonding completed at both sides, response becomes linear elastic # displacement at which the debonding is finished at both sides w1 = (L1 * T * (Kr + Km) * (L1 + l1)) / Kr / Km - T * L1**2 / 2. / Kr # print 'alt w0', w0, 'alt w1', w1 , 'alt w0+w1', w0 + w1 q2 = self.linel(w - w0, l1, T, Kr, Km, L1) + Q0 # print self.linel(0, l1, T, Kr, Km, L1) # cut out definition ranges and add all parts q0 = H(w) * q0 * H(w0 - w) q1 = H(w - w0) * q1 * H(w1 + w0 - w) q2 = H(w - w1 - w0) * q2 q = q0 + q1 + q2 # include breaking strain q = q * H(Kr * xi - q) return q
class Filament(RF): '''Linear elastic, brittle filament. ''' implements(IRF) title = Str('brittle filament') xi = Float( 0.017857, auto_set=False, enter_set=True, distr=['weibull_min', 'uniform'], scale=0.0178, shape=4.0, ) theta = Float(0.01, auto_set=False, enter_set=True, distr=['uniform', 'norm'], loc=0.01, scale=0.001) lambd = Float(0.2, auto_set=False, enter_set=True, distr=['uniform'], loc=0.0, scale=0.1) A = Float(5.30929158457e-10, auto_set=False, enter_set=True, distr=['weibull_min', 'uniform', 'norm'], scale=5.3e-10, shape=8) E_mod = Float(70.0e9, auto_set=False, enter_set=True, distr=['weibull_min', 'uniform', 'norm'], scale=70e9, shape=8) eps = Float(ctrl_range=(0, 0.1, 100), auto_set=False, enter_set=True) C_code = ''' double eps_ = ( eps - theta * ( 1 + lambd ) ) / ( ( 1 + theta ) * ( 1 + lambd ) ); // Computation of the q( ... ) function if ( eps_ < 0 || eps_ > xi ){ q = 0.0; }else{ q = E_mod * A * eps_; } ''' def __call__(self, eps, xi, theta, lambd, A, E_mod): ''' Implements the response function with arrays as variables. first extract the variable discretizations from the orthogonal grid. ''' # NOTE: as each variable is an array oriented in different direction # the algebraic expressions (-+*/) perform broadcasting,. i.e. performing # the operation for all combinations of values. Thus, the resulgin eps # is contains the value of local strain for any combination of # global strain, xi, theta and lambda # eps_ = (eps - theta * (1 + lambd)) / ((1 + theta) * (1 + lambd)) # cut off all the negative strains due to delayed activation # eps_ *= Heaviside(eps_) # broadcast eps also in the xi - dimension # (by multiplying with array containing ones with the same shape as xi ) # eps_grid = eps_ * Heaviside(xi - eps_) # cut off all the realizations with strain greater than the critical one. # # eps_grid[ eps_grid >= xi ] = 0 # transform it to the force # q_grid = E_mod * A * eps_grid return q_grid
class CBClamped(RF): ''' Crack bridged by a fiber with constant frictional interface to rigid; free fiber end; ''' implements(IRF) title = Str('crack bridge with rigid matrix') tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) r = Float(0.013, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='fiber radius') E_f = Float(72e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) m = Float(5., auto_set=False, enter_set=True, input=True, distr=['uniform']) sV0 = Float(3.e-3, auto_set=False, enter_set=True, input=True, distr=['uniform']) V_f = Float(0.0175, auto_set=False, enter_set=True, input=True, distr=['uniform']) lm = Float(np.inf, auto_set=False, enter_set=True, input=True, distr=['uniform']) w = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) x_label = Str('crack opening [mm]') y_label = Str('composite stress [MPa]') C_code = Str('') def e_broken(self, Pf, depsf, r, m, sV0, mask): '''weibull_fibers_cdf_mc''' s_free = ((depsf * (m + 1.) * sV0**m) / (2. * pi * r**2.))**(1. / (m + 1.)) xi_free = s_free * (-np.log(1. - Pf))**(1. / (m + 1)) s_fixed = ((depsf * sV0**m) / (2. * pi * r**2.))**(1. / (m + 1.)) xi_fixed = s_fixed * (-np.log(1. - Pf))**(1. / (m + 1)) return xi_free, xi_fixed def __call__(self, w, tau, E_f, V_f, r, m, sV0, lm, Pf): '''free and fixed fibers combined''' T = 2. * tau / r k = np.sqrt(T / E_f) ef0cb = k * np.sqrt(w) ef0lin = w / lm + T * lm / 4. / E_f depsf = T / E_f a0 = ef0cb / depsf mask = a0 < lm / 2.0 e_int = ef0cb * mask + ef0lin * (mask == False) xi_free, xi_fixed = self.e_broken(Pf, depsf, r, m, sV0, mask) axi = xi_free / depsf mask_xi = axi < lm / 2. e_broken = xi_free / (m + 1.) * (mask_xi) + xi_fixed / 2. * (mask_xi == False) xi = xi_free * (mask_xi) + xi_fixed * (mask_xi == False) e = e_int * (e_int < xi) + e_broken * (e_int > xi) return e * E_f * V_f * r**2 def __call__2(self, w, tau, E_f, V_f, r, m, sV0, Pf): '''free debonding only = __call__ with lm=infty''' #strain and debonded length of intact fibers T = 2. * tau / r ef0_inf = np.sqrt(T * w / E_f) #scale parameter with respect to a reference volume s0 = ((T * (m + 1) * sV0**m) / (2. * E_f * pi * r**2))**(1. / (m + 1)) # strain at fiber breakage ef0_break = s0 * (-np.log(1. - Pf))**(1. / (m + 1)) # debonded length at fiber breakage a_break = ef0_break * E_f / T #mean pullout length of broken fibers mu_Lpo = a_break / (m + 1) # strain carried by broken fibers ef0_residual = T / E_f * mu_Lpo if self.include_pullout == True: ef0_tot = ef0_residual * H(ef0_inf - ef0_break) + ef0_inf * H( ef0_break - ef0_inf) else: ef0_tot = ef0_inf * H(ef0_break - ef0_inf) return ef0_tot * E_f * V_f * r**2
class MRquarter(MushRoofModel): implements(ISimModel) mushroof_part = 'quarter' n_elems_xy_quarter = Int(6, ps_levels=[3, 15, 5]) n_elems_z = Int(2, ps_levels=[1, 4, 2]) #---------------------------------------------------- # elements #---------------------------------------------------- vtk_r = Float(0.9) # default roof fe_roof = Instance( FETSEval, ps_levels=[ 'fe2d5_quad_serendipity', 'fe_quad_serendipity', 'fe_linear', 'fe_quad_lagrange' ], depends_on='+initial_strain_roof, +initial_strain_col, +vtk_r') def _fe_roof_default(self): fets = self.fe2d5_quad_serendipity # fets = self.fe_quad_serendipity fets.vtk_r *= self.vtk_r return fets #---------------------------------------------------- # grid and geometric transformation #---------------------------------------------------- fe_grid_roof = Property(Instance(FEGrid), depends_on='+ps_levels, +input') @cached_property def _get_fe_grid_roof(self): return FEGrid(coord_min=(0.0, 0.0, 0.0), coord_max=(1.0, 1.0, 1.0), geo_transform=self.hp_shell, shape=(self.n_elems_xy, self.n_elems_xy, self.n_elems_z), fets_eval=self.fe2d5_quad_serendipity) # fets_eval = self.fe_roof) # fets_eval = self.fe_quad_serendipity) mats_roof = Property(Instance(MATS2D5MicroplaneDamage), depends_on='+input') # mats_roof = Property( Instance( MATS3DElastic), depends_on = '+input' ) @cached_property def _get_mats_roof(self): # return MATS3DElastic(E=self.E_roof, nu=self.nu) return MATS2D5MicroplaneDamage(E=29100.0, nu=0.2, n_mp=30, symmetrization='sum-type', model_version='compliance', phi_fn=self.phi_fn) fe2d5_quad_serendipity = Property(Instance(FETSEval, transient=True), depends_on='+input') def _get_fe2d5_quad_serendipity(self): return FETS2D58H20U(mats_eval=self.mats_roof) fe_quad_serendipity = Property(Instance(FETSEval, transient=True), depends_on='+input') def _get_fe_quad_serendipity(self): return FETS3D8H20U(mats_eval=self.mats_roof) shrink_factor = Float(1.0) # shell # hp_shell = Property(Instance(HPShell), depends_on='+ps_levels, +input') @cached_property def _get_hp_shell(self): return HPShell(length_xy_quarter=self.length_xy_quarter / self.shrink_factor, length_z=self.length_z / self.shrink_factor, n_elems_xy_quarter=self.n_elems_xy_quarter, n_elems_z=self.n_elems_z, scalefactor_delta_h=self.scalefactor_delta_h, mushroof_part='quarter', shift_elems=False, X0=self.X0) #---------------------------------------------------- # ps_study #---------------------------------------------------- def peval(self): ''' Evaluate the model and return the array of results specified in the method get_sim_outputs. ''' U = self.tloop.eval() u_center_top_z = U[self.center_top_dof][0, 0, 2] print 'u_center_top_z', u_center_top_z return np.array([u_center_top_z], dtype='float_') # max_princ_stress = max(self.max_princ_stress._get_field_data().flatten()) # return np.array([ u_center_top_z, max_princ_stress ], # dtype = 'float_') def get_sim_outputs(self): ''' Specifies the results and their order returned by the model evaluation. ''' return [ SimOut(name='u_z_free_corner', unit='m'), SimOut(name='maximum principal stress', unit='MPa'), ] #---------------------------------------------------- # response tracer #---------------------------------------------------- rtrace_list = List def _rtrace_list_default(self): return [self.eps_app, self.sig_app, self.max_omega_i, self.phi_pdc] max_lambda = Float(1.0, input=True) '''Maximum lambda factor to impose on the structure. The final loading loading level is calculated as the reference boundary conditions multiplied by the lambda_factor and uls_factor. Thus, lambda factor defines the load level as a multiple of the load level predicted by the linear analysis. ''' f_w_diagram = Property(Instance(RTraceGraph), depends_on='+ps_levels, +input') @cached_property def _get_f_w_diagram(self): domain = self.fe_grid_roof w_z = domain[-1, -1, -1, -1, -1, -1].dofs[0, 0, 2] return RTraceGraph( name='load - corner deflection', var_x='U_k', idx_x=w_z, transform_x='-x', var_y='time', idx_y=0, # transform_y='y * %g' % self.lambda_factor, record_on='update') n_steps = Int(15.0, auto_set=False, enter_set=False, input=True) time_fn_load = Instance(MFnLineArray, input=True) def _time_fn_load_default(self): eta = 0.27 * 1.2 return MFnLineArray(xdata=[0.0, 1.0, 3.0, 5.0, 8.0, 15.0], ydata=[0.0, 1.0, 1.0 / eta, 1.78 / eta, 8.0, 9.05]) boundary_x1 = Property(depends_on='+input') @cached_property def _get_boundary_x1(self): return self.fe_grid_roof.domain[-1, :, -1, -1, :, -1] #---------------------------------------------------- # time loop #---------------------------------------------------- tloop = Property(depends_on='+ps_levels, +input') @cached_property def _get_tloop(self): domain = self.fe_grid_roof #---------------------------------------------------- # loading and boundaries #---------------------------------------------------- #--- LC1: dead load # g = 22.4 kN/m^3 # orientation: global z-direction; material_density_roof = -22.43e-3 # [MN/m^3] #--- LC2 additional dead load # gA = 0,20 kN/m^2 # orientation: global z-direction (following the curved structure); additional_dead_load = -0.20e-3 # [MN/m^2] #--- LC2 additional boundary load # gA = 0,35 kN/m^2 # orientation: global z-direction (following the curved structure); boundary_dead_load = -0.35e-3 # [MN/m] #--- LC3 snow # s = 0,79 kN/m^2 # orientation: global z-direction (projection); surface_load_s = -0.85e-3 # [MN/m^2] #--- LC4 wind (pressure) # w = 0,13 kN/m^2 # orientation: local t-direction (surface normal); surface_load_w = -0.13e-3 # [MN/m^2] # NOTE: additional line-loads at the edge of the roof need to be considered! upper_surface = domain[:, :, -1, :, :, -1] whole_domain = domain[:, :, :, :, :, :] boundary_x1 = domain[-1, :, -1, -1, :, -1] boundary_y1 = domain[:, -1, -1, :, -1, -1] time_fn_load = self.time_fn_load time_fn_permanent_load = MFnLineArray(xdata=[0.0, 1.0], ydata=[0.0, 1.0]) time_fn_snow_load = MFnLineArray(xdata=[0.0, 1.0], ydata=[0.0, 0.0]) force_bc = [ # own weight BCSlice(name='self weight', var='f', value=material_density_roof, dims=[2], integ_domain='global', time_function=time_fn_load.get_value, slice=whole_domain), # LC2: additional dead-load BCSlice(name='additional load', var='f', value=additional_dead_load, dims=[2], integ_domain='global', time_function=time_fn_load.get_value, slice=upper_surface), # LC2: additional boundary-load BCSlice(name='additional boundary load 1', var='f', value=boundary_dead_load, dims=[2], integ_domain='global', time_function=time_fn_load.get_value, slice=boundary_x1), # LC2: additional boundary-load BCSlice(name='additional boundary load 2', var='f', value=boundary_dead_load, dims=[2], integ_domain='global', time_function=time_fn_load.get_value, slice=boundary_y1), # LC3: snow load BCSlice(name='snow load', var='f', value=surface_load_s, dims=[2], integ_domain='global', time_function=time_fn_snow_load.get_value, slice=upper_surface), # # LC3: wind # BCSlice( var = 'f', value = surface_load_w, dims = [2], # integ_domain = 'global', # slice = upper_surface ) ] bc_symplane_yz = BCSlice(var='u', value=0., dims=[0], slice=domain[0, :, :, 0, :, :]) bc_symplane_xz = BCSlice(var='u', value=0., dims=[1], slice=domain[:, 0, :, :, 0, :]) bc_support_000 = BCSlice(var='u', value=0., dims=[2], slice=domain[0, 0, 0, :, :, 0]) # bc_column = [ # BCSlice( var = 'u' , dims = [0, 1, 2], # slice = domain[self.n_elems_xy_quarter - 1, # self.n_elems_xy_quarter - 1, # 0, # 0, -1, 0 ], # value = 0. ), # BCSlice( var = 'u' , dims = [0, 1, 2], # slice = domain[self.n_elems_xy_quarter - 1, # self.n_elems_xy_quarter - 1 , # 0, # - 1, 0, 0], # value = 0. )] # bc_corner_load = BCSlice( var = 'f', value = -nodal_load, dims = [2], slice = domain[-1,-1,-1,-1,-1,-1] ) # bc_topface_load = BCSlice( var = 'f', value = -nodal_load, dims = [2], slice = domain[:,:,-1,:,:,-1] ) # support_z_dofs = domain[0, 0, 0, :, : , 0].dofs[:, :, 2] # support_f_w = RTraceGraph(name='force - corner deflection', # var_x='time', idx_x=0, # transform_x='x * %g' % lambda_failure, # var_y='F_int', idx_y_arr=np.unique(support_z_dofs.flatten()), # transform_y='-y', # record_on='update') rtrace_list = [self.f_w_diagram] + self.rtrace_list ts = TS(sdomain=[domain], dof_resultants=True, bcond_list=[bc_symplane_yz, bc_symplane_xz, bc_support_000] + force_bc, rtrace_list=rtrace_list) step = 1.0 # self.n_steps # Add the time-loop control tloop = TLoop(tstepper=ts, RESETMAX=0, KMAX=70, tolerance=0.5e-3, tline=TLine(min=0.0, step=step, max=1.0)) # self.max_lambda)) return tloop
class CBRandXi(RF): ''' Crack bridged by a fiber with constant frictional interface to rigid; free fiber end; ''' implements(IRF) title = Str('crack bridge with rigid matrix') tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) r = Float(0.013, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='fiber radius') E_f = Float(72e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) m = Float(5., auto_set=False, enter_set=True, input=True, distr=['uniform']) sV0 = Float(3.e-3, auto_set=False, enter_set=True, input=True, distr=['uniform']) V_f = Float(0.0175, auto_set=False, enter_set=True, input=True, distr=['uniform']) w = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) include_pullout = Bool(True) x_label = Str('crack opening [mm]') y_label = Str('composite stress [MPa]') C_code = Str('') def __call__(self, w, tau, E_f, V_f, r, m, sV0): #strain and debonded length of intact fibers T = 2. * tau / r ef0_inf = np.sqrt(T * w / E_f) #scale parameter with respect to a reference volume s0 = ((T * (m + 1) * sV0**m) / (2. * E_f * pi * r**2))**(1. / (m + 1)) k = np.sqrt(T / E_f) ef0 = k * np.sqrt(w) G = 1 - np.exp(-(ef0 / s0)**(m + 1)) mu_int = ef0 * E_f * V_f * (1 - G) I = s0 * gamma(1 + 1. / (m + 1)) * gammainc(1 + 1. / (m + 1), (ef0 / s0)**(m + 1)) mu_broken = E_f * V_f * I / (m + 1) res = mu_int + mu_broken return res * r**2
class ExpBT3PT(ExType): '''Experiment: Bending Test Three Point ''' # label = Str('three point bending test') implements(IExType) file_ext = 'raw' #-------------------------------------------------------------------- # register a change of the traits with metadata 'input' #-------------------------------------------------------------------- input_change = Event @on_trait_change('+input, ccs.input_change, +ironing_param') def _set_input_change(self): self.input_change = True #-------------------------------------------------------------------------------- # specify inputs: #-------------------------------------------------------------------------------- length = Float(0.46, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) width = Float(0.1, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) thickness = Float(0.02, unit='m', input=True, table_field=True, auto_set=False, enter_set=True) # age of the concrete at the time of testing age = Int(33, unit='d', input=True, table_field=True, auto_set=False, enter_set=True) loading_rate = Float(4.0, unit='mm/min', input=True, table_field=True, auto_set=False, enter_set=True) #-------------------------------------------------------------------------- # composite cross section #-------------------------------------------------------------------------- ccs = Instance(CompositeCrossSection) def _ccs_default(self): '''default settings ''' # fabric_layout_key = 'MAG-07-03' # fabric_layout_key = '2D-02-06a' fabric_layout_key = '2D-05-11' # fabric_layout_key = '2D-09-12' # concrete_mixture_key = 'PZ-0708-1' # concrete_mixture_key = 'FIL-10-09' concrete_mixture_key = 'barrelshell' orientation_fn_key = 'all0' # orientation_fn_key = 'all90' # orientation_fn_key = '90_0' n_layers = 6 s_tex_z = 0.020 / (n_layers + 1) ccs = CompositeCrossSection(fabric_layup_list=[ plain_concrete(s_tex_z * 0.5), FabricLayUp(n_layers=n_layers, orientation_fn_key=orientation_fn_key, s_tex_z=s_tex_z, fabric_layout_key=fabric_layout_key), plain_concrete(s_tex_z * 0.5) ], concrete_mixture_key=concrete_mixture_key) return ccs #-------------------------------------------------------------------------- # Get properties of the composite #-------------------------------------------------------------------------- # E-modulus of the composite at the time of testing E_c = Property(Float, unit='MPa', depends_on='input_change', table_field=True) def _get_E_c(self): return self.ccs.get_E_c_time(self.age) # E-modulus of the composite after 28 days E_c28 = DelegatesTo('ccs', listenable=False) # reinforcement ration of the composite rho_c = DelegatesTo('ccs', listenable=False) #-------------------------------------------------------------------------------- # define processing #-------------------------------------------------------------------------------- # flag distinguishes weather data from a displacement gauge is available # stored in a separate ASC-file with a corresponding file name # flag_ASC_file = Bool(False) def _read_data_array(self): ''' Read the experiment data. ''' if exists(self.data_file): print 'READ FILE' file_split = self.data_file.split('.') # first check if a '.csv' file exists. If yes use the # data stored in the '.csv'-file and ignore # the data in the '.raw' file! # file_name = file_split[0] + '.csv' if not os.path.exists(file_name): file_name = file_split[0] + '.raw' if not os.path.exists(file_name): raise IOError, 'file %s does not exist' % file_name print 'file_name', file_name _data_array = loadtxt_bending(file_name) self.data_array = _data_array # check if a '.ASC'-file exists. If yes append this information # to the data array. # file_name = file_split[0] + '.ASC' if not os.path.exists(file_name): print 'NOTE: no data from displacement gauge is available (no .ASC file)' self.flag_ASC_file = False else: print 'NOTE: additional data from displacement gauge for center deflection is available (.ASC-file loaded)!' self.flag_ASC_file = True # add data array read in from .ASC-file; the values are assigned by '_set_array_attribs' based on the # read in values in 'names_and_units' read in from the corresponding .DAT-file # self.data_array_ASC = loadtxt(file_name, delimiter=';') else: print 'WARNING: data_file with path %s does not exist == False' % ( self.data_file) names_and_units = Property(depends_on='data_file') @cached_property def _get_names_and_units(self): '''names and units corresponding to the returned '_data_array' by 'loadtxt_bending' ''' names = ['w_raw', 'eps_c_raw', 'F_raw'] units = ['mm', '1*E-3', 'N'] print 'names, units from .raw-file', names, units return names, units names_and_units_ASC = Property(depends_on='data_file') @cached_property def _get_names_and_units_ASC(self): ''' Extract the names and units of the measured data. The order of the names in the .DAT-file corresponds to the order of the .ASC-file. ''' file_split = self.data_file.split('.') file_name = file_split[0] + '.DAT' data_file = open(file_name, 'r') lines = data_file.read().split() names = [] units = [] for i in range(len(lines)): if lines[i] == '#BEGINCHANNELHEADER': name = lines[i + 1].split(',')[1] unit = lines[i + 3].split(',')[1] names.append(name) units.append(unit) print 'names, units extracted from .DAT-file', names, units return names, units factor_list_ASC = Property(depends_on='data_file') def _get_factor_list_ASC(self): return self.names_and_units_ASC[0] def _set_array_attribs(self): '''Set the measured data as named attributes defining slices into the processed data array. ''' for i, factor in enumerate(self.factor_list): self.add_trait( factor, Array(value=self.processed_data_array[:, i], transient=True)) if self.flag_ASC_file: for i, factor in enumerate(self.factor_list_ASC): self.add_trait( factor, Array(value=self.data_array_ASC[:, i], transient=True)) elastomer_law = Property(depends_on='input_change') @cached_property def _get_elastomer_law(self): elastomer_path = os.path.join(simdb.exdata_dir, 'bending_tests', 'three_point', '2011-06-10_BT-3PT-12c-6cm-0-TU_ZiE', 'elastomer_f-w.raw') _data_array_elastomer = loadtxt_bending(elastomer_path) # force [kN]: # NOTE: after conversion 'F_elastomer' is a positive value # F_elastomer = -0.001 * _data_array_elastomer[:, 2].flatten() # displacement [mm]: # NOTE: after conversion 'w_elastomer' is a positive value # w_elastomer = -1.0 * _data_array_elastomer[:, 0].flatten() mfn_displacement_elastomer = MFnLineArray(xdata=F_elastomer, ydata=w_elastomer) return frompyfunc(mfn_displacement_elastomer.get_value, 1, 1) w_wo_elast = Property(depends_on='input_change') @cached_property def _get_w_wo_elast(self): # use the machine displacement for the center displacement: # subtract the deformation of the elastomer cushion between the cylinder # and change sign in positive values for vertical displacement [mm] # return self.w_raw - self.elastomer_law(self.F_raw) M_ASC = Property(Array('float_'), depends_on='input_change') @cached_property def _get_M_ASC(self): return self.F_ASC * self.length / 4.0 M_raw = Property(Array('float_'), depends_on='input_change') @cached_property def _get_M_raw(self): return self.F_raw * self.length / 4.0 # # get only the ascending branch of the response curve # # # max_force_idx = Property(Int) # def _get_max_force_idx(self): # '''get the index of the maximum force''' # return argmax(-self.Kraft) # # f_asc = Property(Array) # def _get_f_asc(self): # '''get only the ascending branch of the response curve''' # return -self.Kraft[:self.max_force_idx + 1] K_bending_elast = Property(Array('float_'), depends_on='input_change') @cached_property def _get_K_bending_elast(self): '''calculate the analytical bending stiffness of the beam (3 point bending) ''' t = self.thickness w = self.width L = self.length # coposite E-modulus # E_c = self.E_c # moment of inertia # I_yy = t**3 * w / 12. delta_11 = (L**3) / 48 / E_c / I_yy # [MN/m]=[kN/mm] bending stiffness with respect to a force applied at center of the beam # K_bending_elast = 1 / delta_11 # print 'K_bending_elast', K_bending_elast return K_bending_elast F_cr = Property(Array('float_'), depends_on='input_change') @cached_property def _get_F_cr(self): '''calculate the analytical cracking load of the beam ''' t = self.thickness w = self.width L = self.length # approx. flectural tensile strength # f_cfl = 6. # MPa # resistant moment # W_yy = t**2 * w / 6. # analytical cracking load of the beam # corresponds to l = 0.46m and f_cfl = approx. 8.4 MPa# # F_cr = W_yy * f_cfl * 1000. / L # [kN] return F_cr def process_source_data(self): '''read in the measured data from file and assign attributes after array processing. ''' super(ExpBT3PT, self).process_source_data() #--------------------------------------------- # process data from .raw file (machine data) #--------------------------------------------- # convert machine force [N] to [kN] and return only positive values # self.F_raw *= -0.001 # convert machine displacement [mm] to positive values # and remove offset # self.w_raw *= -1.0 self.w_raw -= self.w_raw[0] # convert [permille] to [-] and return only positive values # self.eps_c_raw *= -0.001 # access the derived arrays to initiate their processing # self.w_wo_elast self.M_raw #--------------------------------------------- # process data from .ASC file (displacement gauge) #--------------------------------------------- # only if separate ASC.-file with force-displacement data from displacement gauge is available # if self.flag_ASC_file == True: self.F_ASC = -1.0 * self.Kraft # remove offset and change sign to return positive displacement values # if hasattr(self, "WA50"): self.WA50 *= -1 self.WA50 -= self.WA50[0] WA50_avg = np.average(self.WA50) if hasattr(self, "W10_u"): self.W10_u *= -1 self.W10_u -= self.W10_u[0] W10_u_avg = np.average(self.W10_u) # check which displacement gauge has been used depending on weather two names are listed in .DAT file or only one # and assign values to 'w_ASC' # if hasattr(self, "W10_u") and hasattr(self, "WA50"): if W10_u_avg > WA50_avg: self.w_ASC = self.W10_u print 'self.W10_u assigned to self.w_ASC' else: self.w_ASC = self.WA50 print 'self.WA50 assigned to self.w_ASC' elif hasattr(self, "W10_u"): self.w_ASC = self.W10_u print 'self.W10_u assigned to self.w_ASC' elif hasattr(self, "WA50"): self.w_ASC = self.WA50 print 'self.WA50 assigned to self.w_ASC' # convert strain from [permille] to [-], # switch to positive values for compressive strains # and remove offset # self.eps_c_ASC = -0.001 * self.DMS_l self.eps_c_ASC -= self.eps_c_ASC[0] # access the derived arrays to initiate their processing # self.M_ASC #-------------------------------------------------------------------------------- # plot templates #-------------------------------------------------------------------------------- plot_templates = { 'force / machine displacement (incl. w_elast)': '_plot_force_machine_displacement', 'force / machine displacement (without w_elast)': '_plot_force_machine_displacement_wo_elast', 'force / machine displacement (without w_elast, interpolated)': '_plot_force_machine_displacement_wo_elast_interpolated', 'force / machine displacement (analytical offset)': '_plot_force_machine_displacement_wo_elast_analytical_offset', 'force / gauge displacement': '_plot_force_gauge_displacement', 'force / gauge displacement (analytical offset)': '_plot_force_gauge_displacement_with_analytical_offset', 'force / gauge displacement (interpolated)': '_plot_force_gauge_displacement_interpolated', # 'smoothed force / gauge displacement' : '_plot_smoothed_force_gauge_displacement', # 'smoothed force / machine displacement' : '_plot_smoothed_force_machine_displacement_wo_elast', # 'moment / eps_c (ASC)': '_plot_moment_eps_c_ASC', 'moment / eps_c (raw)': '_plot_moment_eps_c_raw', # # 'smoothed moment / eps_c (ASC)' : '_plot_smoothed_moment_eps_c_ASC', # 'smoothed moment / eps_c (raw)' : '_plot_smoothed_moment_eps_c_raw', # # 'analytical bending stiffness' : '_plot_analytical_bending_stiffness' } default_plot_template = 'force / deflection (displacement gauge)' def _plot_analytical_bending_stiffness(self, axes, color='red', linewidth=1., linestyle='--'): '''plot the analytical bending stiffness of the beam (3 point bending) ''' t = self.thickness w = self.width L = self.length # composite E-modulus # E_c = self.E_c # moment of inertia # I_yy = t**3 * w / 12. delta_11 = L**3 / 48 / E_c / I_yy K_linear = 1 / delta_11 # [MN/m] bending stiffness with respect to a force applied at center of the beam w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., K_linear]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_force_machine_displacement_wo_elast(self, axes, color='blue', linewidth=1., linestyle='-'): # get the index of the maximum stress # max_force_idx = argmax(self.F_raw) # get only the ascending branch of the response curve # f_asc = self.F_raw[:max_force_idx + 1] w_asc = self.w_wo_elast[:max_force_idx + 1] axes.plot(w_asc, f_asc, color=color, linewidth=linewidth, linestyle=linestyle) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') # xkey = 'deflection [mm]' # ykey = 'force [kN]' # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) def _plot_force_machine_displacement_wo_elast_interpolated( self, axes, color='green', linewidth=1., linestyle='-'): # get the index of the maximum stress # max_force_idx = argmax(self.F_raw) # get only the ascending branch of the response curve # f_asc = self.F_raw[:max_force_idx + 1] w_asc = np.copy(self.w_wo_elast[:max_force_idx + 1]) # interpolate the starting point of the center deflection curve based on the slope of the curve # (remove offset in measured displacement where there is still no force measured) # idx_10 = np.where(f_asc > f_asc[-1] * 0.10)[0][0] idx_8 = np.where(f_asc > f_asc[-1] * 0.08)[0][0] f8 = f_asc[idx_8] f10 = f_asc[idx_10] w8 = w_asc[idx_8] w10 = w_asc[idx_10] m = (f10 - f8) / (w10 - w8) delta_w = f8 / m w0 = w8 - delta_w * 0.9 # print 'w0', w0 f_asc_interpolated = np.hstack([0., f_asc[idx_8:]]) w_asc_interpolated = np.hstack([w0, w_asc[idx_8:]]) # print 'type( w_asc_interpolated )', type(w_asc_interpolated) w_asc_interpolated -= float(w0) axes.plot(w_asc_interpolated, f_asc_interpolated, color=color, linewidth=linewidth, linestyle=linestyle) # fw_arr = np.hstack([f_asc_interpolated[:, None], w_asc_interpolated[:, None]]) # print 'fw_arr.shape', fw_arr.shape # np.savetxt('BT-3PT-12c-6cm-TU_f-w_interpolated.csv', fw_arr, delimiter=';') # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_force_machine_displacement_wo_elast_analytical_offset( self, axes, color='green', linewidth=1., linestyle='-'): # get the index of the maximum stress # max_force_idx = argmax(self.F_raw) # get only the ascending branch of the response curve # f_asc = self.F_raw[:max_force_idx + 1] w_asc = np.copy(self.w_wo_elast[:max_force_idx + 1]) M_asc = f_asc * self.length / 4. eps_c_asc = self.eps_c_raw[:max_force_idx + 1] t = self.thickness w = self.width # coposite E-modulus # E_c = self.E_c # resistant moment # W_yy = t**2 * w / 6. K_I_analytic = W_yy * E_c # [MN/m] bending stiffness with respect to center moment K_I_analytic *= 1000. # [kN/m] bending stiffness with respect to center moment # interpolate the starting point of the center deflection curve based on the slope of the curve # (remove offset in measured displacement where there is still no force measured) # idx_lin = np.where(M_asc <= K_I_analytic * eps_c_asc)[0][0] idx_lin = int(idx_lin * 0.7) # idx_lin = 50 # idx_lin = np.where(M_asc - M_asc[0] / eps_c_asc <= 0.90 * K_I_analytic)[0][0] print 'idx_lin', idx_lin print 'F_asc[idx_lin]', f_asc[idx_lin] print 'M_asc[idx_lin]', M_asc[idx_lin] print 'w_asc[idx_lin]', w_asc[idx_lin] w_lin_epsc = w_asc[idx_lin] w_lin_analytic = f_asc[idx_lin] / self.K_bending_elast f_asc_offset_analytic = f_asc[idx_lin:] w_asc_offset_analytic = w_asc[idx_lin:] w_asc_offset_analytic -= np.array([w_lin_epsc]) w_asc_offset_analytic += np.array([w_lin_analytic]) axes.plot(w_asc_offset_analytic, f_asc_offset_analytic, color=color, linewidth=linewidth, linestyle=linestyle) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_force_machine_displacement(self, axes, color='black', linewidth=1., linestyle='-'): xdata = self.w_raw ydata = self.F_raw axes.plot(xdata, ydata, color=color, linewidth=linewidth, linestyle=linestyle) # xkey = 'deflection [mm]' # ykey = 'force [kN]' # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_force_gauge_displacement_with_analytical_offset( self, axes, color='black', linewidth=1., linestyle='-'): # skip the first values (= first seconds of testing) # and start with the analytical bending stiffness instead to avoid artificial offset of F-w-diagram # # w_max = np.max(self.w_ASC) # cut_idx = np.where(self.w_ASC > 0.001 * w_max)[0] cut_idx = np.where(self.w_ASC > 0.01)[0] print 'F_cr ', self.F_cr # cut_idx = np.where(self.F_ASC > 0.6 * self.F_cr)[0] print 'cut_idx', cut_idx[0] print 'w_ASC[cut_idx[0]]', self.w_ASC[cut_idx[0]] xdata = np.copy(self.w_ASC[cut_idx]) ydata = np.copy(self.F_ASC[cut_idx]) # specify offset if force does not start at the origin with value 0. F_0 = ydata[0] print 'F_0 ', F_0 offset_w = F_0 / self.K_bending_elast xdata -= xdata[0] xdata += offset_w f_asc_interpolated = np.hstack([0, ydata]) w_asc_interpolated = np.hstack([0, xdata]) # fw_arr = np.hstack([f_asc_interpolated[:, None], w_asc_interpolated[:, None]]) # print 'fw_arr.shape', fw_arr.shape # np.savetxt('BT-3PT-6c-2cm-TU_f-w_interpolated.csv', fw_arr, delimiter=';') xdata = self.w_raw ydata = self.F_raw axes.plot(xdata, ydata, color='blue', linewidth=linewidth, linestyle=linestyle) axes.plot(w_asc_interpolated, f_asc_interpolated, color=color, linewidth=linewidth, linestyle=linestyle) # xkey = 'deflection [mm]' # ykey = 'force [kN]' # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_force_gauge_displacement(self, axes, offset_w=0., color='black', linewidth=1., linestyle='-'): xdata = self.w_ASC ydata = self.F_ASC # specify offset if force does not start at the origin xdata += offset_w axes.plot(xdata, ydata, color=color, linewidth=linewidth, linestyle=linestyle) # xkey = 'deflection [mm]' # ykey = 'force [kN]' # axes.set_xlabel('%s' % (xkey,)) # axes.set_ylabel('%s' % (ykey,)) # fw_arr = np.hstack([xdata[:, None], ydata[:, None]]) # print 'fw_arr.shape', fw_arr.shape # np.savetxt('BT-3PT-6c-2cm-TU-80cm-V3_f-w_asc.csv', fw_arr, delimiter=';') # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_smoothed_force_gauge_displacement(self, axes): # get the index of the maximum stress # max_force_idx = argmax(self.F_ASC) # get only the ascending branch of the response curve # F_asc = self.F_ASC[:max_force_idx + 1] w_asc = self.w_ASC[:max_force_idx + 1] n_points = int(self.n_fit_window_fraction * len(w_asc)) F_smooth = smooth(F_asc, n_points, 'flat') w_smooth = smooth(w_asc, n_points, 'flat') axes.plot(w_smooth, F_smooth, color='blue', linewidth=2) def _plot_force_gauge_displacement_interpolated(self, axes, color='green', linewidth=1., linestyle='-'): '''get only the ascending branch of the meassured load-displacement curve)''' # get the index of the maximum stress # max_force_idx = argmax(self.F_ASC) # get only the ascending branch of the response curve # f_asc = self.F_ASC[:max_force_idx + 1] w_asc = self.w_ASC[:max_force_idx + 1] # interpolate the starting point of the center deflection curve based on the slope of the curve # (remove offset in measured displacement where there is still no force measured) # idx_10 = np.where(f_asc > f_asc[-1] * 0.10)[0][0] idx_8 = np.where(f_asc > f_asc[-1] * 0.08)[0][0] f8 = f_asc[idx_8] f10 = f_asc[idx_10] w8 = w_asc[idx_8] w10 = w_asc[idx_10] m = (f10 - f8) / (w10 - w8) delta_w = f8 / m w0 = w8 - delta_w * 0.9 print 'w0', w0 f_asc_interpolated = np.hstack([0., f_asc[idx_8:]]) w_asc_interpolated = np.hstack([w0, w_asc[idx_8:]]) print 'type( w_asc_interpolated )', type(w_asc_interpolated) w_asc_interpolated -= float(w0) # w_offset = f_asc[idx_10] / self.K_bending_elast # f_asc_interpolated = np.hstack([0., f_asc[ idx_10: ]]) # w_asc_interpolated = np.hstack([0, w_asc[ idx_10: ] - w_asc[idx_10] + w_offset]) axes.plot(w_asc_interpolated, f_asc_interpolated, color=color, linewidth=linewidth, linestyle=linestyle) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') def _plot_smoothed_force_machine_displacement_wo_elast(self, axes): # get the index of the maximum stress # max_force_idx = argmax(self.F_raw) # get only the ascending branch of the response curve # F_asc = self.F_raw[:max_force_idx + 1] w_asc = self.w_wo_elast[:max_force_idx + 1] n_points = int(self.n_fit_window_fraction * len(w_asc)) F_smooth = smooth(F_asc, n_points, 'flat') w_smooth = smooth(w_asc, n_points, 'flat') axes.plot(w_smooth, F_smooth, color='blue', linewidth=2) # plot analytical bending stiffness # w_linear = 2 * np.array([0., 1.]) F_linear = 2 * np.array([0., self.K_bending_elast]) axes.plot(w_linear, F_linear, linestyle='--') # secant_stiffness_w10 = ( f_smooth[10] - f_smooth[0] ) / ( w_smooth[10] - w_smooth[0] ) # w0_lin = array( [0.0, w_smooth[10] ], dtype = 'float_' ) # f0_lin = array( [0.0, w_smooth[10] * secant_stiffness_w10 ], dtype = 'float_' ) # axes.plot( w0_lin, f0_lin, color = 'black' ) def _plot_moment_eps_c_ASC(self, axes): xkey = 'compressive strain [1*E-3]' ykey = 'moment [kNm]' xdata = self.eps_c_ASC ydata = self.M_ASC axes.set_xlabel('%s' % (xkey, )) axes.set_ylabel('%s' % (ykey, )) axes.plot(xdata, ydata) # plot stiffness in uncracked state t = self.thickness w = self.width # composite E-modulus # E_c = self.E_c # resistant moment # W_yy = t**2 * w / 6. max_M = np.max(self.M_raw) K_linear = W_yy * E_c # [MN/m] bending stiffness with respect to center moment K_linear *= 1000. # [kN/m] bending stiffness with respect to center moment w_linear = np.array([0., max_M / K_linear]) M_linear = np.array([0., max_M]) axes.plot(w_linear, M_linear, linestyle='--') def _plot_moment_eps_c_raw(self, axes, color='black', linewidth=1.5, linestyle='-'): xkey = 'compressive strain [1*E-3]' ykey = 'moment [kNm]' xdata = self.eps_c_raw ydata = self.M_raw axes.set_xlabel('%s' % (xkey, )) axes.set_ylabel('%s' % (ykey, )) axes.plot(xdata, ydata, color=color, linewidth=linewidth, linestyle=linestyle) # plot stiffness in uncracked state t = self.thickness w = self.width # composite E-modulus # E_c = self.E_c # resistant moment # W_yy = t**2 * w / 6. max_M = np.max(self.M_raw) K_linear = W_yy * E_c # [MN/m] bending stiffness with respect to center moment K_linear *= 1000. # [kN/m] bending stiffness with respect to center moment w_linear = np.array([0., max_M / K_linear]) M_linear = np.array([0., max_M]) axes.plot(w_linear, M_linear, linestyle='--') n_fit_window_fraction = Float(0.1) smoothed_M_eps_c_ASC = Property(depends_on='input_change') @cached_property def _get_smoothed_M_eps_c_ASC(self): # get the index of the maximum stress max_idx = argmax(self.M_ASC) # get only the ascending branch of the response curve m_asc = self.M_ASC[:max_idx + 1] eps_c_asc = self.eps_c_ASC[:max_idx + 1] n_points = int(self.n_fit_window_fraction * len(eps_c_asc)) m_smoothed = smooth(m_asc, n_points, 'flat') eps_c_smoothed = smooth(eps_c_asc, n_points, 'flat') return m_smoothed, eps_c_smoothed smoothed_eps_c_ASC = Property def _get_smoothed_eps_c_ASC(self): return self.smoothed_M_eps_c_ASC[1] smoothed_M_ASC = Property def _get_smoothed_M_ASC(self): return self.smoothed_M_eps_c_ASC[0] def _plot_smoothed_moment_eps_c_ASC(self, axes): axes.plot(self.smoothed_eps_c_ASC, self.smoothed_M_ASC, color='blue', linewidth=2) smoothed_M_eps_c_raw = Property(depends_on='input_change') @cached_property def _get_smoothed_M_eps_c_raw(self): # get the index of the maximum stress max_idx = argmax(self.M_raw) # get only the ascending branch of the response curve m_asc = self.M_raw[:max_idx + 1] eps_c_asc = self.eps_c_raw[:max_idx + 1] n_points = int(self.n_fit_window_fraction * len(eps_c_asc)) m_smoothed = smooth(m_asc, n_points, 'flat') eps_c_smoothed = smooth(eps_c_asc, n_points, 'flat') return m_smoothed, eps_c_smoothed smoothed_eps_c_raw = Property def _get_smoothed_eps_c_raw(self): return self.smoothed_M_eps_c_raw[1] smoothed_M_raw = Property def _get_smoothed_M_raw(self): return self.smoothed_M_eps_c_raw[0] def _plot_smoothed_moment_eps_c_raw(self, axes): axes.plot(self.smoothed_eps_c_raw, self.smoothed_M_raw, color='blue', linewidth=2) #-------------------------------------------------------------------------------- # view #-------------------------------------------------------------------------------- traits_view = View(VGroup( Group(Item('length', format_str="%.3f"), Item('width', format_str="%.3f"), Item('thickness', format_str="%.3f"), label='geometry'), Group(Item('loading_rate'), Item('age'), label='loading rate and age'), Group(Item('E_c', show_label=True, style='readonly', format_str="%.0f"), Item('ccs@', show_label=False), label='composite cross section')), scrollable=True, resizable=True, height=0.8, width=0.6)
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 CBEMClampedFiberStress(RF): ''' Crack bridged by a fiber with constant frictional interface to the elastic matrix; clamped fiber end; Gives tension. ''' implements(IRF) title = Str('crack bridge - clamped fiber with constant friction') xi = Float(0.0179, auto_set=False, enter_set=True, input=True, distr=['weibull_min', 'uniform']) tau = Float(2.5, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm']) l = Float(10.0, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='free length') r = Float(0.013, auto_set=False, input=True, enter_set=True, desc='fiber radius in mm') E_f = Float(72e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) E_m = Float(30e3, auto_set=False, enter_set=True, input=True, distr=['uniform']) V_f = Float(0.0175, auto_set=False, enter_set=True, input=True, distr=['uniform']) theta = Float(0.01, auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='slack') phi = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform', 'norm'], desc='bond quality') Ll = Float(1., auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - left', ctrl_range=(0.0, 1.0, 10)) Lr = Float(.5, auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='embedded length - right', ctrl_range=(0.0, 1.0, 10)) w = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) x = Float(auto_set=False, enter_set=True, input=True, distr=['uniform'], desc='crack width', ctrl_range=(0.0, 1.0, 10)) x_label = Str('crack opening [mm]') y_label = Str('force [N]') C_code = Str('') def crackbridge(self, w, l, T, Kf, Km, Vf): # Phase A : Both sides debonding . Kc = Kf + Km c = Kc * T * l / 2. q0 = (np.sqrt(c**2 + w * Kf * Km * Kc * T) - c) / Km return q0 / Vf def pullout(self, w, l, T, Kf, Km, Vf, Lmin, Lmax): # Phase B : Debonding of shorter side is finished Kc = Kf + Km c = Kc * T * (Lmin + l) f = T**2 * Lmin**2 * Kc**2 q1 = (np.sqrt(c**2. + f + 2 * w * Kc * T * Kf * Km) - c) / Km return q1 / Vf def linel(self, w, l, T, Kf, Km, Vf, Lmax, Lmin): # Phase C: Both sides debonded - linear elastic behavior. Kc = Kf + Km q2 = (2. * w * Kf * Km + T * Kc * (Lmin**2 + Lmax**2)) / (2. * Km * (Lmax + l + Lmin)) return q2 / Vf def __call__(self, w, tau, l, E_f, E_m, theta, xi, phi, Ll, Lr, V_f, r): # assigning short and long embedded length Lmin = np.minimum(Ll, Lr) Lmax = np.maximum(Ll, Lr) Lmin = np.maximum(Lmin - l / 2., 1e-15) Lmax = np.maximum(Lmax - l / 2., 1e-15) # maximum condition for free length l = np.minimum(l / 2., Lr) + np.minimum(l / 2., Ll) # defining variables w = w - theta * l l = l * (1 + theta) w = H(w) * w T = 2. * tau * V_f / r Km = (1. - V_f) * E_m Kf = V_f * E_f Kc = Km + Kf # double sided debonding # q0 = self.crackbridge(w, l, T, Kr, Km, Lmin, Lmax) q0 = self.crackbridge(w, l, T, Kf, Km, V_f) # displacement at which the debonding to the closer clamp is finished w0 = (Lmin + l) * Lmin * Kc * T / Kf / Km # debonding of one side; the other side is clamped q1 = self.pullout(w, l, T, Kf, Km, V_f, Lmin, Lmax) # displacement at which the debonding is finished at both sides e1 = Lmax * Kc * T / Km / Kf w1 = e1 * (l + Lmax / 2.) + (e1 + e1 * Lmin / Lmax) * Lmin / 2. # debonding completed at both sides, response becomes linear elastic q2 = self.linel(w, l, T, Kf, Km, V_f, Lmax, Lmin) # cut out definition ranges q0 = H(w) * (q0 + 1e-15) * H(w0 - w) q1 = H(w - w0) * q1 * H(w1 - w) q2 = H(w - w1) * q2 # add all parts q = q0 + q1 + q2 # include breaking strain q = q * H(E_f * xi - q) return q