Ejemplo n.º 1
0
class CustomShape(CrossSectionShapeBase):
    cs_points_str = Str(
        '100, 0\n100, 140\n25, 150\n25, 750\n100, 760\n100, 900\n-100, 900\n-100, 760\n-25, 750\n-25, 150\n-100, 140\n-100, 0'
    )
    apply_points_btn = Button()

    @tr.observe("apply_points_btn")
    def apply_points_btn_clicked(self, event):
        print(
            'This should update the plot with the new points, but maybe it\'s not needed'
        )

    ipw_view = View(
        Item('cs_points_str',
             latex=r'\mathrm{Points}',
             editor=TextAreaEditor()),
        Item('apply_points_btn',
             editor=ButtonEditor(label='Apply points', icon='refresh')),
    )

    cs_points = tr.Property()

    def _get_cs_points(self):
        return self._parse_2d_points_str_into_array(self.cs_points_str)

    @staticmethod
    def _parse_2d_points_str_into_array(points_str):
        """ This will parse str of points written like this '0, 0\n 1, 1' to ([0, 0], [1, 1]) """
        points_str = points_str.replace('\n', ', ').replace('\r', ', ')
        points_array = np.fromstring(points_str, dtype=int, sep=',')
        if points_array.size % 2 != 0:
            points_array = np.append(points_array, 0)
        points_array = points_array.reshape((int(points_array.size / 2), 2))
        return points_array.reshape((int(points_array.size / 2), 2))

    def get_cs_area(self):
        points_xy = self.cs_points
        x = points_xy[:, 0]
        y = points_xy[:, 1]
        # See https://stackoverflow.com/a/30408825 for following Implementation of Shoelace formula
        return 0.5 * np.abs(
            np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))

    def get_cs_i(self):
        pass

    def get_b(self, z_positions_array):
        # TODO get b for polygon given its points coordinates
        pass

    def update_plot(self, ax):
        cs = Polygon(self.cs_points)
        patch_collection = PatchCollection([cs],
                                           facecolor=(.5, .5, .5, 0.2),
                                           edgecolors=(0, 0, 0, 1))
        ax.add_collection(patch_collection)
        # ax.scatter(0, CustomShape.get_cs_i(self)[0], color='white', s=self.B_w, marker="+")

        ax.autoscale_view()
        ax.set_aspect('equal')
Ejemplo n.º 2
0
class TFBilinear(TimeFunction):
    loading_ratio = Float(0.5, TIME=True)
    time_ratio = Float(0.5, TIME=True)

    ipw_view = View(
        Item('t_max'),
        Item('loading_ratio', editor=FloatRangeEditor(low=0, high=1)),
        Item('time_ratio', editor=FloatRangeEditor(low=0, high=1)),
        Item('range_factor'))

    range_factor = Float(1)

    def _generate_time_function(self):
        # Define the monotonic range beyond the unit range
        # allow for interactive extensions. The fact
        d_history = np.array([
            0, 1 * self.loading_ratio * self.range_factor,
            1 * self.range_factor
        ])
        t_arr = np.array([
            0, self.t_max * self.time_ratio * self.range_factor,
            self.t_max * self.range_factor
        ])
        return interp1d(t_arr,
                        d_history,
                        bounds_error=False,
                        fill_value=self.t_max)
Ejemplo n.º 3
0
class TLine(BMCSLeafNode):
    '''
    Time line for the control parameter.

    This class sets the time-range of the computation - the start and stop time.
    val is the value of the current time.

    TODO - the info page including the number of load steps
    and estimated computation time.

    TODO - the slide bar is not read-only. How to include a real progress bar?
    '''
    node_name = 'time range'

    min = bu.Float(0.0, TIME=True)
    max = bu.Float(1.0, TIME=True)
    step = bu.Float(0.1, TIME=True)
    val = bu.Float(0.0)

    def _val_changed(self):
        if self.time_change_notifier:
            self.time_change_notifier(self.val)

    @on_trait_change('min,max')
    def _time_range_changed(self):
        if self.time_range_change_notifier:
            self.time_range_change_notifier(self.max)

    time_change_notifier = Callable

    time_range_change_notifier = Callable

    ipw_view = View(Item('min', full_size=True), Item('max'), Item('step'),
                    Item('val', style='readonly'))
Ejemplo n.º 4
0
class BarLayer(ReinfLayer):
    """"Layer consisting of discrete bar reinforcement"""
    name = 'Bar layer'
    ds = Float(16, CS=True)
    count = Int(1, CS=True)

    A = tr.Property(Float, depends_on='+CS')
    """cross section area of reinforcement layers"""
    @tr.cached_property
    def _get_A(self):
        return self.count * np.pi * (self.ds / 2.)**2

    P = tr.Property(Float, depends_on='+CS')
    """permeter of reinforcement layers"""

    @tr.cached_property
    def _get_P(self):
        return self.count * np.pi * (self.ds)

    def _matmod_default(self):
        return 'steel'

    ipw_view = View(
        Item('matmod', latex=r'\mathrm{behavior}'),
        Item('z', latex=r'z \mathrm{[mm]}'),
        Item('ds', latex=r'ds \mathrm{[mm]}'),
        Item('count', latex='count'),
        Item('A', latex=r'A [mm^2]'),
    )
Ejemplo n.º 5
0
class CrossSectionDesign(Model):
    name = 'Cross Section Design'

    matrix = EitherType(
        options=[
            ('piecewise linear', PWLConcreteMatMod),
            ('EC2', EC2ConcreteMatMod),
            ('EC2 with plateau', EC2PlateauConcreteMatMod),
            # ('EC2 softening tension', ConcreteMaterialModelAdv),
        ],
        MAT=True)

    cross_section_layout = Instance(CrossSectionLayout)

    def _cross_section_layout_default(self):
        return CrossSectionLayout(cs_design=self)

    tree = ['matrix', 'cross_section_layout', 'cross_section_shape']

    csl = tr.Property

    def _get_csl(self):
        return self.cross_section_layout

    H = tr.Property(Float)

    def _get_H(self):
        return self.cross_section_shape_.H

    def _set_H(self, value):
        self.cross_section_shape_.H = value

    cross_section_shape = EitherType(
        options=[
            ('rectangle', Rectangle),
            # ('circle', Circle),
            ('I-shape', IShape),
            ('T-shape', TShape),
            ('custom', CustomShape)
        ],
        CS=True,
        tree=True)

    ipw_view = View(
        Item('matrix',
             latex=r'\mathrm{concrete behavior}',
             editor=EitherTypeEditor(show_properties=False)),
        Item('cross_section_shape',
             latex=r'\mathrm{shape}',
             editor=EitherTypeEditor(show_properties=False)),
        Item('cross_section_layout', latex=r'\mathrm{layout}'),
    )

    def subplots(self, fig):
        return fig.subplots(1, 1)

    def update_plot(self, ax):
        self.cross_section_shape_.update_plot(ax)
        self.cross_section_layout.update_plot(ax)
Ejemplo n.º 6
0
class TFMonotonic(TimeFunction):

    ipw_view = View(Item('t_max'), Item('range_factor'))

    range_factor = Float(10000)

    def _generate_time_function(self):
        # Define the monotonic range beyond the unit range
        # allow for interactive extensions. The fact
        d_history = np.array([0, 1 * self.range_factor])
        t_arr = np.array([0, self.t_max * self.range_factor])
        return interp1d(t_arr, d_history)
Ejemplo n.º 7
0
class TFCyclicSymmetricConstant(TimeFunction):
    number_of_cycles = Int(10, TIME=True)

    ipw_view = View(Item('number_of_cycles'), Item('t_max'))

    def _generate_time_function(self):
        d_levels = np.zeros((self.number_of_cycles * 2, ))
        d_levels.reshape(-1, 2)[:, 0] = -1
        d_levels.reshape(-1, 2)[:, 1] = 1
        d_history = d_levels.flatten()
        t_arr = np.linspace(0, self.t_max, len(d_history))
        return interp1d(t_arr, d_history)
Ejemplo n.º 8
0
class TFCyclicNonsymmetricIncreasing(TimeFunction):
    number_of_cycles = Int(10, TIME=True)

    ipw_view = View(Item('number_of_cycles'), Item('t_max'))

    def _generate_time_function(self):
        d_levels = np.linspace(0, 1, self.number_of_cycles * 2)
        d_levels.reshape(-1, 2)[:, 0] *= 0
        d_history = d_levels.flatten()
        t_arr = np.linspace(0, self.t_max, len(d_history))
        return interp1d(t_arr,
                        d_history,
                        bounds_error=False,
                        fill_value=self.t_max)
Ejemplo n.º 9
0
class CrossSectionShapeBase(InteractiveModel):
    """"This class describes the geometry of the cross section."""
    name = 'Cross Section Shape'

    H = Float(400, GEO=True)

    ipw_view = View(Item('H', minmax=(10, 3000), latex='H [mm]'))
Ejemplo n.º 10
0
class TFCyclicSin(TimeFunction):
    number_of_cycles = Int(10, TIME=True)
    phase_shift = Float(0, TIME=True)

    ipw_view = View(Item('number_of_cycles'), Item('phase_shift'),
                    Item('t_max'))

    def _generate_time_function(self):
        t = sp.symbols(r't')
        p = self.phase_shift
        tf = sp.sin(2 * self.number_of_cycles * sp.pi * t / self.t_max)
        if p > 0:
            T = self.t_max / self.number_of_cycles
            pT = p * T
            tf = sp.Piecewise((0, t < pT), (tf.subs(t, t - pT), True))
        return sp.lambdify(t, tf, 'numpy')
Ejemplo n.º 11
0
class FabricLayer(ReinfLayer):
    """Reinforcement with a grid structure
    """
    name = 'Fabric layer'
    width = Float(100, CS=True)
    spacing = Float(14, CS=True)
    A_roving = Float(1, CS=True)

    A = tr.Property(Float, depends_on='+CS')
    """cross section area of reinforcement layers"""
    @tr.cached_property
    def _get_A(self):
        return int(self.width / self.spacing) * self.A_roving

    P = tr.Property(Float, depends_on='+CS')
    """cross section area of reinforcement layers"""

    @tr.cached_property
    def _get_P(self):
        raise NotImplementedError

    def _matmod_default(self):
        return 'carbon'

    ipw_view = View(
        Item('matmod', latex=r'\mathrm{behavior}'),
        Item('z', latex='z \mathrm{[mm]}'),
        Item('width', latex='\mathrm{fabric~width} \mathrm{[mm]}'),
        Item('spacing', latex='\mathrm{rov~spacing} \mathrm{[mm]}'),
        Item('A_roving', latex='A_r \mathrm{[mm^2]}'),
        Item('A', latex=r'A [mm^2]', readonly=True),
    )
Ejemplo n.º 12
0
class TFCyclicNonsymmetricConstant(TimeFunction):
    number_of_cycles = Int(10, TIME=True)
    unloading_ratio = Float(0.5, TIME=True)
    shift_cycles = Int(0, TIME=True)

    ipw_view = View(
        Item('number_of_cycles'), Item('shift_cycles'),
        Item('unloading_ratio', editor=FloatRangeEditor(low=0, high=1)),
        Item('t_max'))

    def _generate_time_function(self):
        d_1 = np.zeros(1)
        d_2 = np.zeros(((self.number_of_cycles + self.shift_cycles) * 2, ))
        d_2.reshape(-1, 2)[self.shift_cycles:, 0] = 1
        d_2.reshape(-1, 2)[self.shift_cycles:, 1] = self.unloading_ratio
        d_history = d_2.flatten()
        d_arr = np.hstack((d_1, d_history))
        t_arr = np.linspace(0, self.t_max, len(d_arr))
        return interp1d(t_arr,
                        d_arr,
                        bounds_error=False,
                        fill_value=self.t_max)
Ejemplo n.º 13
0
class ReinfLayer(InteractiveModel):
    # TODO: changes in the ipw interactive window doesn't reflect on mkappa
    #  (maybe because these are lists and changing the elements doesn't notify)
    name = 'Reinf layer'

    cs_layout = tr.WeakRef

    z = Float(50, CS=True)
    """z positions of reinforcement layers"""

    P = Float(100, CS=True)
    """Perimeter"""

    A = Float(100, CS=True)
    """Cross sectional area"""

    matmod = EitherType(
        options=[('steel', SteelReinfMatMod), ('carbon', CarbonReinfMatMod)])

    ipw_view = View(
        Item('matmod',
             latex=r'\mathrm{behavior}',
             editor=EitherTypeEditor(show_properties=False)),
        Item('z', latex='z \mathrm{[mm]}'),
        Item('A', latex='A \mathrm{[mm^2]}'),
    )

    tree = ['matmod']

    def get_N(self, eps):
        return self.A * self.matmod_.get_sig(eps)

    def update_plot(self, ax):
        eps_range = self.matmod_.get_eps_plot_range()
        N_range = self.get_N(eps_range)
        ax.plot(eps_range, N_range, color='red')
        ax.fill_between(eps_range, N_range, 0, color='red', alpha=0.1)
        ax.set_xlabel(r'$\varepsilon$ [-]')
        ax.set_ylabel(r'$F$ [N]')
Ejemplo n.º 14
0
class MATS3DIfcElastic(MATSEval):

    node_name = "Elastic interface"

    E_n = Float(1e+6, tooltip='Normal stiffness of the interface [MPa]',
                MAT=True, unit='MPa/m', symbol='E_\mathrm{n}',
                desc='Normal stiffness of the interface',
                auto_set=False, enter_set=True)

    E_s = Float(1.0, tooltip='Shear stiffness of the interface [MPa]',
                MAT=True, unit='MPa/m', symbol='E_\mathrm{s}',
                desc='Shear-modulus of the interface',
                auto_set=True, enter_set=True)

    state_var_shapes = {}

    D_rs = Property(depends_on='E_n,E_s')

    @cached_property
    def _get_D_rs(self):
        return np.array([[self.E_n, 0, 0],
                         [0, self.E_s, 0],
                         [0, 0, self.E_s]], dtype=np.float_)

    def get_corr_pred(self, u_r, tn1):
        tau = np.einsum(
            'rs,...s->...r',
            self.D_rs, u_r)
        grid_shape = tuple([1 for _ in range(len(u_r.shape[:-1]))])
        D = self.D_rs.reshape(grid_shape + self.D_rs.shape)
        return tau, D

    ipw_view = View(
        Item('E_s'),
        Item('E_n')
    )
Ejemplo n.º 15
0
class Rectangle(CrossSectionShapeBase):

    B = Float(200, GEO=True)

    ipw_view = View(
        *CrossSectionShapeBase.ipw_view.
        content,  # this will add View Items of the base class CrossSectionShapeBase
        Item('B', minmax=(10, 500), latex='B \mathrm{[mm]}'))

    def get_cs_area(self):
        return self.B * self.H

    def get_cs_i(self):
        return self.B * self.H**3 / 12

    def get_b(self, z_positions_array):
        return np.full_like(z_positions_array, self.B)

    def update_plot(self, ax):

        ax.fill(
            [-self.B / 2, self.B / 2, self.B / 2, -self.B / 2, -self.B / 2],
            [0, 0, self.H, self.H, 0],
            color='gray',
            alpha=0.2)
        ax.plot(
            [-self.B / 2, self.B / 2, self.B / 2, -self.B / 2, -self.B / 2],
            [0, 0, self.H, self.H, 0],
            color='black')
        ax.autoscale_view()
        ax.set_aspect('equal')

        ax.annotate('Area = {} $mm^2$'.format(int(Rectangle.get_cs_area(self)),
                                              0),
                    xy=(-self.H / 2 * 0.8, self.H / 2),
                    color='blue')
        ax.annotate('I = {} $mm^4$'.format(int(Rectangle.get_cs_i(self)), 0),
                    xy=(-self.H / 2 * 0.8, self.H / 2 * 0.8),
                    color='blue')
Ejemplo n.º 16
0
class TFSelector(TimeFunction):
    name = 'time function'
    profile = EitherType(options=[
        ('monotonic', TFMonotonic), ('bilinear', TFBilinear),
        ('cyclic-sym-incr', TFCyclicSymmetricConstant),
        ('cyclic-sym-const', TFCyclicSymmetricIncreasing),
        ('cyclic-nonsym-incr', TFCyclicNonsymmetricConstant),
        ('cyclic-nonsym-const', TFCyclicNonsymmetricIncreasing)
    ],
                         TIME=True)

    t_max = tr.Property(Float)

    def _get_t_max(self):
        return self.profile_.t_max

    ipw_view = View(Item('profile'))

    def __call__(self, arg):
        return self.profile_(arg)

    def update_plot(self, axes):
        self.profile_.update_plot(axes)
Ejemplo n.º 17
0
class Circle(CrossSectionShapeBase):

    # TODO->Rostia: provide input field instead minmax range
    # H from the base class is used as the D, for the diameter of the circular section

    H = Float(250, GEO=True)

    ipw_view = View(Item('H', minmax=(10, 3000), latex='D [mm]'), )

    def get_cs_area(self):
        return np.pi * self.H * self.H

    def get_cs_i(self):
        return np.pi * self.H**4 / 4

    def get_b(self, z_positions_array):
        return np.full_like(z_positions_array, self.H)

        # TODO->Saeed: complete this

    def update_plot(self, ax):
        # TODO->Saeed: fix this

        circle = MPL_Circle((0, self.H / 2),
                            self.H / 2,
                            facecolor=(.5, .5, .5, 0.2),
                            edgecolor=(0, 0, 0, 1))

        ax.add_patch(circle)
        ax.autoscale_view()
        ax.set_aspect('equal')
        ax.annotate('Area = {} $mm^2$'.format(int(self.get_cs_area()), 0),
                    xy=(-self.H / 2 * 0.8, self.H / 2),
                    color='blue')
        ax.annotate('I = {} $mm^4$'.format(int(self.get_cs_i()), 0),
                    xy=(-self.H / 2 * 0.8, self.H / 2 * 0.8),
                    color='blue')
