Ejemplo n.º 1
0
class SteelReinfMatMod(ReinfMatMod, bu.InjectSymbExpr):
    name = 'Steel'

    symb_class = SteelReinfMatModSymbExpr

    E_s = bu.Float(200000, MAT=True, desc='E modulus of steel')
    f_sy = bu.Float(500, MAT=True, desc='steel yield stress')
    eps_ud = bu.Float(0.025, MAT=True, desc='steel failure strain')

    eps_sy = tr.Property(bu.Float, depends_on='+MAT')

    @tr.cached_property
    def _get_eps_sy(self):
        return self.f_sy / self.E_s

    ipw_view = bu.View(
        bu.Item('factor'),
        bu.Item('E_s', latex=r'E_\mathrm{s} \mathrm{[N/mm^{2}]}'),
        bu.Item('f_sy', latex=r'f_\mathrm{sy} \mathrm{[N/mm^{2}]}'),
        bu.Item('eps_ud', latex=r'\varepsilon_\mathrm{ud} \mathrm{[-]}'),
        bu.Item('eps_sy',
                latex=r'\varepsilon_\mathrm{sy} \mathrm{[-]}',
                readonly=True),
    )

    def get_eps_plot_range(self):
        return np.linspace(-1.1 * self.eps_ud, 1.1 * self.eps_ud, 300)

    def get_sig(self, eps):
        temp = self.f_sy
        self.f_sy *= self.factor
        sig = self.symb.get_sig(eps)
        self.f_sy = temp
        return sig
Ejemplo n.º 2
0
class AbaqusDamageFn(DamageFnInjectSymbExpr):

    name = 'Abaqus damage function'
    symb_class = AbaqusDamageFnSymbExpr

    latex_eq = Str(r'''Damage function (Abaqus)
        \begin{align}
        \omega = g(\kappa) = 1 -\left(\frac{s_0}{\kappa}\right)\left[ 1 - \frac{1 - \exp(- \alpha(\frac{\kappa - s_0}{s_u - s_0})}{1 - \exp(-\alpha)}  \right]
        \end{align}
        where $\kappa$ is the state variable representing 
        the maximum slip that occurred so far in
        in the history of loading.
        ''')

    kappa_u = bu.Float(
        0.003,
        MAT=True,
        symbol="kappa_u",
        unit='mm',
        desc="parameter of the damage function",
    )

    alpha = bu.Float(0.1,
                     MAT=True,
                     symbol=r"\alpha",
                     desc="parameter controlling the slope of damage",
                     unit='-')

    ipw_view = bu.View(
        bu.Item('kappa_0', latex=r'\kappa_0'),
        bu.Item('kappa_u', latex=r'\kappa_\mathrm{u}'),
        bu.Item('alpha'),
    )
Ejemplo n.º 3
0
class CarbonReinfMatMod(ReinfMatMod, bu.InjectSymbExpr):

    name = 'Carbon'

    symb_class = CarbonReinfMatModSymbExpr

    E = bu.Float(200000, MAT=True, desc='E modulus of carbon')
    f_t = bu.Float(2000, MAT=True, desc='carbon breaking stress')

    eps_cr = tr.Property(bu.Float, depends_on='+MAT')

    @tr.cached_property
    def _get_eps_cr(self):
        return self.f_t / self.E

    ipw_view = bu.View(
        bu.Item('factor'),
        bu.Item('E', latex=r'E \mathrm{[N/mm^{2}]}'),
        bu.Item('f_t', latex=r'f_\mathrm{t} \mathrm{[N/mm^{2}]}'),
        bu.Item('eps_cr', latex=r'\varepsilon_\mathrm{cr} \mathrm{[-]}'),
    )

    def get_eps_plot_range(self):
        return np.linspace(-0.1 * self.eps_cr, 1.1 * self.eps_cr, 300)

    def get_sig(self, eps):
        # TODO: factor should be applied only to strength in case of steel/carbon according to EC2
        temp = self.f_t
        self.f_t *= self.factor
        sig = self.symb.get_sig(eps)
        self.f_t = temp
        return sig
Ejemplo n.º 4
0
class WeibullDamageFn(DamageFnInjectSymbExpr):

    name = 'Linear damage function'
    symb_class = WeibullDamageFnSymbExpr

    kappa_0 = bu.Float(1e-5)
    # the inelastic regime starts right from the beginning

    lambda_ = bu.Float(
        0.03,
        MAT=True,
        symbol="lambda_",
        unit='mm',
        desc="Weibull scale parameter",
    )

    m = bu.Float(
        5,
        MAT=True,
        symbol="m",
        desc="Weibull shape parameter",
    )

    ipw_view = bu.View(
        bu.Item('lambda_', latex=r'\lambda'),
        bu.Item('m', latex=r'm'),
    )
Ejemplo n.º 5
0
class CrossSection(BMCSLeafNode, RInputRecord):
    '''Parameters of the pull-out cross section
    '''
    node_name = 'cross-section'

    A_m = bu.Float(15240,
                   CS=True,
                   input=True,
                   unit=r'$\mathrm{mm}^2$',
                   symbol=r'A_\mathrm{m}',
                   auto_set=False,
                   enter_set=True,
                   desc='matrix area')
    A_f = bu.Float(153.9,
                   CS=True,
                   input=True,
                   unit='$\\mathrm{mm}^2$',
                   symbol='A_\mathrm{f}',
                   auto_set=False,
                   enter_set=True,
                   desc='reinforcement area')
    P_b = bu.Float(44,
                   CS=True,
                   input=True,
                   unit='$\\mathrm{mm}$',
                   symbol='p_\mathrm{b}',
                   auto_set=False,
                   enter_set=True,
                   desc='perimeter of the bond interface')

    view = View(Item('A_m'), Item('A_f'), Item('P_b'))

    tree_view = view

    ipw_view = bu.View(bu.Item('A_m'), bu.Item('A_f'), bu.Item('P_b'))
Ejemplo n.º 6
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.º 7
0
class DamageFn(BMCSLeafNode):

    mats = WeakRef

    kappa_0 = bu.Float(0.0004,
                       MAT=True,
                       symbol="s_0",
                       desc="elastic strain limit",
                       unit='mm')

    E_name = Str('E')
    '''Name of the stiffness variable in the material model'''
    def get_f_trial(self, eps_eq_Em, kappa_Em):
        k_Em = np.copy(kappa_Em)
        k_Em[k_Em < self.kappa_0] = self.kappa_0
        return np.where(eps_eq_Em >= k_Em)

    plot_min = bu.Float(1e-9)
    plot_max_ = bu.Float(1e-2)
    # name of the trait controlling the plot range
    x_max_name = bu.Str('s_max')
    # if the parameter database is set get the values from there
    plot_max = Property(bu.Float)

    def _get_plot_max(self):
        if self.mats:
            return getattr(self.mats, self.x_max_name)
        else:
            return self.plot_max_

    def plot(self, ax, **kw):
        ax_omega, ax_d_omega = ax
        n_vals = 200
        kappa_range = np.linspace(self.plot_min, self.plot_max, n_vals)
        omega_range = np.zeros_like(kappa_range)
        I = kappa_range > self.kappa_0
        if len(I) > 0:
            omega_range[I] = self.__call__(kappa_range[I])
        color = kw.pop('color', 'green')
        ax_omega.plot(kappa_range, omega_range, color=color, **kw)
        ax_d_omega.plot(kappa_range,
                        self.diff(kappa_range),
                        color='gray',
                        linestyle='dashed',
                        **kw)
        ax_omega.set_xlabel(r'$\kappa$ [mm]')
        ax_omega.set_ylabel(r'$\omega$ [-]')
        ax_d_omega.set_ylabel(
            r'$\mathrm{d} \omega / \mathrm{d} \kappa$ [-/mm]')

    def subplots(self, fig):
        ax_omega = fig.subplots(1, 1)
        ax_d_omega = ax_omega.twinx()
        return ax_omega, ax_d_omega

    def update_plot(self, axes):
        self.plot(axes)
Ejemplo n.º 8
0
class LiDamageFn(DamageFn):

    name = 'Two parameter damage'

    latex_eq = Str(r'''Damage function (Li)
        \begin{align}
        \omega = g(\kappa) = \frac{\alpha_1}{1 + \exp(-\alpha_2 \kappa + 6 )}
        \end{align}
        where $\kappa$ is the state variable representing 
        the maximum slip that occurred so far in
        in the history of loading.
        ''')

    alpha_1 = bu.Float(
        value=1,
        MAT=True,
        symbol=r'\alpha_1',
        unit='-',
        desc="parameter controlling the shape of the damage function")

    alpha_2 = bu.Float(
        2000.,
        MAT=True,
        symbol=r'\alpha_2',
        unit='-',
        desc="parameter controlling the shape of the damage function")

    def __call__(self, kappa):
        alpha_1 = self.alpha_1
        alpha_2 = self.alpha_2
        s_0 = self.s_0
        omega = np.zeros_like(kappa, dtype=np.float_)
        d_idx = np.where(kappa >= s_0)[0]
        k = kappa[d_idx]
        omega[d_idx] = 1. / \
            (1. + np.exp(-1. * alpha_2 * (k - s_0) + 6.)) * alpha_1
        return omega

    def diff(self, kappa):
        alpha_1 = self.alpha_1
        alpha_2 = self.alpha_2
        s_0 = self.s_0
        return ((alpha_1 * alpha_2 * np.exp(-1. * alpha_2 *
                                            (kappa - s_0) + 6.)) /
                (1 + np.exp(-1. * alpha_2 * (kappa - s_0) + 6.))**2)

    ipw_view = bu.View(
        bu.Item('s_0'),
        bu.Item('alpha_1', editor=bu.FloatRangeEditor(low=0, high=1)),
        bu.Item('alpha_2'),
    )
class WBNumTessellationInvest(WBNumTessellationBase):
    """ A class to investigate the angles for tessellating three cells manually. """

    name = 'WBNumTessellationInvest'

    rot_br = bu.Float(0.5)
    rot_ur = bu.Float(0.5)
    investigate_rot = bu.Bool

    ipw_view = bu.View(
        *WBNumTessellationBase.ipw_view.content,
        bu.Item('investigate_rot'),
        bu.Item('rot_br',
                latex=r'rot~br',
                editor=bu.FloatRangeEditor(low=0,
                                           high=2 * np.pi,
                                           n_steps=150,
                                           continuous_update=True)),
        bu.Item('rot_ur',
                latex=r'rot~ur',
                editor=bu.FloatRangeEditor(low=0,
                                           high=2 * np.pi,
                                           n_steps=150,
                                           continuous_update=True)),
    )

    def setup_plot(self, pb):
        super().setup_plot(pb)

    def update_plot(self, pb):
        if self.k3d_mesh:
            sol = self.sol
            X_Ia = self.X_Ia.astype(np.float32)
            br_X_Ia = self._get_br_X_Ia(
                self.X_Ia,
                self.rot_br if self.investigate_rot else sol[0]).astype(
                    np.float32)
            ur_X_Ia = self._get_ur_X_Ia(
                self.X_Ia,
                self.rot_ur if self.investigate_rot else sol[1]).astype(
                    np.float32)
            self.k3d_mesh['X_Ia'].vertices = X_Ia
            self.k3d_mesh['br_X_Ia'].vertices = br_X_Ia
            self.k3d_mesh['ur_X_Ia'].vertices = ur_X_Ia
            self.k3d_wireframe['X_Ia'].vertices = X_Ia
            self.k3d_wireframe['br_X_Ia'].vertices = br_X_Ia
            self.k3d_wireframe['ur_X_Ia'].vertices = ur_X_Ia
        else:
            self.setup_plot(pb)
class ModelWithHistory(Model):
    """Example model with a cross sectional shape"""
    name = 'Example'

    b = Int(5, desc='input parameter')
    t = Float(0)
    t_max = Float(10)

    ipw_view = View(Item('b', latex=r'\beta', readonly=True),
                    Item('t', latex=r't', readonly=True),
                    Item('t_max', latex=r'\theta'),
                    time_editor=HistoryEditor(
                        var='t',
                        max_var='t_max',
                    ))

    exponent = bu.Float(1)

    def update_plot(self, axes):
        with bu.print_output:
            print('SELF', self)
        x_range = np.linspace(0, self.t_max, 100)
        y_range = x_range**self.exponent
        axes.plot(x_range, y_range)
        y_val = self.t**self.exponent
        axes.plot(self.t, y_val, marker='o')
