Exemple #1
0
class TLineMixIn(tr.HasTraits):
    #=========================================================================
    # TIME LINE
    #=========================================================================
    tline = bu.Instance(TLine)
    r'''Time line defining the time range, discretization and state,  
    '''

    def _tline_default(self):
        return TLine(
            time_change_notifier=self.time_changed,
            time_range_change_notifier=self.time_range_changed
        )

    def time_changed(self, time):
        if not(self.ui is None):
            self.ui.time_changed(time)

    def time_range_changed(self, tmax):
        self.tline.max = tmax
        if not(self.ui is None):
            self.ui.time_range_changed(tmax)

    def set_tmax(self, time):
        self.time_range_changed(time)

    t = tr.DelegatesTo('tline', 'val')
    t_max = tr.DelegatesTo('tline', 'max')
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)
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]')
Exemple #4
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
Exemple #5
0
class WBTessellation5PV2(WBNumTessellation):
    name = 'WBTessellation5PV2'

    wb_cell = bu.Instance(WBCell5ParamV2, ())
    tree = ['wb_cell']
    X_Ia = tr.DelegatesTo('wb_cell')
    I_Fi = tr.DelegatesTo('wb_cell')

    # sigma_sol_num = bu.Int(-1, GEO=True)
    # rho_sol_num = bu.Int(-1, GEO=True)
    sol_num = bu.Int(4, GEO=True)

    # Note: Update traits to 6.3.2 in order for the following command to work!!
    @tr.observe('wb_cell.+GEO', post_init=True)
    def update_after_wb_cell_GEO_changes(self, event):
        self.event_geo = not self.event_geo
        self.update_plot(self.pb)

    ipw_view = bu.View(
        *WBNumTessellation.ipw_view.content,
        # bu.Item('sigma_sol_num'),
        # bu.Item('rho_sol_num'),
        bu.Item('sol_num'),
    )

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

    @tr.cached_property
    def _get_sol(self):
        # rhos, sigmas = self.get_3_cells_angles()
        # print('original sigmas=', sigmas)
        # print('original rhos=', rhos)
        # # rhos = 2 * np.pi - rhos
        # # sigmas = 2 * np.pi - sigmas
        # rhos = -rhos
        # sigmas = -sigmas
        # print('sigmas=', sigmas)
        # print('rhos=', rhos)
        #
        # if self.sigma_sol_num != -1 and self.rho_sol_num != -1:
        #     print('Solution {} was used.'.format(self.sigma_sol_num))
        #     sol = np.array([sigmas[self.sigma_sol_num - 1], rhos[self.rho_sol_num - 1]])
        #     return sol
        #
        # sigma_sol_idx = np.argmin(np.abs(sigmas - sol[0]))
        # rho_sol_idx = np.argmin(np.abs(rhos - sol[1]))
        #
        # if sigma_sol_idx != rho_sol_idx:
        #     print('Warning: sigma_sol_idx != rho_sol_idx, num solution is picked!')
        # else:
        #     diff = np.min(np.abs(sigmas - sol[0])) + np.min(np.abs(rhos - sol[1]))
        #     print('Solution {} was picked (nearst to numerical sol), diff={}'.format(sigma_sol_idx + 1, diff))
        #     sol = np.array([sigmas[sigma_sol_idx], rhos[rho_sol_idx]])

        sol_num = self.sol_num

        # Solving with only 4th solution
        rhos, sigmas = self.get_3_cells_angles(sol_num=sol_num)
        sol = np.array([sigmas[0], rhos[0]])
        print('Ana. solution:', sol)
        return sol

    def get_3_cells_angles(self, sol_num=None):
        a = self.wb_cell.a
        b = self.wb_cell.b
        c = self.wb_cell.c
        gamma = self.wb_cell.gamma
        beta = self.wb_cell.beta

        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

        sin_phi1 = sin_psi1plus6
        sin_phi2 = sin_psi5
        sin_phi3 = sin_psi5
        sin_phi4 = sin_psi1plus6

        cos_phi1 = cos_psi1plus6
        cos_phi2 = cos_psi5
        cos_phi3 = cos_psi5
        cos_phi4 = cos_psi1plus6

        # ALWAYS USE THIS TO FIND DEFINITE ANGLE IN [-pi, pi] from sin and cos
        phi1 = np.arctan2(sin_phi1, cos_phi1)
        phi2 = np.arctan2(sin_phi2, cos_phi2)
        phi3 = phi2
        phi4 = phi1

        e2 = (a - c)**2 + b**2
        e = sqrt(e2)
        f = (a - c)**2 + b**2 * cos(phi1 + phi3)
        W = 2 * (1 - cos(phi1 + phi3)) * (
            (a**2 - cos(phi2) * cos(phi4) * b**2) * c**2 * sin(2 * gamma)**2 -
            (cos(phi2) + cos(phi4)) *
            ((cos(2 * gamma) - 1) * c + 2 * a) * sin(2 * gamma) * a * b * c -
            2 * a**2 * c * (2 * a - c) *
            (cos(2 * gamma) + 1) + 2 * a**2 * b**2 *
            (cos(phi1 + phi3) + 1)) - e2 * (
                cos(phi2) - cos(phi4))**2 * c**2 * sin(2 * gamma)**2

        T_rho = (cos(phi1 + phi3) - 1) * (
            a * (b**2 * (cos(phi1 + phi3) + 1) + 2 * a *
                 (a - c)) * (cos(2 * gamma) + 1) + b *
            ((e2 + f) * cos(phi2) + (a - c) * c *
             (cos(phi2) + cos(phi4))) * sin(2 * gamma) - 2 * a * b**2 *
            (cos(phi1 + phi3) + 1))

        T_sigma = (cos(phi1 + phi3) - 1) * (
            a * (b**2 * (cos(phi1 + phi3) + 1) + 2 * a *
                 (a - c)) * (cos(2 * gamma) + 1) + b *
            ((e2 + f) * cos(phi4) + (a - c) * c *
             (cos(phi2) + cos(phi4))) * sin(2 * gamma) - 2 * a * b**2 *
            (cos(phi1 + phi3) + 1))

        print('W:', W)
        print('T_rho:', T_rho)
        print('T_sigma:', T_sigma)

        rhos = []
        sigmas = []
        get_angle = lambda x: np.arctan(x) * 2

        if sol_num == 1 or sol_num is None:
            sol_P1_t_1 = (b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi2) * f - cos(phi4) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) +
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (b * sin(phi1 + phi3) * sqrt(W) - T_rho))

            sol_Q1_u_1 = (b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi4) * f - cos(phi2) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) +
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (b * sin(phi1 + phi3) * sqrt(W) - T_sigma))

            rhos.append(get_angle(sol_P1_t_1))
            sigmas.append(get_angle(sol_Q1_u_1))

            print('sol_P1_t_1:', sol_P1_t_1)
            print('sol_Q1_u_1:', sol_Q1_u_1)

        if sol_num == 2 or sol_num is None:
            sol_P1_t_2 = (b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi2) * f - cos(phi4) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) -
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (b * sin(phi1 + phi3) * sqrt(W) - T_rho))

            sol_Q1_u_2 = (b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi4) * f - cos(phi2) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) -
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (b * sin(phi1 + phi3) * sqrt(W) - T_sigma))
            rhos.append(get_angle(sol_P1_t_2))
            sigmas.append(get_angle(sol_Q1_u_2))

            print('sol_P1_t_2:', sol_P1_t_2)
            print('sol_Q1_u_2:', sol_Q1_u_2)

        if sol_num == 3 or sol_num is None:
            sol_P2_t_1 = (-b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi2) * f - cos(phi4) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) +
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (-b * sin(phi1 + phi3) * sqrt(W) - T_rho))

            sol_Q2_u_1 = (-b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi4) * f - cos(phi2) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) +
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (-b * sin(phi1 + phi3) * sqrt(W) - T_sigma))

            rhos.append(get_angle(sol_P2_t_1))
            sigmas.append(get_angle(sol_Q2_u_1))

            print('sol_P2_t_1:', sol_P2_t_1)
            print('sol_Q2_u_1:', sol_Q2_u_1)

        if sol_num == 4 or sol_num is None:
            sol_P2_t_2 = (-b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi2) * f - cos(phi4) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) -
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi2) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (-b * sin(phi1 + phi3) * sqrt(W) - T_rho))

            sol_Q2_u_2 = (-b * (a - c) * (cos(phi1 + phi3) - 1) * sqrt(W) - b *
                          (a * b * c * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * cos(2 * gamma) +
                           (cos(phi4) * f - cos(phi2) * e2) * sin(phi1 + phi3)
                           * c * sin(2 * gamma) + a * b * sin(phi1 + phi3) *
                           (cos(phi1 + phi3) - 1) * (2 * a - c)) -
                          (cos(phi1 + phi3) - 1) *
                          (e2 + f) * sqrt(4 * a * b**2 *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a) -
                                          (a**2 + b**2) *
                                          (cos(phi4) * sin(2 * gamma) * b +
                                           (cos(2 * gamma) + 1) * a)**2)
                          ) / (e * (-b * sin(phi1 + phi3) * sqrt(W) - T_sigma))
            print('sol_P2_t_2:', sol_P2_t_2)
            print('sol_Q2_u_2:', sol_Q2_u_2)
            rhos.append(get_angle(sol_P2_t_2))
            sigmas.append(get_angle(sol_Q2_u_2))

        # sol_P is tan(rho/2), sol_Q is tan(sigma/2)

        return -np.array(rhos), -np.array(sigmas)
