class Reinforcement(HasTraits): '''common class for all reinforcement types''' label = Str('reinforcement') r = EitherType(klasses=[FloatType, RV]) V_f = Float E_f = Float xi = EitherType(klasses=[FloatType, RV, WeibullFibers]) tau = EitherType(klasses=[FloatType, RV]) n_int = Int @on_trait_change('n_int') def check(self): if self.n_int < 50: print 'Warning: integration with', self.n_int, 'points might not be accurate enough' print 'a minimum of 50 integration points is recommended'
class InfiniteEmbeddedLength(BaseBC): def __init__(self, **kw): super(InfiniteEmbeddedLength, self).__init__(**kw) self.L = infty type = EitherType(names=['double sided', 'one sided'], klasses=[InfFreeDbl, InfFreeOne])
class MATSExplore(IBVModel): ''' Simulate the loading histories of a material point in 2D space. ''' dim = EitherType(names=['1D', '1D5', '2D', '3D'], klasses=[MATS1DExplore, MATS1D5Explore, MATS2DExplore, MATS3DExplore ]) max_load = Float(5.0, enter_set=True, auto_set=False) time_function = Callable(lambda t: t) n_steps = Float(30, enter_set=True, auto_set=False) tolerance = Float(1e-5, enter_set=True, auto_set=False) n_iterations = Float(10, enter_set=True, auto_set=False) n_restarts = Float(5, enter_set=True, auto_set=False) def _dim_default(self): return MATS2DExplore(explorer=self) def _dim_changed(self): self.dim.explorer = self self.dim._mats_eval_changed() def _tloop_default(self): self.ts = TStepper( tse=self.dim.mats_eval, sdomain=FEUnitElem(mats_eval=self.dim.mats_eval) ) # Put the time-stepper into the time-loop # n_steps = self.n_steps tloop = TLoop(tstepper=self.ts, KMAX=100, RESETMAX=self.n_restarts, tolerance=1e-7, tline=TLine(min=0.0, step=1.0 / n_steps, max=1.0)) return tloop traits_view = View( Item('dim@', show_label=False), resizable=True, width=1.0, height=1.0, scrollable=True, )
class MATS2DExplore(MATSXDExplore): ''' Simulate the loading histories of a material point in 2D space. ''' mats_eval = EitherType( klasses=[MATS2DElastic, MATS2DMicroplaneDamage, MATS2DScalarDamage]) def _mats_eval_default(self): return MATS2DElastic()
class MATS1D5Explore(MATSXDExplore): ''' Simulate the loading histories of a material point in 1D space. Specify the loading scenarios. ''' mats_eval = EitherType(klasses=[MATS1D5Bond]) def _mats_eval_default(self): return MATS1D5Bond()
class MATS3DExplore(MATSXDExplore): ''' Simulate the loading histories of a material point in 3D space. ''' mats_eval = EitherType(klasses=[ MATS3DElastic, MATS3DDesmorat, MATS3DMplCSDEEQ, MATS3DMicroplaneDamage, MATS3DScalarDamage ]) def _mats_eval_default(self): return MATS3DScalarDamage() def _mats_eval_changed(self): pass
class ShortFibers(Reinforcement): '''implements short fiber reinforcement''' phi = EitherType( klasses=[FloatType, RV]) #inclination of short fibers to the crack plane normal Lf = Float #length of short fibers bond_law = Enum('plastic', 'elasto-plastic') tau_fr = Float # frictional bond at debonded interface k_tau = Float #stiffness of the elastic adhesive bond beta = Float #slip hardening coefficient snub = Float # snubbing coefficient le = Property(depends_on='Lf') @cached_property def _get_le(self): return RV('uniform', loc=0.0, scale=self.Lf / 2.)
class MATS2DPlastic(MATS2DEval): ''' Elastic Model. ''' implements(IMATSEval) #------------------------------------------------------------------------- # Parameters of the numerical algorithm (integration) #------------------------------------------------------------------------- stress_state = Enum( "plane strain", "plane stress", ) algorithm = Enum("closest point", "cutting plane") #------------------------------------------------------------------------- # Material parameters #------------------------------------------------------------------------- yf = EitherType(klasses=[J2, DruckerPrager, Gurson, CamClay], label="Yield Face", desc="Yield Face Definition") # yf = Instance( IYieldFace2D, # label = "Yield Face", # desc = "Yield Face Definition") E = Float(210.0e+3, label="E", desc="Young's Modulus") nu = Float(0.2, label='nu', desc="Poison's ratio") K_bar = Float(0., label='K', desc="isotropic softening parameter") H_bar = Float(0., label='H', desc="kinematic softening parameter") tolerance = Float(1.0e-4, label='TOL', desc="tolerance of return mapping") max_iter = Int(20, label='Iterations', desc="maximal number of iterations") D_el = Property(Array(float), depends_on='E, nu') @cached_property def _get_D_el(self): if self.stress_state == "plane_stress": return self._get_D_plane_stress() else: return self._get_D_plane_strain() H_mtx = Property(Array(float), depends_on='K_bar, H_bar') @cached_property def _get_H_mtx(self): H_mtx = diag([self.K_bar, self.H_bar, self.H_bar, self.H_bar]) return H_mtx # This event can be used by the clients to trigger an action upon # the completed reconfiguration of the material model # #------------------------------------------------------------------------- # View specification #------------------------------------------------------------------------- view_traits = View(VSplit( Group(Item('yf'), Item('E'), Item('nu'), Item('K_bar'), Item('H_bar'), Item('tolerance'), Item('max_iter')), Group( Item('stress_state', style='custom'), Item('algorithm', style='custom'), Spring(resizable=True), label='Configuration parameters', show_border=True, ), ), resizable=True) #------------------------------------------------------------------------- # Private initialization methods #------------------------------------------------------------------------- #------------------------------------------------------------------------- # Setup for computation within a supplied spatial context #------------------------------------------------------------------------- def get_state_array_size(self): ''' Return number of number to be stored in state array @param sctx:spatial context ''' return 7 def setup(self, sctx): ''' Intialize state variables. ''' sctx.mats_state_array = zeros(7, float_) def new_cntl_var(self): return zeros(3, float_) def new_resp_var(self): return zeros(3, float_) #------------------------------------------------------------------------- # Evaluation - get the corrector and predictor #------------------------------------------------------------------------- def get_corr_pred(self, sctx, eps_app_eng, d_eps, tn, tn1): ''' Corrector predictor computation. @param eps_app_eng input variable - engineering strain ''' delta_gamma = 0. if sctx.update_state_on: # print "in us" eps_n = eps_app_eng - d_eps sigma, f_trial, epsilon_p, q_1, q_2 = self._get_state_variables( sctx, eps_n) sctx.mats_state_array[:3] = epsilon_p sctx.mats_state_array[3] = q_1 sctx.mats_state_array[4:] = q_2 diff1s = zeros([3]) sigma, f_trial, epsilon_p, q_1, q_2 = self._get_state_variables( sctx, eps_app_eng) # Note: the state variables are not needed here, just gamma diff2ss = self.yf.get_diff2ss(eps_app_eng, self.E, self.nu, sctx) Xi_mtx = inv(inv(self.D_el) + delta_gamma * diff2ss * f_trial) N_mtx_denom = sqrt(dot(dot(diff1s, Xi_mtx), diff1s)) if N_mtx_denom == 0.: N_mtx = zeros(3) else: N_mtx = dot(Xi_mtx, self.diff1s) / N_mtx_denom D_mtx = Xi_mtx - vdot(N_mtx, N_mtx) # print "sigma ",sigma # print "D_mtx ",D_mtx return sigma, D_mtx #------------------------------------------------------------------------- # Subsidiary methods realizing configurable features #------------------------------------------------------------------------- def _get_state_variables(self, sctx, eps_app_eng): epsilon_p = eps_p_old = sctx.mats_state_array[:3] q_1 = sctx.mats_state_array[3] q_2 = sctx.mats_state_array[4:] sigma = dot(self.D_el, (eps_app_eng - epsilon_p)) xi_trial = sigma - q_2 f_trial = self.yf.get_f_trial(xi_trial, q_1) # print "f_trial", f_trial R_k = zeros(3) int_count = 1 while f_trial > self.tolerance or norm(R_k) > self.tolerance: if int_count > self.max_iter: print "Maximal number of iteration reached" break diff1s = self.yf.get_diff1s(eps_app_eng, self.E, self.nu, sctx) diff1q = self.yf.get_diff1q(eps_app_eng, self.E, self.nu, sctx) if self.stress_state == "plane stress": raise NotImplementedError else: if self.algorithm == "cutting plane": delta_gamma_2 = (f_trial / (dot(dot(diff1s, self.D_el), diff1s) + dot(dot(diff1q, self.H_mtx), diff1q))) delta_gamma += delta_gamma_2 epsilon_p += delta_gamma_2 * diff1s q_1 += delta_gamma_2 * self.K_bar * diff1q[0] q_2 += delta_gamma_2 * self.H_bar * diff1q[1:] elif self.algorithm == "closest point": diff2ss = self.yf.get_diff2ss(eps_app_eng, self.E, self.nu, sctx) diff2sq = self.yf.get_diff2sq(eps_app_eng, self.E, self.nu, sctx) A_mtx_inv = inv(self.D_el) + delta_gamma * diff2ss # self.delta_gamma * diff2sq)) A_mtx = inv(A_mtx_inv) delta_gamma_2 = ((f_trial - dot(dot(diff1s, A_mtx), R_k)) / (dot(dot(diff1s, A_mtx), diff1s))) delta_eps_p = dot(dot(inv(self.D_el), A_mtx), R_k + delta_gamma_2 * diff1s) epsilon_p += delta_eps_p delta_gamma += delta_gamma_2 R_k = -epsilon_p + eps_p_old + delta_gamma * diff1s else: raise NotImplementedError sigma = dot(self.D_el, (eps_app_eng - epsilon_p)) xi_trial = sigma - q_2 f_trial = self.yf.get_f_trial(xi_trial, q_1) # print "f_trial_after", self.f_trial int_count += 1 return sigma, f_trial, epsilon_p, q_1, q_2 def _get_D_plane_stress(self): E = self.E nu = self.nu D_stress = zeros([3, 3]) D_stress[0][0] = E / (1.0 - nu * nu) D_stress[0][1] = E / (1.0 - nu * nu) * nu D_stress[1][0] = E / (1.0 - nu * nu) * nu D_stress[1][1] = E / (1.0 - nu * nu) D_stress[2][2] = E / (1.0 - nu * nu) * (1.0 / 2.0 - nu / 2.0) return D_stress def _get_D_plane_strain(self): E = self.E nu = self.nu D_strain = zeros([3, 3]) D_strain[0][0] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[0][1] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1][0] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1][1] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[2][2] = E * (1.0 - nu) / (1.0 + nu) / (2.0 - 2.0 * nu) return D_strain #------------------------------------------------------------------------- # Response trace evaluators #------------------------------------------------------------------------- def get_sig_norm(self, sctx, eps_app_eng): sig_eng, D_mtx = self.get_corr_pred(sctx, eps_app_eng, 0, 0, 0) return array([scalar_sqrt(sig_eng[0]**2 + sig_eng[1]**2)]) def get_eps_p(self, sctx, eps_app_eng): # print "eps tracer ", sctx.mats_state_array[:3] return sctx.mats_state_array[:3] # Declare and fill-in the rte_dict - it is used by the clients to # assemble all the available time-steppers. # rte_dict = Trait(Dict) def _rte_dict_default(self): return { 'sig_app': self.get_sig_app, 'eps_app': self.get_eps_app, 'sig_norm': self.get_sig_norm, 'eps_p': self.get_eps_p }
class Cuypers(HasTraits): ''' Stochastic cracking model due to H. Cuypers (W. Curtin) matrix tensile strength is assumed to follow the Weibull distribution ''' material = Instance(Material) def _material_default(self): return Material() m = Float( 5.3, auto_set=False, enter_set=True, # [-] desc='Weibull shape parameter for the matrix tensional strength [-]', modified=True) orientation = Range( low=0.0, high=Pi / 2, value=0.0, auto_set=False, enter_set=True, # [-] desc='fiber orientation [rad]', modified=True) sigma_mu = Float( 12.0, auto_set=False, enter_set=True, # [N/mm^2] desc='mean matrix tensional strength [MPa]', modified=True) sigma_fu = Float( 720.0, auto_set=False, enter_set=True, # [N/mm^2] desc='fiber tensional strength [MPa]', modified=True) reinf_ratio = EitherType(names=['grid fiber layout', 'explicit value'], klasses=[GridReinforcement, SimplyRatio], modified=True) Pf = Range( low=0.0, high=1.0 - 1e-15, value=0.9, auto_set=False, enter_set=True, # [-] desc='probability of crack spacing to be of final range', modified=True) sigma_ref = Range( low=1e-10, high=80.0, value=12.0, auto_set=False, enter_set=True, # [MPa] desc='reference matrix stress', modified=True) sigma_ref_2 = Property(Float, depends_on='m, sigma_mu') def _get_sigma_ref_2(self): return e**(0.0991 / self.m) * self.scale_sigma_m rho = Property(Float, depends_on='reinf_ratio.rho,orientation') def _get_rho(self): if self.reinf_ratio.rho * cos(self.orientation) == 0: return 1e-15 else: return self.reinf_ratio.rho * cos(self.orientation) V_f = Property(Float, depends_on='reinf_ratio.rho, orientation') @cached_property def _get_V_f(self): return self.rho V_m = Property(Float, depends_on='reinf_ratio.rho, orientation') @cached_property def _get_V_m(self): return 1 - self.rho alpha = Property(Float, depends_on='E_m,E_f,reinf_ratio.rho, orientation') @cached_property def _get_alpha(self): return (self.material.E_m * self.V_m) / (self.material.E_f * self.V_f) E_c = Property(Float, depends_on='E_m,E_f,reinf_ratio.rho, orientation') @cached_property def _get_E_c(self): return self.material.E_f * self.V_f + self.material.E_m * self.V_m delta_final = Property(Float, depends_on='E_m,E_f,rho,r,sigma_ref,tau,m') @cached_property def _get_delta_final(self): return self.sigma_ref * (self.V_m * self.material.r) / ( self.V_f * 2 * self.material.tau) cs_final = Property(Float) def _get_cs_final(self): return 1.337 * self.delta_final def _get_delta(self, sigma_c): return sigma_c * (self.V_m * self.material.r * self.material.E_m) / ( self.V_f * 2 * self.material.tau * self.E_c) # matrix strength scale parameter for the Weibull distribution with sigma_mu as # mean and m as shape parameter scale_sigma_m = Property(Float, depends_on='sigma_mu, m') @cached_property def _get_scale_sigma_m(self): return self.sigma_mu / gamma(1. + 1. / self.m) # composite scale parameter for the Weibull distribution with sigma_mu as # mean and m as shape parameter scale_sigma_c = Property( Float, depends_on='sigma_mu, m, E_m, E_f, reinf_ratio.rho, orientation') @cached_property def _get_scale_sigma_c(self): return self.scale_sigma_m / self.material.E_m * self.E_c def _get_cs(self, sigma_c): Pf = weibull_min.cdf(sigma_c, self.m, scale=self.scale_sigma_c) if Pf == 0: Pf = 1e-15 return self.cs_final * 1.0 / Pf def eps_c(self, sigma_c): cs = self._get_cs(sigma_c) delta = self._get_delta(sigma_c) if cs > 2 * delta: return sigma_c / self.E_c * (1 + self.alpha * delta / cs) else: return sigma_c * (1. / (self.material.E_f * self.V_f) - (self.alpha * cs) / (4. * delta * self.E_c)) def _get_epsilon_c(self, sigma_c): get_epsilon_c = np.vectorize(self.eps_c) return get_epsilon_c(sigma_c) sigma_cu = Property(depends_on='rho, orientation, sigma_fu') @cached_property def _get_sigma_cu(self): '''Ultimate composite strength. The strength is given by the fiber strength related to the composite cross section by the reinforcement ratio rho and projected by the cosine of the fiber inclination into the loading direction. ''' # 0.05 quantile strength of the matrix as a criterion for matrix failure # when this value is higher than the composite strength governed by the # reinforcement stress quantile = weibull_min.ppf(0.05, self.m, scale=self.scale_sigma_m) # composite failure due to matrix failure if self.sigma_fu * self.V_f * cos( self.orientation) < quantile / self.V_m: return quantile / self.V_m else: # composite failure due to fiber failure return self.sigma_fu * self.V_f * cos(self.orientation) csf = Property( depends_on='m, sigma_mu, sigma_ref, Pf, sigma_fu, orientation, E_f, E_m' ) @cached_property def _get_csf(self): # composite stress at Pf probability for CS to be of final range # scale parameter for composite stress scale_sigma_c = self.scale_sigma_m / self.E_m * self.E_c sigma = weibull_min.ppf(self.Pf, self.m, scale=scale_sigma_c) # point of reaching final crack spacing epsilon_csf = np.hstack( (0, self._get_epsilon_c(sigma), self._get_epsilon_c(sigma))) sigma_csf = np.hstack((sigma, sigma, 0)) return epsilon_csf, sigma_csf sig_eps_fn = Property(depends_on='+modified, reinf_ratio.+modified') @cached_property def _get_sig_eps_fn(self): '''Get the stress and strain arrays''' n_points = 100 sigma_c_arr = np.linspace(0, self.sigma_cu, n_points) if self.sigma_cu == self.sigma_fu * self.V_f * cos(self.orientation): epsilon_c_arr = self._get_epsilon_c(sigma_c_arr) else: epsilon_c_arr = sigma_c_arr / self.E_c # stress with respect to reinforcement sigma_f_arr = sigma_c_arr / self.rho # stress of reinforcement with no matrix interaction sigma_fiber = epsilon_c_arr[[0, -1]] * self.material.E_f * self.rho return epsilon_c_arr, sigma_c_arr, sigma_f_arr, sigma_fiber
class PolarDiscr(HasTraits): ''' Manager of the microplane arrays. This class is responsible for the generation and initialization and state management of an array of microplanes. Additionally, it can perform the setup of damage function parameters using the value of the microplane integrator object. ''' mfn_class = Class(None) #------------------------------------------------------------------------- # Common parameters for for isotropic and anisotropic damage function specifications #------------------------------------------------------------------------- n_mp = Range(0, 50, 6, label='Number of microplanes', auto_set=False) E = Float(34e+3, label="E", desc="Young's Modulus", auto_set=False, enter_set=True) nu = Float(0.2, label='nu', desc="Poison's ratio", auto_set=False, enter_set=True) c_T = Float( 0.0, label='c_T', desc='fraction of tangential stress accounted on each microplane', auto_set=False, enter_set=True) #------------------------------------------------------------------------- # list of angles #------------------------------------------------------------------------- alpha_list = Property(Array, depends_on='n_mp') @cached_property def _get_alpha_list(self): return array( [Pi / self.n_mp * (i - 0.5) for i in range(1, self.n_mp + 1)]) #------------------------------------------------------------------------- # Damage function specification #------------------------------------------------------------------------- phi_fn = EitherType(klasses=[ PhiFnGeneral, PhiFnGeneralExtended, PhiFnGeneralExtendedExp, PhiFnStrainSoftening, PhiFnStrainHardening, PhiFnStrainHardeningLinear, PhiFnStrainHardeningBezier ]) def _phi_fn_default(self): print 'setting phi_fn default' return PhiFnStrainSoftening(polar_discr=self) def _phi_fn_changed(self): print 'setting phi_fn changed' self.phi_fn.polar_discr = self varied_params = List(Str, []) #------------------------------------------------------------------------- # Management of spatially varying parameters depending on the value of mats_eval #------------------------------------------------------------------------- varpars = Dict def _varpars_default(self): return self._get_varpars() @on_trait_change('phi_fn,varied_params') def _update_varpars(self): self.varpars = self._get_varpars() def _get_varpars(self): ''' reset the varpar list according to the current phi_fn object. ''' params = self.phi_fn.identify_parameters() varset = {} for key in params: par_val = getattr(self.phi_fn, key) varset[key] = VariedParam(phi_fn=self.phi_fn, mats_eval=self, varname=key) if key in self.varied_params: varset[key].switched_on = True return varset varpar_list = Property(List(VariedParam), depends_on='varpars') @cached_property def _get_varpar_list(self): return [self.varpars[key] for key in self.phi_fn.identify_parameters()] # variable selectable in the table of varied params (just for viewing) current_varpar = Instance(VariedParam) def _current_varpar_default(self): if len(self.varpar_list) > 0: return self.varpar_list[0] return None @on_trait_change('phi_fn') def set_current_varpar(self): if len(self.varpar_list) > 0: self.current_varpar = self.varpar_list[0] #------------------------------------------------------------------------- # Get the damage state for all microplanes #------------------------------------------------------------------------- def get_phi_arr(self, sctx, e_max_arr): ''' Return the damage coefficients ''' # gather the coefficients for parameters depending on the orientation carr_list = [ self.varpars[key].polar_fn_vectorized(self.alpha_list) for key in self.phi_fn.identify_parameters() ] # vectorize the damage function evaluation n_arr = 1 + len(carr_list) phi_fn_vectorized = frompyfunc(self.phi_fn.get_value, n_arr, 1) # damage parameter for each microplane return phi_fn_vectorized(e_max_arr, *carr_list) def get_polar_fn_fracture_energy_arr(self, sctx, e_max_arr): ''' Return the fracture energy contributions ''' carr_list = [ self.varpars[key].polar_fn_vectorized(self.alpha_list) for key in self.phi_fn.identify_parameters() ] # vectorize the damage function evaluation n_arr = 1 + len(carr_list) integ_phi_fn_vectorized = frompyfunc(self.phi_fn.get_integ, n_arr, 1) return self.E * integ_phi_fn_vectorized(e_max_arr, *carr_list) polar_fn_group = Group( Group(Item('n_mp@', width=200), Item('E'), Item('nu'), Item('c_T'), Spring(), label='Elasticity parameters'), Group(Item('phi_fn@', show_label=False), label='Damage parameters'), Group(VSplit( Item('varpar_list', label='List of material variables', show_label=False, editor=varpar_editor), Item('current_varpar', label='Selected variable', show_label=False, style='custom', resizable=True), dock='tab', ), label='Angle-dependent variations'), Include('config_param_vgroup'), layout='tabbed', springy=True, dock='tab', id='ibvpy.mats.matsXD_cmdm.MATSXDPolarDiscr', ) traits_view = View(Include('polar_fn_group'), resizable=True, scrollable=True, width=0.6, height=0.9)
class Reinforcement(HasTraits): label = Str('reinforcement') r = EitherType(klasses=[FloatType, RV]) V_f = Float E_f = Float xi = EitherType(klasses=[FloatType, RV, WeibullFibers]) tau = EitherType(klasses=[FloatType, RV]) n_int = Int results = Property(depends_on='r, V_f, E_f, xi, tau, n_int') @cached_property def _get_results(self): stat_weights = 1.0 if isinstance(self.tau, RV): tau = self.tau.ppf( np.linspace(.5 / self.n_int, 1. - .5 / self.n_int, self.n_int)) stat_weights *= 1. / self.n_int nu_r_tau = np.ones_like(tau) else: tau = self.tau nu_r_tau = 1.0 if isinstance(self.r, RV): r = self.r.ppf( np.linspace(.5 / self.n_int, 1. - .5 / self.n_int, self.n_int)) stat_weights *= 1. / self.n_int r2 = r**2 nu_r = r2 / np.mean(r2) else: r = self.r r2 = r**2 nu_r = nu_r_tau * 1.0 if isinstance(tau, np.ndarray) and isinstance(r, np.ndarray): r = r.reshape(1, self.n_int) tau = tau.reshape(self.n_int, 1) nu_r_r = (r2 / np.mean(r2)).reshape(1, self.n_int) nu_r_tau = np.ones(self.n_int).reshape(self.n_int, 1) nu_r = nu_r_r * nu_r_tau r_arr = (nu_r * np.mean(r2))**0.5 return (2. * tau / r / self.E_f ).flatten(), stat_weights, nu_r.flatten(), r_arr.flatten() else: r_arr = (nu_r * np.mean(r2))**0.5 return 2. * tau / r / self.E_f, stat_weights, nu_r, r_arr depsf_arr = Property(depends_on='r, V_f, E_f, xi, tau, n_int') @cached_property def _get_depsf_arr(self): return self.results[0] stat_weights = Property(depends_on='r, V_f, E_f, xi, tau, n_int') @cached_property def _get_stat_weights(self): return self.results[1] nu_r = Property(depends_on='r, V_f, E_f, xi, tau, n_int') @cached_property def _get_nu_r(self): return self.results[2] r_arr = Property(depends_on='r, V_f, E_f, xi, tau, n_int') @cached_property def _get_r_arr(self): return self.results[3]
class MATS1D5Bond(MATS1D5Eval): '''Bond model for two phases interacting over an interface with zero thickness. Both phases can be associated with arbitrary 1D mats model. Interface behavior can be defined for both sliding and opening using 1D mats models. ''' implements(IMATSEval) #------------------------------------------------------------------------- # Submodels constituting the interface behavior #------------------------------------------------------------------------- mats_phase1 = EitherType(klasses=mats_klasses, desc='Material model of phase 1') mats_ifslip = EitherType(klasses=mats_klasses, desc='Material model for interface slippage') mats_ifopen = EitherType(klasses=mats_klasses, desc='Material model for interface opening') mats_phase2 = EitherType(klasses=mats_klasses, desc='Material model for phase 2') def _mats_phase1_default(self): return MATS1DElastic() def _mats_ifslip_default(self): return MATS1DElastic() def _mats_ifopen_default(self): return MATS1DElastic() def _mats_phase2_default(self): return MATS1DElastic() traits_view = View(Item('mats_phase1@'), Item('mats_ifslip@'), Item('mats_ifopen@'), Item('mats_phase2@'), resizable=True, scrollable=True, width=0.8, height=0.9, buttons=['OK', 'Cancel']) #------------------------------------------------------------------------- # Subsidiary maps to enable generic loop over the material models #------------------------------------------------------------------------- # order of the material model names used for association in generic loops _mats_names = List( ['mats_phase1', 'mats_slip', 'mats_open', 'mats_phase2']) # hidden list of all four involved mats _mats_list = Property( depends_on='mats_phase1, mats_slip, mats_open, mats_phase2') @cached_property def _get__mats_list(self): return [ self.mats_phase1, self.mats_ifslip, self.mats_ifopen, self.mats_phase2 ] # state array map with the sizes of the array _state_sizes = Property( depends_on='mats_phase1, mats_slip, mats_open, mats_phase2') @cached_property def _get__state_sizes(self): return array([mats.get_state_array_size() for mats in self._mats_list], dtype='int_') # state array map with the sizes of the array _state_offsets = Property( depends_on='mats_phase1, mats_slip, mats_open, mats_phase2') @cached_property def _get__state_offsets(self): offsets = zeros_like(self._state_sizes) offsets[1:] = self._state_sizes[:-1].cumsum() return offsets #------------------------------------------------------------------------- # Setup for computation within a supplied spatial context #------------------------------------------------------------------------- def get_state_array_size(self): ''' Return the number of floats to be saved ''' return self._state_sizes.cumsum()[-1] #------------------------------------------------------------------------- # Evaluation - get the corrector and predictor #------------------------------------------------------------------------- def get_corr_pred(self, sctx, eps_app_eng, d_eps_app_eng, tn, tn1, *args, **kw): ''' Corrector predictor computation. @param eps_app_eng input variable - engineering strain ''' sig_app_eng = zeros_like(eps_app_eng) # @todo [rch] dirty - when called form response tracer if isinstance(d_eps_app_eng, int) and d_eps_app_eng == 0: d_eps_app_eng = zeros_like(eps_app_eng) D_mtx = zeros((eps_app_eng.shape[0], eps_app_eng.shape[0]), dtype='float_') mats_state_array = sctx.mats_state_array for i, mats in enumerate(self._mats_list): # extract the stress components eps, d_eps = eps_app_eng[i], d_eps_app_eng[i] size = self._state_sizes[i] offset = self._state_offsets[i] sctx.mats_state_array = mats_state_array[offset:offset + size] sig_app_eng[i], D_mtx[i, i] = mats.get_corr_pred( sctx, eps, d_eps, tn, tn1) sctx.mats_state_array = mats_state_array return sig_app_eng, D_mtx def get_sig1(self, sctx, eps_app_eng, *args, **kw): sig_eng, D_mtx = self.get_corr_pred(sctx, eps_app_eng, 0, 0, 0) return sig_eng[0:1] def get_sig2(self, sctx, eps_app_eng, *args, **kw): sig_eng, D_mtx = self.get_corr_pred(sctx, eps_app_eng, 0, 0, 0) return sig_eng[3:] def get_shear_flow(self, sctx, eps_app_eng, *args, **kw): sig_eng, D_mtx = self.get_corr_pred(sctx, eps_app_eng, 0, 0, 0) return sig_eng[1:2] def get_cohesive_stress(self, sctx, eps_app_eng, *args, **kw): sig_eng, D_mtx = self.get_corr_pred(sctx, eps_app_eng, 0, 0, 0) return sig_eng[2:3] rte_dict = Property def _get_rte_dict(self): rte_dict = {} ix_maps = [0, 1, 2, 3] for name, mats, ix_map, size, offset in \ zip(self._mats_names, self._mats_list, ix_maps, self._state_sizes, self._state_offsets): for key, v_eval in mats.rte_dict.items(): __call_v_eval = RTE1D5Bond(v_eval=v_eval, name=name + '_' + key, size=size, offset=offset, ix_map=ix_map) rte_dict[name + '_' + key] = __call_v_eval # sigma is also achievable through phase1_sig_app and phase_2_sig_app extra_rte_dict = { 'sig1': self.get_sig1, 'sig2': self.get_sig2, 'shear_flow': self.get_shear_flow, 'cohesive_stress': self.get_cohesive_stress, } rte_dict.update(extra_rte_dict) return rte_dict #------------------------------------------------------------------------- # Methods required by the mats_explore tool #------------------------------------------------------------------------- def new_cntl_var(self): return zeros(4, 'float_') def new_resp_var(self): return zeros(4, 'float_')
class RFModelView( ModelView ): title = Str( 'RF browser' ) model = Instance( IRF ) child = Instance( SPIRRID ) def _child_default( self ): return SPIRRID() rf = EitherType( names = ['brittle filament', 'pullout short fiber', 'crack bridge short fiber', 'pullout clamped fiber', 'crack bridge clamped fiber', 'pullout infinite fiber', 'crack bridge infinite fiber', ], klasses = [Filament, POShortFiber, CBShortFiber, POClampedFiber, CBClampedFiber, POInfiniteFiber, CBInfiniteFiber, ], config_change = True ) def _model_default( self ): return self.rf def _rf_changed( self ): self.model = self.rf self.child.rf = self.model for name in self.model.param_keys: self.on_trait_change( self._redraw, 'rf.' + name ) def init( self, info ): for name in self.model.param_keys: self.on_trait_change( self._redraw, 'rf.' + name ) def close( self, info, is_ok ): for name in self.model.param_keys: self.on_trait_change( self._redraw, 'rf.' + name, remove = True ) return is_ok figure = Instance( Figure ) def _figure_default( self ): figure = Figure( facecolor = 'white' ) figure.add_axes( [0.08, 0.13, 0.85, 0.74] ) return figure data_changed = Event( True ) max_x = Float( 0.04, enter_set = True, auto_set = False, config_change = True ) x_points = Int( 80, enter_set = True, auto_set = False, config_change = True ) @on_trait_change( '+config_change' ) def _redraw( self ): in_arr = linspace( 0.0, self.max_x, self.x_points ) self.model = self.rf args = [ in_arr ] + self.model.param_values # get the number of parameters of the response function n_args = len( args ) fn = frompyfunc( self.model.__call__, n_args, 1 ) out_arr = fn( *args ) axes = self.figure.axes[0] axes.plot( in_arr, out_arr, linewidth = 2, label = self.model.title ) axes.set_xlabel( self.model.x_label ) axes.set_ylabel( self.model.y_label ) #axes.legend( loc = 'best' ) self.data_changed = True show = Button def _show_fired( self ): self._redraw() clear = Button def _clear_fired( self ): axes = self.figure.axes[0] axes.clear() self.data_changed = True def default_traits_view( self ): ''' Generates the view from the param items. ''' #rf_param_items = [ Item( 'model.' + name, format_str = '%g' ) for name in self.model.param_keys ] D2_plot_param_items = [ VGroup( Item( 'max_x' , label = 'max x value' ), Item( 'x_points', label = 'No of plot points' ) ) ] if hasattr( self.rf, 'get_q_x' ): D3_plot_param_items = [ VGroup( Item( 'min_x', label = 'min x value' ), Item( 'max_x', label = 'max x value' ), Item( 'min_y', label = 'min y value' ), Item( 'max_y', label = 'max y value' ) ) ] else: D3_plot_param_items = [] control_items = [ Item( 'show', show_label = False ), Item( 'clear', show_label = False ), ] view = View( HSplit( VGroup( Item( '@rf', show_label = False ), label = 'Function parameters', id = 'stats.spirrid.rf_model_view.rf_params', scrollable = True ), VGroup( HGroup( *D2_plot_param_items ), label = 'plot parameters', id = 'stats.spirrid.rf_model_view.2Dplot_params' ), # VGroup( HGroup( *D3_plot_param_items ), # label = '3D plot parameters', # id = 'stats.spirrid.rf_model_view.3Dplot_params' ), VGroup( Item( 'model.comment', show_label = False, style = 'readonly' ), label = 'Comment', id = 'stats.spirrid.rf_model_view.comment', scrollable = True, ), VGroup( HGroup( *control_items ), Item( 'figure', editor = MPLFigureEditor(), resizable = True, show_label = False ), label = 'Plot', id = 'stats.spirrid.rf_model_view.plot' ), dock = 'tab', id = 'stats.spirrid.rf_model_view.split' ), kind = 'modal', resizable = True, dock = 'tab', buttons = [OKButton], id = 'stats.spirrid.rf_model_view' ) return view
class SCM(HasTraits): ''' Stochastic Cracking Theory ''' E_f = Float( 120e+3, auto_set=False, enter_set=True, # [N/mm^2] desc='the E-Modulus of the fiber [MPa]', modified=True) E_m = Float( 34.e+3, auto_set=False, enter_set=True, # [N/mm^2] desc='E-Modulus of the matrix [MPa]', modified=True) tau = Float( 8.0, auto_set=False, enter_set=True, # [N/mm^2] desc='the frictional stress between fiber and matrix [MPa]', modified=True) r = Float( 0.5, auto_set=False, enter_set=True, # [mm] desc='the radius of the fiber', modified=True) m = Float( 2.3, auto_set=False, enter_set=True, # [-] desc= 'the Weibull modulus defining the scatter of the matrix strength [-]', modified=True) orientation = Range( low=0.0, high=Pi / 2, value=0.0, auto_set=False, enter_set=True, # [-] desc='Fiber orientation [rad]', modified=True) sigma_mu = Float( 12.0, auto_set=False, enter_set=True, # [N/mm^2] desc='the matrix tensional strength as a scale parameter' ' of the Weibull distribution [MPa]', modified=True) sigma_fu = Float( 720.0, auto_set=False, enter_set=True, # [N/mm^2] desc='the fiber tensional strength [MPa]', modified=True) reinf_ratio = EitherType(names=['grid fiber layout', 'explicit value'], klasses=[GridReinforcement, SimplyRatio], modified=True) Pf = Range( low=0.0, high=1.0 - 1e-15, value=0.9, auto_set=False, enter_set=True, # [-] desc='probability of crack spacing to be of final range', modified=True) rho = Property(Float, depends_on='reinf_ratio.rho,orientation') def _get_rho(self): if self.reinf_ratio.rho * cos(self.orientation) == 0: return 1e-15 else: return self.reinf_ratio.rho * cos(self.orientation) V_f = Property(Float, depends_on='rho') @cached_property def _get_V_f(self): return self.rho V_m = Property(Float, depends_on='rho') @cached_property def _get_V_m(self): return 1 - self.rho alpha = Property(Float, depends_on='E_m,E_f,rho') @cached_property def _get_alpha(self): return (self.E_m * self.V_m) / (self.E_f * self.V_f) E_c = Property(Float, depends_on='E_m,E_f,rho') @cached_property def _get_E_c(self): return self.E_f * self.V_f + self.E_m * self.V_m delta_final = Property(Float, depends_on='E_m,E_f,rho,r,sigma_mu,tau,m') @cached_property def _get_delta_final(self): return self.sigma_mu * (self.V_m * self.r) / (self.V_f * 2 * self.tau) cs_final = Property(Float) def _get_cs_final(self): return 1.337 * self.delta_final def _get_delta(self, sigma_c): return sigma_c * (self.V_m * self.r * self.E_m) / (self.V_f * 2 * self.tau * self.E_c) # matrix strength scale parameter for the Weibull distribution with sigma_mu as # mean and m as shape parameter scale_sigma_m = Property(Float, depends_on='sigma_mu, m') @cached_property def _get_scale_sigma_m(self): return self.sigma_mu / gamma(1. + 1. / self.m) # composite scale parameter for the Weibull distribution with sigma_mu as # mean and m as shape parameter # TODO: the mean composite cracking stress increases with increasing # reinforcement ratio - this has yet to be implemented scale_sigma_c = Property(Float, depends_on='sigma_mu, m, E_m, E_f, rho') @cached_property def _get_scale_sigma_c(self): return self.scale_sigma_m / self.E_m * self.E_c def _get_cs(self, sigma_c): Pf = weibull_min.cdf(sigma_c, self.m, scale=self.scale_sigma_c) if Pf == 0: Pf = 1e-15 return self.cs_final * 1.0 / Pf def eps_c(self, sigma_c): cs = self._get_cs(sigma_c) delta = self._get_delta(sigma_c) print('delta') print(delta) if cs > 2 * delta: print(sigma_c / self.E_c * (1 + self.alpha * delta / cs)) return sigma_c / self.E_c * (1 + self.alpha * delta / cs) else: print(sigma_c * (1. / (self.E_f * self.V_f) - (self.alpha * cs) / (4. * delta * self.E_c))) return sigma_c * (1. / (self.E_f * self.V_f) - (self.alpha * cs) / (4. * delta * self.E_c)) def _get_epsilon_c(self, sigma_c): get_epsilon_c = frompyfunc(self.eps_c, 1, 1) return get_epsilon_c(sigma_c) sigma_cu = Property(depends_on='rho, orientation, sigma_fu') @cached_property def _get_sigma_cu(self): '''Ultimate composite strength. The strength is given by the fiber strength related to the composite cross section by the reinforcement ratio rho and projected by the cosine of the fiber inclination into the loading direction. ''' # 0.05 quantile strength of the matrix as a criterion for matrix failure # when this value is higher than the composite strength governed by the # reinforcement stress quantile = weibull_min.ppf(0.05, self.m, scale=self.scale_sigma_m) # composite failure due to matrix failure if self.sigma_fu * self.V_f * cos( self.orientation) < quantile / self.V_m: return quantile / self.V_m else: # composite failure due to fiber failure return self.sigma_fu * self.V_f * cos(self.orientation) csf = Property( depends_on='m, sigma_mu, Pf, sigma_fu, orientation, E_f, E_m') @cached_property def _get_csf(self): # composite stress at Pf probability for CS to be of final range # scale parameter for composite stress scale_sigma_c = self.scale_sigma_m / self.E_m * self.E_c sigma = weibull_min.ppf(self.Pf, self.m, scale=scale_sigma_c) # point of reaching final crack spacing epsilon_csf = hstack( (0, self._get_epsilon_c(sigma), self._get_epsilon_c(sigma))) sigma_csf = hstack((sigma, sigma, 0)) return epsilon_csf, sigma_csf sig_eps_fn = Property(depends_on='+modified, reinf_ratio.+modified') @cached_property def _get_sig_eps_fn(self): '''Get the stress and strain arrays''' print('update') n_points = 100 sigma_c_arr = linspace(0, self.sigma_cu, n_points) if self.sigma_cu == self.sigma_fu * self.V_f * cos(self.orientation): epsilon_c_arr = self._get_epsilon_c(sigma_c_arr) else: epsilon_c_arr = sigma_c_arr / self.E_c # stress with respect to reinforcement sigma_f_arr = sigma_c_arr / self.rho # stress of reinforcement with no matrix interaction sigma_fiber = epsilon_c_arr[[0, -1]] * self.E_f * self.rho print(epsilon_c_arr) return epsilon_c_arr, sigma_c_arr, sigma_f_arr, sigma_fiber traits_view = View(Group( Tabbed( VGroup( Group( Item('E_f', resizable=True, full_size=True, label='E-modulus', tooltip="Young's modulus of the fiber"), Item('sigma_fu', resizable=True, label='strength', help="Strength of the fibers"), label='fibers', ), Group( Item('E_m', resizable=True, full_size=True, label='E-modulus', help="Young's modulus of the matrix"), Item('sigma_mu', resizable=True, label='strength', help="Scale parameter of the matrix strength" 'roughly corresponding to the mean strength'), Item('m', resizable=True, label='Weibull-modulus', help= "Weibull modulus of the matrix strength distribution" 'defining the scatter of the strength'), label='matrix', scrollable=False, ), label='Components', dock='tab', id='scm.model.component_params', ), VGroup( Item('tau', resizable=True, full_size=True, springy=False), Item('r', resizable=False, springy=False), springy=True, label='Bond', dock='tab', id='scm.model.params', ), id='scm.model.allparams', ), VGroup( Item('reinf_ratio@', show_label=False, resizable=True), label='Cross section parameters', dock='tab', id='scm.model.reinf_ratio', ), VGroup(Item('orientation', label='fiber orientation'), ), id='scm.model.splitter', springy=False, layout='split', ), id='scm.model', dock='fixed', scrollable=True, resizable=True, buttons=[OKButton], height=0.8, width=0.8)
class RFModelView(ModelView): model = Instance(IRF) def _model_default(self): return Filament() resp_func = EitherType( names=['brittle filament', 'dbl pullout const friction short fiber'], klasses=[Filament, ConstantFrictionFiniteFiber], config_change=True) def init(self, info): for name in self.model.param_keys: self.on_trait_change(self._redraw, 'model.' + name) def close(self, info, is_ok): for name in self.model.param_keys: self.on_trait_change(self._redraw, 'model.' + name, remove=True) return is_ok figure = Instance(Figure) def _figure_default(self): figure = Figure(facecolor='white') figure.add_axes([0.08, 0.13, 0.85, 0.74]) return figure data_changed = Event(True) max_x = Float(0.01, enter_set=True, auto_set=False, config_change=True) n_points = Int(20, enter_set=True, auto_set=False, config_change=True) @on_trait_change('+config_change') def _redraw(self): self.model = self.resp_func figure = self.figure axes = self.figure.axes[0] in_arr = linspace(0.0, self.max_x, self.n_points) args = [in_arr] + self.model.param_values # get the number of parameters of the response function n_args = len(args) fn = frompyfunc(self.model.__call__, n_args, 1) out_arr = fn(*args) axes = self.figure.axes[0] axes.plot(in_arr, out_arr, linewidth=2) axes.set_xlabel(self.model.x_label) axes.set_ylabel(self.model.y_label) axes.legend(loc='best') self.data_changed = True show = Button def _show_fired(self): self._redraw() clear = Button def _clear_fired(self): axes = self.figure.axes[0] axes.clear() self.data_changed = True def default_traits_view(self): ''' Generates the view from the param items. ''' #rf_param_items = [ Item( 'model.' + name, format_str = '%g' ) for name in self.model.param_keys ] plot_param_items = [ Item('max_x', label='max x value'), Item('n_points', label='No of plot points') ] control_items = [ Item('show', show_label=False), Item('clear', show_label=False), ] view = View( HSplit( VGroup( Item('@resp_func', show_label=False), #*rf_param_items, label='Function Parameters', id='stats.spirrid_bak.rf_model_view.rf_params', scrollable=True), VGroup(*plot_param_items, label='Plot Parameters', id='stats.spirrid_bak.rf_model_view.plot_params'), VGroup( Item('model.comment', show_label=False, style='readonly'), label='Comment', id='stats.spirrid_bak.rf_model_view.comment', scrollable=True, ), VGroup(HGroup(*control_items), Item('figure', editor=MPLFigureEditor(), resizable=True, show_label=False), label='Plot', id='stats.spirrid_bak.rf_model_view.plot'), dock='tab', id='stats.spirrid_bak.rf_model_view.split'), kind='modal', resizable=True, dock='tab', buttons=[OKButton], id='stats.spirrid_bak.rf_model_view') return view
class MATS2DScalarDamage(MATS2DEval): ''' Scalar Damage Model. ''' implements(IMATSEval) #--------------------------------------------------------------------------- # Parameters of the numerical algorithm (integration) #--------------------------------------------------------------------------- stress_state = Enum("plane_stress", "plane_strain") stiffness = Enum("secant", "algoritmic") #--------------------------------------------------------------------------- # Material parameters #--------------------------------------------------------------------------- E = Float(34e+3, label="E", desc="Young's Modulus", auto_set=False) nu = Float(0.2, label='nu', desc="Poison's ratio", auto_set=False) epsilon_0 = Float(59e-6, label="eps_0", desc="Breaking Strain", auto_set=False) epsilon_f = Float(191e-4, label="eps_f", desc="Shape Factor", auto_set=False) strain_norm = EitherType( klasses=[Mazars, Euclidean, Energy, Mises, Rankine]) D_el = Property(Array(float), depends_on='E, nu, stress_state') @cached_property def _get_D_el(self): if self.stress_state == "plane_stress": return self._get_D_plane_stress() else: return self._get_D_plane_strain() # This event can be used by the clients to trigger an action upon # the completed reconfiguration of the material model # changed = Event #-------------------------------------------------------------------------- # View specification #-------------------------------------------------------------------------- view_traits = View(VSplit( Group(Item('E'), Item('nu'), Item('epsilon_0'), Item('epsilon_f'), Item('strain_norm')), Group( Item('stress_state', style='custom'), Item('stiffness', style='custom'), Spring(resizable=True), label='Configuration parameters', show_border=True, ), ), resizable=True) #-------------------------------------------------------------------------- # Private initialization methods #-------------------------------------------------------------------------- #-------------------------------------------------------------------------- # Setup for computation within a supplied spatial context #-------------------------------------------------------------------------- def get_state_array_size(self): ''' Return number of number to be stored in state array @param sctx:spatial context ''' return 2 def setup(self, sctx): ''' Intialize state variables. @param sctx:spatial context ''' state_arr_size = self.get_state_array_size() sctx.mats_state_array = zeros(state_arr_size, 'float_') # sctx.update_state_on = False def new_cntl_var(self): ''' Return contoll variable array ''' return zeros(3, float_) def new_resp_var(self): ''' Return contoll response array ''' return zeros(3, float_) #-------------------------------------------------------------------------- # Evaluation - get the corrector and predictor #-------------------------------------------------------------------------- def get_corr_pred(self, sctx, eps_app_eng, d_eps, tn, tn1, eps_avg=None): ''' Corrector predictor computation. @param eps_app_eng input variable - engineering strain ''' if eps_avg != None: pass else: eps_avg = eps_app_eng if sctx.update_state_on: eps_n = eps_avg - d_eps e_max, omega = self._get_state_variables(sctx, eps_n) sctx.mats_state_array[0] = e_max sctx.mats_state_array[1] = omega e_max, omega = self._get_state_variables(sctx, eps_app_eng) if self.stiffness == "algorithmic" and \ e_max > self.epsilon_0 and \ e_max > sctx.mats_state_array[0]: D_e_dam = self._get_alg_stiffness(eps_app_eng, e_max, omega) else: D_e_dam = (1 - omega) * self.D_el sigma = dot(((1 - omega) * self.D_el), eps_app_eng) # You print the stress you just computed and the value of the apparent E return sigma, D_e_dam #-------------------------------------------------------------------------- # Subsidiary methods realizing configurable features #-------------------------------------------------------------------------- def _get_state_variables(self, sctx, eps_app_eng): e_max = sctx.mats_state_array[0] omega = sctx.mats_state_array[1] f_trial = self.strain_norm.get_f_trial(eps_app_eng, self.D_el, self.E, self.nu, e_max) if f_trial > 0: e_max += f_trial omega = self._get_omega(e_max) return e_max, omega def _get_omega(self, kappa): ''' Return new value of damage parameter @param kappa: ''' epsilon_0 = self.epsilon_0 epsilon_f = self.epsilon_f if kappa >= epsilon_0: # return 1.-epsilon_0/kappa*exp(-1*(kappa-epsilon_0)/epsilon_f) return 1. - epsilon_0 / kappa * exp(-1 * (kappa - epsilon_0) / \ (epsilon_f - epsilon_0)) else: return 0. def _get_alg_stiffness(self, eps_app_eng, e_max, omega): ''' Return algorithmic stiffness matrix @param eps_app_eng:strain @param e_max:kappa @param omega:damage parameter ''' epsilon_0 = self.epsilon_0 epsilon_f = self.epsilon_f dodk = epsilon_0 / (e_max * e_max) * exp(-(e_max - epsilon_0) / epsilon_f) + \ epsilon_0 / e_max / epsilon_f * exp(-(e_max - epsilon_0) / epsilon_f) dede = self.strain_norm.get_dede(eps_app_eng, self.D_el, self.E, self.nu) D_alg = (1 - omega) * self.D_el - \ dot(dot(self.D_el, eps_app_eng), dede) * dodk return D_alg def _get_D_plane_stress(self): ''' Elastic Matrix - Plane Stress ''' E = self.E nu = self.nu D_stress = zeros([3, 3]) D_stress[0][0] = E / (1.0 - nu * nu) D_stress[0][1] = E / (1.0 - nu * nu) * nu D_stress[1][0] = E / (1.0 - nu * nu) * nu D_stress[1][1] = E / (1.0 - nu * nu) D_stress[2][2] = E / (1.0 - nu * nu) * (1.0 / 2.0 - nu / 2.0) return D_stress def _get_D_plane_strain(self): ''' Elastic Matrix - Plane Strain ''' E = self.E nu = self.nu D_strain = zeros([3, 3]) D_strain[0][0] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[0][1] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1][0] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1][1] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[2][2] = E * (1.0 - nu) / (1.0 + nu) / (2.0 - 2.0 * nu) return D_strain #-------------------------------------------------------------------------- # Response trace evaluators #-------------------------------------------------------------------------- def get_omega(self, sctx, eps_app_eng, *args, **kw): ''' Return damage parameter for RT @param sctx:spatial context @param eps_app_eng:actual strain ''' return array([sctx.mats_state_array[1]]) # Declare and fill-in the rte_dict - it is used by the clients to # assemble all the available time-steppers. # rte_dict = Trait(Dict) def _rte_dict_default(self): return { 'sig_app': self.get_sig_app, 'eps_app': self.get_eps_app, 'omega': self.get_omega }
class ClampedFibre(BaseBC): type = EitherType(names=['one sided', 'double sided'], klasses=[FinClampOne, FinClampDbl])
class FiniteEmbeddedLength(BaseBC): type = EitherType(names=['double sided', 'one sided'], klasses=[FinFreeDbl, FinFreeOne])
class MATS3DScalarDamage(MATS3DEval): r''' Isotropic damage model. ''' node_name = 'Scalar damage' stiffness = tr.Enum("secant", "algorithmic", input=True) r'''Selector of the stiffness calculation. ''' strain_norm = EitherType(klasses=[ Rankine, ], input=True) r'''Selector of the strain norm defining the load surface. ''' epsilon_0 = tr.Float(5e-2, label="eps_0", desc="Strain at the onset of damage", auto_set=False, input=True) r'''Damage function parameter - slope of the damage function. ''' epsilon_f = tr.Float(191e-1, label="eps_f", desc="Slope of the damage function", auto_set=False, input=True) r'''Damage function parameter - slope of the damage function. ''' changed = tr.Event r'''This event can be used by the clients to trigger an action upon the completed reconfiguration of the material model ''' state_var_shapes = {'kappa': (), 'omega': ()} r''' Shapes of the state variables to be stored in the global array at the level of the domain. ''' def init(self, kappa, omega): r''' Initialize the state variables. ''' kappa[...] = 0 omega[...] = 0 def get_corr_pred(self, eps_Emab_n1, tn1, kappa, omega): r''' Corrector predictor computation. ''' I = self.update_state_variables(eps_Emab_n1, kappa, omega) phi_Em = (1.0 - omega) D_Emabcd = np.einsum('...,abcd->...abcd', phi_Em, self.D_abef) sigma_Emab = np.einsum('...abcd,...cd->...ab', D_Emabcd, eps_Emab_n1) # algorithmic switched off - because the derivative # of the strain norm is still not available if False: # algorithmic: D_Emabcd_red_I = self._get_D_abcd_alg_reduction( kappa[I], eps_Emab_n1[I]) D_Emabcd[I] -= D_Emabcd_red_I return sigma_Emab, D_Emabcd def update_state_variables(self, eps_Emab, kappa, omega): eps_eq_Em = self.strain_norm.get_eps_eq(eps_Emab, kappa) f_trial_Em = eps_eq_Em - self.epsilon_0 I = np.where(f_trial_Em > 0) kappa[I] = eps_eq_Em[I] omega[I] = self._get_omega(eps_eq_Em[I]) return I def _get_omega(self, kappa_Em): r''' Return new value of damage parameter @param kappa_Em: maximum strain norm achieved so far ''' omega_Em = np.zeros_like(kappa_Em) epsilon_0 = self.epsilon_0 epsilon_f = self.epsilon_f I = np.where(kappa_Em >= epsilon_0) omega_Em[I] = ( 1.0 - (epsilon_0 / kappa_Em[I] * np.exp(-1.0 * (kappa_Em[I] - epsilon_0) / (epsilon_f - epsilon_0)))) return omega_Em def _get_domega(self, kappa_Em): ''' Return new value of damage parameter derivative @param kappa_Em: maximum strain norm achieved so far ''' epsilon_0 = self.epsilon_0 epsilon_f = self.epsilon_f domega_Em = np.zeros_like(kappa_Em) I = np.where(kappa_Em >= epsilon_0) factor_1 = epsilon_0 / (kappa_Em[I] * kappa_Em[I]) factor_2 = epsilon_0 / (kappa_Em[I] * (epsilon_f - epsilon_0)) domega_Em[I] = ((factor_1 + factor_2) * np.exp(-(kappa_Em[I] - epsilon_0) / (epsilon_f - epsilon_0))) return domega_Em def _get_D_abcd_alg_reduction(self, kappa_Em, eps_Emab_n1): '''Calculate the stiffness term to be subtracted from the secant stiffness to get the algorithmic stiffness. ''' domega_Em = self._get_domega(kappa_Em) deps_eq_Emcd = self.strain_norm.get_deps_eq(eps_Emab_n1) return np.einsum('...,...cd,abcd,...cd->...abcd', domega_Em, deps_eq_Emcd, self.D_abef, eps_Emab_n1) traits_view = View(VSplit( Group(Item('E'), Item('nu'), Item('epsilon_0'), Item('epsilon_f'), Item('strain_norm')), Group( Item('stiffness', style='custom'), Spring(resizable=True), label='Configuration parameters', show_border=True, ), ), resizable=True) tree_view = View( Group(Item('E', full_size=True, resizable=True), Item('nu'), Item('epsilon_0'), Item('epsilon_f'), Item('strain_norm')), ) # Declare and fill-in the rte_dict - it is used by the clients to # assemble all the available time-steppers. # rte_dict = tr.Trait(tr.Dict) def _rte_dict_default(self): return {'sig_app': self.get_sig_app, 'omega': self.get_omega}
class RespFunc(ResponseFunctionBase): listener_string = 'boundary.+modified,'\ 'approach.+modified,'\ 'material.+modified,'\ 'plot.+modified,'\ 'geometry.+modified,'\ 'boundary.type.+modified' def __init__(self, **kw): super(RespFunc, self).__init__(**kw) self.material.add_listeners = self.add_listeners self.material.remove_listeners = self.remove_listeners self.material.get_value = self.get_value self._boundary_changed() boundary = EitherType( names=['infinite length', 'finite length', 'clamped fibre end'], klasses=[InfiniteEmbeddedLength, FiniteEmbeddedLength, ClampedFibre]) def _boundary_changed(self): self.remove_listeners() self.geometry = self.boundary.geometry self.plot = self.boundary.plot self.get_value() self.add_listeners() def update_parameters(self): self.remove_listeners() self.approach.material = self.material self.boundary.type.geometry = self.geometry self.boundary.type.plot = self.plot self.approach.geometry = self.geometry self.approach.plot = self.plot self.add_listeners() approach = EitherType(names=['stress criterion', 'energy criterion'], klasses=[ StressCriterion, EnergyCriterion, ]) geometry = Instance(Geometry) def _geometry_default(self): return Geometry() material = Instance(Material) def _material_default(self): return Material() plot = Instance(Plot) def _plot_default(self): return Plot() values = Tuple(Array, Array) def get_value(self): self.update_parameters() self.remove_listeners() if self.boundary.type.BC == 'double-sided pull-out with infinite embedded length': self.approach.clamp = False l = self.geometry.l self.geometry.l = 0.0 u_plot = self.plot.u_plot self.plot.u_plot = self.plot.w_plot / 2.0 self.approach.bool_infinite = True x, y = self.approach.get_value() self.approach.bool_infinite = False x *= 2.0 self.values = (x, y) self.geometry.l = l self.plot.u_plot = u_plot elif self.boundary.type.BC == 'double-sided pull-out with finite embedded length': # todo: not complete self.approach.clamp = False l = self.geometry.l L = self.geometry.L self.geometry.l = 0.0 u_plot = self.plot.u_plot self.geometry.L = self.boundary.type.Le self.approach.bool_finite = True x1, y1 = self.approach.get_value() self.geometry.L = self.boundary.geometry.Lf - self.boundary.type.Le x2, y2 = self.approach.get_value() self.values = self.approach.get_value() self.approach.bool_finite = False self.geometry.l = l self.geometry.L = L elif self.boundary.type.BC == 'double-sided pull-out with clamped fibre end': # todo: not complete self.approach.clamp = True self.approach.bool_clamp = True self.values = self.approach.get_value() self.approach.bool_clamp = False elif self.boundary.type.BC == 'one-sided pull-out with clamped fibre end': self.approach.clamp = True self.approach.bool_clamp = True self.approach.L = self.boundary.type.Le self.values = self.approach.get_value() self.approach.bool_clamp = False elif self.boundary.type.BC == 'one-sided pull-out with finite embedded length': self.approach.clamp = False self.approach.bool_finite = True self.values = self.approach.get_value() self.approach.bool_finite = False elif self.boundary.type.BC == 'one-sided pull-out with infinite embedded length': self.approach.clamp = False self.approach.bool_infinite = True self.values = self.approach.get_value() self.approach.bool_infinite = False else: # place holder self.values = self.approach.get_value() self.add_listeners()