Ejemplo n.º 11
0
class EC2ConcreteMatMod(EC2ConcreteMatModBase, bu.InjectSymbExpr):
    name = 'EC2 Concrete'

    symb_class = EC2ConcreteMatModSymbExpr

    # Required attributes
    f_cm = bu.Float(28)

    f_ck = tr.Property(desc='Characteristic compressive strength of concrete',
                       MAT=True)

    def _get_f_ck(self):
        return EC2.get_f_ck_from_f_cm(self.f_cm)

    eps_cy = tr.Property(desc='Matrix compressive yield strain', MAT=True)

    def _get_eps_cy(self):
        return -EC2.get_eps_c1(self.f_ck)

    eps_cu = tr.Property(desc='Ultimate matrix compressive strain', MAT=True)

    def _get_eps_cu(self):
        return -EC2.get_eps_cu1(self.f_ck)

    ipw_view = bu.View(*EC2ConcreteMatModBase.ipw_view.content, )

    def get_sig(self, eps):
        return self.factor * self.symb.get_sig(eps)
Ejemplo n.º 12
0
class PO_ELF_RLM(bu.Model, bu.InjectSymbExpr):
    """
    Pullout elastic long fiber and rigid long matrix
    """
    symb_class = PO_ELF_RLM_Symb

    name = "PO-ELF-RLM"

    E_f = bu.Float(210000, MAT=True)
    tau = bu.Float(8, MAT=True)
    A_f = bu.Float(100, CS=True)
    p = bu.Float(20, CS=True)
    L_b = bu.Float(300, GEO=True)
    w_max = bu.Float(3, BC=True)

    ipw_view = bu.View(bu.Item('E_f', latex=r'E_\mathrm{f}~[\mathrm{MPa}]'),
                       bu.Item('tau', latex=r'\tau~[\mathrm{MPa}]'),
                       bu.Item('A_f', latex=r'A_\mathrm{f}~[\mathrm{mm}^2]'),
                       bu.Item('p', latex=r'p~[\mathrm{mm}]'),
                       bu.Item('L_b', latex=r'L_\mathrm{b}~[\mathrm{mm}]'),
                       bu.Item('w_max', latex=r'w_\max~[\mathrm{mm}]'))

    w_range = tr.Property(depends_on='state_changed')
    """Pull-out range w"""
    @tr.cached_property
    def _get_w_range(self):
        return np.linspace(0, self.w_max, 100)

    def update_plot(self, ax):
        P_range = self.symb.get_Pw_pull(self.w_range)
        ax.plot(self.w_range, P_range * 0.001)
        ax.set_ylabel(r'$P$ [kN]')
        ax.set_xlabel(r'$w$ [mm]')
Ejemplo n.º 13
0
class GfDamageFn(DamageFnInjectSymbExpr):
    name = 'self-regularized'
    symb_class = GfDamageFnSymbExpr

    G_f = bu.Float(
        0.1,
        MAT=True,
        symbol="G_\mathrm{f}",
        unit='N/mm',
        desc="derivative of the damage function at the onset of damage")

    min_G_f = Property(bu.Float, depends_on='state_changed')

    def _get_min_G_f(self):
        return self.E * self.kappa_0**2 / 2

    E_ = bu.Float(10000.0, MAT=True, label="E", desc="Young's modulus")

    E = Property(bu.Float)

    def _get_E(self):
        if self.mats:
            return getattr(self.mats, self.E_name)
        else:
            return self.E_

    def __call__(self, kappa):
        return self.symb.get_omega_(kappa)

    def diff(self, kappa):
        return self.symb.get_d_omega_(kappa)

    ipw_view = bu.View(
        bu.Item('kappa_0', latex=r'\kappa_0 [\mathrm{mm}]'),
        bu.Item('G_f', latex=r'G_\mathrm{f} [\mathrm{N/mm}]'),
        bu.Item('E', latex=r'E [\mathrm{MPa}]', readonly=True),
        bu.Item('min_G_f', latex=r'\min(G_\mathrm{f})', readonly=True),
        bu.Item('plot_max', readonly=True),
    )
Ejemplo n.º 14
0
class PWLConcreteMatMod(ConcreteMatMod, bu.InjectSymbExpr):
    name = 'Concrete PWL'

    symb_class = PWLConcreteMatModSymbExpr

    E_ct = bu.Float(24000, MAT=True, desc='E modulus of matrix on tension')
    E_cc = bu.Float(25000, MAT=True, desc='E modulus of matrix on compression')
    eps_cr = bu.Float(0.001, MAT=True, desc='Matrix cracking strain')
    _eps_cy = bu.Float(-0.003, MAT=True)
    _eps_cu = bu.Float(-0.01, MAT=True)

    # Enforcing negative values for eps_cu and eps_cy
    eps_cy = tr.Property(desc='Matrix compressive yield strain')

    def _set_eps_cy(self, value):
        self._eps_cy = value

    def _get_eps_cy(self):
        return -np.fabs(self._eps_cy)

    eps_cu = tr.Property(desc='Ultimate matrix compressive strain')

    def _set_eps_cu(self, value):
        self._eps_cu = value

    def _get_eps_cu(self):
        return -np.fabs(self._eps_cu)

    eps_tu = bu.Float(0.003, MAT=True, desc='Ultimate matrix tensile strain')

    mu = bu.Float(
        0.33,
        MAT=True,
        desc=
        'Post crack tensile strength ratio (represents how much strength is left after \
                                    the crack because of short steel fibers in the mixture)'
    )

    ipw_view = bu.View(
        bu.Item('factor'),
        bu.Item('E_ct', latex=r'E_\mathrm{ct} \mathrm{[N/mm^{2}]}'),
        bu.Item('E_cc', latex=r'E_\mathrm{cc} \mathrm{[N/mm^{2}]}'),
        bu.Item('eps_cr', latex=r'\varepsilon_{cr}'),
        bu.Item('eps_cy', latex=r'\varepsilon_{cy}', editor=bu.FloatEditor()),
        bu.Item('eps_cu', latex=r'\varepsilon_{cu}', editor=bu.FloatEditor()),
        bu.Item('eps_tu', latex=r'\varepsilon_{tu}'),
        bu.Item('mu', latex=r'\mu'))

    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(eps)
Ejemplo n.º 15
0
class Geometry(BMCSLeafNode, RInputRecord):
    node_name = 'geometry'
    L_x = bu.Float(45,
                   GEO=True,
                   input=True,
                   unit='$\mathrm{mm}$',
                   symbol='L',
                   auto_set=False,
                   enter_set=True,
                   desc='embedded length')

    view = View(Item('L_x'), )

    tree_view = view

    ipw_view = bu.View(bu.Item('L_x'), )
Ejemplo n.º 16
0
class ExpSlopeDamageFn(DamageFnInjectSymbExpr):

    name = 'Exponential with slope'
    symb_class = ExpSlopeDamageFnSymbExpr

    kappa_f = bu.Float(
        0.001,
        MAT=True,
        symbol=r'\kappa_\mathrm{f}',
        unit='mm/mm',
        desc="derivative of the damage function at the onset of damage")

    ipw_view = bu.View(
        bu.Item('kappa_0', latex=r'\kappa_0'),
        bu.Item('kappa_f', latex=r'\kappa_\mathrm{f}'),
    )
Ejemplo n.º 17
0
class EC2PlateauConcreteMatMod(EC2ConcreteMatModBase, bu.InjectSymbExpr):
    name = 'EC2 Concrete with Plateau'

    symb_class = EC2PlateauConcreteMatModSymbExpr

    f_cm = bu.Float(28)

    f_cd = tr.Property(desc='Design compressive strength of concrete',
                       MAT=True)

    def _get_f_cd(self):
        if self.factor == 1:
            return self.f_cm
        else:
            return EC2.get_f_cd(self.f_ck, factor=self.factor)

    f_ck = tr.Property(desc='Characteristic compressive strength of concrete',
                       MAT=True)

    def _get_f_ck(self):
        return EC2.get_f_ck_from_f_cm(self.f_cm)

    n = tr.Property(desc='Exponent used in EC2, eq. 3.17', MAT=True)

    def _get_n(self):
        return EC2.get_n(self.f_ck)

    eps_cy = tr.Property(desc='Matrix compressive yield strain', MAT=True)

    def _get_eps_cy(self):
        return -EC2.get_eps_c2(self.f_ck)

    eps_cu = tr.Property(desc='Ultimate matrix compressive strain', MAT=True)

    def _get_eps_cu(self):
        return -EC2.get_eps_cu2(self.f_ck)

    def get_sig(self, eps):
        sig = self.symb.get_sig(eps)
        # Compression branch is scaled when defining f_cd
        sig_with_scaled_tension_branch = np.where(sig > 0, self.factor * sig,
                                                  sig)
        return sig_with_scaled_tension_branch

    ipw_view = bu.View(*EC2ConcreteMatModBase.ipw_view.content, )
Ejemplo n.º 18
0
class LinearDamageFn(DamageFnInjectSymbExpr):

    name = 'Linear damage function'
    symb_class = LinearDamageFnSymbExpr

    kappa_0 = 0.01

    kappa_u = bu.Float(
        0.03,
        MAT=True,
        symbol="kappa_u",
        unit='mm',
        desc="parameter of the damage function",
    )

    ipw_view = bu.View(
        bu.Item('kappa_0', latex=r'\kappa_0'),
        bu.Item('kappa_u', latex=r'\kappa_\mathrm{u}'),
    )
Ejemplo n.º 19
0
class LDMModel(TStepBC):
    name = 'LDM'

    ipw_view = bu.View(
        bu.Item('w_max', resizable=True, full_size=True),
    )

    lattice_tessellation = Instance(
        LatticeTessellation,
        MESH=True,
        report=True,
        desc='cross section parameters'
    )

    def _lattice_tessellation_default(self):
        return LatticeTessellation()

    control_variable = bu.Enum(options=['u', 'f'], BC=True)

    #=========================================================================
    # Algorithimc parameters
    #=========================================================================
    k_max = Int(400,
                unit='-',
                symbol='k_{\max}',
                desc='maximum number of iterations',
                ALG=True)

    tolerance = Float(1e-4,
                      unit='-',
                      symbol='\epsilon',
                      desc='required accuracy',
                      ALG=True)

    mats = bu.EitherType(options=[
            ('elastic', MATS3DIfcElastic),
            ('slide', Slide34)
        ],
        MAT=True,
        desc='material model type'
    )

    dots_lattice = Property(Instance(XDomainLattice),
                            depends_on='state_changed')
    '''Discretization object.
    '''
    @cached_property
    def _get_dots_lattice(self):
        return XDomainLattice(
            mesh=self.lattice_tessellation
        )

    domains = Property(depends_on='state_changed')

    @cached_property
    def _get_domains(self):
        return [(self.dots_lattice, self.mats_)]

    #=========================================================================
    # Boundary conditions
    #=========================================================================
    w_max = bu.Float(1, BC=True,
                  symbol='w_{\max}',
                  unit='mm',
                  desc='maximum pullout slip',
                  auto_set=False, enter_set=True)

    fixed_dofs = Array(np.int_, value=[], BC=True)

    fixed_bc_list = Property(depends_on=itags_str)
    r'''Foxed boundary condition'''
    @cached_property
    def _get_fixed_bc_list(self):
        return [
            BCDof(node_name='fixed dof %d' % dof, var='u',
                  dof=dof, value=0.0) for dof in self.fixed_dofs
        ]

    control_dofs = Array(np.int_, value=[], BC=True)

    control_bc_list = Property(depends_on='state_changed')
    r'''Control boundary condition - make it accessible directly
    for the visualization adapter as property
    '''
    @cached_property
    def _get_control_bc_list(self):
        return [
            BCDof(node_name='control dof %d' % dof,
                  var=self.control_variable,
                  dof=dof, value=self.w_max,)
            for dof in self.control_dofs
        ]

    bc = Property(depends_on='state_changed')

    @cached_property
    def _get_bc(self):
        return self.control_bc_list + self.fixed_bc_list

    def get_window(self):
        self.record['Pw'] = LatticeRecord()
        self.record['eps'] = Vis3DLattice(var='eps')
        w = BMCSWindow(sim=self)
        fw = Viz2DLatticeFW(name='FW-curve', vis2d=self.hist['Pw'])
        w.viz_sheet.viz2d_list.append(fw)
        viz3d_u_Lb = Viz3DLattice(vis3d=self.hist['eps'])
        w.viz_sheet.add_viz3d(viz3d_u_Lb)
        w.viz_sheet.monitor_chunk_size = 10
        return w