class MATS3DSlideStrain(MATS3DEval):
    r'''
    Isotropic damage model.
    '''
    node_name = 'Slide3D'

    n_a = tr.Array(np.float_, value=[0, 1, 0])

    slide_displ = bu.Instance(Slide34, ())

    slide_displ_ = tr.Property(depends_on='+MAT')
    '''Reconfigure slide with the derived stiffness parameters'''
    @tr.cached_property
    def _get_slide_displ_(self):
        self.slide_displ.trait_set(E_N=self.E, E_T=self.E_T)
        return self.slide_displ

    tree = ['slide_displ']

    ipw_view = bu.View(bu.Item('n_a'), bu.Item('slide_displ'))

    state_var_shapes = tr.Property

    @tr.cached_property
    def _get_state_var_shapes(self):
        return self.slide_displ_.state_var_shapes

    r'''
    Shapes of the state variables
    to be stored in the global array at the level 
    of the domain.
    '''

    def get_eps_N(self, eps_ij, n_i):
        eps_N = np.einsum('...ij,...i,...j->...', eps_ij, n_i, n_i)
        return eps_N

    def get_eps_T(self, eps_ij, n_i):
        delta_ij = np.identity(3)
        eps_T = 0.5 * (
            np.einsum('...i,...jk,...ij->...k', n_i, delta_ij, eps_ij) +
            np.einsum('...j,...ik,...ij->...k', n_i, delta_ij, eps_ij) -
            2 * np.einsum('...i,...j,...k,...ij->...k', n_i, n_i, n_i, eps_ij))
        return eps_T

    def get_eps_T_p(self, eps_T_p, eps_T):
        director_vector = [0, 0, 1]
        eps_T_p = np.einsum('...,...i->...i', eps_T_p, director_vector)
        return eps_T_p

    E_T = tr.Property(depends_on='MAT')

    @tr.cached_property
    def _get_E_T(self):
        n_i = self.n_a
        delta_ij = np.identity(3)
        D_ijkl = self.D_abef
        operator = 0.5 * (np.einsum('i,jk,l->ijkl', n_i, delta_ij, n_i) +
                          np.einsum('j,ik,l->jikl', n_i, delta_ij, n_i) -
                          2 * np.einsum('i,j,k,l->ijkl', n_i, n_i, n_i, n_i))
        E_T = np.einsum('ijkl,ijkl->', D_ijkl, operator)
        return E_T

    def get_corr_pred(self, eps_Emab_n1, tn1, **state):
        r'''
        Corrector predictor computation.
        '''
        n_i = self.n_a
        eps_ij = eps_Emab_n1
        eps_N = np.einsum('...ij,...i,...j->...', eps_ij, n_i, n_i)
        eps_T = self.get_eps_T(eps_ij, n_i)
        eps_T = np.sqrt(np.einsum('...i,...i->...', eps_T, eps_T))
        eps_NT_Ema = np.concatenate(
            [np.transpose(eps_N), np.transpose(eps_T)], axis=-1)
        print('eps_NT_Ema', eps_NT_Ema.shape)
        print(self.state_var_shapes)
        se = self.slide_displ_
        sig_NT_Ema, D_Emab = se.get_corr_pred(eps_NT_Ema, tn1, **state)

        eps_N_p, eps_T_p_x, eps_T_p_y = state['w_pi'], state['s_pi_x'], state[
            's_pi_y']
        eps_T = self.get_eps_T(eps_ij, n_i)
        eps_T_p_i = self.get_eps_T_p(eps_T_p_x, eps_T)
        omega_N_Em, omega_T_Em = state['omega_N'], state['omega_T']
        print(eps_ij.shape)

        phi_Emab = np.zeros_like(eps_ij)
        phi_Emab[:, 1, 1] = 0.
        phi_Emab[:, 2, 2] = np.sqrt(1 - omega_T_Em)
        phi_Emab[:, 0, 0] = np.sqrt(1 - omega_N_Em)

        beta_Emijkl = np.einsum('...ik,...jl->...ijkl', phi_Emab,
                                np.transpose(phi_Emab, (1, 0)))

        eps_ij_p = (np.einsum('i,...j->...ij', n_i, eps_T_p_i) +
                    np.einsum('...i,j->...ij', eps_T_p_i,n_i)) + \
                    np.einsum('...,i,j->...ij',eps_N_p, n_i, n_i)

        D_abef = self.D_abef

        D_Emabcd = np.einsum('...ijkl,klrs,...rstu->...ijtu', beta_Emijkl,
                             D_abef, beta_Emijkl)

        sigma_Emab = np.einsum('...ijkl,...kl->...ij', D_Emabcd,
                               (eps_Emab_n1 - eps_ij_p))

        return sigma_Emab, D_Emabcd