Ejemplo n.º 18
0
class MATS2DMplCSDEEQ(MATSBase, InteractiveModel):

    concrete_type = tr.Int

    gamma_T = tr.Float(100000.,
                       label="Gamma",
                        desc=" Tangential Kinematic hardening modulus",
                        enter_set=True,
                        auto_set=False)

    K_T = tr.Float(10000.,
                label="K",
                desc="Tangential Isotropic harening",
                enter_set=True,
                auto_set=False)

    S_T = tr.Float(0.005,
                label="S",
                desc="Damage strength",
                enter_set=True,
                auto_set=False)

    r_T = tr.Float(9.,
                label="r",
                desc="Damage cumulation parameter",
                enter_set=True,
                auto_set=False)
    e_T = tr.Float(12.,
                label="e",
                desc="Damage cumulation parameter",
                enter_set=True,
                auto_set=False)

    c_T = tr.Float(4.6,
                label="c",
                desc="Damage cumulation parameter",
                enter_set=True,
                auto_set=False)

    tau_pi_bar = tr.Float(1.7,
                       label="Tau_bar",
                       desc="Reversibility limit",
                       enter_set=True,
                       auto_set=False)

    a = tr.Float(0.003,
              label="a",
              desc="Lateral pressure coefficient",
              enter_set=True,
              auto_set=False)

    ipw_view = View(
        Item('gamma_T', latex=r'\gamma_\mathrm{T}', minmax=(10,100000)),
        Item('K_T', latex=r'K_\mathrm{T}', minmax=(10, 10000)),
        Item('S_T', latex=r'S_\mathrm{T}', minmax=(0.001, 0.01)),
        Item('r_T', latex=r'r_\mathrm{T}', minmax=(1, 3)),
        Item('e_T', latex=r'e_\mathrm{T}', minmax=(1, 40)),
        Item('c_T', latex=r'c_\mathrm{T}', minmax=(1, 10)),
        Item('tau_pi_bar', latex=r'\bar{\tau}', minmax=(1, 10)),
        Item('a', latex=r'a', minmax=(0.001, 3)),
    )

    # -------------------------------------------
    # Normal_Tension constitutive law parameters (without cumulative normal strain)
    # -------------------------------------------
    Ad = tr.Float(100.0,
               label="a",
               desc="brittleness coefficient",
               enter_set=True,
               auto_set=False)

    eps_0 = tr.Float(0.00008,
                  label="a",
                  desc="threshold strain",
                  enter_set=True,
                  auto_set=False)

    # -----------------------------------------------
    # Normal_Compression constitutive law parameters
    # -----------------------------------------------
    K_N = tr.Float(10000.,
                label="K_N",
                desc=" Normal isotropic harening",
                enter_set=True,
                auto_set=False)

    gamma_N = tr.Float(5000.,
                    label="gamma_N",
                    desc="Normal kinematic hardening",
                    enter_set=True,
                    auto_set=False)

    sigma_0 = tr.Float(30.,
                    label="sigma_0",
                    desc="Yielding stress",
                    enter_set=True,
                    auto_set=False)

    # -------------------------------------------------------------------------
    # Cached elasticity tensors
    # -------------------------------------------------------------------------

    E = tr.Float(35e+3,
                 label="E",
                 desc="Young's Modulus",
                 auto_set=False,
                 input=True)

    nu = tr.Float(0.2,
                  label='nu',
                  desc="Poison ratio",
                  auto_set=False,
                  input=True)

    def _get_lame_params(self):
        # la = self.E * self.nu / ((1. + self.nu) * (1. - 2. * self.nu))
        # # second Lame parameter (shear modulus)
        # mu = self.E / (2. + 2. * self.nu)
        la = self.E * self.nu / ((1. + self.nu) * (1. - self.nu))
        mu = self.E / (2. + 2. * self.nu)
        return la, mu

    D_abef = tr.Property(tr.Array, depends_on='+input')

    @tr.cached_property
    def _get_D_abef(self):
        la = self._get_lame_params()[0]
        mu = self._get_lame_params()[1]
        delta = np.identity(2)
        D_abef = (np.einsum(',ij,kl->ijkl', la, delta, delta) +
                  np.einsum(',ik,jl->ijkl', mu, delta, delta) +
                  np.einsum(',il,jk->ijkl', mu, delta, delta))
        return D_abef

    @tr.cached_property
    def _get_state_var_shapes(self):
        return {'w_N_Emn': (self.n_mp,),
                'z_N_Emn': (self.n_mp,),
                'alpha_N_Emn': (self.n_mp,),
                'r_N_Emn': (self.n_mp,),
                'eps_N_p_Emn': (self.n_mp,),
                'sigma_N_Emn': (self.n_mp,),
                'w_T_Emn': (self.n_mp,),
                'z_T_Emn': (self.n_mp,),
                'alpha_T_Emna': (self.n_mp, 2),
                'eps_T_pi_Emna': (self.n_mp, 2),
                }

    #--------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    # (without cumulative normal strain for fatigue under tension)
    #--------------------------------------------------------------
    def get_normal_law(self, eps_N_Emn, omega_N_Emn, z_N_Emn,
                       alpha_N_Emn, r_N_Emn, eps_N_p_Emn, eps_aux):

        eps_N_Aux = self._get_e_N_Emn_2(eps_aux)

        E_N = self.E / (1.0 - 2.0 * self.nu)

        # E_N = self.E * (1.0 + 2.0 *  self.nu) / (1.0 - self.nu**2)


        # When deciding if a microplane is in tensile or compression, we define a strain boundary such that that
        # sigmaN <= 0 if eps_N < 0, avoiding entering in the quadrant of compressive strains and traction

        sigma_trial = E_N * (eps_N_Emn - eps_N_p_Emn)
        pos1 = [(eps_N_Emn < -1e-6) & (sigma_trial > 1e-6)] # looking for microplanes violating strain boundary
        sigma_trial [pos1[0]] = 0
        pos = eps_N_Emn > 1e-6                            # microplanes under traction
        pos2 = eps_N_Emn < -1e-6                          # microplanes under compression
        H = 1.0 * pos
        H2 = 1.0 * pos2

        # thermo forces
        sigma_N_Emn_tilde = E_N * (eps_N_Emn - eps_N_p_Emn)
        sigma_N_Emn_tilde[pos1[0]] = 0                      # imposing strain boundary

        Z = self.K_N * z_N_Emn
        X = self.gamma_N * alpha_N_Emn * H2
        h = (self.sigma_0 + Z) * H2

        f_trial = (abs(sigma_N_Emn_tilde - X) - h) * H2

        # threshold plasticity

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
            (E_N / (1 - omega_N_Emn) + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p_Emn = eps_N_p_Emn + delta_lamda * \
            np.sign(sigma_N_Emn_tilde - X)
        z_N_Emn = z_N_Emn + delta_lamda
        alpha_N_Emn = alpha_N_Emn + delta_lamda * \
            np.sign(sigma_N_Emn_tilde - X)

        def R_N(r_N_Emn): return (1.0 / self.Ad) * (-r_N_Emn / (1.0 + r_N_Emn))

        Y_N = 0.5 * H * E_N * (eps_N_Emn - eps_N_p_Emn) ** 2.0
        Y_0 = 0.5 * E_N * self.eps_0 ** 2.0

        f = (Y_N - (Y_0 + R_N(r_N_Emn)))

        # threshold damage

        thres_2 = f > 1e-6

        def f_w(Y): return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))

        omega_N_Emn[f > 1e-6] = f_w(Y_N)[f > 1e-6]
        r_N_Emn[f > 1e-6] = -omega_N_Emn[f > 1e-6]

        sigma_N_Emn = (1.0 - H * omega_N_Emn) * E_N * (eps_N_Emn - eps_N_p_Emn)
        pos1 = [(eps_N_Emn < -1e-6) & (sigma_trial > 1e-6)] # looking for microplanes violating strain boundary
        sigma_N_Emn[pos1[0]] = 0


        Z = self.K_N * z_N_Emn
        X = self.gamma_N * alpha_N_Emn * H2

        return omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn, Z, X, Y_N

    #-------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    #-------------------------------------------------------------------------
    def get_tangential_law(self, eps_T_Emna, omega_T_Emn, z_T_Emn,
                           alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn):

        E_T = self.E / (1.0 + self.nu)

        # E_T = self.E * (1.0 - 4 * self.nu) / \
        #     ((1.0 + self.nu) * (1.0 - 2 * self.nu))

        # E_T = self.E * (1.0 - 3.0 *  self.nu) / (1.0 - self.nu**2)


        # thermo forces

        sig_pi_trial = E_T * (eps_T_Emna - eps_T_pi_Emna)

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        norm_1 = np.sqrt(
            np.einsum(
                '...na,...na->...n',
                (sig_pi_trial - X), (sig_pi_trial - X))
        )
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        # threshold

        f = norm_1 - self.tau_pi_bar - \
            Z + self.a * sigma_N_Emn

        plas_1 = f > 1e-6
        elas_1 = f < 1e-6

        delta_lamda = f / \
            (E_T / (1.0 - omega_T_Emn) + self.gamma_T + self.K_T) * plas_1

        norm_2 = 1.0 * elas_1 + np.sqrt(
            np.einsum(
                '...na,...na->...n',
                (sig_pi_trial - X), (sig_pi_trial - X))) * plas_1

        eps_T_pi_Emna[..., 0] = eps_T_pi_Emna[..., 0] + plas_1 * delta_lamda * \
            ((sig_pi_trial[..., 0] - X[..., 0]) /
             (1.0 - omega_T_Emn)) / norm_2
        eps_T_pi_Emna[..., 1] = eps_T_pi_Emna[..., 1] + plas_1 * delta_lamda * \
            ((sig_pi_trial[..., 1] - X[..., 1]) /
             (1.0 - omega_T_Emn)) / norm_2

        omega_T_Emn += ((1 - omega_T_Emn) ** self.c_T) * \
            (delta_lamda * (Y / self.S_T) ** self.r_T) * \
            (self.tau_pi_bar / (self.tau_pi_bar + self.a * sigma_N_Emn)) ** self.e_T

        alpha_T_Emna[..., 0] = alpha_T_Emna[..., 0] + plas_1 * delta_lamda * \
            (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        alpha_T_Emna[..., 1] = alpha_T_Emna[..., 1] + plas_1 * delta_lamda * \
            (sig_pi_trial[..., 1] - X[..., 1]) / norm_2

        z_T_Emn = z_T_Emn + delta_lamda

        sigma_T_Emna = np.einsum(
            '...n,...na->...na', (1 - omega_T_Emn), E_T * (eps_T_Emna - eps_T_pi_Emna))

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        return omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna, Z, X, Y

#     #-------------------------------------------------------------------------
#     # MICROPLANE-Kinematic constraints
#     #-------------------------------------------------------------------------

    #-------------------------------------------------

    # get the operator of the microplane normals
    _MPNN = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPNN(self):
        print(self._MPN)
        MPNN_nij = np.einsum('ni,nj->nij', self._MPN, self._MPN)
        return MPNN_nij

    # get the third order tangential tensor (operator) for each microplane
    _MPTT = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPTT(self):
        delta = np.identity(2)
        MPTT_nijr = 0.5 * (
            np.einsum('ni,jr -> nijr', self._MPN, delta) +
            np.einsum('nj,ir -> njir', self._MPN, delta) - 2 *
            np.einsum('ni,nj,nr -> nijr', self._MPN, self._MPN, self._MPN)
        )
        return MPTT_nijr

    def _get_e_N_Emn_2(self, eps_Emab):
        # get the normal strain array for each microplane
        return np.einsum('nij,...ij->...n', self._MPNN, eps_Emab)

    def _get_e_T_Emnar_2(self, eps_Emab):
        # get the tangential strain vector array for each microplane
        MPTT_ijr = self._get__MPTT()
        return np.einsum('nija,...ij->...na', MPTT_ijr, eps_Emab)

    #--------------------------------------------------------
    # return the state variables (Damage , inelastic strains)
    #--------------------------------------------------------
    def _get_state_variables(self, eps_Emab,
                             int_var, eps_aux):

        e_N_arr = self._get_e_N_Emn_2(eps_Emab)
        e_T_vct_arr = self._get_e_T_Emnar_2(eps_Emab)

        omega_N_Emn = int_var[:, 0]
        z_N_Emn = int_var[:, 1]
        alpha_N_Emn = int_var[:, 2]
        r_N_Emn = int_var[:, 3]
        eps_N_p_Emn = int_var[:, 4]
        sigma_N_Emn = int_var[:, 5]

        omega_T_Emn = int_var[:, 9]
        z_T_Emn = int_var[:, 10]
        alpha_T_Emna = int_var[:, 11:13]
        eps_T_pi_Emna = int_var[:, 13:15]


        omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn, Z_n, X_n, Y_n = self.get_normal_law(e_N_arr,  omega_N_Emn, z_N_Emn,
                                                                                                        alpha_N_Emn, r_N_Emn, eps_N_p_Emn, eps_aux)

        omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna, Z_T, X_T, Y_T = self.get_tangential_law(e_T_vct_arr, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)

        # Definition internal variables / forces per column:  1) damage N, 2)iso N, 3)kin N, 4)consolidation N, 5) eps p N,
        # 6) sigma N, 7) iso F N, 8) kin F N, 9) energy release N, 10) damage T, 11) iso T, 12-13) kin T, 14-15) eps p T,
        # 16-17) sigma T, 18) iso F T, 19-20) kin F T, 21) energy release T

        int_var[:, 0] = omega_N_Emn
        int_var[:, 1] = z_N_Emn
        int_var[:, 2] = alpha_N_Emn
        int_var[:, 3] = r_N_Emn
        int_var[:, 4] = eps_N_p_Emn
        int_var[:, 5] = sigma_N_Emn
        int_var[:, 6] = Z_n
        int_var[:, 7] = X_n
        int_var[:, 8] = Y_n

        int_var[:, 9] = omega_T_Emn
        int_var[:, 10] = z_T_Emn
        int_var[:, 11:13] = alpha_T_Emna
        int_var[:, 13:15] = eps_T_pi_Emna
        int_var[:, 15:17] = sigma_T_Emna
        int_var[:, 17] = Z_T
        int_var[:, 18:20] = X_T
        int_var[:, 20] = Y_T


        return int_var

    #---------------------------------------------------------------------
    # Extra homogenization of damage tensor in case of two damage parameters
    # Returns the 4th order damage tensor 'beta4' using (ref. [Baz99], Eq.(63))
    #---------------------------------------------------------------------

    def _get_beta_Emabcd_2(self, eps_Emab, omega_N_Emn, z_N_Emn,
                           alpha_N_Emn, r_N_Emn, eps_N_p_Emn, omega_T_Emn, z_T_Emn,
                           alpha_T_Emna, eps_T_pi_Emna, eps_aux):

        # Returns the 4th order damage tensor 'beta4' using
        #(cf. [Baz99], Eq.(63))

        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emnar_2(eps_Emab)

        omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn, Z_n, X_n, Y_n = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, eps_aux)

        omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna, Z_T, X_T, Y_T = self.get_tangential_law(
            eps_T_Emna, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)

        delta = np.identity(2)
        beta_N = np.sqrt(1. - omega_N_Emn)
        beta_T = np.sqrt(1. - omega_T_Emn)

        beta_ijkl = np.einsum('n, ...n,ni, nj, nk, nl -> ...ijkl', self._MPW, beta_N, self._MPN, self._MPN, self._MPN, self._MPN) + \
            0.25 * (np.einsum('n, ...n,ni, nk, jl -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                    np.einsum('n, ...n,ni, nl, jk -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                    np.einsum('n, ...n,nj, nk, il -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                    np.einsum('n, ...n,nj, nl, ik -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) -
                    4.0 * np.einsum('n, ...n, ni, nj, nk, nl -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, self._MPN, self._MPN))

        return beta_ijkl
    #-----------------------------------------------------------
    # Integration of the (inelastic) strains for each microplane
    #-----------------------------------------------------------

    def _get_eps_p_Emab(self, eps_Emab, omega_N_Emn, z_N_Emn,
                        alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
                        omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn, eps_aux):

        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emnar_2(eps_Emab)

        # plastic normal strains
        omegaN, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn, Z_n, X_n, Y_n = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, eps_aux)

        # sliding tangential strains
        omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna, Z_T, X_T, Y_T = self.get_tangential_law(
            eps_T_Emna, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn)

        delta = np.identity(2)

        # 2-nd order plastic (inelastic) tensor
        eps_p_Emab = (
            np.einsum('n,...n,na,nb->...ab',
                      self._MPW, eps_N_p_Emn, self._MPN, self._MPN) +
            0.5 * (
                np.einsum('n,...nf,na,fb->...ab',
                          self._MPW, eps_T_pi_Emna, self._MPN, delta) +
                np.einsum('n,...nf,nb,fa->...ab', self._MPW,
                          eps_T_pi_Emna, self._MPN, delta)
            )
        )

        return eps_p_Emab

    #-------------------------------------------------------------------------
    # Evaluation - get the corrector and predictor
    #-------------------------------------------------------------------------

    def get_corr_pred(self, eps_Emab, t_n1, int_var, eps_aux, F):

        # Definition internal variables / forces per column:  1) damage N, 2)iso N, 3)kin N, 4)consolidation N, 5) eps p N,
        # 6) sigma N, 7) iso F N, 8) kin F N, 9) energy release N, 10) damage T, 11) iso T, 12-13) kin T, 14-15) eps p T,
        # 16-17) sigma T, 18) iso F T, 19-20) kin F T, 21) energy release T

        # Corrector predictor computation.

        #------------------------------------------------------------------
        # Damage tensor (4th order) using product- or sum-type symmetrization:
        #------------------------------------------------------------------


        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emnar_2(eps_Emab)

        omega_N_Emn = int_var[:, 0]
        z_N_Emn = int_var[:, 1]
        alpha_N_Emn = int_var[:, 2]
        r_N_Emn = int_var[:, 3]
        eps_N_p_Emn = int_var[:, 4]
        sigma_N_Emn = int_var[:, 5]

        omega_T_Emn = int_var[:, 9]
        z_T_Emn = int_var[:, 10]
        alpha_T_Emna = int_var[:, 11:13]
        eps_T_pi_Emna = int_var[:, 13:15]

        beta_Emabcd = self._get_beta_Emabcd_2(
            eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn, omega_T_Emn, z_T_Emn,
                           alpha_T_Emna, eps_T_pi_Emna, eps_aux
        )

        #------------------------------------------------------------------
        # Damaged stiffness tensor calculated based on the damage tensor beta4:
        #------------------------------------------------------------------

        D_Emabcd = np.einsum(
            '...ijab, abef, ...cdef->...ijcd', beta_Emabcd, self.D_abef, beta_Emabcd)

        #----------------------------------------------------------------------
        # Return stresses (corrector) and damaged secant stiffness matrix (predictor)
        #----------------------------------------------------------------------
        # plastic strain tensor
        eps_p_Emab = self._get_eps_p_Emab(
            eps_Emab, omega_N_Emn, z_N_Emn,
            alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
            omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn, eps_aux)

        # elastic strain tensor
        eps_e_Emab = eps_Emab - eps_p_Emab

        # calculation of the stress tensor
        sig_Emab = np.einsum('...abcd,...cd->...ab', D_Emabcd, eps_e_Emab)

        return D_Emabcd, sig_Emab, eps_p_Emab

    #-----------------------------------------------
    # number of microplanes
    #-----------------------------------------------
    n_mp = tr.Constant(360)

    #-----------------------------------------------
    # get the normal vectors of the microplanes
    #-----------------------------------------------
    _MPN = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPN(self):
        # microplane normals:
        alpha_list = np.linspace(0, 2 * np.pi, self.n_mp)

        MPN = np.array([[np.cos(alpha), np.sin(alpha)]
                        for alpha in alpha_list])

        return MPN

    #-------------------------------------
    # get the weights of the microplanes
    #-------------------------------------
    _MPW = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPW(self):
        MPW = np.ones(self.n_mp) / self.n_mp * 2

        return MPW
Ejemplo n.º 19
0
class MS1(MATS3DEval, InteractiveModel):
    gamma_T = tr.Float(100000.,
                       label="gamma_T",
                       desc=" Tangential Kinematic hardening modulus",
                       enter_set=True,
                       auto_set=False)

    K_T = tr.Float(10000.,
                   label="K_T",
                   desc="Tangential Isotropic harening",
                   enter_set=True,
                   auto_set=False)

    S_T = tr.Float(0.005,
                   label="S_T",
                   desc="Damage strength",
                   enter_set=True,
                   auto_set=False)

    r_T = tr.Float(9.,
                   label="r",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)
    p_T = tr.Float(12.,
                   label="p_T",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)

    c_T = tr.Float(4.6,
                   label="c_T",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)

    sigma_T_0 = tr.Float(1.7,
                         label="sigma_T_0",
                         desc="Reversibility limit",
                         enter_set=True,
                         auto_set=False)

    m_T = tr.Float(0.003,
                   label="m_T",
                   desc="Lateral pressure coefficient",
                   enter_set=True,
                   auto_set=False)

    # -------------------------------------------
    # Normal_Tension constitutive law parameters (without cumulative normal strain)
    # -------------------------------------------
    Ad = tr.Float(100.0,
                  label="A_d",
                  desc="brittleness coefficient",
                  enter_set=True,
                  auto_set=False)

    eps_0 = tr.Float(0.00008,
                     label="eps_N_0",
                     desc="threshold strain",
                     enter_set=True,
                     auto_set=False)

    # -----------------------------------------------
    # Normal_Compression constitutive law parameters
    # -----------------------------------------------
    K_N = tr.Float(10000.,
                   label="K_N",
                   desc=" Normal isotropic harening",
                   enter_set=True,
                   auto_set=False)

    gamma_N = tr.Float(5000.,
                       label="gamma_N",
                       desc="Normal kinematic hardening",
                       enter_set=True,
                       auto_set=False)

    sigma_N_0 = tr.Float(30.,
                         label="sigma_N_0",
                         desc="Yielding stress",
                         enter_set=True,
                         auto_set=False)

    # -------------------------------------------------------------------------
    # Cached elasticity tensors
    # -------------------------------------------------------------------------

    E = tr.Float(35e+3,
                 label="E",
                 desc="Young's Modulus",
                 auto_set=False,
                 input=True)

    nu = tr.Float(0.2,
                  label='nu',
                  desc="Poison ratio",
                  auto_set=False,
                  input=True)

    ipw_view = View(
        Item('gamma_T', latex=r'\gamma_\mathrm{T}', minmax=(10, 100000)),
        Item('K_T', latex=r'K_\mathrm{T}', minmax=(10, 10000)),
        Item('S_T', latex=r'S_\mathrm{T}', minmax=(0.001, 0.01)),
        Item('r_T', latex=r'r_\mathrm{T}', minmax=(1, 3)),
        Item('p_T', latex=r'e_\mathrm{T}', minmax=(1, 40)),
        Item('c_T', latex=r'c_\mathrm{T}', minmax=(1, 10)),
        Item('sigma_T_0', latex=r'\bar{sigma}^\pi_{T}', minmax=(1, 10)),
        Item('m_T', latex=r'm_\mathrm{T}', minmax=(0.001, 3)),
    )

    n_D = 3

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        return {
            name: (self.n_mp, ) + shape
            for name, shape in self.mic_state_var_shapes.items()
        }

    mic_state_var_shapes = dict(
        omega_N_Emn=(),  # damage N
        z_N_Emn=(),
        alpha_N_Emn=(),
        r_N_Emn=(),
        eps_N_p_Emn=(),
        sigma_N_Emn=(),
        omega_T_Emn=(),
        z_T_Emn=(),
        alpha_T_Emna=(n_D, ),
        eps_T_pi_Emna=(n_D, ),
    )
    '''
    State variables
     1) damage N, 
     2) iso N, 
     3) kin N, 
     4) consolidation N, 
     5) eps p N,
     6) sigma N, 
     7) iso F N, 
     8) kin F N, 
     9) energy release N, 
     10) damage T, 
     11) iso T, 
     12-13) kin T, 
     14-15) eps p T,
     16-17) sigma T, 18) iso F T, 19-20) kin F T, 21) energy release T
    '''

    # --------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    # (without cumulative normal strain for fatigue under tension)
    # --------------------------------------------------------------
    def get_normal_law(self, eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                       r_N_Emn, eps_N_p_Emn):

        E_N = self.E / (1.0 - 2.0 * self.nu)

        # When deciding if a microplane is in tensile or compression,
        # we define a strain boundary such that
        # sigmaN <= 0 if eps_N < 0, avoiding entering in the quadrant
        # of compressive strains and traction

        sigma_trial = E_N * (eps_N_Emn - eps_N_p_Emn)
        # looking for microplanes violating strain boundary
        pos1 = [(eps_N_Emn < -1e-6) & (sigma_trial > 1e-6)]
        sigma_trial[pos1[0]] = 0
        pos = eps_N_Emn > 1e-6  # microplanes under traction
        pos2 = eps_N_Emn < -1e-6  # microplanes under compression
        H = 1.0 * pos
        H2 = 1.0 * pos2

        # thermo forces
        sigma_N_Emn_tilde = E_N * (eps_N_Emn - eps_N_p_Emn)
        sigma_N_Emn_tilde[pos1[0]] = 0  # imposing strain boundary

        Z = self.K_N * z_N_Emn
        X = self.gamma_N * alpha_N_Emn * H2
        h = (self.sigma_N_0 + Z) * H2

        f_trial = (abs(sigma_N_Emn_tilde - X) - h) * H2

        # threshold plasticity

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
                      (E_N / (1 - omega_N_Emn) + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p_Emn += delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)
        z_N_Emn += delta_lamda
        alpha_N_Emn += delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)

        def R_N(r_N_Emn):
            return (1.0 / self.Ad) * (-r_N_Emn / (1.0 + r_N_Emn))

        Y_N = 0.5 * H * E_N * (eps_N_Emn - eps_N_p_Emn)**2.0
        Y_0 = 0.5 * E_N * self.eps_0**2.0

        f = (Y_N - (Y_0 + R_N(r_N_Emn)))

        # threshold damage

        thres_2 = f > 1e-6

        def f_w(Y):
            return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))

        omega_N_Emn[f > 1e-6] = f_w(Y_N)[f > 1e-6]
        omega_N_Emn[...] = np.clip(omega_N_Emn, 0, 0.9999)
        r_N_Emn[f > 1e-6] = -omega_N_Emn[f > 1e-6]

        sigma_N_Emn = (1.0 - H * omega_N_Emn) * E_N * (eps_N_Emn - eps_N_p_Emn)
        pos1 = [(eps_N_Emn < -1e-6) & (sigma_trial > 1e-6)
                ]  # looking for microplanes violating strain boundary
        sigma_N_Emn[pos1[0]] = 0

        Z = self.K_N * z_N_Emn
        X = self.gamma_N * alpha_N_Emn * H2

        return sigma_N_Emn, Z, X, Y_N

    # -------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    # -------------------------------------------------------------------------
    def get_tangential_law(self, eps_T_Emna, omega_T_Emn, z_T_Emn,
                           alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn):

        E_T = self.E * (1.0 - 4 * self.nu) / \
            ((1.0 + self.nu) * (1.0 - 2 * self.nu))

        # thermo forces

        sig_pi_trial = E_T * (eps_T_Emna - eps_T_pi_Emna)

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        norm_1 = np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X)))
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        # threshold

        f = norm_1 - self.sigma_T_0 - Z + self.m_T * sigma_N_Emn

        plas_1 = f > 1e-6
        elas_1 = f < 1e-6

        delta_lamda = f / \
                      (E_T / (1.0 - omega_T_Emn) + self.gamma_T + self.K_T) * plas_1

        norm_2 = 1.0 * elas_1 + np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X))) * plas_1

        eps_T_pi_Emna[..., 0] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 0] - X[..., 0]) /
                                 (1.0 - omega_T_Emn)) / norm_2
        eps_T_pi_Emna[..., 1] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 1] - X[..., 1]) /
                                 (1.0 - omega_T_Emn)) / norm_2

        eps_T_pi_Emna[..., 2] +=  plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 2] - X[..., 2]) /
                                 (1.0 - omega_T_Emn)) / norm_2

        omega_T_Emn += ((1 - omega_T_Emn) ** self.c_T) * \
                       (delta_lamda * (Y / self.S_T) ** self.r_T) * \
                       (self.sigma_T_0 / (self.sigma_T_0 + self.m_T * sigma_N_Emn)) ** self.p_T
        omega_T_Emn[...] = np.clip(omega_T_Emn, 0, 0.9999)

        alpha_T_Emna[..., 0] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        alpha_T_Emna[..., 1] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 1] - X[..., 1]) / norm_2

        alpha_T_Emna[..., 2] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 2] - X[..., 2]) / norm_2

        z_T_Emn += delta_lamda

        sigma_T_Emna = np.einsum('...n,...na->...na', (1 - omega_T_Emn),
                                 E_T * (eps_T_Emna - eps_T_pi_Emna))

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        return sigma_T_Emna, Z, X, Y

    #     #-------------------------------------------------------------------------
    #     # MICROPLANE-Kinematic constraints
    #     #-------------------------------------------------------------------------

    # -------------------------------------------------

    # get the operator of the microplane normals
    _MPNN = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPNN(self):
        MPNN_nij = np.einsum('ni,nj->nij', self._MPN, self._MPN)
        return MPNN_nij

    # get the third order tangential tensor (operator) for each microplane
    _MPTT = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPTT(self):
        delta = self.DELTA
        MPTT_nijr = 0.5 * (
            np.einsum('ni,jr -> nijr', self._MPN, delta) +
            np.einsum('nj,ir -> njir', self._MPN, delta) -
            2 * np.einsum('ni,nj,nr -> nijr', self._MPN, self._MPN, self._MPN))
        return MPTT_nijr

    def _get_e_N_Emn_2(self, eps_Emab):
        # get the normal strain array for each microplane
        return np.einsum('nij,...ij->...n', self._MPNN, eps_Emab)

    def _get_e_T_Emnar_2(self, eps_Emab):
        # get the tangential strain vector array for each microplane
        MPTT_ijr = self._get__MPTT()
        return np.einsum('nija,...ij->...na', MPTT_ijr, eps_Emab)

    # ---------------------------------------------------------------------
    # Extra homogenization of damage tensor in case of two damage parameters
    # Returns the 4th order damage tensor 'beta4' using (ref. [Baz99], Eq.(63))
    # ---------------------------------------------------------------------

    def _get_beta_Emabcd_2(self, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                           r_N_Emn, eps_N_p_Emn, sigma_N_Emn, omega_T_Emn,
                           z_T_Emn, alpha_T_Emna, eps_T_pi_Emna):
        # Returns the 4th order damage tensor 'beta4' using
        # (cf. [Baz99], Eq.(63))

        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emnar_2(eps_Emab)

        sigma_N_Emn, Z_n, X_n, Y_n = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)

        sigma_T_Emna, Z_T, X_T, Y_T = self.get_tangential_law(
            eps_T_Emna, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_N_Emn)

        delta = self.DELTA
        beta_N = np.sqrt(1. - omega_N_Emn)
        beta_T = np.sqrt(1. - omega_T_Emn)

        beta_ijkl = np.einsum('n, ...n,ni, nj, nk, nl -> ...ijkl', self._MPW, beta_N, self._MPN, self._MPN, self._MPN,
                              self._MPN) + \
                    0.25 * (np.einsum('n, ...n,ni, nk, jl -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,ni, nl, jk -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,nj, nk, il -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,nj, nl, ik -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) -
                            4.0 * np.einsum('n, ...n, ni, nj, nk, nl -> ...ijkl', self._MPW, beta_T, self._MPN,
                                            self._MPN, self._MPN, self._MPN))

        return beta_ijkl

    DELTA = np.identity(n_D)

    # -----------------------------------------------------------
    # Integration of the (inelastic) strains for each microplane
    # -----------------------------------------------------------

    def _get_eps_p_Emab(self, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                        r_N_Emn, eps_N_p_Emn, omega_T_Emn, z_T_Emn,
                        alpha_T_Emna, eps_T_pi_Emna, sigma_N_Emn):
        eps_N_Emn = self._get_e_N_Emn_2(eps_Emab)
        eps_T_Emna = self._get_e_T_Emnar_2(eps_Emab)

        # plastic normal strains
        sigma_N_Emn, Z_n, X_n, Y_n = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn)

        # sliding tangential strains
        sigma_T_Emna, Z_T, X_T, Y_T = self.get_tangential_law(
            eps_T_Emna, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_N_Emn)

        # 2-nd order plastic (inelastic) tensor
        eps_p_Emab = (np.einsum('n,...n,na,nb->...ab', self._MPW, eps_N_p_Emn,
                                self._MPN, self._MPN) + 0.5 *
                      (np.einsum('n,...nf,na,fb->...ab', self._MPW,
                                 eps_T_pi_Emna, self._MPN, self.DELTA) +
                       np.einsum('n,...nf,nb,fa->...ab', self._MPW,
                                 eps_T_pi_Emna, self._MPN, self.DELTA)))

        return eps_p_Emab

    def get_corr_pred(self, eps_Emab, t_n1, **Eps_k):
        """Evaluation - get the corrector and predictor
        """
        # Corrector predictor computation.

        # ------------------------------------------------------------------
        # Damage tensor (4th order) using product- or sum-type symmetrization:
        # ------------------------------------------------------------------
        beta_Emabcd = self._get_beta_Emabcd_2(eps_Emab, **Eps_k)

        # ------------------------------------------------------------------
        # Damaged stiffness tensor calculated based on the damage tensor beta4:
        # ------------------------------------------------------------------

        D_Emabcd = np.einsum('...ijab, abef, ...cdef->...ijcd', beta_Emabcd,
                             self.D_abef, beta_Emabcd)

        # ----------------------------------------------------------------------
        # Return stresses (corrector) and damaged secant stiffness matrix (predictor)
        # ----------------------------------------------------------------------
        # plastic strain tensor
        eps_p_Emab = self._get_eps_p_Emab(eps_Emab, **Eps_k)

        # elastic strain tensor
        eps_e_Emab = eps_Emab - eps_p_Emab

        # calculation of the stress tensor
        sig_Emab = np.einsum('...abcd,...cd->...ab', D_Emabcd, eps_e_Emab)

        return sig_Emab, D_Emabcd