Ejemplo n.º 20
0
class Slide32(bu.InteractiveModel, bu.InjectSymbExpr):

    name = 'Slide 3.4'
    symb_class = Slide23Expr

    E_T = bu.Float(28000, MAT=True)
    gamma_T = bu.Float(10, MAT=True)
    K_T = bu.Float(8, MAT=True)
    S_T = bu.Float(1, MAT=True)
    c_T = bu.Float(1, MAT=True)
    bartau = bu.Float(28000, MAT=True)
    E_N = bu.Float(28000, MAT=True)
    S_N = bu.Float(1, MAT=True)
    c_N = bu.Float(1, MAT=True)
    m = bu.Float(0.1, MAT=True)
    f_t = bu.Float(3, MAT=True)
    f_c = bu.Float(30, MAT=True)
    f_c0 = bu.Float(20, MAT=True)
    eta = bu.Float(0.5, MAT=True)
    r = bu.Float(1, MAT=True)

    c_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_c_NT(self):
        return np.sqrt(self.c_N * self.c_T)

    S_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_S_NT(self):
        return np.sqrt(self.S_N * self.S_T)

    def C_codegen(self):

        import os
        import os.path as osp

        C_code = []
        for symb_name, symb_params in self.symb.symb_expressions:
            c_func_name = 'get_' + symb_name
            c_func = ccode(c_func_name, getattr(self.symb, symb_name),
                           'SLIDE33')
            C_code.append(c_func)

        code_dirname = 'sympy_codegen'
        code_fname = 'SLIDE33_3D'

        home_dir = osp.expanduser('~')
        code_dir = osp.join(home_dir, code_dirname)
        if not osp.exists(code_dir):
            os.makedirs(code_dir)

        code_file = osp.join(code_dir, code_fname)

        print('generated code_file', code_file)
        h_file = code_file + '.h'
        c_file = code_file + '.c'

        h_f = open(h_file, 'w')
        c_f = open(c_file, 'w')

        if True:
            for function_C in C_code:

                h_f.write(function_C[1][1])
                c_f.write(function_C[0][1])
        h_f.close()
        c_f.close()

    ipw_view = bu.View(bu.Item('E_T', latex='E_T'), bu.Item('S_T'),
                       bu.Item('c_T'),
                       bu.Item('gamma_T', latex=r'\gamma_\mathrm{T}'),
                       bu.Item('K_T'), bu.Item('bartau', latex=r'\bar{\tau}'),
                       bu.Item('E_N'), bu.Item('S_N'), bu.Item('c_N'),
                       bu.Item('m'), bu.Item('f_t'),
                       bu.Item('f_c', latex=r'f_\mathrm{c}'),
                       bu.Item('f_c0', latex=r'f_\mathrm{c0}'),
                       bu.Item('eta', minmax=(0, 1)), bu.Item('r'))

    damage_interaction = tr.Enum('final', 'geometric', 'arithmetic')

    get_phi_ = tr.Property

    def _get_get_phi_(self):
        return self.symb.get_phi_final_

    get_Phi_ = tr.Property

    def _get_get_Phi_(self):
        return self.symb.get_Phi_final_

    def get_f_df(self, s_x_n1, s_y_n1, w_n1, Sig_k, Eps_k):

        if self.debug_level == 1:
            print('>>>>>>>>>>>>> get_f_df(): INPUT')
            print('u_N', w_n1)
            print('u_T_x', s_x_n1)
            print('u_T_y', s_y_n1)
            print('Eps_k', Eps_k)
            print('Sig_k', Sig_k)
            print('<<<<<<<<<<<<< get_f_df(): INPUT')

        Sig_k = self.symb.get_Sig_(s_x_n1, s_y_n1, w_n1, Sig_k, Eps_k)[0]
        dSig_dEps_k = self.symb.get_dSig_dEps_(s_x_n1, s_y_n1, w_n1, Sig_k,
                                               Eps_k)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        f_k = np.array([self.symb.get_f_(Eps_k, Sig_k, H_sig_pi)])
        df_dSig_k = self.symb.get_df_dSig_(Eps_k, Sig_k, H_sig_pi)
        ddf_dEps_k = self.symb.get_ddf_dEps_(Eps_k, Sig_k, H_sig_pi)
        df_dEps_k = np.einsum('ik,ji->jk', df_dSig_k, dSig_dEps_k) + ddf_dEps_k
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi)
        dEps_dlambda_k = Phi_k
        df_dlambda = np.einsum('ki,kj->ij', df_dEps_k, dEps_dlambda_k)
        df_k = df_dlambda

        if self.debug_level == 1:
            print('>>>>>>>>>>>>> get_f_df(): OUTPUT')
            print('Sig_k', Sig_k)
            print('f_k', f_k)
            print('df_k', df_k)
            print('<<<<<<<<<<<<< get_f_df(): OUTPUT')

        return f_k, df_k, Sig_k

    def get_Eps_k1(self, s_x_n1, s_y_n1, w_n1, Eps_n, lam_k, Sig_k, Eps_k):
        '''Evolution equations:
        The update of state variables
        for an updated $\lambda_k$ is performed using this procedure.
        '''
        Sig_k = self.symb.get_Sig_(s_x_n1, s_y_n1, w_n1, Sig_k, Eps_k)[0]
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi)
        Eps_k1 = Eps_n + lam_k * Phi_k[:, 0]
        Eps_k1[-2] = min(0.99, Eps_k1[-2])
        Eps_k1[-1] = min(0.99, Eps_k1[-1])
        return Eps_k1

    rtol = bu.Float(1e-3, ALG=True)
    '''Relative tolerance of the return mapping algorithm related 
    to the tensile strength
    '''

    f_lambda_recording = bu.Bool(False)

    f_list = tr.List
    lam_list = tr.List

    lam_max = bu.Float(1)

    def reset_flam_profile(self):
        self.f_list = []
        self.lam_list = []

    def record_flam_profile(self, lam_k, s_x_n1, s_y_n1, w_n1, Sig_n, Eps_n):
        Eps_k = np.copy(Eps_n)
        Sig_k = np.copy(Sig_n)
        lam_range = np.linspace(0, self.lam_max, 30)
        f_range = np.zeros_like(lam_range)
        for i, lam in enumerate(lam_range):
            Eps_k = self.get_Eps_k1(s_x_n1, s_y_n1, w_n1, Eps_n, lam - lam_k,
                                    Sig_k, Eps_k)
            f_k, df_k, Sig_k = self.get_f_df(s_x_n1, s_y_n1, w_n1, Sig_k,
                                             Eps_k)
            f_range[i] = f_k
        self.lam_list.append(lam_range)
        self.f_list.append(np.array(f_range))

    debug_level = bu.Int(0)

    def get_sig_n1(self, s_x_n1, s_y_n1, w_n1, Sig_n, Eps_n, k_max):
        '''Return mapping iteration:
        This function represents a user subroutine in a finite element
        code or in a lattice model. The input is $s_{n+1}$ and the state variables
        representing the state in the previous solved step $\boldsymbol{\mathcal{E}}_n$.
        The procedure returns the stresses and state variables of
        $\boldsymbol{\mathcal{S}}_{n+1}$ and $\boldsymbol{\mathcal{E}}_{n+1}$
        '''
        if self.f_lambda_recording:
            self.reset_flam_profile()
        Eps_k = np.copy(Eps_n)
        Sig_k = np.copy(Sig_n)
        lam_k = 0
        f_k, df_k, Sig_k = self.get_f_df(s_x_n1, s_y_n1, w_n1, Sig_k, Eps_k)
        f_k_norm = np.linalg.norm(f_k)
        f_k_trial = f_k[0]
        k = 0
        while k < k_max:
            if self.debug_level == 1:
                print('============= RETURN STEP:', k)
            if self.f_lambda_recording:
                self.record_flam_profile(lam_k, s_x_n1, s_y_n1, w_n1, Sig_k,
                                         Eps_k)
            if f_k_trial < 0 or f_k_norm < self.f_t * self.rtol:
                if self.debug_level == 1:
                    print('============= SUCCESS')
                return Eps_k, Sig_k, k + 1
            dlam = np.linalg.solve(df_k, -f_k)
            lam_k += dlam
            if self.debug_level == 1:
                print('lam_k', lam_k, dlam)
            Eps_k = self.get_Eps_k1(s_x_n1, s_y_n1, w_n1, Eps_n, lam_k, Sig_k,
                                    Eps_k)
            f_k, df_k, Sig_k = self.get_f_df(s_x_n1, s_y_n1, w_n1, Sig_k,
                                             Eps_k)
            f_k_norm = np.linalg.norm(f_k)
            k += 1
        else:
            raise ConvergenceError('no convergence for step',
                                   [s_x_n1, s_y_n1, w_n1])

    Eps_names = tr.Property

    @tr.cached_property
    def _get_Eps_names(self):
        return [eps.codename for eps in self.symb.Eps]

    Sig_names = tr.Property

    @tr.cached_property
    def _get_Sig_names(self):
        return [sig.codename for sig in self.symb.Sig]

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        '''State variables shapes:
        variables are using the codename string in the Cymbol definition
        Since the same string is used in the lambdify method via print_Symbol
        method defined in Cymbol as well'''
        return {eps_name: () for eps_name in self.Eps_names + self.Sig_names}

    def plot_f_state(self, ax, Eps, Sig, color='red'):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        lower_tau = -10
        upper_tau = 10
        tau_x, tau_y, sig = Sig[:3]
        tau = np.sqrt(tau_x**2 + tau_y**2)
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Eps_ts = np.zeros_like(Sig_ts)
        Sig_ts[0, ...] = tau_x_ts
        Sig_ts[2, ...] = sig_ts
        Sig_ts[3:, ...] = Sig[3:, np.newaxis, np.newaxis]
        Sig_ts[4, ...] = np.sqrt(Sig_ts[4, ...]**2 + Sig_ts[5, ...]**2)
        Sig_ts[5, ...] = 0
        Eps_ts[...] = Eps[:, np.newaxis, np.newaxis]
        Eps_ts[0, ...] = np.sqrt(Eps_ts[0, ...]**2 + Eps_ts[1, ...]**2)
        Eps_ts[1, ...] = 0
        Eps_ts[4, ...] = np.sqrt(Eps_ts[4, ...]**2 + Eps_ts[5, ...]**2)
        Eps_ts[5, ...] = 0
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])

        #phi_ts = np.array([self.symb.get_phi_(Eps_ts, Sig_ts)])
        ax.set_title('threshold function')

        omega_N = Eps_ts[-1, :]
        omega_T = Eps_ts[-2, :]
        sig_ts_eff = sig_ts / (1 - H_sig_pi * omega_N)
        tau_x_ts_eff = tau_x_ts / (1 - omega_T)
        #ax.contour(sig_ts_eff, tau_x_ts_eff, f_ts[0,...], [0], colors=('green',))
        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], [0], colors=(color, ))
        #ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot(sig, tau, marker='H', color='red')
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)
        ax.set_ylim(ymin=0, ymax=upper_tau)

    def plot_f(self, ax):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Sig_ts[0, :] = tau_x_ts
        Sig_ts[2, :] = sig_ts
        Eps_ts = np.zeros_like(Sig_ts)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])
        phi_ts = np.array([self.get_phi_(Eps_ts, Sig_ts, H_sig_pi)])
        ax.set_title('threshold function')
        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], levels=0)
        ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)

    def plot_phi_Y(self, ax):
        lower_N = 0
        upper_N = 1
        lower_T = 0
        upper_T = 1
        Y_N, Y_T = np.mgrid[lower_N:upper_N:201j, lower_T:upper_T:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + Y_T.shape)
        Sig_ts[0, :] = Y_N
        Sig_ts[2, :] = Y_T
        Eps_ts = np.zeros_like(Sig_ts)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        phi_ts = np.array([self.get_phi_(Eps_ts, Sig_ts, H_sig_pi)])
        ax.set_title('potential function')
        ax.contour(Y_N, Y_T, phi_ts[0, ...])  #, levels=0)

    def update_plot(self, ax):
        self.plot_f(ax)

    def plot3d(self, pb):
        delta_f = self.f_t * 0.05
        lower = -self.f_c - delta_f
        upper = self.f_t + delta_f
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Sig_ts[0, :] = tau_x_ts
        Sig_ts[2, :] = sig_ts
        Eps_ts = np.zeros_like(Sig_ts)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])

        # max_f_c = self.f_c
        # max_f_t = self.f_t
        # max_tau_bar = self.bartau
        # X_a, Y_a = np.mgrid[-1.1*max_f_c:1.1*max_f_t:210j, -max_tau_bar:max_tau_bar:210j]
        # Z_a = self.symb.get_f_solved(X_a, Y_a) * self.z_scale
        # #ax.contour(X_a, Y_a, Z_a, levels=8)
        Z_0 = np.zeros_like(f_ts)
        self.surface = k3d.surface(f_ts.astype(np.float32))
        pb.plot_fig += self.surface
        self.surface0 = k3d.surface(Z_0.astype(np.float32), color=0xbbbbbe)
        pb.plot_fig += self.surface0