Exemple #7
0
class PullOutModel(TStepBC, BMCSRootNode, Vis2D):
    name = 'Pullout'
    node_name = 'Pull out simulation'

    hist_type = PulloutHist2

    history = tr.Property()

    @tr.cached_property
    def _get_history(self):
        return self.hist

    time_line = tr.Property()

    @tr.cached_property
    def _get_time_line(self):
        return self.sim.tline

    tree = [
        'time_line', 'cross_section', 'geometry', 'material_model',
        'loading_scenario', 'history'
    ]

    def run(self):
        self.sim.run()

    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

    ipw_view = bu.View(bu.Item('w_max'),
                       bu.Item('n_e_x'),
                       bu.Item('fixed_boundary'),
                       bu.Item('material_model'),
                       bu.Item('loading_scenario'),
                       time_editor=bu.ProgressEditor(
                           run_method='run',
                           reset_method='reset',
                           interrupt_var='interrupt',
                           time_var='t',
                           time_max='t_max',
                       ))

    @tr.on_trait_change("state_changed")
    def report_change(self):
        self.model_structure_changed = True

    # =========================================================================
    # Test setup parameters
    # =========================================================================
    loading_scenario = bu.EitherType(
        options=[('monotonic', MonotonicLoadingScenario),
                 ('cyclic', CyclicLoadingScenario)],
        report=True,
        TIME=True,
        desc='object defining the loading scenario')

    cross_section = bu.Instance(CrossSection,
                                report=True,
                                desc='cross section parameters')

    def _cross_section_default(self):
        return CrossSection()

    geometry = bu.Instance(
        Geometry,
        report=True,
        desc='geometry parameters of the boundary value problem')

    def _geometry_default(self):
        return Geometry()

    control_variable = bu.Enum(options=['u', 'f'],
                               auto_set=False,
                               enter_set=True,
                               desc=r'displacement or force control: [u|f]',
                               BC=True)

    # =========================================================================
    # Discretization
    # =========================================================================
    n_e_x = bu.Int(20,
                   MESH=True,
                   auto_set=False,
                   enter_set=True,
                   symbol='n_\mathrm{E}',
                   unit='-',
                   desc='number of finite elements along the embedded length')

    # =========================================================================
    # 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)

    material_model = bu.EitherType(
        options=[
            ('multilinear', MATS1D5BondSlipMultiLinear),
            ('trilinear', MATS1D5BondSlipTriLinear),
            ('damage', MATS1D5BondSlipD),
            ('elasto-plasticity', MATS1D5BondSlipEP),
            # ('cumulative fatigue', MATSBondSlipFatigue)
        ],
        MAT=True)
    '''Material model'''

    mm = Property

    def _get_mm(self):
        return self.material_model_

    material = Property

    def _get_material(self):
        return self.material_model_

    # =========================================================================
    # Finite element type
    # =========================================================================
    fets_eval = Property(bu.Instance(FETS1D52ULRH), depends_on="state_changed")
    '''Finite element time stepper implementing the corrector
    predictor operators at the element level'''

    @cached_property
    def _get_fets_eval(self):
        return FETS1D52ULRH(A_m=self.cross_section.A_m,
                            P_b=self.cross_section.P_b,
                            A_f=self.cross_section.A_f)

    dots_grid = Property(bu.Instance(XDomainFEInterface1D),
                         depends_on="state_changed")
    '''Discretization object.
    '''

    @cached_property
    def _get_dots_grid(self):
        geo = self.geometry
        return XDomainFEInterface1D(dim_u=2,
                                    coord_max=[geo.L_x],
                                    shape=[self.n_e_x],
                                    fets=self.fets_eval)

    fe_grid = Property

    def _get_fe_grid(self):
        return self.dots_grid.mesh

    domains = Property(depends_on='state_changed')

    @cached_property
    def _get_domains(self):
        return [(self.dots_grid, self.material_model_)]

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

    u_f0_max = Property(depends_on='BC')

    @cached_property
    def _get_u_f0_max(self):
        return self.w_max

    def _set_u_f0_max(self, value):
        self.w_max = value

    fixed_boundary = bu.Enum(
        options=[
            'non-loaded end (matrix)', 'loaded end (matrix)',
            'non-loaded end (reinf)', 'clamped left'
        ],
        BC=True,
        desc=
        'which side of the specimen is fixed [non-loaded end [matrix], loaded end [matrix], non-loaded end [reinf]]'
    )

    fixed_dofs = Property(depends_on="state_changed")

    @cached_property
    def _get_fixed_dofs(self):
        if self.fixed_boundary == 'non-loaded end (matrix)':
            return [0]
        elif self.fixed_boundary == 'non-loaded end (reinf)':
            return [1]
        elif self.fixed_boundary == 'loaded end (matrix)':
            return [self.controlled_dof - 1]
        elif self.fixed_boundary == 'clamped left':
            return [0, 1]

    controlled_dof = Property(depends_on="state_changed")

    @cached_property
    def _get_controlled_dof(self):
        return 2 + 2 * self.n_e_x - 1

    free_end_dof = Property(depends_on="state_changed")

    @cached_property
    def _get_free_end_dof(self):
        return 1

    fixed_bc_list = Property(depends_on="state_changed")
    '''Foxed boundary condition'''

    @cached_property
    def _get_fixed_bc_list(self):
        return [
            BCDof(node_name='fixed left end', var='u', dof=dof, value=0.0)
            for dof in self.fixed_dofs
        ]

    control_bc = Property(depends_on="state_changed")
    '''Control boundary condition - make it accessible directly
    for the visualization adapter as property
    '''

    @cached_property
    def _get_control_bc(self):
        return BCDof(node_name='pull-out displacement',
                     var=self.control_variable,
                     dof=self.controlled_dof,
                     value=self.w_max,
                     time_function=self.loading_scenario_)

    bc = Property(depends_on="state_changed")

    @cached_property
    def _get_bc(self):
        return [self.control_bc] + self.fixed_bc_list

    X_M = Property()

    def _get_X_M(self):
        state = self.fe_domain[0]
        return state.xmodel.x_Ema[..., 0].flatten()

    # =========================================================================
    # Getter functions @todo move to the PulloutStateRecord
    # =========================================================================

    P = tr.Property

    def _get_P(self):
        c_dof = self.controlled_dof
        return self.F_k[c_dof]

    w_L = tr.Property

    def _get_w_L(self):
        c_dof = self.controlled_dof
        return self.U_n[c_dof]

    w_0 = tr.Property

    def _get_w_0(self):
        f_dof = self.free_end_dof
        return self.U_n[f_dof]

    def get_shear_integ(self):
        sf_t_Em = np.array(self.tloop.sf_Em_record)
        w_ip = self.fets_eval.ip_weights
        J_det = self.tstepper.J_det
        P_b = self.cross_section.P_b
        shear_integ = np.einsum('tEm,m,em->t', sf_t_Em, w_ip, J_det) * P_b
        return shear_integ

    def plot_omega(self, ax, vot):
        X_J = self.X_J
        omega = self.get_omega(vot)
        ax.fill_between(X_J, 0, omega, facecolor='lightcoral', alpha=0.3)
        ax.plot(X_J, omega, linewidth=2, color='lightcoral', label='bond')
        ax.set_ylabel('damage')
        ax.set_xlabel('bond length')
        ax.legend(loc=2)
        return 0.0, 1.05

    def plot_eps_s(self, ax, vot):
        eps_p = self.get_eps_p(vot).T
        s = self.get_s(vot)
        ax.plot(eps_p[1], s, linewidth=2, color='lightcoral')
        ax.set_ylabel('reinforcement strain')
        ax.set_xlabel('slip')

    def subplots(self, fig):
        (ax_geo, ax_Pw), (ax_energy, ax_dG_t) = fig.subplots(2, 2)
        return ax_geo, ax_Pw, ax_energy, ax_dG_t

    def update_plot(self, axes):
        if len(self.history.U_t) == 0:
            return
        ax_geo, ax_Pw, ax_energy, ax_dG_t = axes
        self.history.t_slider = self.t
        self.history.plot_geo(ax_geo)
        self.history.plot_Pw(ax_Pw)
        self.history.plot_G_t(ax_energy)
        self.history.plot_dG_t(ax_dG_t)