Ejemplo n.º 20
0
class MKappa(InteractiveModel, InjectSymbExpr):
    """Class returning the moment curvature relationship."""
    name = 'Moment-Curvature'

    symb_class = MKappaSymbolic
    cs_design = Instance(CrossSectionDesign, ())

    tree = ['cs_design']
    # Use PrototypedFrom only when the prototyped object is a class
    # (The prototyped attribute behaves similarly
    # to a delegated attribute, until it is explicitly
    # changed; from that point forward, the prototyped attribute
    # changes independently from its prototype.)
    # (it's kind of like tr.DelegatesTo('cs_design.cross_section_shape'))
    cross_section_shape = tr.DelegatesTo('cs_design')
    cross_section_shape_ = tr.DelegatesTo('cs_design')
    cross_section_layout = tr.DelegatesTo('cs_design')
    matrix_ = tr.DelegatesTo('cs_design')

    # Geometry
    H = tr.DelegatesTo('cross_section_shape_')

    DEPSTR = 'state_changed'

    n_m = Int(
        100,
        DSC=True,
        desc=
        'Number of discretization points along the height of the cross-section'
    )

    # @todo: fix the dependency - `H` should be replaced by _GEO
    z_m = tr.Property(depends_on=DEPSTR)

    @tr.cached_property
    def _get_z_m(self):
        return np.linspace(0, self.H, self.n_m)

    low_kappa = Float(0.0, BC=True, GEO=True)
    high_kappa = Float(0.00002, BC=True, GEO=True)
    n_kappa = Int(100, BC=True)
    step_kappa = tr.Property(Float, depends_on='low_kappa, high_kappa')

    @tr.cached_property
    def _get_step_kappa(self):
        return float((self.high_kappa - self.low_kappa) / self.n_kappa)

    kappa_slider = Float(0.0000001)

    ipw_view = View(
        Item(
            'low_kappa',
            latex=r'\text{Low}~\kappa'),  #, editor=FloatEditor(step=0.00001)),
        Item('high_kappa', latex=r'\text{High}~\kappa'
             ),  # , editor=FloatEditor(step=0.00001)),
        Item('n_kappa', latex='n_{\kappa}'),
        Item('plot_strain'),
        Item('solve_for_eps_bot_pointwise'),
        Item('n_m', latex='n_m'),
        Item('kappa_slider', latex='\kappa', readonly=True),
        # editor=FloatRangeEditor(low_name='low_kappa',
        #                         high_name='high_kappa',
        #                         n_steps_name='n_kappa')
        # ),
        time_editor=HistoryEditor(
            var='kappa_slider',
            min_var='low_kappa',
            max_var='high_kappa',
        ),
    )

    idx = tr.Property(depends_on='kappa_slider')

    apply_material_safety_factors = tr.Bool(False)

    @tr.cached_property
    def _get_idx(self):
        ks = self.kappa_slider
        idx = np.argmax(ks <= self.kappa_t)
        return idx

    kappa_t = tr.Property(tr.Array(np.float_), depends_on=DEPSTR)
    '''Curvature values for which the bending moment must be found
    '''

    @tr.cached_property
    def _get_kappa_t(self):
        return np.linspace(self.low_kappa, self.high_kappa, self.n_kappa)

    z_j = tr.Property

    def _get_z_j(self):
        return self.cross_section_layout.z_j

    A_j = tr.Property

    def _get_A_j(self):
        return self.cross_section_layout.A_j

    # Normal force in steel (tension and compression)
    def get_N_s_tj(self, kappa_t, eps_bot_t):
        # get the strain at the height of the reinforcement
        eps_z_tj = self.symb.get_eps_z(kappa_t[:, np.newaxis],
                                       eps_bot_t[:, np.newaxis],
                                       self.z_j[np.newaxis, :])
        # Get the crack bridging force in each reinforcement layer
        # given the corresponding crack-bridge law.
        N_s_tj = self.cross_section_layout.get_N_tj(eps_z_tj)
        return N_s_tj

    # TODO - [RC] avoid repeated evaluations of stress profile in
    #            N and M calculations for the same inputs as it
    #            is the case now.
    def get_sig_c_z(self, kappa_t, eps_bot_t, z_tm):
        """Get the stress profile over the height"""
        eps_z = self.symb.get_eps_z(kappa_t[:, np.newaxis],
                                    eps_bot_t[:, np.newaxis], z_tm)
        sig_c_z = self.matrix_.get_sig(eps_z)
        return sig_c_z

    # Normal force in concrete (tension and compression)
    def get_N_c_t(self, kappa_t, eps_bot_t):
        z_tm = self.z_m[np.newaxis, :]
        b_z_m = self.cross_section_shape_.get_b(z_tm)
        N_z_tm2 = b_z_m * self.get_sig_c_z(kappa_t, eps_bot_t, z_tm)
        return np.trapz(N_z_tm2, x=z_tm, axis=-1)

    def get_N_t(self, kappa_t, eps_bot_t):
        N_s_t = np.sum(self.get_N_s_tj(kappa_t, eps_bot_t), axis=-1)
        N_c_t = self.get_N_c_t(kappa_t, eps_bot_t)
        return N_c_t + N_s_t

    # SOLVER: Get eps_bot to render zero force

    # num_of_trials = tr.Int(30)

    eps_bot_t = tr.Property(depends_on=DEPSTR)
    r'''Resolve the tensile strain to get zero normal force for the prescribed curvature'''

    # @tr.cached_property
    # def _get_eps_bot_t(self):
    #     initial_step = (self.high_kappa - self.low_kappa) / self.num_of_trials
    #     for i in range(self.num_of_trials):
    #         print('Solution started...')
    #         res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t),
    #                    0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6)
    #         if res.success:
    #             print('success high_kappa: ', self.high_kappa)
    #             if i == 0:
    #                 print('Note: high_kappa success from 1st try! selecting a higher value for high_kappa may produce '
    #                       'a more reliable result!')
    #             return res.x
    #         else:
    #             print('failed high_kappa: ', self.high_kappa)
    #             self.high_kappa -= initial_step
    #             self.kappa_t = np.linspace(self.low_kappa, self.high_kappa, self.n_kappa)
    #
    #     print('No solution', res.message)
    #     return res.x

    # @tr.cached_property
    # def _get_eps_bot_t(self):
    #     res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t),
    #                0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6)
    #     if not res.success:
    #         raise SolutionNotFoundError('No solution', res.message)
    #     return res.x

    solve_for_eps_bot_pointwise = Bool(True, BC=True, GEO=True)

    @tr.cached_property
    def _get_eps_bot_t(self):
        if self.solve_for_eps_bot_pointwise:
            """ INFO: Instability in eps_bot solutions was caused by unsuitable init_guess value causing a convergence 
            to non-desired solutions. Solving the whole kappa_t array improved the init_guess after each
            calculated value, however, instability still there. The best results were obtained by taking the last 
            solution as the init_guess for the next solution like in the following.. """
            # One by one solution for kappa values
            eps_bot_sol_for_pos_kappa = self._get_eps_bot_piecewise_sol(
                kappa_pos=True)
            eps_bot_sol_for_neg_kappa = self._get_eps_bot_piecewise_sol(
                kappa_pos=False)
            res = np.concatenate(
                [eps_bot_sol_for_neg_kappa, eps_bot_sol_for_pos_kappa])
            return res
        else:
            # Array solution for the whole kappa_t
            res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t),
                       0.0000001 + np.zeros_like(self.kappa_t),
                       tol=1e-6)
            if not res.success:
                print('No solution', res.message)
            return res.x

    def _get_eps_bot_piecewise_sol(self, kappa_pos=True):
        if kappa_pos:
            kappas = self.kappa_t[np.where(self.kappa_t >= 0)]
        else:
            kappas = self.kappa_t[np.where(self.kappa_t < 0)]

        res = []
        if kappa_pos:
            init_guess = 0.00001
            kappa_loop_list = kappas
        else:
            init_guess = -0.00001
            kappa_loop_list = reversed(kappas)

        for kappa in kappa_loop_list:
            sol = root(
                lambda eps_bot: self.get_N_t(np.array([kappa]), eps_bot),
                np.array([init_guess]),
                tol=1e-6).x[0]

            # This condition is to avoid having init_guess~0 which causes non-convergence
            if abs(sol) > 1e-5:
                init_guess = sol
            res.append(sol)

        if kappa_pos:
            return res
        else:
            return list(reversed(res))

    # POSTPROCESSING
    kappa_cr = tr.Property(depends_on=DEPSTR)
    '''Curvature at which a critical strain is attained at the eps_bot'''

    @tr.cached_property
    def _get_kappa_cr(self):
        res = root(lambda kappa: self.get_N_t(kappa, self.eps_cr),
                   0.0000001 + np.zeros_like(self.eps_cr),
                   tol=1e-10)
        if not res.success:
            print('No kappa_cr solution (for plot_norm() function)',
                  res.message)
        return res.x

    M_s_t = tr.Property(depends_on=DEPSTR)
    '''Bending moment (steel)
    '''

    @tr.cached_property
    def _get_M_s_t(self):
        if len(self.z_j) == 0:
            return np.zeros_like(self.kappa_t)

        eps_z_tj = self.symb.get_eps_z(self.kappa_t[:, np.newaxis],
                                       self.eps_bot_t[:, np.newaxis],
                                       self.z_j[np.newaxis, :])

        # Get the crack bridging force in each reinforcement layer
        # given the corresponding crack-bridge law.
        N_tj = self.cross_section_layout.get_N_tj(eps_z_tj)
        return -np.einsum('tj,j->t', N_tj, self.z_j)

    M_c_t = tr.Property(depends_on=DEPSTR)
    '''Bending moment (concrete)
    '''

    @tr.cached_property
    def _get_M_c_t(self):
        z_tm = self.z_m[np.newaxis, :]
        b_z_m = self.cross_section_shape_.get_b(z_tm)
        N_z_tm2 = b_z_m * self.get_sig_c_z(self.kappa_t, self.eps_bot_t, z_tm)
        return -np.trapz(N_z_tm2 * z_tm, x=z_tm, axis=-1)

    M_t = tr.Property(depends_on=DEPSTR)
    '''Bending moment
    '''

    @tr.cached_property
    def _get_M_t(self):
        # print('M - k recalculated')
        eta_factor = 1.
        return eta_factor * (self.M_c_t + self.M_s_t)

    # @tr.cached_property
    # def _get_M_t(self):
    #     initial_step = (self.high_kappa - self.low_kappa) / self.num_of_trials
    #     for i in range(self.num_of_trials):
    #         try:
    #             M_t = self.M_c_t + self.M_s_t
    #         except SolutionNotFoundError:
    #             print('failed high_kappa: ', self.high_kappa)
    #             self.high_kappa -= initial_step
    #         else:
    #             # This will run when no exception has been received
    #             print('success high_kappa: ', self.high_kappa)
    #             if i == 0:
    #                 print('Note: high_kappa success from 1st try! selecting a higher value for high_kappa may produce '
    #                       'a more reliable result!')
    #             return M_t
    #     print('No solution has been found!')
    #     return M_t

    N_s_tj = tr.Property(depends_on=DEPSTR)
    '''Normal forces (steel)
    '''

    @tr.cached_property
    def _get_N_s_tj(self):
        return self.get_N_s_tj(self.kappa_t, self.eps_bot_t)

    eps_tm = tr.Property(depends_on=DEPSTR)
    '''strain profiles
    '''

    @tr.cached_property
    def _get_eps_tm(self):
        return self.symb.get_eps_z(self.kappa_t[:, np.newaxis],
                                   self.eps_bot_t[:, np.newaxis],
                                   self.z_m[np.newaxis, :])

    sig_tm = tr.Property(depends_on=DEPSTR)
    '''strain profiles
    '''

    @tr.cached_property
    def _get_sig_tm(self):
        return self.get_sig_c_z(self.kappa_t, self.eps_bot_t,
                                self.z_m[np.newaxis, :])

    M_norm = tr.Property(depends_on=DEPSTR)
    '''
    '''

    @tr.cached_property
    def _get_M_norm(self):
        # Section modulus @TODO optimize W for var b
        W = (self.b * self.H**2) / 6
        sig_cr = self.E_ct * self.eps_cr
        return W * sig_cr

    kappa_norm = tr.Property()

    def _get_kappa_norm(self):
        return self.kappa_cr

    inv_M_kappa = tr.Property(depends_on=DEPSTR)
    '''Return the inverted data points
    '''

    @tr.cached_property
    def _get_inv_M_kappa(self):
        try:
            """cut off the descending tails"""
            M_t = self.M_t
            I_max = np.argmax(M_t)
            I_min = np.argmin(M_t)
            M_I = np.copy(M_t[I_min:I_max + 1])
            kappa_I = np.copy(self.kappa_t[I_min:I_max + 1])
            # find the index corresponding to zero kappa
            idx = np.argmax(0 <= kappa_I)
            # and modify the values such that the
            # Values of moment are non-descending
            M_plus = M_I[idx:]
            M_diff = M_plus[:, np.newaxis] - M_plus[np.newaxis, :]
            n_ij = len(M_plus)
            ij = np.mgrid[0:n_ij:1, 0:n_ij:1]
            M_diff[np.where(ij[1] >= ij[0])] = 0
            i_x = np.argmin(M_diff, axis=1)
            M_I[idx:] = M_plus[i_x]
            return M_I, kappa_I
        except ValueError:
            print(
                'M inverse has not succeeded, the M-Kappa solution may have failed due to '
                'a wrong kappa range or not suitable material law!')
            return np.array([0]), np.array([0])

    def get_kappa_M(self, M):
        M_I, kappa_I = self.inv_M_kappa
        return np.interp(M, M_I, kappa_I)

    def plot_norm(self, ax1, ax2):
        idx = self.idx
        ax1.plot(self.kappa_t / self.kappa_norm, self.M_t / self.M_norm)
        ax1.plot(self.kappa_t[idx] / self.kappa_norm,
                 self.M_t[idx] / self.M_norm,
                 marker='o')
        ax2.barh(self.z_j,
                 self.N_s_tj[idx, :],
                 height=2,
                 color='red',
                 align='center')
        # ax2.fill_between(eps_z_arr[idx,:], z_arr, 0, alpha=0.1);
        ax3 = ax2.twiny()
        #  ax3.plot(self.eps_tm[idx, :], self.z_m, color='k', linewidth=0.8)
        ax3.plot(self.sig_tm[idx, :], self.z_m)
        ax3.axvline(0, linewidth=0.8, color='k')
        ax3.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1)
        mpl_align_xaxis(ax2, ax3)

    M_scale = Float(1e+6)
    plot_strain = Bool(False)

    def plot(self, ax1, ax2, ax3):
        self.plot_mk_and_stress_profile(ax1, ax2)
        if self.plot_strain:
            self.plot_strain_profile(ax3)
        else:
            self.plot_mk_inv(ax3)

    @staticmethod
    def subplots(fig):
        ax1, ax2, ax3 = fig.subplots(1, 3)
        return ax1, ax2, ax3

    def update_plot(self, axes):
        self.plot(*axes)

    def plot_mk_inv(self, ax3):
        try:
            M, kappa = self.inv_M_kappa
            ax3.plot(M / self.M_scale, kappa)
        except ValueError:
            print(
                'M inverse has not succeeded, the M-Kappa solution may have failed due to a wrong kappa range!'
            )

        ax3.set_xlabel('Moment [kNm]')
        ax3.set_ylabel('Curvature[mm$^{-1}$]')

    def plot_mk_and_stress_profile(self, ax1, ax2):
        self.plot_mk(ax1)
        idx = self.idx
        ax1.plot(self.kappa_t[idx],
                 self.M_t[idx] / self.M_scale,
                 color='orange',
                 marker='o')

        if len(self.z_j):
            ax2.barh(self.z_j,
                     self.N_s_tj[idx, :] / self.A_j,
                     height=4,
                     color='red',
                     align='center')
            ax2.set_ylabel('z [mm]')
            ax2.set_xlabel('$\sigma_r$ [MPa]')

        ax22 = ax2.twiny()
        ax22.set_xlabel('$\sigma_c$ [MPa]')
        ax22.plot(self.sig_tm[idx, :], self.z_m)
        ax22.axvline(0, linewidth=0.8, color='k')
        ax22.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1)
        mpl_align_xaxis(ax2, ax22)

    def plot_mk(self, ax1):
        ax1.plot(self.kappa_t, self.M_t / self.M_scale, label='M-K')
        ax1.set_ylabel('Moment [kNm]')
        ax1.set_xlabel('Curvature [mm$^{-1}$]')
        ax1.legend()

    def plot_strain_profile(self, ax):
        ax.set_ylabel('z [mm]')
        ax.set_xlabel(r'$\varepsilon$ [-]')
        ax.plot(self.eps_tm[self.idx, :], self.z_m)
        ax.axvline(0, linewidth=0.8, color='k')
        ax.fill_betweenx(self.z_m, self.eps_tm[self.idx, :], 0, alpha=0.1)

    def get_mk(self):
        return self.M_t / self.M_scale, self.kappa_t