Ejemplo n.º 21
0
class EC2ConcreteMatModBase(ConcreteMatMod):
    # Optional attributes
    # mu must be between 0 and 1
    mu = bu.Float(
        0.0,
        MAT=True,
        desc=
        'Post crack tensile strength ratio (represents how much strength is left after \
                                    the crack because of short steel fibers in the mixture)'
    )

    _eps_cr = None
    eps_cr = tr.Property(desc='Matrix cracking strain', MAT=True)

    def _set_eps_cr(self, value):
        self._eps_cr = value

    def _get_eps_cr(self):
        if self._eps_cr is not None:
            return self._eps_cr
        else:
            return EC2.get_f_ctm(self.f_ck) / self.E_ct

    _eps_tu = None
    eps_tu = tr.Property(desc='Ultimate matrix tensile strain', MAT=True)

    def _set_eps_tu(self, value):
        self._eps_tu = value

    def _get_eps_tu(self):
        if self._eps_tu is not None:
            return self._eps_tu
        else:
            return self.eps_cr

    _E_cc = None
    E_cc = tr.Property(desc='E modulus of matrix on compression', MAT=True)

    def _set_E_cc(self, value):
        self._E_cc = value

    def _get_E_cc(self):
        if self._E_cc is not None:
            return self._E_cc
        else:
            return EC2.get_E_cm(self.f_ck)

    _E_ct = None
    E_ct = tr.Property(desc='E modulus of matrix on tension', MAT=True)

    def _set_E_ct(self, value):
        self._E_ct = value

    def _get_E_ct(self):
        if self._E_ct is not None:
            return self._E_ct
        else:
            return EC2.get_E_cm(self.f_ck)

    ipw_view = bu.View(
        bu.Item('f_cm', latex=r'^*f_\mathrm{cm}', editor=bu.FloatEditor()),
        bu.Item('eps_cr', latex=r'\varepsilon_{cr}', editor=bu.FloatEditor()),
        bu.Item('eps_tu', latex=r'\varepsilon_{tu}', editor=bu.FloatEditor()),
        bu.Item('mu', latex=r'\mu'),
        bu.Item('E_ct',
                latex=r'E_\mathrm{ct} \mathrm{[N/mm^{2}]}',
                editor=bu.FloatEditor()),
        bu.Item('E_cc',
                latex=r'E_\mathrm{cc} \mathrm{[N/mm^{2}]}',
                editor=bu.FloatEditor()),
        bu.Item('factor'),
    )

    def get_eps_plot_range(self):
        return np.linspace(1.5 * self.eps_cu, 1.5 * self.eps_tu, 300)
Ejemplo n.º 22
0
class Slide34(MATSEval, bu.InjectSymbExpr):

    name = 'Slide 3.4'
    symb_class = Slide34Expr

    E_T = bu.Float(28000, MAT=True)
    gamma_T = bu.Float(10, MAT=True)
    K_T = bu.Float(8, MAT=True)
    S_T = bu.Float(1, MAT=True)
    c_T = bu.Float(1, MAT=True)
    bartau = bu.Float(28000, MAT=True)
    E_N = bu.Float(28000, MAT=True)
    S_N = bu.Float(1, MAT=True)
    c_N = bu.Float(1, MAT=True)
    m = bu.Float(0.1, MAT=True)
    f_t = bu.Float(3, MAT=True)
    f_c = bu.Float(30, MAT=True)
    f_c0 = bu.Float(20, MAT=True)
    eta = bu.Float(0.5, MAT=True)
    r = bu.Float(1, MAT=True)

    c_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_c_NT(self):
        return np.sqrt(self.c_N * self.c_T)

    S_NT = tr.Property(bu.Float, depends_on='state_changed')

    @tr.cached_property
    def _get_S_NT(self):
        return np.sqrt(self.S_N * self.S_T)

    debug = bu.Bool(False)

    def C_codegen(self):

        import os
        import os.path as osp

        C_code = []
        for symb_name, symb_params in self.symb.symb_expressions:
            c_func_name = 'get_' + symb_name
            c_func = ccode(c_func_name, getattr(self.symb, symb_name),
                           'SLIDE33')
            C_code.append(c_func)

        code_dirname = 'sympy_codegen'
        code_fname = 'SLIDE33_3D'

        home_dir = osp.expanduser('~')
        code_dir = osp.join(home_dir, code_dirname)
        if not osp.exists(code_dir):
            os.makedirs(code_dir)

        code_file = osp.join(code_dir, code_fname)

        print('generated code_file', code_file)
        h_file = code_file + '.h'
        c_file = code_file + '.c'

        h_f = open(h_file, 'w')
        c_f = open(c_file, 'w')

        if True:
            for function_C in C_code:

                h_f.write(function_C[1][1])
                c_f.write(function_C[0][1])
        h_f.close()
        c_f.close()

    ipw_view = bu.View(
        bu.Item('E_T', latex='E_T'),
        bu.Item('S_T'),
        bu.Item('c_T'),
        bu.Item('gamma_T'),
        bu.Item('K_T'),
        bu.Item('bartau', latex=r'\bar{\tau}'),
        bu.Item('E_N'),
        bu.Item('S_N'),
        bu.Item('c_N'),
        bu.Item('m'),
        bu.Item('f_t'),
        bu.Item('f_c', latex=r'f_\mathrm{c}'),
        bu.Item('f_c0', latex=r'f_\mathrm{c0}'),
        bu.Item('eta'),
        bu.Item('r'),
        bu.Item('c_NT', readonly=True),
        bu.Item('S_NT', readonly=True),
    )

    damage_interaction = tr.Enum('final', 'geometric', 'arithmetic')

    get_phi_ = tr.Property

    def _get_get_phi_(self):
        return self.symb.get_phi_final_

    get_Phi_ = tr.Property

    def _get_get_Phi_(self):
        return self.symb.get_Phi_final_

    def get_f_df(self, u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k):
        if self.debug:
            print('w_n1', u_N_n1.dtype, u_N_n1.shape)
            print('s_x_n1', u_Tx_n1.dtype, u_Tx_n1.shape)
            print('s_y_n1', u_Ty_n1.dtype, u_Ty_n1.shape)
            print('Eps_k', Eps_k.dtype, Eps_k.shape)
            print('Sig_k', Sig_k.dtype, Sig_k.shape)
        ONES = np.ones_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ONES', ONES.dtype)
        ZEROS = np.zeros_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ZEROS', ZEROS.dtype)
        Sig_k = self.symb.get_Sig_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k)[0]
        if self.debug:
            print('Sig_k', Sig_k.dtype, Sig_k.shape)
        dSig_dEps_k = self.symb.get_dSig_dEps_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k,
                                               Eps_k, ZEROS, ONES)
        if self.debug:
            print('dSig_dEps_k', dSig_dEps_k.dtype)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        if self.debug:
            print('H_sig_pi', H_sig_pi.dtype)
        f_k = np.array([self.symb.get_f_(Eps_k, Sig_k, H_sig_pi)])
        if self.debug:
            print('f_k', f_k.dtype)
        df_dSig_k = self.symb.get_df_dSig_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        if self.debug:
            print('df_dSig_k', df_dSig_k.dtype)
        ddf_dEps_k = self.symb.get_ddf_dEps_(Eps_k, Sig_k, H_sig_pi, ZEROS,
                                             ONES)
        if self.debug:
            print('ddf_dEps_k', ddf_dEps_k.dtype)
        df_dEps_k = np.einsum('ik...,ji...->jk...', df_dSig_k,
                              dSig_dEps_k) + ddf_dEps_k
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        dEps_dlambda_k = Phi_k
        df_dlambda = np.einsum('ki...,kj...->ij...', df_dEps_k, dEps_dlambda_k)
        df_k = df_dlambda
        return f_k, df_k, Sig_k

    def get_Eps_k1(self, u_N_n1, u_Tx_n1, u_Ty_n1, Eps_n, lam_k, Sig_k, Eps_k):
        '''Evolution equations:
        The update of state variables
        for an updated $\lambda_k$ is performed using this procedure.
        '''
        ONES = np.ones_like(u_Tx_n1)
        ZEROS = np.zeros_like(u_Tx_n1)
        Sig_k = self.symb.get_Sig_(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k, Eps_k)[0]
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_k)
        Phi_k = self.get_Phi_(Eps_k, Sig_k, H_sig_pi, ZEROS, ONES)
        Eps_k1 = Eps_n + lam_k * Phi_k[:, 0]
        return Eps_k1

    rtol = bu.Float(1e-3, ALG=True)
    '''Relative tolerance of the return mapping algorithm related 
    to the tensile strength
    '''

    Eps_names = tr.Property

    @tr.cached_property
    def _get_Eps_names(self):
        return [eps.codename for eps in self.symb.Eps]

    Sig_names = tr.Property

    @tr.cached_property
    def _get_Sig_names(self):
        return [sig.codename for sig in self.symb.Sig]

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        '''State variables shapes:
        variables are using the codename string in the Cymbol definition
        Since the same string is used in the lambdify method via print_Symbol
        method defined in Cymbol as well'''
        return {eps_name: () for eps_name in self.Eps_names + self.Sig_names}

    k_max = bu.Int(100, ALG=True)
    '''Maximum number of iterations'''

    def get_corr_pred(self, eps_Ema, t_n1, **state):
        '''Return mapping iteration:
        This function represents a user subroutine in a finite element
        code or in a lattice model. The input is $s_{n+1}$ and the state variables
        representing the state in the previous solved step $\boldsymbol{\mathcal{E}}_n$.
        The procedure returns the stresses and state variables of
        $\boldsymbol{\mathcal{S}}_{n+1}$ and $\boldsymbol{\mathcal{E}}_{n+1}$
        '''
        eps_aEm = np.einsum('...a->a...', eps_Ema)
        dim = len(eps_aEm)

        if dim == 2:  # hack - only one slip considered - 2D version
            select_idx = (1, 0)
            u_Tx_n1, u_N_n1 = eps_aEm
            u_Ty_n1 = np.zeros_like(u_Tx_n1)
        else:
            raise ValueError('3D not implemented here')

        ONES = np.ones_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ONES', ONES.dtype)
        ZEROS = np.zeros_like(u_Tx_n1, dtype=np.float_)
        if self.debug:
            print('ZEROS', ZEROS.dtype)

        # Transform state to Eps_k and Sig_k
        Eps_n = np.array([state[eps_name] for eps_name in self.Eps_names],
                         dtype=np.float_)
        Eps_k = np.copy(Eps_n)
        #Sig_k = np.array([state[sig_name] for sig_name in self.Sig_names], dtype=np.float_)
        Sig_k = np.zeros_like(Eps_k)
        f_k, df_k, Sig_k = self.get_f_df(u_N_n1, u_Tx_n1, u_Ty_n1, Sig_k,
                                         Eps_k)
        f_k, df_k = f_k[0, ...], df_k[0, 0, ...]
        f_k_trial = f_k
        # indexes of inelastic entries
        L = np.where(f_k_trial > 0)
        # f norm in inelastic entries - to allow also positive values less the rtol
        f_k_norm_I = np.fabs(f_k_trial[L])
        lam_k = np.zeros_like(f_k_trial)
        k = 0
        while k < self.k_max:
            if self.debug:
                print('k', k)
            # which entries are above the tolerance
            I = np.where(f_k_norm_I > (self.f_t * self.rtol))
            if self.debug:
                print('f_k_norm_I', f_k_norm_I, self.f_t * self.rtol,
                      len(I[0]))
            if (len(I[0]) == 0):
                # empty inelastic entries - accept state
                #return Eps_k, Sig_k, k + 1
                dSig_dEps_k = self.symb.get_dSig_dEps_(u_N_n1, u_Tx_n1,
                                                       u_Ty_n1, Sig_k, Eps_k,
                                                       ZEROS, ONES)
                ix1, ix2 = np.ix_(select_idx, select_idx)
                D_ = np.einsum('ab...->...ab', dSig_dEps_k[ix1, ix2, ...])
                sig_ = np.einsum('a...->...a', Sig_k[select_idx, ...])
                # quick fix
                _, _, _, _, _, _, omega_T, omega_N = Eps_k
                D_ = np.zeros(sig_.shape + (sig_.shape[-1], ))
                D_[..., 0, 0] = self.E_N * (1 - omega_N)
                D_[..., 1, 1] = self.E_T * (1 - omega_T)
                if dim == 3:
                    D_[..., 2, 2] = self.E_T  #* (1 - omega_T)
                for eps_name, Eps_ in zip(self.Eps_names, Eps_k):
                    state[eps_name][...] = Eps_[...]
                for sig_name, Sig_ in zip(self.Sig_names, Sig_k):
                    state[sig_name][...] = Sig_[...]
                return sig_, D_

            if self.debug:
                print('I', I)
                print('L', L)
            LL = tuple(Li[I] for Li in L)
            L = LL
            if self.debug:
                print('new L', L)
                print('f_k', f_k[L].shape, f_k[L].dtype)
                print('df_k', df_k[L].shape, df_k[L].dtype)
            # return mapping on inelastic entries
            dlam_L = -f_k[L] / df_k[L]  # np.linalg.solve(df_k[I], -f_k[I])
            if self.debug:
                print('dlam_I', dlam_L, dlam_L.dtype)
            lam_k[L] += dlam_L
            if self.debug:
                print('lam_k_L', lam_k, lam_k.dtype, lam_k[L].shape)
            L_slice = (slice(None), ) + L
            Eps_k_L = self.get_Eps_k1(u_N_n1[L], u_Tx_n1[L], u_Ty_n1[L],
                                      Eps_n[L_slice], lam_k[L], Sig_k[L_slice],
                                      Eps_k[L_slice])
            Eps_k[L_slice] = Eps_k_L
            f_k_L, df_k_L, Sig_k_L = self.get_f_df(u_N_n1[L], u_Tx_n1[L],
                                                   u_Ty_n1[L], Sig_k[L_slice],
                                                   Eps_k_L)
            f_k[L], df_k[L] = f_k_L[0, ...], df_k_L[0, 0, ...]
            Sig_k[L_slice] = Sig_k_L
            if self.debug:
                print('Sig_k', Sig_k)
                print('f_k', f_k)
            f_k_norm_I = np.fabs(f_k[L])
            k += 1
        else:
            raise ConvergenceError('no convergence for entries',
                                   [L, u_N_n1[I], u_Tx_n1[I], u_Ty_n1[I]])
        # add the algorithmic stiffness
        # recalculate df_k and -f_k for a unit increment of epsilon and solve for lambda
        #

    def plot_f_state(self, ax, Eps, Sig):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        lower_tau = 0
        upper_tau = 10
        sig, tau_x, tau_y = Sig[:3]
        tau = np.sqrt(tau_x**2 + tau_y**2)
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Eps_ts = np.zeros_like(Sig_ts)
        Sig_ts[0, ...] = sig_ts
        Sig_ts[1, ...] = tau_x_ts
        Sig_ts[3:, ...] = Sig[3:, np.newaxis, np.newaxis]
        Eps_ts[...] = Eps[:, np.newaxis, np.newaxis]
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])

        #phi_ts = np.array([self.symb.get_phi_(Eps_ts, Sig_ts)])
        ax.set_title('threshold function')

        omega_N = Eps_ts[-1, :]
        omega_T = Eps_ts[-2, :]
        sig_ts_eff = sig_ts / (1 - H_sig_pi * omega_N)
        tau_x_ts_eff = tau_x_ts / (1 - omega_T)
        ax.contour(sig_ts_eff,
                   tau_x_ts_eff,
                   f_ts[0, ...],
                   levels=0,
                   colors=('green', ))

        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], levels=0, colors=('red', ))
        #ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot(sig, tau, marker='H', color='red')
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)
        ax.set_ylim(ymin=0, ymax=10)

    def plot_f(self, ax):
        lower = -self.f_c * 1.05
        upper = self.f_t + 0.05 * self.f_c
        lower_tau = -self.bartau * 2
        upper_tau = self.bartau * 2
        sig_ts, tau_x_ts = np.mgrid[lower:upper:201j, lower_tau:upper_tau:201j]
        Sig_ts = np.zeros((len(self.symb.Eps), ) + tau_x_ts.shape)
        Sig_ts[0, :] = sig_ts
        Sig_ts[1, :] = tau_x_ts
        Eps_ts = np.zeros_like(Sig_ts)
        H_sig_pi = self.symb.get_H_sig_pi_(Sig_ts)
        f_ts = np.array([self.symb.get_f_(Eps_ts, Sig_ts, H_sig_pi)])
        phi_ts = np.array([self.get_phi_(Eps_ts, Sig_ts, H_sig_pi)])
        ax.set_title('threshold function')
        ax.contour(sig_ts, tau_x_ts, f_ts[0, ...], levels=0)
        ax.contour(sig_ts, tau_x_ts, phi_ts[0, ...])
        ax.plot([lower, upper], [0, 0], color='black', lw=0.4)
        ax.plot([0, 0], [lower_tau, upper_tau], color='black', lw=0.4)

    def plot_sig_w(self, ax):
        pass

    def plot_tau_s(self, ax):
        pass

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

    def update_plot(self, axes):
        (ax_sig_w, ax_tau_s), (ax_f, _) = axes
        self.plot_sig_w(ax_sig_w)
        self.plot_tau_s(ax_tau_s)
        self.plot_f(ax_f)