Exemple #8
0
class WBShellFETriangularMesh(FETriangularMesh):
    """Directly mapped mesh with one-to-one mapping
    """
    name = 'WBShellFETriangularMesh'

    plot_backend = 'k3d'

    geo = bu.Instance(WBShellGeometry4P)

    I_CDij = tr.DelegatesTo('geo')
    unique_node_map = tr.DelegatesTo('geo')
    n_phi_plus = tr.DelegatesTo('geo')

    direct_mesh = bu.Bool(False, DSC=True)

    subdivision = bu.Float(3, DSC=True)

    # Will be used in the parent class. Should be here to catch GEO dependency
    show_wireframe = bu.Bool(True, GEO=True)

    ipw_view = bu.View(
        *FETriangularMesh.ipw_view.content,
        bu.Item('subdivision'),
        bu.Item('direct_mesh'),
        bu.Item('export_vtk'),
        bu.Item('show_wireframe'),
    )

    mesh = tr.Property(depends_on='state_changed')

    @tr.cached_property
    def _get_mesh(self):

        X_Id = self.geo.X_Ia
        I_Fi = self.geo.I_Fi
        mesh_size = np.linalg.norm(X_Id[1] - X_Id[0]) / self.subdivision

        with pygmsh.geo.Geometry() as geom:
            xpoints = np.array(
                [geom.add_point(X_d, mesh_size=mesh_size) for X_d in X_Id])
            for I_i in I_Fi:
                # Create points.

                Facet(geom, xpoints[I_i])
            #                geom.add_polygon(X_id, mesh_size=mesh_size)
            gmsh.model.geo.remove_all_duplicates()
            mesh = geom.generate_mesh()
        return mesh

    X_Id = tr.Property

    def _get_X_Id(self):
        if self.direct_mesh:
            return self.geo.X_Ia
        return np.array(self.mesh.points, dtype=np.float_)

    I_Fi = tr.Property

    def _get_I_Fi(self):
        if self.direct_mesh:
            return self.geo.I_Fi
        return self.mesh.cells[1][1]

    bc_fixed_nodes = tr.Array(np.int_, value=[])
    bc_loaded_nodes = tr.Array(np.int_, value=[])

    export_vtk = bu.Button

    @tr.observe('export_vtk')
    def write(self, event=None):
        self.mesh.write("test_shell_mesh.vtk")

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

        X_Id = self.X_Id.astype(np.float32)

        fixed_nodes = self.bc_fixed_nodes
        loaded_nodes = self.bc_loaded_nodes

        X_Ma = X_Id[fixed_nodes]
        k3d_fixed_nodes = k3d.points(X_Ma, color=0x22ffff, point_size=100)
        pb.plot_fig += k3d_fixed_nodes
        pb.objects['fixed_nodes'] = k3d_fixed_nodes

        X_Ma = X_Id[loaded_nodes]
        k3d_loaded_nodes = k3d.points(X_Ma, color=0xff22ff, point_size=100)
        pb.plot_fig += k3d_loaded_nodes
        pb.objects['loaded_nodes'] = k3d_loaded_nodes

    def update_plot(self, pb):
        super(WBShellFETriangularMesh, self).update_plot(pb)

        fixed_nodes = self.bc_fixed_nodes
        loaded_nodes = self.bc_loaded_nodes
        X_Id = self.X_Id.astype(np.float32)
        pb.objects['fixed_nodes'].positions = X_Id[fixed_nodes]
        pb.objects['loaded_nodes'].positions = X_Id[loaded_nodes]