Ejemplo n.º 21
0
class MSX(MATS3DEval):

    name = 'MSX'

    double_pvw = Bool(True, MAT=True)

    ipw_view = View(Item('E'), Item('nu'), Item('double_pvw'), Item('eps_max'),
                    Item('n_eps'))

    mic = EitherType(options=[('contim', VCoNTIM), ('untim', VUNTIM),
                              ('dntim', VDNTIM)],
                     on_option_change='reset_mic')

    integ_scheme = EitherType(options=[('3DM28', MSIS3DM28)])

    @tr.on_trait_change('E, nu')
    def _set_E(self, event):
        self.reset_mic()

    def reset_mic(self):
        self.mic_.E_N = self.E / (1.0 - 2.0 * self.nu)
        self.mic_.E_T = self.E * (1.0 - 4 * self.nu) / \
                 ((1.0 + self.nu) * (1.0 - 2 * self.nu))

    tree = ['mic', 'integ_scheme']

    state_var_shapes = tr.Property(depends_on='mic, integ_scheme')

    @tr.cached_property
    def _get_state_var_shapes(self):
        sv_shapes = {
            name: (self.integ_scheme_.n_mp, ) + shape
            for name, shape in self.mic_.state_var_shapes.items()
        }
        return sv_shapes

    def _get_e_a(self, eps_ab):
        """
        Get the microplane projected strains
        """
        # get the normal strain array for each microplane
        e_N = np.einsum('nij,...ij->...n', self.integ_scheme_.MPNN, eps_ab)
        # get the tangential strain vector array for each microplane
        MPTT_ijr = self.integ_scheme_.MPTT
        e_T_a = np.einsum('nija,...ij->...na', MPTT_ijr, eps_ab)
        return np.concatenate([e_N[..., np.newaxis], e_T_a], axis=-1)

    def _get_beta_abcd(self, eps_ab, omega_N, omega_T, **Eps):
        """
        Returns the 4th order damage tensor 'beta4' using
        (cf. [Baz99], Eq.(63))
        """
        MPW = self.integ_scheme_.MPW
        MPN = self.integ_scheme_.MPN

        delta = np.identity(3)
        beta_N = np.sqrt(1. - omega_N)
        beta_T = np.sqrt(1. - omega_T)

        beta_ijkl = (
            np.einsum('n,...n,ni,nj,nk,nl->...ijkl', MPW, beta_N, MPN, MPN,
                      MPN, MPN) + 0.25 *
            (np.einsum('n,...n,ni,nk,jl->...ijkl', MPW, beta_T, MPN, MPN,
                       delta) + np.einsum('n,...n,ni,nl,jk->...ijkl', MPW,
                                          beta_T, MPN, MPN, delta) +
             np.einsum('n,...n,nj,nk,il->...ijkl', MPW, beta_T, MPN, MPN,
                       delta) + np.einsum('n,...n,nj,nl,ik->...ijkl', MPW,
                                          beta_T, MPN, MPN, delta) -
             4.0 * np.einsum('n,...n,ni,nj,nk,nl->...ijkl', MPW, beta_T, MPN,
                             MPN, MPN, MPN)))
        return beta_ijkl

    def NT_to_ab(self, v_N, v_T_a):
        """
        Integration of the (inelastic) strains for each microplane
        """
        MPW = self.integ_scheme_.MPW
        MPN = self.integ_scheme_.MPN

        delta = np.identity(3)
        # 2-nd order plastic (inelastic) tensor
        tns_ab = (np.einsum('n,...n,na,nb->...ab', MPW, v_N, MPN, MPN) + 0.5 *
                  (np.einsum('n,...nf,na,fb->...ab', MPW, v_T_a, MPN, delta) +
                   np.einsum('n,...nf,nb,fa->...ab', MPW, v_T_a, MPN, delta)))
        return tns_ab

    def get_corr_pred(self, eps_ab, t_n1, **Eps):
        """
        Corrector predictor computation.
        """
        # ------------------------------------------------------------------
        # Damage tensor (4th order) using product- or sum-type symmetrization:
        # ------------------------------------------------------------------
        eps_a = self._get_e_a(eps_ab)
        sig_a, D_ab = self.mic_.get_corr_pred(eps_a, t_n1, **Eps)
        beta_abcd = self._get_beta_abcd(eps_ab, **Eps)
        # ------------------------------------------------------------------
        # Damaged stiffness tensor calculated based on the damage tensor beta4:
        # ------------------------------------------------------------------
        D_abcd = np.einsum('...ijab, abef, ...cdef->...ijcd', beta_abcd,
                           self.D_abef, beta_abcd)

        if self.double_pvw:
            # ----------------------------------------------------------------------
            # Return stresses (corrector) and damaged secant stiffness matrix (predictor)
            # ----------------------------------------------------------------------
            eps_p_a = self.mic_.get_eps_NT_p(**Eps)
            if eps_p_a:
                eps_N_p, eps_T_p_a = eps_p_a
                eps_p_ab = self.NT_to_ab(eps_N_p, eps_T_p_a)
                eps_e_ab = eps_ab - eps_p_ab
            else:
                eps_e_ab = eps_ab
            sig_ab = np.einsum('...abcd,...cd->...ab', D_abcd, eps_e_ab)
        else:
            sig_N, sig_T_a = sig_a[..., 0], sig_a[..., 1:]
            sig_ab = self.NT_to_ab(sig_N, sig_T_a)

        return sig_ab, D_abcd

    def update_plot(self, axes):
        ax_sig, ax_d_sig = axes
        eps_max = self.eps_max
        n_eps = self.n_eps
        eps11_range = np.linspace(1e-9, eps_max, n_eps)
        eps_range = np.zeros((n_eps, 3, 3))
        eps_range[:, 0, 0] = eps11_range
        state_vars = {
            var: np.zeros((1, ) + shape)
            for var, shape in self.state_var_shapes.items()
        }
        sig11_range, d_sig1111_range = [], []
        for eps_ab in eps_range:
            try:
                sig_ab, D_range = self.get_corr_pred(eps_ab[np.newaxis, ...],
                                                     1, **state_vars)
            except ReturnMappingError:
                break
            sig11_range.append(sig_ab[0, 0, 0])
            d_sig1111_range.append(D_range[0, 0, 0, 0, 0])
        sig11_range = np.array(sig11_range, dtype=np.float_)
        eps11_range = eps11_range[:len(sig11_range)]
        ax_sig.plot(eps11_range, sig11_range, color='blue')
        d_sig1111_range = np.array(d_sig1111_range, dtype=np.float_)
        ax_d_sig.plot(eps11_range,
                      d_sig1111_range,
                      linestyle='dashed',
                      color='gray')
        ax_sig.set_xlabel(r'$\varepsilon_{11}$ [-]')
        ax_sig.set_ylabel(r'$\sigma_{11}$ [MPa]')
        ax_d_sig.set_ylabel(
            r'$\mathrm{d} \sigma_{11} / \mathrm{d} \varepsilon_{11}$ [MPa]')
        ax_d_sig.plot(eps11_range[:-1], (sig11_range[:-1] - sig11_range[1:]) /
                      (eps11_range[:-1] - eps11_range[1:]),
                      color='orange',
                      linestyle='dashed')
