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 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)
Example #3
0
class FETriangularMesh(bu.Model):
    name = 'FETriangularMesh'

    X_Id = tr.Array(np.float_,
                    value=[[0, 0, 0], [2, 0, 0], [2, 2, 0], [1, 1, 0]])
    I_Fi = tr.Array(np.int_, value=[
        [0, 1, 3],
        [1, 2, 3],
    ])

    fets = tr.Instance(FETSEval)

    def _fets_default(self):
        return FETS2D3U1M()

    show_node_labels = bu.Bool(False)

    n_nodal_dofs = tr.DelegatesTo('fets')
    dof_offset = tr.Int(0)

    n_active_elems = tr.Property

    def _get_n_active_elems(self):
        return len(self.I_Fi)

    ipw_view = bu.View(bu.Item('show_node_labels'), )

    #=========================================================================
    # 3d Visualization
    #=========================================================================
    plot_backend = 'k3d'
    show_wireframe = bu.Bool(True)

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

        fe_mesh = k3d.mesh(X_Id,
                           I_Fi,
                           color=0x999999,
                           opacity=1.0,
                           side='double')
        pb.plot_fig += fe_mesh
        pb.objects['mesh'] = fe_mesh

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

        if self.show_node_labels:
            self._add_nodes_labels_to_fig(pb, X_Id)

    NODES_LABELS = 'nodes_labels'

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

        mesh = pb.objects['mesh']
        mesh.vertices = X_Id
        mesh.indices = I_Fi
        if self.show_wireframe:
            wireframe = pb.objects['mesh_wireframe']
            wireframe.vertices = X_Id
            wireframe.indices = I_Fi

        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_Id)
        else:
            if self.NODES_LABELS in pb.objects:
                pb.clear_object(self.NODES_LABELS)

    def _add_nodes_labels_to_fig(self, pb, X_Id):
        text_list = []
        for I, X_d in enumerate(X_Id):
            k3d_text = k3d.text('%g' % I,
                                tuple(X_d),
                                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
Example #4
0
class WBTessellationBase(bu.Model):
    name = 'WB Tessellation Base'

    plot_backend = 'k3d'

    # show_wireframe = bu.Bool(True, GEO=True)
    show_node_labels = bu.Bool(False, GEO=True)
    wb_cell = bu.EitherType(options=[('WBCell4Param', WBCell4Param),
                                     ('WBCell5Param', WBCell5Param),
                                     ('WBCell5ParamV2', WBCell5ParamV2),
                                     ('WBCell5ParamV3', WBCell5ParamV3)],
                            GEO=True)
    X_Ia = tr.DelegatesTo('wb_cell_')
    I_Fi = tr.DelegatesTo('wb_cell_')
    tree = ['wb_cell']

    event_geo = bu.Bool(True, 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(
        bu.Item('wb_cell'),
        # bu.Item('show_wireframe'),
        bu.Item('show_node_labels'),
    )

    def _get_br_X_Ia(self, X_Ia, rot=None):
        br_X_Ia = self._get_cell_matching_v1_to_v2(X_Ia, np.array([4, 6]),
                                                   np.array([5, 1]))
        return self.rotate_cell(br_X_Ia, np.array([4, 6]),
                                self.sol[0] if rot is None else rot)

    def _get_ur_X_Ia(self, X_Ia, rot=None):
        ur_X_Ia = self._get_cell_matching_v1_to_v2(X_Ia, np.array([6, 2]),
                                                   np.array([3, 5]))
        return self.rotate_cell(ur_X_Ia, np.array([6, 2]),
                                self.sol[1] if rot is None else rot)

    def _get_ul_X_Ia(self, X_Ia, rot=None):
        br_X_Ia = self._get_cell_matching_v1_to_v2(X_Ia, np.array([5, 1]),
                                                   np.array([4, 6]))
        return self.rotate_cell(br_X_Ia, np.array([5, 1]),
                                -self.sol[0] if rot is None else rot)

    def _get_bl_X_Ia(self, X_Ia, rot=None):
        br_X_Ia = self._get_cell_matching_v1_to_v2(X_Ia, np.array([3, 5]),
                                                   np.array([6, 2]))
        return self.rotate_cell(br_X_Ia, np.array([3, 5]),
                                -self.sol[1] if rot is None else rot)

    def _get_cell_matching_v1_to_v2(self, X_Ia, v1_ids, v2_ids):
        v1_2a = np.array([X_Ia[v1_ids[0]], X_Ia[v1_ids[1]], X_Ia[0]]).T
        v2_2a = np.array([
            X_Ia[v2_ids[0]], X_Ia[v2_ids[1]],
            X_Ia[v2_ids[0]] + X_Ia[v2_ids[1]] - X_Ia[0]
        ]).T
        rot, trans = get_best_rot_and_trans_3d(v1_2a, v2_2a)

        translated_X_Ia = trans.flatten() + np.einsum('ba, Ia -> Ib', rot,
                                                      X_Ia)

        return self.rotate_cell(translated_X_Ia, v1_ids, angle=np.pi)

    def rotate_cell(self, cell_X_Ia, v1_ids, angle=np.pi):
        # Rotating around vector #######
        # 1. Bringing back to origin (because rotating is around a vector originating from origin)
        cell_X_Ia_copy = np.copy(cell_X_Ia)
        cell_X_Ia = cell_X_Ia_copy - cell_X_Ia_copy[v1_ids[1]]

        # 2. Rotating
        rot_around_v1 = get_rot_matrix_around_vector(
            cell_X_Ia[v1_ids[0]] - cell_X_Ia[v1_ids[1]], angle)
        cell_X_Ia = np.einsum('ba, Ia -> Ib', rot_around_v1, cell_X_Ia)

        # 3. Bringing back in position
        return cell_X_Ia + cell_X_Ia_copy[v1_ids[1]]

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

    @tr.cached_property
    def _get_sol(self):
        # No solution is provided in base class, a default value is provided for visualization
        return np.array([np.pi, np.pi])

    # Plotting ##########################################################################

    def setup_plot(self, pb):
        self.pb = pb
        pb.clear_fig()
        I_Fi = self.I_Fi
        X_Ia = self.X_Ia
        br_X_Ia = self._get_br_X_Ia(X_Ia)
        ur_X_Ia = self._get_ur_X_Ia(X_Ia)

        self.add_cell_to_pb(pb, X_Ia, I_Fi, 'X_Ia')
        self.add_cell_to_pb(pb, br_X_Ia, I_Fi, 'br_X_Ia')
        self.add_cell_to_pb(pb, ur_X_Ia, I_Fi, 'ur_X_Ia')

    k3d_mesh = {}
    k3d_wireframe = {}
    k3d_labels = {}

    def update_plot(self, pb):
        if self.k3d_mesh:
            X_Ia = self.X_Ia.astype(np.float32)
            br_X_Ia = self._get_br_X_Ia(self.X_Ia).astype(np.float32)
            ur_X_Ia = self._get_ur_X_Ia(self.X_Ia).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)

    def add_cell_to_pb(self, pb, X_Ia, I_Fi, obj_name):
        plot = pb.plot_fig

        wb_mesh = k3d.mesh(
            X_Ia.astype(np.float32),
            I_Fi.astype(np.uint32),
            # opacity=0.9,
            color=0x999999,
            side='double')
        rand_color = random.randint(0, 0xFFFFFF)
        plot += wb_mesh

        self.k3d_mesh[obj_name] = wb_mesh

        # wb_points = k3d.points(X_Ia.astype(np.float32),
        #                          color=0x999999,
        #                        point_size=100)
        # plot +=wb_points

        if self.show_node_labels:
            texts = []
            for I, X_a in enumerate(X_Ia):
                k3d_text = k3d.text('%g' % I,
                                    tuple(X_a),
                                    label_box=False,
                                    size=0.8,
                                    color=rand_color)
                plot += k3d_text
                texts.append(k3d_text)
            self.k3d_labels[obj_name] = texts

        wb_mesh_wireframe = k3d.mesh(X_Ia.astype(np.float32),
                                     I_Fi.astype(np.uint32),
                                     color=0x000000,
                                     wireframe=True)
        plot += wb_mesh_wireframe
        self.k3d_wireframe[obj_name] = wb_mesh_wireframe
class EnergyDissipation(bu.InteractiveModel):
    name='Energy'

    colors = dict( # color associations
        stored_energy = 'darkgreen', # recoverable
        free_energy_kin = 'darkcyan', # freedom - sky
        free_energy_iso = 'darkslateblue', # freedom - sky
        plastic_diss_s = 'darkorange', # fire - heat
        plastic_diss_w = 'red', # fire - heat
        damage_diss_s = 'darkgray', # ruined
        damage_diss_w = 'black'  # ruined
    )
    slider_exp = tr.WeakRef(bu.InteractiveModel)

    t_arr = tr.DelegatesTo('slider_exp')
    Sig_arr = tr.DelegatesTo('slider_exp')
    Eps_arr = tr.DelegatesTo('slider_exp')
    s_x_t = tr.DelegatesTo('slider_exp')
    s_y_t = tr.DelegatesTo('slider_exp')
    w_t = tr.DelegatesTo('slider_exp')
    iter_t = tr.DelegatesTo('slider_exp')

    show_iter = bu.Bool(False)
    E_plastic_work = bu.Bool(False)
    E_iso_free_energy = bu.Bool(True)
    E_kin_free_energy = bu.Bool(True)
    E_plastic_diss = bu.Bool(True)
    E_damage_diss = bu.Bool(True)

    ipw_view = bu.View(
        bu.Item('show_iter'),
        bu.Item('E_damage_diss'),
        bu.Item('E_plastic_work'),
        bu.Item('E_iso_free_energy'),
        bu.Item('E_kin_free_energy'),
        bu.Item('E_plastic_diss'),
    )

    WUG_t = tr.Property
    def _get_W_t(self):
        W_arr = (
                cumtrapz(self.Sig_arr[:, 0], self.s_x_t, initial=0) +
                cumtrapz(self.Sig_arr[:, 1], self.s_y_t, initial=0) +
                cumtrapz(self.Sig_arr[:, 2], self.w_t, initial=0)
        )
        s_x_el_t = (self.s_x_t - self.Eps_arr[:, 0])
        s_y_el_t = (self.s_y_t - self.Eps_arr[:, 1])
        w_el_t = (self.w_t - self.Eps_arr[:, 2])
        U_arr = (
                self.Sig_arr[:, 0] * s_x_el_t / 2.0 +
                self.Sig_arr[:, 1] * s_y_el_t / 2.0 +
                self.Sig_arr[:, 2] * w_el_t / 2.0
        )
        G_arr = W_arr - U_arr
        return W_arr, U_arr, G_arr

    Eps = tr.Property
    """Energy dissipated in associatiation with individual internal variables 
    """
    def _get_Eps(self):
        Eps_names = self.slider_exp.slide_model.Eps_names
        E_i = cumtrapz(self.Sig_arr, self.Eps_arr, initial=0, axis=0)
        return SimpleNamespace(**{Eps_name: E for Eps_name, E in zip(Eps_names, E_i.T)})

    mechanisms = tr.Property
    """Energy in association with mechanisms (damage and plastic dissipation)
    or free energy
    """
    def _get_mechanisms(self):
        E_i = cumtrapz(self.Sig_arr, self.Eps_arr, initial=0, axis=0)
        E_T_x_pi_, E_T_y_pi_, E_N_pi_, E_z_, E_alpha_x_, E_alpha_y_, E_omega_T_, E_omega_N_ = E_i.T
        E_plastic_work_T = E_T_x_pi_ + E_T_y_pi_
        E_plastic_work_N = E_N_pi_
        E_plastic_work = E_plastic_work_T + E_plastic_work_N
        E_iso_free_energy = E_z_
        E_kin_free_energy = E_alpha_x_ + E_alpha_y_
        E_plastic_diss_T = E_plastic_work_T - E_iso_free_energy - E_kin_free_energy
        E_plastic_diss_N = E_plastic_work_N
        E_plastic_diss = E_plastic_diss_T + E_plastic_diss_N
        E_damage_diss = E_omega_T_ + E_omega_N_

        return SimpleNamespace(**{'plastic_work_N': E_plastic_work_N,
                                  'plastic_work_T': E_plastic_work_T,
                                  'plastic_work': E_plastic_work,
                                  'iso_free_energy': E_iso_free_energy,
                                  'kin_free_energy': E_kin_free_energy,
                                  'plastic_diss_N': E_plastic_diss_N,
                                  'plastic_diss_T': E_plastic_diss_T,
                                  'plastic_diss': E_plastic_diss,
                                  'damage_diss_N': E_omega_N_,
                                  'damage_diss_T': E_omega_T_,
                                  'damage_diss': E_damage_diss})

    def plot_energy(self, ax, ax_i):

        W_arr = (
                cumtrapz(self.Sig_arr[:, 0], self.s_x_t, initial=0) +
                cumtrapz(self.Sig_arr[:, 1], self.s_y_t, initial=0) +
                cumtrapz(self.Sig_arr[:, 2], self.w_t, initial=0)
        )

        s_x_el_t = (self.s_x_t - self.Eps_arr[:, 0])
        s_y_el_t = (self.s_y_t - self.Eps_arr[:, 1])
        w_el_t = (self.w_t - self.Eps_arr[:, 2])
        U_arr = (
                self.Sig_arr[:, 0] * s_x_el_t / 2.0 +
                self.Sig_arr[:, 1] * s_y_el_t / 2.0 +
                self.Sig_arr[:, 2] * w_el_t / 2.0
        )
        G_arr = W_arr - U_arr
        ax.plot(self.t_arr, W_arr, lw=0.5, color='black', label=r'$W$ - Input work')
        ax.plot(self.t_arr, G_arr, '--', color='black', lw = 0.5, label=r'$W^\mathrm{inel}$ - Inelastic work')
        ax.fill_between(self.t_arr, W_arr, G_arr,
                        color=self.colors['stored_energy'], alpha=0.2)
        ax.set_xlabel('$t$ [-]');
        ax.set_ylabel(r'$E$ [Nmm]')
        ax.legend()

        E_i = cumtrapz(self.Sig_arr, self.Eps_arr, initial=0, axis=0)
        E_T_x_pi_, E_T_y_pi_, E_N_pi_, E_z_, E_alpha_x_, E_alpha_y_, E_omega_T_, E_omega_N_ = E_i.T
        E_plastic_work_T = E_T_x_pi_ + E_T_y_pi_
        E_plastic_work_N = E_N_pi_
        E_plastic_work = E_plastic_work_T + E_plastic_work_N
        E_iso_free_energy = E_z_
        E_kin_free_energy = E_alpha_x_ + E_alpha_y_
        E_plastic_diss_T = E_plastic_work_T - E_iso_free_energy - E_kin_free_energy
        E_plastic_diss_N = E_plastic_work_N
        E_plastic_diss = E_plastic_diss_T + E_plastic_diss_N
        E_damage_diss = E_omega_T_ + E_omega_N_

        E_level = 0
        if self.E_damage_diss:
            ax.plot(self.t_arr, E_damage_diss + E_level, color='black', lw=1)
            ax_i.plot(self.t_arr, E_damage_diss, color='gray', lw=2,
                      label=r'damage diss.: $Y\dot{\omega}$')
            ax.fill_between(self.t_arr, E_omega_N_ + E_level, E_level, color='black',
                            hatch='|');
            E_d_level = E_level + E_omega_N_
            ax.fill_between(self.t_arr, E_omega_T_ + E_d_level, E_d_level, color='gray',
                            alpha=0.3);
        E_level = E_damage_diss
        if self.E_plastic_work:
            ax.plot(self.t_arr, E_plastic_work + E_level, lw=0.5, color='black')
            # ax.fill_between(self.t_arr, E_plastic_work + E_level, E_level, color='red', alpha=0.3)
            label = r'plastic work: $\sigma \dot{\varepsilon}^\pi$'
            ax_i.plot(self.t_arr, E_plastic_work, color='red', lw=2,label=label)
            ax.fill_between(self.t_arr, E_plastic_work_N + E_level, E_level, color='orange',
                            alpha=0.3);
            E_p_level = E_level + E_plastic_work_N
            ax.fill_between(self.t_arr, E_plastic_work_T + E_p_level, E_p_level, color='red',
                            alpha=0.3);
        if self.E_plastic_diss:
            ax.plot(self.t_arr, E_plastic_diss + E_level, lw=.4, color='black')
            label = r'apparent pl. diss.: $\sigma \dot{\varepsilon}^\pi - X\dot{\alpha} - Z\dot{z}$'
            ax_i.plot(self.t_arr, E_plastic_diss, color='red', lw=2, label=label)
            ax.fill_between(self.t_arr, E_plastic_diss_N + E_level, E_level, color='red',
                            hatch='-');
            E_d_level = E_level + E_plastic_diss_N
            ax.fill_between(self.t_arr, E_plastic_diss_T + E_d_level, E_d_level, color='red',
                            alpha=0.3);
            E_level += E_plastic_diss
        if self.E_iso_free_energy:
            ax.plot(self.t_arr, E_iso_free_energy + E_level, '-.', lw=0.5, color='black')
            ax.fill_between(self.t_arr, E_iso_free_energy + E_level, E_level, color='royalblue',
                            hatch='|')
            ax_i.plot(self.t_arr, -E_iso_free_energy, '-.', color='royalblue', lw=2,
                      label=r'iso. diss.: $Z\dot{z}$')
        E_level += E_iso_free_energy
        if self.E_kin_free_energy:
            ax.plot(self.t_arr, E_kin_free_energy + E_level, '-.', color='black', lw=0.5)
            ax.fill_between(self.t_arr, E_kin_free_energy + E_level, E_level, color='royalblue', alpha=0.2);
            ax_i.plot(self.t_arr, -E_kin_free_energy, '-.', color='blue', lw=2,
                      label=r'free energy: $X\dot{\alpha}$')

        ax_i.legend()
        ax_i.set_xlabel('$t$ [-]');
        ax_i.set_ylabel(r'$E$ [Nmm]')

    @staticmethod
    def subplots(fig):
        ax_work, ax_energies = fig.subplots(1, 2)
        ax_iter = ax_work.twinx()
        return ax_work, ax_energies, ax_iter

    def update_plot(self, axes):
        ax_work, ax_energies, ax_iter = axes
        self.plot_energy(ax_work, ax_energies)
        if self.show_iter:
            ax_iter.plot(self.t_arr, self.iter_t)
            ax_iter.set_ylabel(r'$n_\mathrm{iter}$')

    def xsubplots(self, fig):
        ((ax1, ax2), (ax3, ax4)) = fig.subplots(2, 2, figsize=(10, 5), tight_layout=True)
        ax11 = ax1.twinx()
        ax22 = ax2.twinx()
        ax33 = ax3.twinx()
        ax44 = ax4.twinx()
        return ax1, ax11, ax2, ax22, ax3, ax33, ax4, ax44

    def xupdate_plot(self, axes):
        ax1, ax11, ax2, ax22, ax3, ax33, ax4, ax44 = axes
        self.get_response([6, 0, 0])
        # plot_Sig_Eps(s_x_t, Sig_arr, Eps_arr, iter_t, *axes)
        s_x_pi_, s_y_pi_, w_pi_, z_, alpha_x_, alpha_y_, omega_s_, omega_w_ = self.Eps_arr.T
        tau_x_pi_, tau_y_pi_, sig_pi_, Z_, X_x_, X_y_, Y_s_, Y_w_ = self.Sig_arr.T
        ax1.plot(self.w_t, sig_pi_, color='green')
        ax11.plot(self.s_x_t, tau_x_pi_, color='red')
        ax2.plot(self.w_t, omega_w_, color='green')
        ax22.plot(self.w_t, omega_s_, color='red')
Example #6
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
Example #7
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
Example #8
0
class WBCell(bu.Model):
    name = 'Waterbomb cell'

    plot_backend = 'k3d'

    K3D_NODES_LABELS = 'nodes_labels'
    K3D_WIREFRAME = 'wireframe'
    K3D_CELL_MESH = 'cell_mesh'

    show_base_cell_ui = bu.Bool(True)
    show_node_labels = bu.Bool(False, GEO=True)
    show_wireframe = bu.Bool(True, GEO=True)
    opacity = bu.Float(0.6, GEO=True)

    ipw_view = bu.View(
        bu.Item('show_node_labels'),
        bu.Item('show_wireframe'),
    ) if show_base_cell_ui else bu.View()

    X_Ia = tr.Property(depends_on='+GEO')
    '''Array with nodal coordinates I - node, a - dimension
    '''
    @tr.cached_property
    def _get_X_Ia(self):
        return np.array([[0., 0., 0.],
                         [1000., 930.99634691, 365.02849483],
                         [-1000., 930.99634691, 365.02849483],
                         [1000., -930.99634691, 365.02849483],
                         [-1000., -930.99634691, 365.02849483],
                         [764.84218728, 0., 644.21768724],
                         [-764.84218728, 0., 644.21768724]])

    I_Fi = tr.Property
    '''Triangle mapping '''
    @tr.cached_property
    def _get_I_Fi(self):
        return np.array([[0, 1, 2], [0, 3, 4], [0, 1, 5], [0, 5, 3], [0, 2, 6], [0, 6, 4]]).astype(np.int32)

    def setup_plot(self, pb):
        X_Ia = self.X_Ia.astype(np.float32)
        I_Fi = self.I_Fi.astype(np.uint32)
        cell_mesh = k3d.mesh(X_Ia, I_Fi,
                                opacity=self.opacity,
                                color=0x999999,
                                side='double')
        pb.plot_fig += cell_mesh
        pb.objects[self.K3D_CELL_MESH] = cell_mesh

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

    def update_plot(self, pb):
        # If cell interface was embedded in higher class, this method will be called when user changes parameters
        #  However, cell mesh object will not be there because setup_plot was not called
        if self.K3D_CELL_MESH in pb.objects:
            X_Ia = self.X_Ia.astype(np.float32)
            I_Fi = self.I_Fi.astype(np.uint32)
            cell_mesh = pb.objects[self.K3D_CELL_MESH]
            cell_mesh.vertices = X_Ia
            cell_mesh.indices = I_Fi
            cell_mesh.attributes = X_Ia[:, 2]

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

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

    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.K3D_WIREFRAME] = k3d_mesh_wireframe

    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.K3D_NODES_LABELS] = text_list