Ejemplo n.º 23
0
class ConcreteMatMod(MatMod):

    factor = bu.Float(1, MAT=True)  # 0.85 / 1.5
    '''Factor to embed a EC2 based safety factors.
Ejemplo n.º 24
0
class SlideExplorer(bu.Model):
    name = 'Explorer'

    tree = [
        'slide_model', 'inel_state_evolution', 'energy_dissipation', 'tf_s_x',
        'tf_s_y', 'tf_w'
    ]

    slide_model = bu.Instance(Slide32, (), tree=True)
    #    slide_model = bu.Instance(Slide34, (), tree=True)

    energy_dissipation = bu.Instance(EnergyDissipation, tree=True)
    '''Viewer to the energy dissipation'''
    def _energy_dissipation_default(self):
        return EnergyDissipation(slider_exp=self)

    inel_state_evolution = bu.Instance(InelStateEvolution, tree=True)
    '''Viewer to the inelastic state evolution'''

    def _inel_state_evolution_default(self):
        return InelStateEvolution(slider_exp=self)

    time_fn = bu.Instance(TimeFunction, (), tree=True)

    def __init__(self, *args, **kw):
        super(SlideExplorer, self).__init__(*args, **kw)
        self.reset_i()

    n_Eps = tr.Property()

    def _get_n_Eps(self):
        return len(self.slide_model.symb.Eps)

    s_x_1 = bu.Float(0, INC=True)
    s_y_1 = bu.Float(0, INC=True)
    w_1 = bu.Float(0, INC=True)

    tf_s_x = bu.Instance(TimeFunction, TIME=True)

    def _tf_s_x_default(self):
        return TFSelector()

    tf_s_y = bu.Instance(TimeFunction, TIME=True)

    def _tf_s_y_default(self):
        return TFSelector()

    tf_w = bu.Instance(TimeFunction, TIME=True)

    def _tf_w_default(self):
        return TFSelector()

    n_steps = bu.Int(10, ALG=True)
    k_max = bu.Int(20, ALG=True)

    Sig_arr = tr.Array
    Eps_arr = tr.Array

    Sig_t = tr.Property

    def _get_Sig_t(self):
        return self.Sig_arr

    Eps_t = tr.Property

    def _get_Eps_t(self):
        return self.Eps_arr

    ipw_view = bu.View(bu.Item('s_x_1', latex=r's_x'),
                       bu.Item('s_y_1', latex=r's_y'),
                       bu.Item('w_1', latex=r'w'),
                       bu.Item('n_steps'),
                       bu.Item('k_max'),
                       bu.Item('t_max', readonly=True),
                       time_editor=bu.ProgressEditor(
                           run_method='run',
                           reset_method='reset',
                           interrupt_var='sim_stop',
                           time_var='t',
                           time_max='t_max',
                       ))

    def reset_i(self):
        self.s_x_0, self.s_y_0, self.w_0 = 0, 0, 0
        self.t0 = 0
        self.t = 0
        self.t_max = 1
        self.Sig_arr = np.zeros((0, self.n_Eps))
        self.Eps_arr = np.zeros((0, self.n_Eps))
        self.Sig_record = []
        self.Eps_record = []
        self.iter_record = []
        self.t_arr = []
        self.s_x_t, self.s_y_t, self.w_t = [], [], []
        self.Eps_n1 = np.zeros((self.n_Eps, ), dtype=np.float_)
        self.Sig_n1 = np.zeros((self.n_Eps, ), dtype=np.float_)
        self.s_x_1 = 0
        self.s_y_1 = 0
        self.w_1 = 0

    t = bu.Float(0)
    t_max = bu.Float(1)

    def _t_max_changed(self):
        self.inel_state_evolution.t_max = self.t_max

    def get_response_i(self, update_progress=lambda t: t):
        # global Eps_record, Sig_record, iter_record
        # global t_arr, s_x_t, s_y_t, w_t, s_x_0, s_y_0, w_0, t0, Eps_n1
        n_steps = self.n_steps
        t_i = np.linspace(0, 1, n_steps + 1)
        t1 = self.t0 + 1
        self.t_max = t1
        ti_arr = np.linspace(self.t0, t1, n_steps + 1)
        delta_t = t1 - self.t0
        tf_s_x = self.tf_s_x(np.linspace(0, delta_t, n_steps + 1))
        tf_s_y = self.tf_s_y(np.linspace(0, delta_t, n_steps + 1))
        tf_w = self.tf_w(np.linspace(0, delta_t, n_steps + 1))
        # si_x_t = tf_s_x * np.linspace(self.s_x_0, self.s_x_1, n_steps + 1) + 1e-9
        # si_y_t = tf_s_y * np.linspace(self.s_y_0, self.s_y_1, n_steps + 1) + 1e-9
        # wi_t = tf_w * np.linspace(self.w_0, self.w_1, n_steps + 1) + 1e-9
        si_x_t = self.s_x_0 + tf_s_x * (self.s_x_1 - self.s_x_0) + 1e-9
        si_y_t = self.s_y_0 + tf_s_y * (self.s_y_1 - self.s_y_0) + 1e-9
        wi_t = self.w_0 + tf_w * (self.w_1 - self.w_0) + 1e-9
        for i, (t, s_x_n1, s_y_n1,
                w_n1) in enumerate(zip(t_i, si_x_t, si_y_t, wi_t)):
            if self.slide_model.debug_level == 1:
                print('============= INCREMENT', i)

            try:
                self.Eps_n1, self.Sig_n1, k = self.slide_model.get_sig_n1(
                    s_x_n1, s_y_n1, w_n1, self.Sig_n1, self.Eps_n1, self.k_max)
            except ConvergenceError as e:
                print(e)
                break
            self.Sig_record.append(self.Sig_n1)
            self.Eps_record.append(self.Eps_n1)
            self.iter_record.append(k)
            self.t = t

        self.Sig_arr = np.array(self.Sig_record, dtype=np.float_)
        self.Eps_arr = np.array(self.Eps_record, dtype=np.float_)
        self.iter_t = np.array(self.iter_record, dtype=np.int_)
        n_i = len(self.iter_t)
        self.t_arr = np.hstack([self.t_arr, ti_arr])[:n_i]
        self.s_x_t = np.hstack([self.s_x_t, si_x_t])[:n_i]
        self.s_y_t = np.hstack([self.s_y_t, si_y_t])[:n_i]
        self.w_t = np.hstack([self.w_t, wi_t])[:n_i]
        self.t0 = t1
        self.s_x_0, self.s_y_0, self.w_0 = self.s_x_1, self.s_y_1, self.w_1
        # set the last step index in the response browser
        self.inel_state_evolution.t_max = self.t_arr[-1]
        return

    # ## Plotting functions
    # To simplify postprocessing examples, here are two aggregate plotting functions, one for the state and force variables, the other one for the evaluation of energies

    def plot_sig_w(self, ax):
        sig_t = self.Sig_arr.T[2, ...]
        ax.plot(self.w_t, sig_t, color='orange', lw=3)

    def plot3d_Sig_Eps(self, ax3d):
        tau_x, tau_y = self.Sig_arr.T[:2, ...]
        tau = np.sqrt(tau_x**2 + tau_y**2)
        ax3d.plot3D(self.s_x_t, self.s_y_t, tau, color='orange', lw=2)

    def run(self, update_progress=lambda t: t):
        try:
            self.get_response_i(update_progress)
        except ValueError:
            print('No convergence reached')
            return

    def reset(self):
        self.reset_i()

    def subplots(self, fig):
        ax = fig.add_gridspec(1, 3)
        ax1 = fig.add_subplot(ax[0, 0:2], projection='3d')
        ax2 = fig.add_subplot(ax[0:, -1])
        return ax1, ax2

    def update_plot(self, axes):
        ax_sxy, ax_sig = axes
        self.plot_sig_w(ax_sig)
        ax_sig.set_xlabel(r'$w$ [mm]')
        ax_sig.set_ylabel(r'$\sigma$ [MPa]')

        #    plot_tau_s(ax1, Eps_arr[-1,...],s_max,500,get_g3,**kw)
        ax_sxy.plot(self.s_x_t, self.s_y_t, 0, color='red', lw=1)
        self.plot3d_Sig_Eps(ax_sxy)
        ax_sxy.set_xlabel(r'$s_x$ [mm]')
        ax_sxy.set_ylabel(r'$s_y$ [mm]')
        ax_sxy.set_zlabel(r'$\| \tau \| = \sqrt{\tau_x^2 + \tau_y^2}$ [MPa]')
Ejemplo n.º 25
0
class GfDamageFn2(DamageFn):
    '''Class defining the damage function coupled with the fracture
    energy of a cohesive crack model.
    '''
    name = 'damage function Gf'

    L_s = bu.Float(1.0,
                   MAT=True,
                   label="L_s",
                   desc="Length of the softening zone")

    E_ = bu.Float(34000.0, MAT=True, label="E", desc="Young's modulus")

    E = Property(bu.Float)

    def _get_E(self):
        if self.mats:
            return getattr(self.mats, self.E_name)
        else:
            return self.E_

    f_t = bu.Float(4.5, MAT=True, label="f_t", desc="Tensile strength")

    f_t_Em = Array(np.float_, value=None)

    G_f = bu.Float(
        0.004,
        MAT=True,
        label="G_f",
        desc="Fracture energy",
    )

    s_0 = Property(bu.Float)

    def _get_s_0(self):
        return self.f_t / self.E

    eps_ch = Property(bu.Float)

    def _get_eps_ch(self):
        return self.G_f / self.f_t

    ipw_view = bu.View(
        bu.Item('L_s', latex=r'L_\mathrm{s}'),
        bu.Item('f_t', latex=r'f_\mathrm{t}'),
        bu.Item('G_f', latex=r'G_\mathrm{f}'),
        bu.Item('E', readonly=True),
        bu.Item('s_0', latex=r's_0', readonly=True),
    )

    def __call__(self, kappa):
        L_s = self.L_s
        f_t = self.f_t
        G_f = self.G_f
        E = self.E
        s_0 = self.s_0
        return (1 - f_t * np.exp(-f_t * (kappa - s_0) * L_s / G_f) /
                (E * kappa))

    def diff(self, kappa):
        L_s = self.L_s
        f_t = self.f_t
        G_f = self.G_f
        E = self.E
        s_0 = self.s_0
        return (f_t * np.exp(L_s * (s_0 - kappa) * f_t / G_f) /
                (E * G_f * kappa**2) * (G_f + L_s * kappa * f_t))
Ejemplo n.º 26
0
class WBCell5ParamV2(WBCell):
    name = 'waterbomb cell 5p v2'

    plot_backend = 'k3d'

    gamma = bu.Float(np.pi / 6, GEO=True)
    a = bu.Float(500, GEO=True)
    b = bu.Float(750, GEO=True)
    c = bu.Float(400, GEO=True)
    # beta = bu.Float(np.pi / 3, GEO=True)
    beta = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_beta(self):
        return round(self.beta_1 + self.beta_0, 8)

    beta_1 = bu.Float(0, GEO=True)
    beta_0 = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_beta_0(self):
        """ This is the value of beta that makes the cell symmetric, derived in wb_cell_4p_deriving_beta_0.ipynb"""
        return np.arccos(self.a * (1 - 2 * sin(self.gamma)) /
                         sqrt(self.a**2 + self.b**2))

    continuous_update = True

    ipw_view = bu.View(
        bu.Item('gamma',
                latex=r'\gamma',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=np.pi / 2,
                    n_steps=501,
                    continuous_update=continuous_update)),
        # bu.Item('beta', latex=r'\beta', editor=bu.FloatRangeEditor(
        #     low=1e-6, high=np.pi - 1e-6, n_steps=501, continuous_update=continuous_update)),
        bu.Item('beta_1',
                latex=r'\beta_1',
                editor=bu.FloatRangeEditor(
                    low=-4,
                    high=4,
                    n_steps=501,
                    continuous_update=continuous_update)),
        bu.Item('a',
                latex='a',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        bu.Item('b',
                latex='b',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        bu.Item('c',
                latex='c',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high=2000,
                    n_steps=201,
                    continuous_update=continuous_update)),
        *WBCell.ipw_view.content,
    )

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_Ia(self):
        return self.get_cell_vertices()

    def get_cell_vertices(self,
                          a=0.5,
                          b=0.75,
                          c=0.4,
                          gamma=np.pi / 6,
                          beta=np.pi / 3):
        a = self.a
        b = self.b
        c = self.c
        gamma = self.gamma
        beta = self.beta

        # phi1 is angle between OU_ur line and z axis
        cos_psi1 = ((b**2 - a**2) - a * sqrt(a**2 + b**2) * cos(beta)) / (
            b * sqrt(a**2 + b**2) * sin(beta))
        sin_psi1 = sqrt(a**2 * (3 * b**2 - a**2) + 2 * a *
                        (b**2 - a**2) * sqrt(a**2 + b**2) * cos(beta) -
                        (a**2 + b**2)**2 * cos(beta)**2) / (
                            b * sqrt(a**2 + b**2) * sin(beta))
        cos_psi5 = (sqrt(a**2 + b**2) * cos(beta) -
                    a * cos(2 * gamma)) / (b * sin(2 * gamma))
        sin_psi5 = sqrt(
            b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma) -
            (a**2 + b**2) *
            (cos(beta)**2 + cos(2 * gamma)**2)) / (b * sin(2 * gamma))
        cos_psi6 = (a - sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma)) / (
            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        sin_psi6 = sqrt(b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) *
                        cos(2 * gamma) - (a**2 + b**2) *
                        (cos(beta)**2 + cos(2 * gamma)**2)) / (
                            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        cos_psi1plus6 = cos_psi1 * cos_psi6 - sin_psi1 * sin_psi6
        sin_psi1plus6 = sin_psi1 * cos_psi6 + cos_psi1 * sin_psi6

        cos_phi1 = cos_psi1plus6
        cos_phi2 = cos_psi5
        cos_phi3 = cos_psi5
        cos_phi4 = cos_psi1plus6
        sin_phi1 = sin_psi1plus6
        sin_phi2 = sin_psi5
        sin_phi3 = sin_psi5
        sin_phi4 = sin_psi1plus6

        U_ur = np.array([
            a * sin(gamma) - b * cos_phi1 * cos(gamma), b * sin_phi1,
            a * cos(gamma) + b * cos_phi1 * sin(gamma)
        ])
        U_ul = np.array([
            -a * sin(gamma) + b * cos_phi2 * cos(gamma), b * sin_phi2,
            a * cos(gamma) + b * cos_phi2 * sin(gamma)
        ])
        U_lr = np.array([
            a * sin(gamma) - b * cos_phi3 * cos(gamma), -b * sin_phi3,
            a * cos(gamma) + b * cos_phi3 * sin(gamma)
        ])
        U_ll = np.array([
            -a * sin(gamma) + b * cos_phi4 * cos(gamma), -b * sin_phi4,
            a * cos(gamma) + b * cos_phi4 * sin(gamma)
        ])
        V_r = np.array([c * sin(gamma), 0, c * cos(gamma)])
        V_l = np.array([-c * sin(gamma), 0, c * cos(gamma)])

        X_Ia = np.vstack(
            (np.zeros(3), U_lr, U_ll, U_ur, U_ul, V_r, V_l)).astype(np.float32)
        return X_Ia
Ejemplo n.º 27
0
class FRPDamageFn(DamageFn):

    name = 'FRP damage function'

    B = bu.Float(10.4,
                 MAT=True,
                 symbol="B",
                 unit='mm$^{-1}$',
                 desc="parameter controlling the damage maximum stress level")

    Gf = bu.Float(1.19,
                  MAT=True,
                  symbol="G_\mathrm{f}",
                  unit='N/mm',
                  desc="fracture energy")

    E_bond = bu.Float(0.0)

    E_b = Property(Float)

    def _get_E_b(self):
        return self.mats.E_b

    def _set_E_b(self, value):
        self.E_bond = value
        self.mats.E_b = value

    @observe('B, Gf')
    def _update_dependent_params(self, event=None):
        self.E_b = 1.734 * self.Gf * self.B**2.0

        # calculation of s_0, implicit function solved using Newton method

        def f_s(s_0):            return s_0 / \
  (np.exp(- self.B * s_0) - np.exp(-2.0 * self.B * s_0)) - \
  2.0 * self.B * self.Gf / self.E_b

        self.s_0 = newton(f_s, 0.00000001, tol=1e-5, maxiter=20)

    def __call__(self, kappa):

        b = self.B
        Gf = self.Gf
        Eb = self.E_b  # 1.734 * Gf * b**2
        s_0 = self.s_0
        # calculation of s_0, implicit function solved using Newton method

        #         def f_s(s_0): return s_0 / \
        #             (np.exp(-b * s_0) - np.exp(-2.0 * b * s_0)) - 2.0 * b * Gf / Eb
        #         s_0 = newton(f_s, 0.00000001, tol=1e-5, maxiter=20)

        omega = np.zeros_like(kappa, dtype=np.float_)
        I = np.where(kappa >= s_0)[0]
        kappa_I = kappa[I]

        omega[I] = 1 - \
            (2.0 * b * Gf * (np.exp(-b * kappa_I)
                             - np.exp(-2.0 * b * kappa_I))) / (kappa_I * Eb)

        return omega

    def diff(self, kappa):

        nz_ix = np.where(kappa != 0.0)[0]

        b = self.B
        Gf = self.Gf
        Eb = 1.734 * Gf * b**2

        domega_dkappa = np.zeros_like(kappa)
        kappa_nz = kappa[nz_ix]
        domega_dkappa[nz_ix] = (
            (2.0 * b * Gf *
             (np.exp(-b * kappa_nz) - np.exp(-2.0 * b * kappa_nz))) /
            (Eb * kappa_nz**2.0) - (2.0 * b * Gf *
                                    (-b * np.exp(-b * kappa_nz) +
                                     2.0 * b * np.exp(-2.0 * b * kappa_nz))) /
            (Eb * kappa_nz))
        return domega_dkappa

    latex_eq = r'''Damage function (FRP)
        \begin{align}
        \omega = g(\kappa) = 
        1 - {\frac {{\exp(-2\,Bs)}-{\exp(-Bs)}}{Bs}}
        \end{align}
        where $\kappa$ is the state variable representing 
        the maximum slip that occurred so far in
        in the history of loading.
        '''

    ipw_view = bu.View(
        bu.Item('s_0', readonly=True),
        bu.Item('E_bond', readonly=True),
        bu.Item('B'),
        bu.Item('Gf'),
    )
Ejemplo n.º 28
0
class WBShellAnalysis(TStepBC, bu.InteractiveModel):
    name = 'WBShellAnalysis'
    plot_backend = 'k3d'
    id = bu.Str
    """ if you saved boundary conditions for your current analysis, this id will make sure these bcs
     are loaded automatically next time you create an instance with the same id """

    h = bu.Float(10, GEO=True)
    show_wireframe = bu.Bool(True, GEO=True)

    ipw_view = bu.View(
        bu.Item('h',
                editor=bu.FloatRangeEditor(low=1, high=100, n_steps=100),
                continuous_update=False),
        bu.Item('show_wireframe'),
        time_editor=bu.ProgressEditor(run_method='run',
                                      reset_method='reset',
                                      interrupt_var='interrupt',
                                      time_var='t',
                                      time_max='t_max'),
    )

    n_phi_plus = tr.Property()

    def _get_n_phi_plus(self):
        return self.xdomain.mesh.n_phi_plus

    tree = ['geo', 'bcs', 'tmodel', 'xdomain']

    geo = bu.Instance(WBShellGeometry4P, ())

    tmodel = bu.Instance(MATS2DElastic, ())
    # tmodel = bu.Instance(MATSShellElastic, ())

    bcs = bu.Instance(BoundaryConditions)
    def _bcs_default(self):
        return BoundaryConditions(geo=self.geo, n_nodal_dofs=self.xdomain.fets.n_nodal_dofs, id=self.id)

    xdomain = tr.Property(tr.Instance(TriXDomainFE),
                          depends_on="state_changed")
    '''Discretization object.'''

    @tr.cached_property
    def _get_xdomain(self):
        # prepare the mesh generator
        # mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=False, subdivision=2)
        mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=True)
        # construct the domain with the kinematic strain mapper and stress integrator
        return TriXDomainFE(
            mesh=mesh,
            integ_factor=self.h,
        )

        # mesh = WBShellFETriangularMesh(geo=self.geo, direct_mesh=True)
        # mesh.fets = FETS2DMITC(a= self.h)
        # return TriXDomainMITC(
        #     mesh=mesh
        # )

    domains = tr.Property(depends_on="state_changed")

    @tr.cached_property
    def _get_domains(self):
        return [(self.xdomain, self.tmodel)]

    def reset(self):
        self.sim.reset()

    t = tr.Property()

    def _get_t(self):
        return self.sim.t

    def _set_t(self, value):
        self.sim.t = value

    t_max = tr.Property()

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

    def _set_t_max(self, value):
        self.sim.t_max = value

    interrupt = tr.Property()

    def _get_interrupt(self):
        return self.sim.interrupt

    def _set_interrupt(self, value):
        self.sim.interrupt = value

    bc = tr.Property(depends_on="state_changed")
    # @tr.cached_property
    def _get_bc(self):
        bc_fixed, _, _ = self.bcs.bc_fixed
        bc_loaded, _, _ = self.bcs.bc_loaded
        return bc_fixed + bc_loaded

    def run(self):
        s = self.sim
        s.tloop.k_max = 10
        s.tline.step = 1
        s.tloop.verbose = False
        s.run()

    def get_max_vals(self):
        self.run()
        U_1 = self.hist.U_t[-1]
        U_max = np.max(np.fabs(U_1))
        return U_max

    def export_abaqus(self):
        al = AbaqusLink(shell_analysis=self)
        al.model_name = 'test_name'
        al.build_inp()

    def setup_plot(self, pb):
        print('analysis: setup_plot')
        X_Id = self.xdomain.mesh.X_Id
        if len(self.hist.U_t) == 0:
            U_1 = np.zeros_like(X_Id)
            print('analysis: U_I', )
        else:
            U_1 = self.hist.U_t[-1]
            U_1 = U_1.reshape(-1, self.xdomain.fets.n_nodal_dofs)[:, :3]

        X1_Id = X_Id + U_1
        X1_Id = X1_Id.astype(np.float32)

        I_Ei = self.xdomain.I_Ei.astype(np.uint32)

        # Original state mesh
        wb_mesh_0 = k3d.mesh(self.xdomain.X_Id.astype(np.float32),
                             I_Ei,
                             color=0x999999, opacity=0.5,
                             side='double')
        pb.plot_fig += wb_mesh_0
        pb.objects['wb_mesh_0'] = wb_mesh_0

        # Deformed state mesh
        wb_mesh_1 = k3d.mesh(X1_Id,
                             I_Ei,
                             color_map=k3d.colormaps.basic_color_maps.Jet,
                             attribute=U_1[:, 2],
                             color_range=[np.min(U_1), np.max(U_1)],
                             side='double')
        pb.plot_fig += wb_mesh_1
        pb.objects['wb_mesh_1'] = wb_mesh_1

        if self.show_wireframe:
            k3d_mesh_wireframe = k3d.mesh(X1_Id,
                                          I_Ei,
                                          color=0x000000,
                                          wireframe=True)
            pb.plot_fig += k3d_mesh_wireframe
            pb.objects['mesh_wireframe'] = k3d_mesh_wireframe

    def update_plot(self, pb):
        X_Id = self.xdomain.mesh.X_Id
        print('analysis: update_plot')
        if len(self.hist.U_t) == 0:
            U_1 = np.zeros_like(X_Id)
            print('analysis: U_I', )
        else:
            U_1 = self.hist.U_t[-1]
            U_1 = U_1.reshape(-1, self.xdomain.fets.n_nodal_dofs)[:, :3]

        X1_Id = X_Id + U_1
        X1_Id = X1_Id.astype(np.float32)

        I_Ei = self.xdomain.I_Ei.astype(np.uint32)

        mesh = pb.objects['wb_mesh_1']
        mesh.vertices = X1_Id
        mesh.indices = I_Ei
        mesh.attribute = U_1[:, 2]
        mesh.color_range = [np.min(U_1), np.max(U_1)]
        if self.show_wireframe:
            wireframe = pb.objects['mesh_wireframe']
            wireframe.vertices = X1_Id
            wireframe.indices = I_Ei

    def get_Pw(self):
        import numpy as np
        F_to = self.hist.F_t
        U_to = self.hist.U_t
        _, _, loaded_dofs = self.bcs.bc_loaded
        F_loaded = np.sum(F_to[:, loaded_dofs], axis=-1)
        U_loaded = np.average(U_to[:, loaded_dofs], axis=-1)
        return U_loaded, F_loaded
Ejemplo n.º 29
0
class WBTessellation4P(bu.Model):
    name = 'WB Tessellation 4P'

    wb_cell = bu.Instance(WBCell4Param)

    def _wb_cell_default(self):
        wb_cell = WBCell4Param()
        self.update_wb_cell_params(wb_cell)
        return wb_cell

    tree = ['wb_cell']

    plot_backend = 'k3d'

    n_phi_plus = bu.Int(5, GEO=True)
    n_x_plus = bu.Int(3, GEO=True)
    gamma = bu.Float(1.25, GEO=True)
    a = bu.Float(1000, GEO=True)
    a_high = bu.Float(2000)
    b = bu.Float(1000, GEO=True)
    b_high = bu.Float(2000)
    c = bu.Float(1000, GEO=True)
    c_high = bu.Float(2000)

    show_wireframe = bu.Bool(True, GEO=True)
    show_nodes = bu.Bool(False, GEO=True)
    show_node_labels = bu.Bool(False, GEO=True)
    WIREFRAME = 'k3d_mesh_wireframe'
    NODES = 'k3d_nodes'
    NODES_LABELS = 'k3d_nodes_labels'

    @tr.observe('+GEO', post_init=True)
    def update_wb_cell(self, event):
        self.update_wb_cell_params(self.wb_cell)

    def update_wb_cell_params(self, wb_cell):
        wb_cell.trait_set(
            gamma=self.gamma,
            a=self.a,
            a_high=self.a_high,
            b=self.b,
            b_high=self.b_high,
            c=self.c,
            c_high=self.c_high,
        )

    ipw_view = bu.View(
        # bu.Item('wb_cell'),
        *WBCell4Param.ipw_view.content,
        bu.Item('n_phi_plus', latex=r'n_\phi'),
        bu.Item('n_x_plus', latex=r'n_x'),
        # bu.Item('show_wireframe'),
        # bu.Item('show_node_labels'),
        bu.Item('show_nodes'),
    )

    def get_phi_range(self, delta_phi):
        return np.arange(-(self.n_phi_plus - 1), self.n_phi_plus) * delta_phi

    def get_X_phi_range(self, delta_phi, R_0):
        """Given an array of angles and radius return an array of coordinates
        """
        phi_range = self.get_phi_range((delta_phi))
        return np.array([
            np.fabs(R_0) * np.sin(phi_range),
            np.fabs(R_0) * np.cos(phi_range) + R_0
        ]).T

    def get_X_x_range(self, delta_x):
        return np.arange(-(self.n_x_plus - 1), self.n_x_plus) * delta_x

    cell_map = tr.Property

    def _get_cell_map(self):
        delta_x = self.wb_cell.delta_x
        delta_phi = self.wb_cell.delta_phi
        R_0 = self.wb_cell.R_0

        X_x_range = self.get_X_x_range(delta_x)
        X_phi_range = self.get_X_phi_range(delta_phi, R_0)
        n_idx_x = len(X_x_range)
        n_idx_phi = len(X_phi_range)
        idx_x = np.arange(n_idx_x)
        idx_phi = np.arange(n_idx_phi)

        idx_x_ic = idx_x[(n_idx_x) % 2::2]
        idx_x_id = idx_x[(n_idx_x + 1) % 2::2]
        idx_phi_ic = idx_phi[(n_idx_phi) % 2::2]
        idx_phi_id = idx_phi[(n_idx_phi + 1) % 2::2]

        n_ic = len(idx_x_ic) * len(idx_phi_ic)
        n_id = len(idx_x_id) * len(idx_phi_id)

        n_cells = n_ic + n_id
        return n_cells, n_ic, n_id, idx_x_ic, idx_x_id, idx_phi_ic, idx_phi_id

    n_cells = tr.Property

    def _get_n_cells(self):
        n_cells, _, _, _, _, _, _ = self.cell_map
        return n_cells

    X_cells_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates of uncoupled cells
    I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_cells_Ia(self):

        delta_x = self.wb_cell.delta_x
        delta_phi = self.wb_cell.delta_phi
        R_0 = self.wb_cell.R_0

        X_Ia_wb_rot = np.copy(self.wb_cell.X_Ia)
        X_Ia_wb_rot[..., 2] -= R_0
        X_cIa = np.array([X_Ia_wb_rot], dtype=np.float_)
        rotation_axes = np.array([[1, 0, 0]], dtype=np.float_)
        rotation_angles = self.get_phi_range(delta_phi)
        q = axis_angle_to_q(rotation_axes, rotation_angles)
        X_dIa = qv_mult(q, X_cIa)
        X_dIa[..., 2] += R_0

        X_x_range = self.get_X_x_range(delta_x)
        X_phi_range = self.get_X_phi_range(delta_phi, R_0)
        n_idx_x = len(X_x_range)
        n_idx_phi = len(X_phi_range)
        idx_x = np.arange(n_idx_x)
        idx_phi = np.arange(n_idx_phi)

        idx_x_ic = idx_x[(n_idx_x) % 2::2]
        idx_x_id = idx_x[(n_idx_x + 1) % 2::2]
        idx_phi_ic = idx_phi[(n_idx_phi) % 2::2]
        idx_phi_id = idx_phi[(n_idx_phi + 1) % 2::2]

        X_E = X_x_range[idx_x_ic]
        X_F = X_x_range[idx_x_id]

        X_CIa = X_dIa[idx_phi_ic]
        X_DIa = X_dIa[idx_phi_id]

        expand = np.array([1, 0, 0])
        X_E_a = np.einsum('i,j->ij', X_E, expand)
        X_ECIa = X_CIa[np.newaxis, :, :, :] + X_E_a[:, np.newaxis,
                                                    np.newaxis, :]
        X_F_a = np.einsum('i,j->ij', X_F, expand)
        X_FDIa = X_DIa[np.newaxis, :, :, :] + X_F_a[:, np.newaxis,
                                                    np.newaxis, :]

        X_Ia = np.vstack(
            [X_ECIa.flatten().reshape(-1, 3),
             X_FDIa.flatten().reshape(-1, 3)])
        return X_Ia

    I_cells_Fi = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_I_cells_Fi(self):
        I_Fi_cell = self.wb_cell.I_Fi
        n_I_cell = self.wb_cell.n_I
        n_cells = self.n_cells
        i_range = np.arange(n_cells) * n_I_cell
        I_Fi = (I_Fi_cell[np.newaxis, :, :] +
                i_range[:, np.newaxis, np.newaxis]).reshape(-1, 3)
        return I_Fi

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''

    @tr.cached_property
    def _get_X_Ia(self):
        idx_unique, idx_remap = self.unique_node_map
        return self.X_cells_Ia[idx_unique]

    I_Fi = tr.Property(depends_on='+GEO')
    '''Facet - node mapping
    '''

    @tr.cached_property
    def _get_I_Fi(self):
        _, idx_remap = self.unique_node_map
        return idx_remap[self.I_cells_Fi]

    node_match_threshold = tr.Property(depends_on='+GEO')

    def _get_node_match_threshold(self):
        min_length = np.min([self.a, self.b, self.c])
        return min_length * 1e-4

    unique_node_map = tr.Property(depends_on='+GEO')
    '''Property containing the mapping between the crease pattern nodes
    with duplicate nodes and pattern with compressed nodes array.
    The criterion for removing a node is geometric, the threshold
    is specified in node_match_threshold.
    '''

    def _get_unique_node_map(self):
        # reshape the coordinates in array of segments to the shape (n_N, n_D
        x_0 = self.X_cells_Ia
        # construct distance vectors between every pair of nodes
        x_x_0 = x_0[:, np.newaxis, :] - x_0[np.newaxis, :, :]
        # calculate the distance between every pair of nodes
        dist_0 = np.sqrt(np.einsum('...i,...i', x_x_0, x_x_0))
        # identify those at the same location
        zero_dist = dist_0 < self.node_match_threshold
        # get their indices
        i_idx, j_idx = np.where(zero_dist)
        # take only the upper triangle indices
        upper_triangle = i_idx < j_idx
        idx_multi, idx_delete = i_idx[upper_triangle], j_idx[upper_triangle]
        # construct a boolean array with True at valid and False at deleted
        # indices
        idx_unique = np.ones((len(x_0), ), dtype='bool')
        idx_unique[idx_delete] = False
        # Boolean array of nodes to keep - includes both those that
        # are unique and redirection nodes to be substituted for duplicates
        idx_keep = np.ones((len(x_0), ), dtype=np.bool_)
        idx_keep[idx_delete] = False
        # prepare the enumeration map map
        ij_map = np.ones_like(dist_0, dtype=np.int_) + len(x_0)
        i_ = np.arange(len(x_0))
        # indexes of nodes that are being kept
        idx_row = i_[idx_keep]
        # enumerate the kept nodes by putting their number onto the diagonal
        ij_map[idx_keep, idx_keep] = np.arange(len(idx_row))
        # broadcast the substitution nodes into the interaction positions
        ij_map[i_idx, j_idx] = ij_map[i_idx, i_idx]
        # get the substitution node by picking up the minimum index within ac column
        idx_remap = np.min(ij_map, axis=0)

        return idx_unique, idx_remap

    I_CDij = tr.Property(depends_on='+GEO')

    @tr.cached_property
    def _get_I_CDij(self):
        n_cells, n_ic, n_id, _, x_cell_idx, _, y_cell_idx = self.cell_map
        x_idx, y_idx = x_cell_idx / 2, y_cell_idx / 2
        n_x_, n_y_ = len(x_idx), len(y_idx)
        I_cell_offset = (n_ic + np.arange(n_x_ * n_y_).reshape(
            n_x_, n_y_)) * self.wb_cell.n_I
        I_CDij_map = (I_cell_offset.T[:, :, np.newaxis, np.newaxis] +
                      self.wb_cell.I_boundary[np.newaxis, np.newaxis, :, :])
        return I_CDij_map

    def setup_plot(self, pb):
        self.pb = pb
        X_Ia = self.X_Ia.astype(np.float32)
        I_Fi = self.I_Fi.astype(np.uint32)

        I_M = self.I_CDij[(0, -1), :, (0, -1), :]
        _, idx_remap = self.unique_node_map
        J_M = idx_remap[I_M]
        X_Ma = X_Ia[J_M.flatten()]

        k3d_mesh = k3d.mesh(X_Ia, I_Fi, color=0x999999, side='double')

        pb.objects['k3d_mesh'] = k3d_mesh
        pb.plot_fig += k3d_mesh

        if self.show_nodes:
            self._add_nodes_to_fig(pb, X_Ma)

        if self.wb_cell.show_node_labels:
            self._add_nodes_labels_to_fig(pb, X_Ia)

        if self.show_wireframe:
            self._add_wireframe_to_fig(pb, X_Ia, I_Fi)

    def update_plot(self, pb):
        X_Ia = self.X_Ia.astype(np.float32)
        I_Fi = self.I_Fi.astype(np.uint32)

        I_M = self.I_CDij[(0, -1), :, (0, -1), :]
        _, idx_remap = self.unique_node_map
        J_M = idx_remap[I_M]
        X_Ma = X_Ia[J_M.flatten()]

        mesh = pb.objects['k3d_mesh']
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

        if self.show_nodes:
            if self.NODES in pb.objects:
                pb.objects[self.NODES].positions = X_Ma
            else:
                self._add_nodes_to_fig(pb, X_Ma)
        else:
            if self.NODES in pb.objects:
                pb.clear_object(self.NODES)

        if self.show_wireframe:
            if self.WIREFRAME in pb.objects:
                wireframe = pb.objects[self.WIREFRAME]
                wireframe.vertices = X_Ia
                wireframe.indices = I_Fi
            else:
                self._add_wireframe_to_fig(pb, X_Ia, I_Fi)
        else:
            if self.WIREFRAME in pb.objects:
                pb.clear_object(self.WIREFRAME)

        if self.show_node_labels:
            if self.NODES_LABELS in pb.objects:
                pb.clear_object(self.NODES_LABELS)
            self._add_nodes_labels_to_fig(pb, X_Ia)
        else:
            if self.NODES_LABELS in pb.objects:
                pb.clear_object(self.NODES_LABELS)

    def _add_nodes_labels_to_fig(self, pb, X_Ia):
        text_list = []
        for I, X_a in enumerate(X_Ia):
            k3d_text = k3d.text('%g' % I,
                                tuple(X_a),
                                label_box=False,
                                size=0.8,
                                color=0x00FF00)
            pb.plot_fig += k3d_text
            text_list.append(k3d_text)
        pb.objects[self.NODES_LABELS] = text_list

    def _add_wireframe_to_fig(self, pb, X_Ia, I_Fi):
        k3d_mesh_wireframe = k3d.mesh(X_Ia,
                                      I_Fi,
                                      color=0x000000,
                                      wireframe=True)
        pb.plot_fig += k3d_mesh_wireframe
        pb.objects[self.WIREFRAME] = k3d_mesh_wireframe

    def _add_nodes_to_fig(self, pb, X_Ma):
        k3d_points = k3d.points(X_Ma, point_size=300)
        pb.objects[self.NODES] = k3d_points
        pb.plot_fig += k3d_points

    def _show_or_hide_fig_object(self, pb, show_obj, obj_name, obj_add_fun,
                                 obj_update_fun):
        if show_obj:
            if obj_name in pb.objects:
                obj_update_fun()
            else:
                obj_add_fun()
        else:
            if obj_name in pb.objects:
                pb.clear_object(obj_name)

    def export_fold_file(self, path=None):
        # See https://github.com/edemaine/fold/blob/master/doc/spec.md for fold file specification
        # Viewer: https://edemaine.github.io/fold/examples/foldviewer.html

        output_data = {
            "file_spec": 1,
            "file_creator": "BMCS software suite",
            "file_author": "RWTH Aachen - Institute of Structural Concrete",
            "file_title": "Preliminary Base",
            "file_classes": ["singleModel"],
            "frame_title": "Preliminary Base Crease Pattern",
            "frame_classes": ["creasePattern"],
            "vertices_coords": self.X_Ia.tolist(),
            "faces_vertices": self.I_Fi.tolist(),
            # To be completed
        }

        if path is None:
            path = time.strftime("%Y%m%d-%H%M%S") + '-shell.fold'

        with open(path, 'w') as outfile:
            json.dump(output_data, outfile, sort_keys=True, indent=4)
Ejemplo n.º 30
0
class MATS3DScalarDamage(MATS3DEval):
    r'''
    Isotropic damage model.
    '''
    node_name = 'Scalar damage'

    stiffness = tr.Enum("secant", "algorithmic", input=True)
    r'''Selector of the stiffness calculation.
    '''
    strain_norm = EitherType(klasses=[
        Rankine,
    ], MAT=True)
    r'''Selector of the strain norm defining the load surface.
    '''

    # TODO - generalize
    # damage_fn = EitherType(klasses=[], MAT=True)
    # r'''Selector of the damage function.
    # '''

    epsilon_0 = bu.Float(5e-2,
                         label="eps_0",
                         desc="Strain at the onset of damage",
                         auto_set=False,
                         MAT=True)
    r'''Damage function parameter - slope of the damage function.
    '''

    epsilon_f = bu.Float(191e-1,
                         label="eps_f",
                         desc="Slope of the damage function",
                         auto_set=False,
                         MAT=True)
    r'''Damage function parameter - slope of the damage function.
    '''

    ipw_view = bu.View(bu.Item('epsilon_0'), bu.Item('epsilon_f'))

    changed = tr.Event
    r'''This event can be used by the clients to trigger 
    an action upon the completed reconfiguration of 
    the material model
    '''

    state_var_shapes = {'kappa': (), 'omega': ()}
    r'''
    Shapes of the state variables
    to be stored in the global array at the level 
    of the domain.
    '''
    def init(self, kappa, omega):
        r'''
        Initialize the state variables.
        '''
        kappa[...] = 0
        omega[...] = 0

    def get_corr_pred(self, eps_Emab_n1, tn1, kappa, omega):
        r'''
        Corrector predictor computation.
        '''
        self.update_state_variables(eps_Emab_n1, kappa, omega)
        phi_Em = (1.0 - omega)
        D_Emabcd = np.einsum('...,abcd->...abcd', phi_Em, self.D_abef)
        sigma_Emab = np.einsum('...abcd,...cd->...ab', D_Emabcd, eps_Emab_n1)

        # algorithmic switched off - because the derivative
        # of the strain norm is still not available
        if False:  # algorithmic:
            D_Emabcd_red_I = self._get_D_abcd_alg_reduction(
                kappa[I], eps_Emab_n1[I])
            D_Emabcd[I] -= D_Emabcd_red_I

        return sigma_Emab, D_Emabcd

    def update_state_variables(self, eps_Emab, kappa, omega):
        eps_eq_Em = self.strain_norm.get_eps_eq(eps_Emab, kappa)
        f_trial_Em = eps_eq_Em - self.epsilon_0
        I = np.where(f_trial_Em > 0)
        kappa[I] = eps_eq_Em[I]
        omega[I] = self._get_omega(eps_eq_Em[I])
        return I

    def _get_omega(self, kappa_Em):
        r'''
        Return new value of damage parameter
        @param kappa_Em: maximum strain norm achieved so far 
        '''
        omega_Em = np.zeros_like(kappa_Em)
        epsilon_0 = self.epsilon_0
        epsilon_f = self.epsilon_f
        I = np.where(kappa_Em >= epsilon_0)
        omega_Em[I] = (
            1.0 - (epsilon_0 / kappa_Em[I] * np.exp(-1.0 *
                                                    (kappa_Em[I] - epsilon_0) /
                                                    (epsilon_f - epsilon_0))))
        return omega_Em

    def _get_domega(self, kappa_Em):
        '''
        Return new value of damage parameter derivative
        @param kappa_Em: maximum strain norm achieved so far
        '''
        epsilon_0 = self.epsilon_0
        epsilon_f = self.epsilon_f
        domega_Em = np.zeros_like(kappa_Em)
        I = np.where(kappa_Em >= epsilon_0)
        factor_1 = epsilon_0 / (kappa_Em[I] * kappa_Em[I])
        factor_2 = epsilon_0 / (kappa_Em[I] * (epsilon_f - epsilon_0))
        domega_Em[I] = ((factor_1 + factor_2) *
                        np.exp(-(kappa_Em[I] - epsilon_0) /
                               (epsilon_f - epsilon_0)))
        return domega_Em

    def _get_D_abcd_alg_reduction(self, kappa_Em, eps_Emab_n1):
        '''Calculate the stiffness term to be subtracted
        from the secant stiffness to get the algorithmic stiffness.
        '''
        domega_Em = self._get_domega(kappa_Em)
        deps_eq_Emcd = self.strain_norm.get_deps_eq(eps_Emab_n1)
        return np.einsum('...,...cd,abcd,...cd->...abcd', domega_Em,
                         deps_eq_Emcd, self.D_abef, eps_Emab_n1)

    traits_view = View(VSplit(
        Group(Item('E'), Item('nu'), Item('epsilon_0'), Item('epsilon_f'),
              Item('strain_norm')),
        Group(
            Item('stiffness', style='custom'),
            Spring(resizable=True),
            label='Configuration parameters',
            show_border=True,
        ),
    ),
                       resizable=True)

    tree_view = View(
        Group(Item('E', full_size=True, resizable=True), Item('nu'),
              Item('epsilon_0'), Item('epsilon_f'), Item('strain_norm')), )

    # Declare and fill-in the rte_dict - it is used by the clients to
    # assemble all the available time-steppers.
    #
    rte_dict = tr.Trait(tr.Dict)

    def _rte_dict_default(self):
        return {'omega': self.get_omega}