Ejemplo n.º 22
0
class MS1EEQ(MATS3DEval, InteractiveModel):
    gamma_T = tr.Float(1000000.,
                       label="gamma_T",
                       desc=" Tangential Kinematic hardening modulus",
                       enter_set=True,
                       auto_set=False)

    K_T = tr.Float(10000.,
                   label="K_T",
                   desc="Tangential Isotropic harening",
                   enter_set=True,
                   auto_set=False)

    S_T = tr.Float(0.005,
                   label="S_T",
                   desc="Damage strength",
                   enter_set=True,
                   auto_set=False)

    r_T = tr.Float(9.,
                   label="r",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)
    p_T = tr.Float(2.,
                   label="p_T",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)

    c_T = tr.Float(3,
                   label="c_T",
                   desc="Damage cumulation parameter",
                   enter_set=True,
                   auto_set=False)

    sigma_T_0 = tr.Float(1.7,
                         label="sigma_T_0",
                         desc="Reversibility limit",
                         enter_set=True,
                         auto_set=False)

    m_T = tr.Float(0.1,
                   label="m_T",
                   desc="Lateral pressure coefficient",
                   enter_set=True,
                   auto_set=False)

    # -------------------------------------------
    # Normal_Tension constitutive law parameters (without cumulative normal strain)
    # -------------------------------------------
    Ad = tr.Float(10.0,
                  label="A_d",
                  desc="brittleness coefficient",
                  enter_set=True,
                  auto_set=False)

    eps_0 = tr.Float(.0001,
                     label="eps_N_0",
                     desc="threshold strain",
                     enter_set=True,
                     auto_set=False)

    # -----------------------------------------------
    # Normal_Compression constitutive law parameters
    # -----------------------------------------------
    K_N = tr.Float(10000.,
                   label="K_N",
                   desc=" Normal isotropic harening",
                   enter_set=True,
                   auto_set=False)

    gamma_N = tr.Float(5000.,
                       label="gamma_N",
                       desc="Normal kinematic hardening",
                       enter_set=True,
                       auto_set=False)

    sigma_N_0 = tr.Float(10.,
                         label="sigma_N_0",
                         desc="Yielding stress",
                         enter_set=True,
                         auto_set=False)

    # -------------------------------------------------------------------------
    # Cached elasticity tensors
    # -------------------------------------------------------------------------

    E = tr.Float(35e+3,
                 label="E",
                 desc="Young's Modulus",
                 auto_set=False,
                 input=True)

    nu = tr.Float(0.2,
                  label='nu',
                  desc="Poison ratio",
                  auto_set=False,
                  input=True)

    ipw_view = View(
        Item('gamma_T', latex=r'\gamma_\mathrm{T}', minmax=(10, 100000)),
        Item('K_T', latex=r'K_\mathrm{T}', minmax=(10, 10000)),
        Item('S_T', latex=r'S_\mathrm{T}', minmax=(0.001, 0.01)),
        Item('r_T', latex=r'r_\mathrm{T}', minmax=(1, 3)),
        Item('p_T', latex=r'e_\mathrm{T}', minmax=(1, 40)),
        Item('c_T', latex=r'c_\mathrm{T}', minmax=(1, 10)),
        Item('sigma_T_0', latex=r'\bar{sigma}^\pi_{T}', minmax=(1, 10)),
        Item('m_T', latex=r'm_\mathrm{T}', minmax=(0.001, 3)),
    )

    n_D = 3

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        dictionay = {
            name: (self.n_mp, ) + shape
            for name, shape in self.mic_state_var_shapes.items()
        }

        dictionay_macro = {
            name: shape
            for name, shape in self.mac_state_var_shapes.items()
        }
        dictionay.update(dictionay_macro)
        return dictionay

    mic_state_var_shapes = dict(
        omega_N_Emn=(),  # damage N
        z_N_Emn=(),
        alpha_N_Emn=(),
        r_N_Emn=(),
        eps_N_p_Emn=(),
        sigma_N_Emn=(),
        omega_T_Emn=(),
        z_T_Emn=(),
        alpha_T_Emna=(n_D, ),
        eps_T_pi_Emna=(n_D, ),
        sigma_T_Emna=(n_D, ),
        plastic_dissip_T_Emn=(),
        damage_dissip_T_Emn=(),
        plastic_dissip_N_Emn=(),
        damage_dissip_N_Emn=(),
    )

    mac_state_var_shapes = dict(
        total_work_microplane=(),
        total_work_macro=(),
        eps_aux=(
            n_D,
            n_D,
        ),
    )
    '''
    State variables
     1) damage N, 
     2) iso N, 
     3) kin N, 
     4) consolidation N, 
     5) eps p N,
     6) sigma N, 
     7) iso F N, 
     8) kin F N, 
     9) energy release N, 
     10) damage T, 
     11) iso T, 
     12-13) kin T, 
     14-15) eps p T,
     16-17) sigma T, 18) iso F T, 19-20) kin F T, 21) energy release T
    '''

    # --------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    # (without cumulative normal strain for fatigue under tension)
    # --------------------------------------------------------------
    def get_normal_law(self, eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                       r_N_Emn, eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn,
                       alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna,
                       plastic_dissip_T_Emn, damage_dissip_T_Emn,
                       plastic_dissip_N_Emn, damage_dissip_N_Emn,
                       total_work_microplane, total_work_macro, eps_aux):

        E_N = self.E / (1.0 - 2.0 * self.nu)

        sigma_N_Emn_tilde = E_N * (eps_N_Emn - eps_N_p_Emn)

        r_N_Emn_aux = copy.deepcopy(r_N_Emn)
        omega_N_Emn_aux = copy.deepcopy(omega_N_Emn)

        pos = sigma_N_Emn_tilde > 1e-6  # microplanes under tension
        pos2 = sigma_N_Emn_tilde < -1e-6  # microplanes under compression
        tension = 1.0 * pos
        compression = 1.0 * pos2

        # thermo forces

        Z = self.K_N * z_N_Emn * compression
        X = self.gamma_N * alpha_N_Emn * compression
        h = (self.sigma_N_0 + Z) * compression

        f_trial = (abs(sigma_N_Emn_tilde - X) - h) * compression

        # threshold plasticity

        thres_1 = f_trial > 1e-10

        delta_lamda = f_trial / \
                      (E_N / (1 - omega_N_Emn) + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p_Emn += delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)
        z_N_Emn += delta_lamda
        alpha_N_Emn += delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)

        def R_N(r_N_Emn):
            return (1.0 / self.Ad) * (-r_N_Emn / (1.0 + r_N_Emn))

        Y_N = 0.5 * tension * E_N * (eps_N_Emn - eps_N_p_Emn)**2.0
        Y_0 = 0.5 * E_N * self.eps_0**2.0

        f = (Y_N - (Y_0 + R_N(r_N_Emn))) * tension

        # threshold damage

        thres_2 = f > 1e-6

        def f_w(Y):
            return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))

        omega_N_Emn[f > 1e-6] = f_w(Y_N)[f > 1e-6]
        omega_N_Emn[...] = np.clip(omega_N_Emn, 0, 1.0)
        r_N_Emn[f > 1e-6] = -omega_N_Emn[f > 1e-6]

        sigma_N_Emn[...] = (1.0 - tension * omega_N_Emn) * E_N * (eps_N_Emn -
                                                                  eps_N_p_Emn)
        Z = self.K_N * z_N_Emn * compression
        X = self.gamma_N * alpha_N_Emn * compression
        # sigma_N_Emn = E_N * (eps_N_Emn - eps_N_p_Emn)
        # pos1 = [(eps_N_Emn < -1e-6) & (sigma_trial > 1e-6)]  # looking for microplanes violating strain boundary
        # sigma_N_Emn[pos1[0]] = 0

        delta_eps_N_p_Emn = np.zeros_like(eps_N_p_Emn)
        delta_alpha_N_Emn = np.zeros_like(alpha_N_Emn)
        delta_z_N_Emn = np.zeros_like(z_N_Emn)
        delta_omega_N_Emn = np.zeros_like(omega_N_Emn)
        delta_r_N_Emn = np.zeros_like(r_N_Emn)

        delta_eps_N_p_Emn = delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)

        delta_alpha_N_Emn = delta_lamda * \
                      np.sign(sigma_N_Emn_tilde - X)

        delta_z_N_Emn = delta_lamda

        delta_omega_N_Emn = omega_N_Emn - omega_N_Emn_aux

        delta_r_N_Emn = r_N_Emn - r_N_Emn_aux

        plastic_dissip_N_Emn[...] = np.einsum('...n,...n->...n', sigma_N_Emn, delta_eps_N_p_Emn) - \
                                    np.einsum('...n,...n->...n', X, delta_alpha_N_Emn) - np.einsum('...n,...n->...n',
                                                                                                      Z, delta_z_N_Emn)

        damage_dissip_N_Emn[...] = np.einsum('...n,...n->...n', Y_N, delta_omega_N_Emn) - \
                                   np.einsum('...n,...n->...n', R_N(r_N_Emn), delta_r_N_Emn)

        return sigma_N_Emn, Z, X, Y_N

    # -------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    # -------------------------------------------------------------------------
    def get_tangential_law(self, eps_T_Emna, eps_Emab, omega_N_Emn, z_N_Emn,
                           alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn,
                           omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
                           sigma_T_Emna, plastic_dissip_T_Emn,
                           damage_dissip_T_Emn, plastic_dissip_N_Emn,
                           damage_dissip_N_Emn, total_work_microplane,
                           total_work_macro, eps_aux):


        E_T = self.E * (1.0 - 4 * self.nu) / \
            ((1.0 + self.nu) * (1.0 - 2 * self.nu))

        # thermo forces

        sig_pi_trial = E_T * (eps_T_Emna - eps_T_pi_Emna)

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        norm_1 = np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X)))
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        E_N = self.E / (1.0 - 2.0 * self.nu)

        eps_N_Emn = self._get_e_N_Emn(eps_Emab)
        sigma_N_Emn = (1.0 - omega_N_Emn) * E_N * (eps_N_Emn - eps_N_p_Emn)

        f = norm_1 - self.sigma_T_0 - Z + self.m_T * sigma_N_Emn

        plas_1 = f > 1e-15
        elas_1 = f < 1e-15

        delta_lamda = f / \
                      (E_T / (1.0 - omega_T_Emn) + self.gamma_T + self.K_T) * plas_1

        norm_2 = 1.0 * elas_1 + np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X))) * plas_1

        eps_T_pi_Emna[..., 0] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 0] - X[..., 0]) /
                                 (1.0 - omega_T_Emn)) / norm_2
        eps_T_pi_Emna[..., 1] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 1] - X[..., 1]) /
                                 (1.0 - omega_T_Emn)) / norm_2

        eps_T_pi_Emna[..., 2] +=  plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 2] - X[..., 2]) /
                                 (1.0 - omega_T_Emn)) / norm_2
        omega_T_Emn += plas_1 * ((1 - omega_T_Emn) ** self.c_T) * \
                       (delta_lamda * (Y / self.S_T) ** self.r_T) * \
                       (self.sigma_T_0 / (self.sigma_T_0 - self.m_T * sigma_N_Emn)) ** self.p_T
        omega_T_Emn[...] = np.clip(omega_T_Emn, 0, 1.0)

        alpha_T_Emna[..., 0] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        alpha_T_Emna[..., 1] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 1] - X[..., 1]) / norm_2

        alpha_T_Emna[..., 2] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 2] - X[..., 2]) / norm_2

        z_T_Emn += plas_1 * delta_lamda

        sigma_T_Emna[...] = np.einsum('...n,...na->...na', (1 - omega_T_Emn),
                                      E_T * (eps_T_Emna - eps_T_pi_Emna))

        Z = self.K_T * z_T_Emn
        X = self.gamma_T * alpha_T_Emna
        Y = 0.5 * E_T * \
            np.einsum(
                '...na,...na->...n',
                (eps_T_Emna - eps_T_pi_Emna),
                (eps_T_Emna - eps_T_pi_Emna))

        # Energy dissipation evaluation

        delta_eps_T_pi_Emna = np.zeros_like(eps_T_pi_Emna)
        delta_alpha_T_Emna = np.zeros_like(alpha_T_Emna)
        delta_z_T_Emn = np.zeros_like(z_T_Emn)
        delta_omega_T_Emn = np.zeros_like(omega_T_Emn)

        delta_eps_T_pi_Emna[..., 0] = plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 0] - X[..., 0]) /
                                 (1.0 - omega_T_Emn)) / norm_2
        delta_eps_T_pi_Emna[..., 1] = plas_1 * delta_lamda * \
                                      ((sig_pi_trial[..., 1] - X[..., 1]) /
                                       (1.0 - omega_T_Emn)) / norm_2
        delta_eps_T_pi_Emna[..., 2] = plas_1 * delta_lamda * \
                                      ((sig_pi_trial[..., 2] - X[..., 2]) /
                                       (1.0 - omega_T_Emn)) / norm_2

        delta_alpha_T_Emna[..., 0] = plas_1 * delta_lamda * \
                                (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        delta_alpha_T_Emna[..., 1] = plas_1 * delta_lamda * \
                                (sig_pi_trial[..., 1] - X[..., 1]) / norm_2

        delta_alpha_T_Emna[..., 2] = plas_1 * delta_lamda * \
                                (sig_pi_trial[..., 2] - X[..., 2]) / norm_2

        delta_z_T_Emn = plas_1 * delta_lamda

        delta_omega_T_Emn = plas_1 * ((1 - omega_T_Emn) ** self.c_T) * \
                       (delta_lamda * (Y / self.S_T) ** self.r_T) * \
                       (self.sigma_T_0 / (self.sigma_T_0 - self.m_T * sigma_N_Emn)) ** self.p_T

        plastic_dissip_T_Emn[...] = np.einsum('...na,...na->...n',
                                              sigma_T_Emna,
                                              delta_eps_T_pi_Emna)  #- \
        #np.einsum('...na,...na->...n', X, delta_alpha_T_Emna) - np.einsum('...n,...n->...n', Z, delta_z_T_Emn)

        damage_dissip_T_Emn[...] = np.einsum('...n,...n->...n', Y,
                                             delta_omega_T_Emn)

        # if plastic_dissip_T_Emn.any() < -1e-15:
        #     print(sigma_T_Emna, delta_eps_T_pi_Emna)
        return sigma_T_Emna, Z, X, Y, plastic_dissip_T_Emn

    #     #-------------------------------------------------------------------------
    #     # MICROPLANE-Kinematic constraints
    #     #-------------------------------------------------------------------------

    # -------------------------------------------------

    # get the operator of the microplane normals
    _MPNN = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPNN(self):
        MPNN_nij = np.einsum('ni,nj->nij', self._MPN, self._MPN)
        return MPNN_nij

    # get the third order tangential tensor (operator) for each microplane
    _MPTT = tr.Property(depends_on='n_mp')

    @tr.cached_property
    def _get__MPTT(self):
        delta = self.DELTA
        MPTT_nijr = 0.5 * (
            np.einsum('ni,jr -> nijr', self._MPN, delta) +
            np.einsum('nj,ir -> njir', self._MPN, delta) -
            2 * np.einsum('ni,nj,nr -> nijr', self._MPN, self._MPN, self._MPN))
        return MPTT_nijr

    def _get_e_N_Emn(self, eps_Emab):
        # get the normal strain array for each microplane
        return np.einsum('nij,...ij->...n', self._MPNN, eps_Emab)

    def _get_e_T_Emna(self, eps_Emab):
        # get the tangential strain vector array for each microplane
        MPTT_ijr = self._get__MPTT()
        return np.einsum('nija,...ij->...na', MPTT_ijr, eps_Emab)

    # ---------------------------------------------------------------------
    # Extra homogenization of damage tensor in case of two damage parameters
    # Returns the 4th order damage tensor 'beta4' using (ref. [Baz99], Eq.(63))
    # ---------------------------------------------------------------------

    def _get_beta_Emabcd(self, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                         r_N_Emn, eps_N_p_Emn, sigma_N_Emn, omega_T_Emn,
                         z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna,
                         plastic_dissip_T_Emn, damage_dissip_T_Emn,
                         plastic_dissip_N_Emn, damage_dissip_N_Emn,
                         total_work_microplane, total_work_macro, eps_aux):
        # Returns the 4th order damage tensor 'beta4' using
        # (cf. [Baz99], Eq.(63))

        eps_N_Emn = self._get_e_N_Emn(eps_Emab)
        eps_T_Emna = self._get_e_T_Emna(eps_Emab)

        sigma_N_Emn, Z, X, Y_N = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
            sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_T_Emna, plastic_dissip_T_Emn, damage_dissip_T_Emn,
            plastic_dissip_N_Emn, damage_dissip_N_Emn, total_work_microplane,
            total_work_macro, eps_aux)

        # sliding tangential strains
        sigma_T_Emna, Z_T, X_T, Y_T, plastic_dissip_T_Emn = self.get_tangential_law(
            eps_T_Emna, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
            eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna,
            eps_T_pi_Emna, sigma_T_Emna, plastic_dissip_T_Emn,
            damage_dissip_T_Emn, plastic_dissip_N_Emn, damage_dissip_N_Emn,
            total_work_microplane, total_work_macro, eps_aux)

        delta = self.DELTA
        beta_N = np.sqrt(1. - omega_N_Emn)
        beta_T = np.sqrt(1. - omega_T_Emn)

        beta_ijkl = np.einsum('n, ...n,ni, nj, nk, nl -> ...ijkl', self._MPW, beta_N, self._MPN, self._MPN, self._MPN,
                              self._MPN) + \
                    0.25 * (np.einsum('n, ...n,ni, nk, jl -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,ni, nl, jk -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,nj, nk, il -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) +
                            np.einsum('n, ...n,nj, nl, ik -> ...ijkl', self._MPW, beta_T, self._MPN, self._MPN, delta) -
                            4.0 * np.einsum('n, ...n, ni, nj, nk, nl -> ...ijkl', self._MPW, beta_T, self._MPN,
                                            self._MPN, self._MPN, self._MPN))

        return beta_ijkl

    def _get_phi(self, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
                 eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna,
                 eps_T_pi_Emna, plastic_dissip_T_Emn):

        phi_n = np.sqrt(1.0 - omega_N_Emn) * np.sqrt(1.0 - omega_T_Emn)
        phi_ab = np.einsum('...n,n,nab->...ab', phi_n, self._MPW, self._MPNN)
        return phi_ab

    DELTA = np.identity(n_D)

    # -----------------------------------------------------------
    # Integration of the (inelastic) strains for each microplane
    # -----------------------------------------------------------

    def _get_eps_p_Emab(self, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                        r_N_Emn, eps_N_p_Emn, sigma_N_Emn, omega_T_Emn,
                        z_T_Emn, alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna,
                        plastic_dissip_T_Emn, damage_dissip_T_Emn,
                        plastic_dissip_N_Emn, damage_dissip_N_Emn,
                        total_work_microplane, total_work_macro, eps_aux):
        eps_N_Emn = self._get_e_N_Emn(eps_Emab)
        eps_T_Emna = self._get_e_T_Emna(eps_Emab)

        sigma_N_Emn, Z, X, Y_N = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
            sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_T_Emna, plastic_dissip_T_Emn, damage_dissip_T_Emn,
            plastic_dissip_N_Emn, damage_dissip_N_Emn, total_work_microplane,
            total_work_macro, eps_aux)

        # sliding tangential strains
        sigma_T_Emna, Z_T, X_T, Y_T, plastic_dissip_T_Emn = self.get_tangential_law(
            eps_T_Emna, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
            eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna,
            eps_T_pi_Emna, sigma_T_Emna, plastic_dissip_T_Emn,
            damage_dissip_T_Emn, plastic_dissip_N_Emn, damage_dissip_N_Emn,
            total_work_microplane, total_work_macro, eps_aux)

        # 2-nd order plastic (inelastic) tensor
        eps_p_Emab = (np.einsum('n,...n,na,nb->...ab', self._MPW, eps_N_p_Emn,
                                self._MPN, self._MPN) + 0.5 *
                      (np.einsum('n,...nf,na,fb->...ab', self._MPW,
                                 eps_T_pi_Emna, self._MPN, self.DELTA) +
                       np.einsum('n,...nf,nb,fa->...ab', self._MPW,
                                 eps_T_pi_Emna, self._MPN, self.DELTA)))

        return eps_p_Emab

    def get_total_work(self, eps_Emab, sig_Emab, omega_N_Emn, z_N_Emn,
                       alpha_N_Emn, r_N_Emn, eps_N_p_Emn, sigma_N_Emn,
                       omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
                       sigma_T_Emna, plastic_dissip_T_Emn, damage_dissip_T_Emn,
                       plastic_dissip_N_Emn, damage_dissip_N_Emn,
                       total_work_microplane, total_work_macro, eps_aux):

        delta_eps_Emab = eps_Emab - eps_aux
        delta_eps_N_Emn = self._get_e_N_Emn(eps_Emab) - self._get_e_N_Emn(
            eps_aux)
        delta_eps_T_Emna = self._get_e_T_Emna(eps_Emab) - self._get_e_T_Emna(
            eps_aux)

        # work_microplane = np.einsum('...n,...n->...n', sigma_N_Emn, delta_eps_N_Emn) + np.einsum('...na,...na->...n',
        #                                                                                          sigma_T_Emna,
        #                                                                                          delta_eps_T_Emna)
        delta_eps_vector = np.einsum('...na,...n->...na', self._MPN,
                                     delta_eps_N_Emn) + delta_eps_T_Emna
        sigma_vector = np.einsum('...na,...n->...na', self._MPN,
                                 sigma_N_Emn) + sigma_T_Emna

        work_microplane = np.einsum('...na,...na->...n', sigma_vector,
                                    delta_eps_vector)

        total_work_microplane[...] = np.einsum('...n,...n->...', self._MPW,
                                               work_microplane)

        total_work_macro[...] = np.einsum('...ij,...ij->...', sig_Emab,
                                          delta_eps_Emab)

        eps_aux[...] = eps_Emab

        return total_work_microplane, total_work_macro

    def get_corr_pred(self, eps_Emab, t_n1, omega_N_Emn, z_N_Emn, alpha_N_Emn,
                      r_N_Emn, eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn,
                      alpha_T_Emna, eps_T_pi_Emna, sigma_T_Emna,
                      plastic_dissip_T_Emn, damage_dissip_T_Emn,
                      plastic_dissip_N_Emn, damage_dissip_N_Emn,
                      total_work_microplane, total_work_macro, eps_aux):
        """Evaluation - get the corrector and predictor

        """
        # Corrector predictor computation.

        eps_N_Emn = self._get_e_N_Emn(eps_Emab)
        eps_T_Emna = self._get_e_T_Emna(eps_Emab)

        sigma_N_Emn, Z, X, Y_N = self.get_normal_law(
            eps_N_Emn, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
            sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_T_Emna, plastic_dissip_T_Emn, damage_dissip_T_Emn,
            plastic_dissip_N_Emn, damage_dissip_N_Emn, total_work_microplane,
            total_work_macro, eps_aux)

        # sliding tangential strains
        sigma_T_Emna, Z_T, X_T, Y_T, plastic_dissip_T_Emn = self.get_tangential_law(
            eps_T_Emna, eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
            eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna,
            eps_T_pi_Emna, sigma_T_Emna, plastic_dissip_T_Emn,
            damage_dissip_T_Emn, plastic_dissip_N_Emn, damage_dissip_N_Emn,
            total_work_microplane, total_work_macro, eps_aux)

        eps_N_Emn_effective = np.einsum('...n,...n->...n', eps_N_Emn,
                                        (1 - omega_N_Emn))
        eps_T_Emna_effective = np.einsum('...na,...n->...na', eps_T_Emna,
                                         (1 - omega_T_Emn))
        delta = np.identity(3)
        eps_Emab_effective = (
            np.einsum('n,...n,na,nb->...ab', self._MPW, eps_N_Emn_effective,
                      self._MPN, self._MPN) + 0.5 *
            (np.einsum('n,...nf,na,fb->...ab', self._MPW, eps_T_Emna_effective,
                       self._MPN, delta) +
             np.einsum('n,...nf,nb,fa->...ab', self._MPW, eps_T_Emna_effective,
                       self._MPN, delta)))

        sigma_Emab_effective = np.einsum('...abcd,...cd->...ab', self.D_abef,
                                         eps_Emab_effective)

        sigma_N_Emn_effective = self._get_e_N_Emn(sigma_Emab_effective)
        sigma_T_Emna_effective = self._get_e_T_Emna(sigma_Emab_effective)

        sigma_N_Emn[...] = np.einsum('...n,...n->...n', sigma_N_Emn_effective,
                                     (1 - omega_N_Emn))
        sigma_T_Emna[...] = np.einsum('...na,...n->...na',
                                      sigma_T_Emna_effective,
                                      (1 - omega_T_Emn))

        sig_Emab = (np.einsum('n,...n,na,nb->...ab', self._MPW, sigma_N_Emn,
                              self._MPN, self._MPN) + 0.5 *
                    (np.einsum('n,...nf,na,fb->...ab', self._MPW, sigma_T_Emna,
                               self._MPN, delta) +
                     np.einsum('n,...nf,nb,fa->...ab', self._MPW, sigma_T_Emna,
                               self._MPN, delta)))

        # ------------------------------------------------------------------
        # Damage tensor (4th order) using product- or sum-type symmetrization:
        # ------------------------------------------------------------------
        beta_Emabcd = self._get_beta_Emabcd(
            eps_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn, eps_N_p_Emn,
            sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna, eps_T_pi_Emna,
            sigma_T_Emna, plastic_dissip_T_Emn, damage_dissip_T_Emn,
            plastic_dissip_N_Emn, damage_dissip_N_Emn, total_work_microplane,
            total_work_macro, eps_aux)

        # ------------------------------------------------------------------
        # Damaged stiffness tensor calculated based on the damage tensor beta4:
        # ------------------------------------------------------------------

        D_Emabcd = np.einsum('...ijab, abef, ...cdef->...ijcd', beta_Emabcd,
                             self.D_abef, beta_Emabcd)

        total_work_micro, total_work_macro = self.get_total_work(
            eps_Emab, sig_Emab, omega_N_Emn, z_N_Emn, alpha_N_Emn, r_N_Emn,
            eps_N_p_Emn, sigma_N_Emn, omega_T_Emn, z_T_Emn, alpha_T_Emna,
            eps_T_pi_Emna, sigma_T_Emna, plastic_dissip_T_Emn,
            damage_dissip_T_Emn, plastic_dissip_N_Emn, damage_dissip_N_Emn,
            total_work_microplane, total_work_macro, eps_aux)

        return sig_Emab, D_Emabcd