Example #9
0
class WBCell5Param(WBCell, bu.InjectSymbExpr):
    name = 'waterbomb cell 5p'
    symb_class = WBCellSymb5ParamXL

    plot_backend = 'k3d'

    gamma = bu.Float(1, GEO=True)
    x_ur = bu.Float(1000, GEO=True)
    a = bu.Float(1000, GEO=True)
    b = bu.Float(1000, GEO=True)
    c = bu.Float(1000, GEO=True)
    a_low = bu.Float(2000)
    b_low = bu.Float(2000)
    c_low = bu.Float(2000)
    a_high = bu.Float(2000)
    b_high = bu.Float(2000)
    c_high = bu.Float(2000)
    y_sol1 = bu.Bool(False, GEO=True)
    x_sol1 = bu.Bool(False, GEO=True)

    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=101,
                    continuous_update=continuous_update)),
        bu.Item('x_ur',
                latex=r'x^\urcorner',
                editor=bu.FloatRangeEditor(
                    low=-2000,
                    high=3000,
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('a',
                latex='a',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='a_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('b',
                latex='b',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='b_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('c',
                latex='c',
                editor=bu.FloatRangeEditor(
                    low=1e-6,
                    high_name='c_high',
                    n_steps=101,
                    continuous_update=continuous_update)),
        bu.Item('y_sol1'),
        bu.Item('x_sol1'),
        *WBCell.ipw_view.content,
    )

    n_I = tr.Property

    def _get_n_I(self):
        return len(self.X_Ia)

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

    @tr.cached_property
    def _get_X_Ia(self):
        gamma = self.gamma
        alpha = np.pi / 2 - gamma

        P_1 = self.symb.get_P_1()
        P_2 = self.symb.get_P_2()
        P_3 = self.symb.get_P_3()

        #        print('P', P_1, P_2, P_3, P_1*P_2*P_3)

        x_ur = self.x_ur

        if self.y_sol1:
            A = self.symb.get_A1_()
            B = self.symb.get_B1_()
            C = self.symb.get_C1_()
            if self.x_sol1:
                x_ul = self.symb.get_x_ul11_(A, B, C)
            else:
                x_ul = self.symb.get_x_ul12_(A, B, C)
            y_ul = self.symb.get_y_ul1_(x_ul)
            y_ur = self.symb.get_y_ur1_(x_ul)
        else:
            A = self.symb.get_A2_()
            B = self.symb.get_B2_()
            C = self.symb.get_C2_()
            if self.x_sol1:
                x_ul = self.symb.get_x_ul21_(A, B, C)
            else:
                x_ul = self.symb.get_x_ul22_(A, B, C)
            y_ul = self.symb.get_y_ul2_(x_ul)
            y_ur = self.symb.get_y_ur2_(x_ul)

        z_ur = self.symb.get_z_ur_(x_ul)
        z_ul = self.symb.get_z_ul_(x_ul)

        x_ll = -x_ur
        x_lr = -x_ul
        y_ll = -y_ur
        y_lr = -y_ul
        z_ll = z_ur
        z_lr = z_ul

        V_r_1 = self.symb.get_V_r_1().flatten()
        V_l_1 = self.symb.get_V_l_1().flatten()

        return np.array(
            [
                [0, 0, 0],  # 0 point
                [x_ur, y_ur, z_ur],  #U++
                [x_ul, y_ul, z_ul],  #U-+  ul
                [x_lr, y_lr, z_lr],  #U+-
                [x_ll, y_ll, z_ll],  #U--
                V_r_1,
                V_l_1,
            ],
            dtype=np.float_)

    I_boundary = tr.Array(np.int_, value=[
        [2, 1],
        [6, 5],
        [4, 3],
    ])
    '''Boundary nodes in 2D array to allow for generation of shell boundary nodes'''

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

    @tr.cached_property
    def _get_X_theta_Ia(self):
        D_a = self.symb.get_D_(self.alpha).T
        theta = self.symb.get_theta_sol(self.alpha)
        XD_Ia = D_a + self.X_Ia
        X_center = XD_Ia[1, :]

        rotation_axes = np.array([[1, 0, 0]], dtype=np.float_)
        rotation_angles = np.array([-theta], dtype=np.float_)
        rotation_centers = np.array([X_center], dtype=np.float_)

        x_single = np.array([XD_Ia], dtype='f')
        x_pulled_back = x_single - rotation_centers[:, np.newaxis, :]
        q = axis_angle_to_q(rotation_axes, rotation_angles)
        x_rotated = qv_mult(q, x_pulled_back)
        x_pushed_forward = x_rotated + rotation_centers[:, np.newaxis, :]
        x_translated = x_pushed_forward  #  + self.translations[:, np.newaxis, :]
        return x_translated[0, ...]

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

    @tr.cached_property
    def _get_delta_x(self):
        return self.symb.get_delta_x()

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

    @tr.cached_property
    def _get_delta_phi(self):
        return self.symb.get_delta_phi()

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

    @tr.cached_property
    def _get_R_0(self):
        return self.symb.get_R_0()
Example #10
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]