Ejemplo n.º 23
0
class IShape(CrossSectionShapeBase):

    H = Float(900, GEO=True)
    B_w = Float(50, GEO=True)
    B_f_bot = Float(200, GEO=True)
    B_f_top = Float(200, GEO=True)
    H_f_bot = Float(145, GEO=True)
    H_f_top = Float(145, GEO=True)

    ipw_view = View(
        *CrossSectionShapeBase.ipw_view.content,
        Item('B_w', minmax=(10, 3000), latex=r'B_w \mathrm{[mm]}'),
        Item('B_f_bot', minmax=(10, 3000), latex=r'B_{f_{bot}} \mathrm{[mm]}'),
        Item('B_f_top', minmax=(10, 3000), latex=r'B_{f_{top}} \mathrm{[mm]}'),
        Item('H_f_bot', minmax=(10, 3000), latex=r'H_{f_{bot}} \mathrm{[mm]}'),
        Item('H_f_top', minmax=(10, 3000), latex=r'H_{f_{top}} \mathrm{[mm]}'),
    )

    def get_cs_area(self):
        return self.B_f_bot * self.H_f_bot + self.B_f_top * self.H_f_top + self.B_w * (
            self.H - self.H_f_top - self.H_f_bot)

    def get_cs_i(self):
        pass

    get_b = tr.Property(tr.Callable, depends_on='+input')

    @tr.cached_property
    def _get_get_b(self):
        z_ = sp.Symbol('z')
        b_p = sp.Piecewise((self.B_f_bot, z_ < self.H_f_bot),
                           (self.B_w, z_ < self.H - self.H_f_top),
                           (self.B_f_top, True))
        return sp.lambdify(z_, b_p, 'numpy')

    def update_plot(self, ax):
        # Start drawing from bottom center of the cross section
        cs_points = np.array([[self.B_f_bot / 2, 0],
                              [self.B_f_bot / 2, self.H_f_bot],
                              [self.B_w / 2, self.H_f_bot],
                              [self.B_w / 2, self.H - self.H_f_top],
                              [self.B_f_top / 2, self.H - self.H_f_top],
                              [self.B_f_top / 2, self.H],
                              [-self.B_f_top / 2, self.H],
                              [-self.B_f_top / 2, self.H - self.H_f_top],
                              [-self.B_w / 2, self.H - self.H_f_top],
                              [-self.B_w / 2, self.H_f_bot],
                              [-self.B_f_bot / 2, self.H_f_bot],
                              [-self.B_f_bot / 2, 0]])

        cs = Polygon(cs_points)

        patch_collection = PatchCollection([cs],
                                           facecolor=(.5, .5, .5, 0.2),
                                           edgecolors=(0, 0, 0, 1))

        ax.add_collection(patch_collection)
        # ax.scatter(0, TShape.get_cs_i(self)[0], color='white', s=self.B_w, marker="+")

        ax.autoscale_view()
        ax.set_aspect('equal')

        ax.annotate('Area = {} $mm^2$'.format(int(IShape.get_cs_area(self)),
                                              0),
                    xy=(0, self.H / 2),
                    color='blue')
Ejemplo n.º 24
0
class VUNTIM(MATS3DEval):
    """
    Vectorized uncoupled normal tngential interface model
    """
    # -------------------------------------------------------------------------
    # Elasticity
    # -------------------------------------------------------------------------

    E_N = Float(10000, MAT=True)

    E_T = Float(1000, MAT=True)

    gamma_T = Float(100000., MAT=True)

    K_T = Float(10000., MAT=True)

    S_T = Float(0.005, MAT=True)

    r_T = Float(9., MAT=True)

    e_T = Float(12., MAT=True)

    c_T = Float(4.6, MAT=True)

    tau_pi_bar = Float(1.7, MAT=True)

    a = Float(0.003, MAT=True)

    # ------------------------------------------------------------------------------
    # Normal_Tension constitutive law parameters (without cumulative normal strain)
    # ------------------------------------------------------------------------------
    Ad = Float(100.0, MAT=True)

    eps_0 = Float(0.00008, MAT=True)

    # -----------------------------------------------
    # Normal_Compression constitutive law parameters
    # -----------------------------------------------
    K_N = Float(10000., MAT=True)

    gamma_N = Float(5000., MAT=True)

    sig_0 = Float(30., MAT=True)

    ipw_view = View(
        Item('E_N'),
        Item('E_T'),
        Item('Ad'),
        Item('eps_0'),
        Item('K_N'),
        Item('gamma_N'),
        Item('sig_0'),
        Item('gamma_T', latex=r'\gamma_\mathrm{T}', minmax=(10, 100000)),
        Item('K_T', latex=r'K_\mathrm{T}', minmax=(10, 10000)),
        Item('S_T', latex=r'S_\mathrm{T}', minmax=(0.001, 0.01)),
        Item('r_T', latex=r'r_\mathrm{T}', minmax=(1, 3)),
        Item('e_T', latex=r'e_\mathrm{T}', minmax=(1, 40)),
        Item('c_T', latex=r'c_\mathrm{T}', minmax=(1, 10)),
        Item('tau_pi_bar', latex=r'\bar{\sigma}^\pi_{T}', minmax=(1, 10)),
        Item('a', latex=r'a_\mathrm{T}', minmax=(0.001, 3)),
    )

    n_D = 3

    state_var_shapes = dict(
        omega_N=(),  # damage N
        z_N=(),
        alpha_N=(),
        r_N=(),
        eps_N_p=(),
        sig_N=(),
        omega_T=(),
        z_T=(),
        alpha_T_a=(n_D, ),
        eps_T_p_a=(n_D, ),
        sig_T_a=(n_D, ),
    )

    # --------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    # --------------------------------------------------------------
    def get_normal_law(self, eps_N, **Eps):
        omega_N, z_N, alpha_N, r_N, eps_N_p = [
            Eps[key]
            for key in ['omega_N', 'z_N', 'alpha_N', 'r_N', 'eps_N_p']
        ]
        E_N = self.E_N

        # When deciding if a microplane is in tensile or compression, we define a strain boundary such that that
        # sigN <= 0 if eps_N < 0, avoiding entering in the quadrant of compressive strains and traction

        sig_trial = E_N * (eps_N - eps_N_p)
        pos1 = [(eps_N < -1e-6) & (sig_trial > 1e-6)
                ]  # looking for microplanes violating strain boundary
        sig_trial[pos1[0]] = 0
        pos = sig_trial > 1e-6  # microplanes under traction
        pos2 = sig_trial < -1e-6  # microplanes under compression
        H = 1.0 * pos
        H2 = 1.0 * pos2

        # thermo forces
        sig_N_tilde = E_N * (eps_N - eps_N_p)
        sig_N_tilde[pos1[0]] = 0  # imposing strain boundary

        Z = self.K_N * z_N
        X = self.gamma_N * alpha_N * H2
        h = (self.sig_0 + Z) * H2

        f_trial = (abs(sig_N_tilde - X) - h) * H2

        # threshold plasticity

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
                      (E_N / (1 - omega_N) + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p = eps_N_p + delta_lamda * \
                      np.sign(sig_N_tilde - X)
        z_N = z_N + delta_lamda
        alpha_N = alpha_N + delta_lamda * \
                      np.sign(sig_N_tilde - X)

        def R_N(r_N):
            return (1.0 / self.Ad) * (-r_N / (1.0 + r_N))

        Y_N = 0.5 * H * E_N * (eps_N - eps_N_p)**2.0
        Y_0 = 0.5 * E_N * self.eps_0**2.0

        f = (Y_N - (Y_0 + R_N(r_N)))

        # threshold damage

        thres_2 = f > 1e-6

        def f_w(Y):
            return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))

        omega_N[f > 1e-6] = f_w(Y_N)[f > 1e-6]
        r_N[f > 1e-6] = -omega_N[f > 1e-6]

        sig_N = (1.0 - H * omega_N) * E_N * (eps_N - eps_N_p)
        pos1 = [(eps_N < -1e-6) & (sig_trial > 1e-6)
                ]  # looking for microplanes violating strain boundary
        sig_N[pos1[0]] = 0

        return sig_N

    # -------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    # -------------------------------------------------------------------------
    def get_tangential_law(self, eps_T_a, **Eps):

        omega_T, z_T, alpha_T_a, eps_T_p_a, sig_N, = [
            Eps[key] for key in [
                'omega_T',
                'z_T',
                'alpha_T_a',
                'eps_T_p_a',
                'sig_N',
            ]
        ]

        E_T = self.E_T

        # thermodynamic forces
        sig_pi_trial = E_T * (eps_T_a - eps_T_p_a)

        Z = self.K_T * z_T
        X = self.gamma_T * alpha_T_a

        norm_1 = np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X)))
        Y = 0.5 * E_T * np.einsum('...na,...na->...n', (eps_T_a - eps_T_p_a),
                                  (eps_T_a - eps_T_p_a))

        # threshold
        f = norm_1 - self.tau_pi_bar - Z + self.a * sig_N

        plas_1 = f > 1e-6
        elas_1 = f < 1e-6

        delta_lamda = f / (E_T /
                           (1.0 - omega_T) + self.gamma_T + self.K_T) * plas_1

        norm_2 = 1.0 * elas_1 + np.sqrt(
            np.einsum('...na,...na->...n', (sig_pi_trial - X),
                      (sig_pi_trial - X))) * plas_1

        eps_T_p_a[..., 0] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 0] - X[..., 0]) /
                                 (1.0 - omega_T)) / norm_2
        eps_T_p_a[..., 1] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 1] - X[..., 1]) /
                                 (1.0 - omega_T)) / norm_2
        eps_T_p_a[..., 2] += plas_1 * delta_lamda * \
                                ((sig_pi_trial[..., 2] - X[..., 2]) /
                                 (1.0 - omega_T)) / norm_2
        omega_T[...] += ((1.0 - omega_T) ** self.c_T) * \
                       (delta_lamda * (Y / self.S_T) ** self.r_T) * \
                       (self.tau_pi_bar / (self.tau_pi_bar - self.a * sig_N)) ** self.e_T
        alpha_T_a[..., 0] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 0] - X[..., 0]) / norm_2
        alpha_T_a[..., 1] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 1] - X[..., 1]) / norm_2
        alpha_T_a[..., 2] += plas_1 * delta_lamda * \
                               (sig_pi_trial[..., 2] - X[..., 2]) / norm_2
        z_T[...] += delta_lamda

        sig_T_a = np.einsum('...n,...na->...na', (1 - omega_T),
                            E_T * (eps_T_a - eps_T_p_a))

        return sig_T_a

    def get_corr_pred(self, eps_a, t_n1, **Eps):
        eps_a_ = np.einsum('...a->a...', eps_a)
        eps_N_n1 = eps_a_[0, ...]
        eps_T_a_n1 = np.einsum('a...->...a', eps_a_[1:, ...])

        sig_N = self.get_normal_law(eps_N_n1, **Eps)
        sig_T_a = self.get_tangential_law(eps_T_a_n1, **Eps)

        D_ = np.zeros(eps_a.shape + (eps_a.shape[-1], ))
        D_[..., 0, 0] = self.E_N  # * (1 - omega_N)
        D_[..., 1, 1] = self.E_T  # * (1 - omega_T)
        D_[..., 2, 2] = self.E_T  # * (1 - omega_T)
        D_[..., 3, 3] = self.E_T  # * (1 - omega_T)
        sig_a = np.concatenate([sig_N[..., np.newaxis], sig_T_a], axis=-1)
        return sig_a, D_

    def get_eps_NT_p(self, **Eps):
        """Plastic strain tensor
        """
        return Eps['eps_N_p'], Eps['eps_T_p_a']

    def plot_idx(self, ax_sig, ax_d_sig, idx=0):
        eps_max = self.eps_max
        n_eps = self.n_eps
        eps1_range = np.linspace(1e-9, eps_max, n_eps)
        Eps = {
            var: np.zeros((1, ) + shape)
            for var, shape in self.state_var_shapes.items()
        }
        eps_range = np.zeros((n_eps, 4))
        eps_range[:, idx] = eps1_range

        # monotonic load in the normal direction
        sig1_range, d_sig11_range = [], []
        for eps_a in eps_range:
            sig_a, D_range = self.get_corr_pred(eps_a[np.newaxis, ...], 1,
                                                **Eps)
            sig1_range.append(sig_a[0, idx])
            d_sig11_range.append(D_range[0, idx, idx])
        sig1_range = np.array(sig1_range, dtype=np.float_)
        eps1_range = eps1_range[:len(sig1_range)]

        ax_sig.plot(eps1_range, sig1_range, color='blue')
        d_sig11_range = np.array(d_sig11_range, dtype=np.float_)
        ax_d_sig.plot(eps1_range,
                      d_sig11_range,
                      linestyle='dashed',
                      color='gray')
        ax_sig.set_xlabel(r'$\varepsilon_{11}$ [-]')
        ax_sig.set_ylabel(r'$\sigma_{11}$ [MPa]')
        ax_d_sig.set_ylabel(
            r'$\mathrm{d} \sigma_{11} / \mathrm{d} \varepsilon_{11}$ [MPa]')
        ax_d_sig.plot(eps1_range[:-1], (sig1_range[:-1] - sig1_range[1:]) /
                      (eps1_range[:-1] - eps1_range[1:]),
                      color='orange',
                      linestyle='dashed')

    def subplots(self, fig):
        ax_sig_N, ax_sig_T = fig.subplots(1, 2)
        ax_d_sig_N = ax_sig_N.twinx()
        ax_d_sig_T = ax_sig_T.twinx()
        return ax_sig_N, ax_d_sig_N, ax_sig_T, ax_d_sig_T

    def update_plot(self, axes):
        ax_sig_N, ax_d_sig_N, ax_sig_T, ax_d_sig_T = axes
        self.plot_idx(ax_sig_N, ax_d_sig_N, 0)
        self.plot_idx(ax_sig_T, ax_d_sig_T, 1)
Ejemplo n.º 25
0
class VDNTIM(MATS3DEval):
    """
    Vectorized uncoupled normal tngential interface model
    """
    # -------------------------------------------------------------------------
    # Elasticity
    # -------------------------------------------------------------------------
    name = 'equiv damage NTIM'

    E_N = Float(10000, MAT=True)

    E_T = Float(1000, MAT=True)

    epsilon_0 = Float(59.0e-6, MAT=True,
                      label="a",
                      desc="Lateral pressure coefficient",
                      enter_set=True,
                      auto_set=False)

    epsilon_f = Float(250.0e-6, MAT=True,
                      label="a",
                      desc="Lateral pressure coefficient",
                      enter_set=True,
                      auto_set=False)

    c_T = Float(0.01, MAT=True,
                label="a",
                desc="Lateral pressure coefficient",
                enter_set=True,
                auto_set=False)

    ipw_view = View(
        Item('E_T', readonly=True),
        Item('E_N', readonly=True),
        Item('epsilon_0'),
        Item('epsilon_f'),
        Item('c_T'),
        Item('eps_max'),
        Item('n_eps')
    )

    n_D = 3

    state_var_shapes = dict(
        kappa=(),
        omega_N=(),
        omega_T=()
    )

    def get_e_equiv(self, eps_N, eps_T):
        r"""
        Returns a list of the microplane equivalent strains
        based on the list of microplane strain vectors
        """
        # positive part of the normal strain magnitude for each microplane
        e_N_pos = (np.abs(eps_N) + eps_N) / 2.0
        # tangent strain ratio
        c_T = self.c_T
        # equivalent strain for each microplane
        eps_equiv = np.sqrt(e_N_pos * e_N_pos + c_T * eps_T)
        return eps_equiv

    def get_normal_law(self, eps_N, eps_T_a, kappa, omega_N, omega_T):
        E_N = self.E_N
        E_T = self.E_T
        eps_T = np.sqrt(np.einsum('...a,...a->...', eps_T_a, eps_T_a))
        eps_equiv = self.get_e_equiv(eps_N, eps_T)
        kappa[...] = np.max(np.array([kappa, eps_equiv]), axis=0)
        epsilon_0 = self.epsilon_0
        epsilon_f = self.epsilon_f
        I = np.where(kappa >= epsilon_0)
        omega_N[I] = (
            1.0 - (epsilon_0 / kappa[I] *
                   np.exp(-1.0 * (kappa[I] - epsilon_0) / (epsilon_f - epsilon_0))
            )
        )
        sig_N = (1 - omega_N) * E_N * eps_N
        sig_T_a = (1 - omega_N[..., np.newaxis]) * E_T * eps_T_a
        sig_a = np.concatenate([sig_N[...,np.newaxis], sig_T_a], axis=-1)
        return sig_a

    def get_corr_pred(self, eps_a, t_n1, **Eps):
        eps_a_ = np.einsum('...a->a...',eps_a)
        eps_N = eps_a_[0,...]
        eps_T_a = np.einsum('a...->...a', eps_a_[1:,...])
        sig_a = self.get_normal_law(eps_N, eps_T_a, **Eps)

        D_ = np.zeros(eps_a.shape + (eps_a.shape[-1],))
        D_[..., 0, 0] = self.E_N# * (1 - omega_N)
        D_[..., 1, 1] = self.E_T# * (1 - omega_T)
        D_[..., 2, 2] = self.E_T# * (1 - omega_T)
        D_[..., 3, 3] = self.E_T# * (1 - omega_T)

        return sig_a, D_

    def get_eps_NT_p(self, **Eps):
        """Plastic strain tensor
        """
        return None

    def plot_idx(self, ax_sig, ax_d_sig, idx=0):
        eps_max = self.eps_max
        n_eps = self.n_eps
        eps1_range = np.linspace(1e-9,eps_max,n_eps)
        Eps = { var : np.zeros( (1,) + shape )
            for var, shape in self.state_var_shapes.items()
        }
        eps_range = np.zeros((n_eps, 4))
        eps_range[:,idx] = eps1_range

        # monotonic load in the normal direction
        sig1_range, d_sig11_range = [], []
        for eps_a in eps_range:
            sig_a, D_range = self.get_corr_pred(eps_a[np.newaxis, ...], 1, **Eps)
            sig1_range.append(sig_a[0, idx])
            d_sig11_range.append(D_range[0, idx, idx])
        sig1_range = np.array(sig1_range, dtype=np.float_)
        eps1_range = eps1_range[:len(sig1_range)]

        ax_sig.plot(eps1_range, sig1_range,color='blue')
        d_sig11_range = np.array(d_sig11_range, dtype=np.float_)
        ax_d_sig.plot(eps1_range, d_sig11_range, linestyle='dashed', color='gray')
        ax_sig.set_xlabel(r'$\varepsilon_{11}$ [-]')
        ax_sig.set_ylabel(r'$\sigma_{11}$ [MPa]')
        ax_d_sig.set_ylabel(r'$\mathrm{d} \sigma_{11} / \mathrm{d} \varepsilon_{11}$ [MPa]')
        ax_d_sig.plot(eps1_range[:-1],
                    (sig1_range[:-1]-sig1_range[1:])/(eps1_range[:-1]-eps1_range[1:]),
                    color='orange', linestyle='dashed')

    def subplots(self, fig):
        ax_sig_N, ax_sig_T = fig.subplots(1,2)
        ax_d_sig_N = ax_sig_N.twinx()
        ax_d_sig_T = ax_sig_T.twinx()
        return ax_sig_N, ax_d_sig_N, ax_sig_T, ax_d_sig_T

    def update_plot(self, axes):
        ax_sig_N, ax_d_sig_N, ax_sig_T, ax_d_sig_T = axes
        self.plot_idx(ax_sig_N, ax_d_sig_N, 0)
        self.plot_idx(ax_sig_T, ax_d_sig_T, 1)
Ejemplo n.º 26
0
class MATS2DScalarDamage(MATS2DEval):
    r'''Isotropic damage model.
    '''

    name = 'isotropic damage model'
    node_name = 'isotropic damage model'

    tree = ['omega_fn','strain_norm']

    omega_fn = EitherType(
        options=[('exp-slope', ExpSlopeDamageFn),
                 ('linear', LinearDamageFn),
                 ('abaqus', AbaqusDamageFn),
                 ('fracture-energy', GfDamageFn),
                 ('weibull-CDF', WeibullDamageFn),
                 ],
        MAT=True,
        on_option_change='link_omega_to_mats'
    )

    D_alg = Float(0)
    r'''Selector of the stiffness calculation.
    '''

    eps_max = Float(0.03, ALG=True)
    # upon change of the type attribute set the link to the material model
    def link_omega_to_mats(self):
        self.omega_fn_.trait_set(mats=self,
                                 E_name='E',
                                 x_max_name='eps_max')

    #=========================================================================
    # Material model
    #=========================================================================
    strain_norm = EitherType(
        options=[('Rankine', SN2DRankine),
                 ('Masars', SN2DMasars),
                 ('Energy', SN2DEnergy)],
        on_option_change='link_strain_norm_to_mats'
    )

    def link_strain_norm_to_mats(self):
        self.strain_norm_.trait_set(mats=self)

    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 get_corr_pred(self, eps_ab_n1, tn1, kappa, omega):
        r'''
        Corrector predictor computation.
        @param eps_app_eng input variable - engineering strain
        '''
        eps_eq = self.strain_norm_.get_eps_eq(eps_ab_n1, kappa)
        I = self.omega_fn_.get_f_trial(eps_eq, kappa)
        eps_eq_I = eps_eq[I]
        kappa[I] = eps_eq_I
        omega[I] = self._omega(eps_eq_I)
        phi = (1.0 - omega)
        D_abcd = np.einsum(
            '...,abcd->...abcd',
            phi, self.D_abcd
        )
        sig_ab = np.einsum(
            '...abcd,...cd->...ab',
            D_abcd, eps_ab_n1
        )
        if self.D_alg > 0:
            domega_ds_I = self._omega_derivative(eps_eq_I)
            deps_eq_I = self.strain_norm_.get_deps_eq(eps_ab_n1[I])
            D_red_I = np.einsum('...,...ef,cdef,...ef->...cdef', domega_ds_I,
                                deps_eq_I, self.D_abcd, eps_ab_n1[I]) * self.D_alg
            D_abcd[I] -= D_red_I

        return sig_ab, D_abcd

    def _omega(self, kappa):
        return self.omega_fn_(kappa)

    def _omega_derivative(self, kappa):
        return self.omega_fn_.diff(kappa)

    ipw_view = View(
        Item('E'),
        Item('nu'),
        Item('strain_norm'),
        Item('omega_fn'),
        Item('stress_state'),
        Item('D_alg', latex=r'\theta_\mathrm{alg. stiff.}',
                editor=FloatRangeEditor(low=0,high=1)),
        Item('eps_max'),
        Item('G_f', latex=r'G_\mathrm{f}^{\mathrm{estimate}}', readonly=True),
    )

    G_f = tr.Property(Float, depends_on='state_changed')
    @tr.cached_property
    def _get_G_f(self):
        eps_max = self.eps_max
        n_eps = 1000
        eps11_range = np.linspace(1e-9,eps_max,n_eps)
        eps_range = np.zeros((len(eps11_range), 2, 2))
        eps_range[:,1,1] = eps11_range
        state_vars = { var : np.zeros( (len(eps11_range),) + shape )
            for var, shape in self.state_var_shapes.items()
        }
        sig_range, D = self.get_corr_pred(eps_range, 1, **state_vars)
        sig11_range = sig_range[:,1,1]
        return np.trapz(sig11_range, eps11_range)

    def subplots(self, fig):
        ax_sig = fig.subplots(1,1)
        ax_d_sig = ax_sig.twinx()
        return ax_sig, ax_d_sig

    def update_plot(self, axes):
        ax_sig, ax_d_sig = axes
        eps_max = self.eps_max
        n_eps = 100
        eps11_range = np.linspace(1e-9,eps_max,n_eps)
        eps_range = np.zeros((n_eps, 2, 2))
        eps_range[:,0,0] = eps11_range
        state_vars = { var : np.zeros( (n_eps,) + shape )
            for var, shape in self.state_var_shapes.items()
        }
        sig_range, D_range = self.get_corr_pred(eps_range, 1, **state_vars)
        sig11_range = sig_range[:,0,0]
        ax_sig.plot(eps11_range, sig11_range,color='blue')
        d_sig1111_range = D_range[...,0,0,0,0]
        ax_d_sig.plot(eps11_range, d_sig1111_range,
                      linestyle='dashed', color='gray')
        ax_sig.set_xlabel(r'$\varepsilon_{11}$ [-]')
        ax_sig.set_ylabel(r'$\sigma_{11}$ [MPa]')
        ax_d_sig.set_ylabel(r'$\mathrm{d} \sigma_{11} / \mathrm{d} \varepsilon_{11}$ [MPa]')

        ax_d_sig.plot(eps11_range[:-1],
                    (sig11_range[:-1]-sig11_range[1:])/(eps11_range[:-1]-eps11_range[1:]),
                    color='orange', linestyle='dashed')

    def get_omega(self, eps_ab, tn1, **Eps):
        return Eps['omega']

    var_dict = tr.Property(tr.Dict(tr.Str, tr.Callable))
    '''Dictionary of response variables
    '''
    @tr.cached_property
    def _get_var_dict(self):
        var_dict = dict(omega=self.get_omega)
        var_dict.update(super()._get_var_dict())
        return var_dict
Ejemplo n.º 27
0
class ConcreteMaterialModelAdv(ConcreteMatMod, bu.InjectSymbExpr):

    name = 'Concrete behavior'
    node_name = 'material model'

    symb_class = ConcreteMaterialModelAdvExpr

    d_a = Float(16, MAT=True)  ## dia of steel mm
    E_c = Float(28000, MAT=True)  ## tensile strength of Concrete in MPa
    f_t = Float(3, MAT=True)  ## Fracture Energy in N/m
    c_1 = Float(3, MAT=True)
    c_2 = Float(6.93, MAT=True)
    f_c = Float(33.3, MAT=True)
    L_fps = Float(50, MAT=True)
    a = Float(1.038, MAT=True)
    b = Float(0.245, MAT=True)
    interlock_factor = Float(1, MAT=True)

    ipw_view = View(
        Item('d_a', latex=r'd_a'), Item('E_c', latex=r'E_c'),
        Item('f_t', latex=r'f_t'), Item('f_c', latex=r'f_c'),
        Item('c_1', latex=r'c_1'), Item('c_2', latex=r'c_2'),
        Item('L_fps', latex=r'L_{fps}'), Item('a', latex=r'a'),
        Item('b', latex=r'b'),
        Item('interlock_factor',
             latex=r'\gamma_\mathrm{ag}',
             editor=FloatRangeEditor(low=0, high=1)),
        Item('w_cr', latex=r'w_\mathrm{cr}', readonly=True),
        Item('L_c', latex=r'L_\mathrm{c}', readonly=True),
        Item('G_f', latex=r'G_\mathrm{f}', readonly=True))

    # G_f_baz = tr.Property(depends_on='_ITR, _INC, _GEO, _MAT, _DSC')
    # @tr.cached_property
    # def _get_G_f_baz(self):
    #     xi = self.sz_crack_tip_orientation.
    #     return self.symb.G_f_baz

    L_c = tr.Property(Float, depends_on='state_changed')

    @tr.cached_property
    def _get_L_c(self):
        return self.E_c * self.G_f / self.f_t**2

    w_cr = tr.Property(Float, depends_on='state_changed')

    @tr.cached_property
    def _get_w_cr(self):
        return (self.f_t / self.E_c) * self._get_L_c()

    G_f = tr.Property(Float, depends_on='state_changed')

    @tr.cached_property
    def _get_G_f(self):
        '''Calculating fracture energy
        '''
        return (0.028 * self.f_c**0.18 * self.d_a**0.32)

    def get_w_tc(self):
        '''Calculating point of softening curve resulting in 0 stress
        '''
        return (5.14 * self.G_f_baz / self.f_t)

    def get_sig_a(self, u_a):  #w, s
        '''Calculating stresses
        '''
        sig_w = self.get_sig_w(u_a[..., 0], u_a[..., 1])
        tau_s = self.get_tau_s(u_a[..., 0], u_a[..., 1])
        return np.einsum('b...->...b', np.array([sig_w, tau_s],
                                                dtype=np.float_))  #, tau_s

    def get_sig_w(self, w, s):
        return self.symb.get_sig_w(w, s)

    def get_tau_s(self, w, s):
        return self.symb.get_tau_s(w, s) * self.interlock_factor

    w_min_factor = Float(1.2)
    w_max_factor = Float(15)

    def plot3d_sig_w(self, ax3d, vot=1.0):

        w_min = -(self.f_c / self.E_c * self._get_L_c()) * self.w_min_factor
        w_max = self.w_cr * self.w_max_factor
        w_range = np.linspace(w_min, w_max, 100)
        s_max = 3
        s_data = np.linspace(-1.1 * s_max, 1.1 * s_max, 100)
        s_, w_ = np.meshgrid(s_data, w_range)

        sig_w = self.get_sig_w(w_, s_)

        ax3d.plot_surface(w_, s_, sig_w, cmap='viridis', edgecolor='none')
        ax3d.set_xlabel(r'$w\;\;\mathrm{[mm]}$', fontsize=12)
        ax3d.set_ylabel(r'$s\;\;\mathrm{[mm]}$', fontsize=12)
        ax3d.set_zlabel(r'$\sigma_w\;\;\mathrm{[MPa]}$', fontsize=12)
        ax3d.set_title('crack opening law', fontsize=12)

    # def plot_sig_w(self, ax, vot=1.0):
    #         w_min = -(self.f_c / self.E_c * self._get_L_c()) * self.w_min_factor
    #         w_max = self.w_cr * self.w_max_factor
    #         w_range = np.linspace(w_min, w_max, 100)
    #         s_max = 3
    #         s_data = np.linspace(-1.1 * s_max, 1.1 * s_max, 100)
    #         sig_w = self.get_sig_w(w_range, s_data)
    #         ax.plot(w_range, sig_w, lw=2, color='red')
    #         ax.fill_between(w_range, sig_w,
    #                         color='red', alpha=0.2)
    #         ax.set_xlabel(r'$w\;\;\mathrm{[mm]}$', fontsize=12)
    #         ax.set_ylabel(r'$\sigma\;\;\mathrm{[MPa]}$', fontsize=12)
    #         ax.set_title('crack opening law', fontsize=12)

    def plot3d_tau_s(self, ax3d, vot=1.0):
        w_min = 1e-9  #-1
        w_max = 3
        w_data = np.linspace(w_min, w_max, 100)
        s_max = 3
        s_data = np.linspace(-1.1 * s_max, 1.1 * s_max, 100)  #-1.1
        s_, w_ = np.meshgrid(s_data, w_data)
        tau_s = self.get_tau_s(w_, s_)
        ax3d.plot_surface(w_, s_, tau_s, cmap='viridis', edgecolor='none')
        ax3d.set_xlabel(r'$w\;\;\mathrm{[mm]}$', fontsize=12)
        ax3d.set_ylabel(r'$s\;\;\mathrm{[mm]}$', fontsize=12)
        ax3d.set_zlabel(r'$\tau\;\;\mathrm{[MPa]}$', fontsize=12)
        ax3d.set_title('aggregate interlock law', fontsize=12)

    # def subplots(self, fig):
    #     #ax_2d = fig.add_subplot(1, 2, 2)
    #     ax_3d_1 = fig.add_subplot(1, 2, 2, projection='3d')
    #     ax_3d_2 = fig.add_subplot(1, 2, 1, projection='3d')
    #     return ax_3d_1, ax_3d_2
    #
    # def update_plot(self, axes):
    #     '''Plotting function
    #     '''
    #     ax_3d_1, ax_3d_2 = axes
    #     self.plot3d_sig_w(ax_3d_1)
    #     self.plot3d_tau_s(ax_3d_2)

    def get_eps_plot_range(self):
        return np.linspace(1.1 * self.eps_cu, 1.1 * self.eps_tu, 300)

    def get_sig(self, eps):
        return self.factor * self.symb.get_sig_w(eps)
Ejemplo n.º 28
0
class TShape(CrossSectionShapeBase):

    B_f = Float(400, GEO=True)
    B_w = Float(100, GEO=True)
    H_w = Float(300, GEO=True)

    ipw_view = View(
        *CrossSectionShapeBase.ipw_view.content,
        Item('B_f', minmax=(10, 3000), latex=r'B_f \mathrm{[mm]}'),
        Item('B_w', minmax=(10, 3000), latex=r'B_w \mathrm{[mm]}'),
        Item('H_w', minmax=(10, 3000), latex=r'H_w \mathrm{[mm]}'),
    )

    def get_cs_area(self):
        return self.B_w * self.H_w + self.B_f * (self.H - self.H_w)

    def get_cs_i(self):
        A_f = self.B_f * (self.H - self.H_w)
        Y_f = (self.H - self.H_w) / 2 + self.H_w
        I_f = self.B_f * (self.H - self.H_w)**3 / 12
        A_w = self.B_w * self.H_w
        Y_w = self.H_w / 2
        I_w = self.B_w * self.H_w**3 / 12
        Y_c = (Y_f * A_f + Y_w * A_w) / (A_f + A_w)
        I_c = I_f + A_f * (Y_c - Y_f)**2 + I_w + A_w * (Y_c - Y_w)**2
        return Y_c, I_c

    get_b = tr.Property(tr.Callable, depends_on='+input')

    @tr.cached_property
    def _get_get_b(self):
        z_ = sp.Symbol('z')
        b_p = sp.Piecewise((self.B_w, z_ < self.H_w), (self.B_f, True))
        return sp.lambdify(z_, b_p, 'numpy')

    def update_plot(self, ax):
        # Start drawing from bottom center of the cross section
        cs_points = np.array([[self.B_w / 2, 0], [self.B_w / 2, self.H_w],
                              [self.B_f / 2, self.H_w], [self.B_f / 2, self.H],
                              [-self.B_f / 2, self.H],
                              [-self.B_f / 2, self.H_w],
                              [-self.B_w / 2, self.H_w], [-self.B_w / 2, 0]])

        cs = Polygon(cs_points)

        patch_collection = PatchCollection([cs],
                                           facecolor=(.5, .5, .5, 0.2),
                                           edgecolors=(0, 0, 0, 1))

        ax.add_collection(patch_collection)
        ax.scatter(0,
                   TShape.get_cs_i(self)[0],
                   color='white',
                   s=self.B_w,
                   marker="+")

        ax.autoscale_view()
        ax.set_aspect('equal')

        ax.annotate('Area = {} $mm^2$'.format(int(TShape.get_cs_area(self)),
                                              0),
                    xy=(-self.H / 2 * 0.8, (self.H / 2 + self.H_w / 2)),
                    color='blue')
        ax.annotate('I = {} $mm^4$'.format(int(TShape.get_cs_i(self)[1]), 0),
                    xy=(-self.H / 2 * 0.8, (self.H / 2 + self.H_w / 2) * 0.9),
                    color='blue')
        ax.annotate('$Y_c$',
                    xy=(0, TShape.get_cs_i(self)[0] * 0.85),
                    color='purple')