Exemplo n.º 1
0
class PluginController(_traitsui.Controller):
    update_tree = _traits.Event()
    selected_object = _traits.Any()
    edit_node = _traits.Instance(Model)
    win_handle = _traits.Any()

    def init(self, info):
        self.selected_object = self.model
        self.edit_node = self.model.calculator
        self.win_handle = info.ui.control
        return True
Exemplo n.º 2
0
class PluginController(_traitsui.Controller):
    update_tree = _traits.Event()
    selected_object = _traits.Any()
    edit_node = _traits.Instance(ModelController)
    win_handle = _traits.Any()

    def init(self, info):
        self.selected_object = self.model
        self.win_handle = info.ui.control
        # self.edit_node = self.model.calculator
        return True

    @_traits.on_trait_change('selected_object')
    def _tree_selection_made(self, obj, name, new):
        if isinstance(new, ModelController):
            self.edit_node = new
        elif isinstance(new, WindowLauncher):
            self.edit_node = new.owner_ref
        else:
            self.edit_node = self.dummy_model_controller

    @_traits.on_trait_change('update_tree')
    def _tree_update(self, info):
        print("Tree update")
Exemplo n.º 3
0
class TriXDomainFE(XDomainFE):
    name = 'TriXDomainFE'
    '''
    Finite element discretization with dofs and mappings derived from the FE definition
    '''

    mesh = tr.Instance(FETriangularMesh, ())
    fets = tr.DelegatesTo('mesh')

    tree = ['mesh']

    change = tr.Event(GEO=True)

    plot_backend = 'k3d'

    n_dofs = tr.Property

    def _get_n_dofs(self):
        return len(self.mesh.X_Id) * self.mesh.n_nodal_dofs

    eta_w = tr.Property
    r'''Weight factors for numerical integration.
    '''

    def _get_eta_w(self):
        return self.fets.w_m

    Na_deta = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''

    @tr.cached_property
    def _get_Na_deta(self):
        return np.einsum('imr->mri', self.fets.dN_imr)

    x_0 = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''

    @tr.cached_property
    def _get_x_0(self):
        return self.mesh.X_Id

    x = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''

    @tr.cached_property
    def _get_x(self):
        return self.x_0

    F = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''

    @tr.cached_property
    def _get_F(self):
        return self.mesh.I_Fi

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

    @tr.cached_property
    def _get_T_Fab(self):
        return self.F_L_bases[:, 0, :]

    I_Ei = tr.Property

    def _get_I_Ei(self):
        return self.F_N

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

    @tr.cached_property
    def _get_x_Eia(self):
        X_Eia = self.X_Id[self.I_Ei, :]
        X_E0a = X_Eia[:, 0, :]
        X_Eia -= X_E0a[:, np.newaxis, :]
        # X_Eic = np.einsum('Eac,Eic->Eia', self.T_Fab, X_Eia)
        # return X_Eic[...,:-1]
        return X_Eia[..., :-1]

    def U2u(self, U_Eia):
        u0_Eia = U_Eia[..., :-1]
        return u0_Eia

    def xU2u(self, U_Eia):
        # u1_Eia = np.einsum('Eab,Eib->Eia', self.T_Fab, U_Eia)
        # u2_Eie =  np.einsum('ea,Eia->Eie', DELTA23_ab, u1_Eia)
        u2_Eie = np.einsum('ea,Eia->Eie', DELTA23_ab, U_Eia)
        return u2_Eie

    def f2F(self, f_Eid):
        F0_Eia = np.concatenate([f_Eid, np.zeros_like(f_Eid[..., :1])],
                                axis=-1)
        return F0_Eia

    def xf2F(self, f_Eid):
        F1_Eia = np.einsum('da,Eid->Eia', DELTA23_ab, f_Eid)
        # F2_Eia = np.einsum('Eab,Eia->Eib', self.T_Fab, F1_Eia)
        # return F2_Eia
        return F1_Eia

    def k2K(self, K_Eiejf):
        K0_Eicjf = np.concatenate(
            [K_Eiejf, np.zeros_like(K_Eiejf[:, :, :1, :, :])], axis=2)
        K0_Eicjd = np.concatenate(
            [K0_Eicjf, np.zeros_like(K0_Eicjf[:, :, :, :, :1])], axis=4)
        return K0_Eicjd

    def xk2K(self, K_Eiejf):
        K1_Eiejf = np.einsum('ea,fb,Eiejf->Eiajb', DELTA23_ab, DELTA23_ab,
                             K_Eiejf)  # correct
        # T_Eeafb = np.einsum('Eea,Efb->Eeafb', self.T_Fab, self.T_Fab)
        #K_Eab = np.einsum('Eeafb,ef->Eab', T_Eeafb, k_ef)
        # K2_Eiajb = np.einsum('Eeafb,Eiejf->Eiajb', T_Eeafb, K1_Eiejf)
        #K2_Eicjd = np.einsum('Eca,Ebd,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        #K2_Eicjd = np.einsum('Eac,Edb,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        return K1_Eiejf

    I_CDij = tr.Property

    def _get_I_CDij(self):
        return self.mesh.I_CDij

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

    @tr.cached_property
    def _get_bc_J_F_xyz(self):
        ix2 = int((self.mesh.n_phi_plus) / 2)
        F_I = self.I_CDij[ix2, :, 0, :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        return idx_remap[F_I]

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

    @tr.cached_property
    def _get_bc_J_xyz(self):
        I_M = self.I_CDij[(0, -1), :, (0, -1), :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

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

    @tr.cached_property
    def _get_bc_J_x(self):
        I_M = self.I_CDij[:, (0, -1), :, (0, -1)].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

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

        X_Ma = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_xyz = k3d.points(X_Ma)
        pb.plot_fig += self.k3d_fixed_xyz

        X_Ma = X_Ia[self.bc_J_x]
        self.k3d_fixed_x = k3d.points(X_Ma, color=0x22ffff)
        pb.plot_fig += self.k3d_fixed_x

        X_Ma = X_Ia[self.bc_J_F_xyz]
        self.k3d_load_z = k3d.points(X_Ma, color=0xff22ff)
        pb.plot_fig += self.k3d_load_z

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

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

        self.k3d_fixed_xyz.positions = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_x.positions = X_Ia[self.bc_J_x]
        self.k3d_load_z.positions = X_Ia[self.bc_J_F_xyz]

        mesh = self.k3d_mesh
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

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

    @tr.cached_property
    def _get_B_Eso(self):
        xx_Ei, yy_Ei = np.einsum('...a->a...', self.X_Id[self.I_Ei, :-1])
        # xx_Ei, yy_Ei = np.einsum('...a->a...', self.mesh.X_Id[self.mesh.I_Fi, :-1])

        y23 = yy_Ei[:, 1] - yy_Ei[:, 2]
        y31 = yy_Ei[:, 2] - yy_Ei[:, 0]
        y12 = yy_Ei[:, 0] - yy_Ei[:, 1]
        x32 = xx_Ei[:, 2] - xx_Ei[:, 1]
        x13 = xx_Ei[:, 0] - xx_Ei[:, 2]
        x21 = xx_Ei[:, 1] - xx_Ei[:, 0]
        x23 = -x32
        y32 = -y23
        y13 = -y31

        J_Ear = np.array([[x13, y13], [x23, y23]])
        J_Ear = np.einsum('ar...->...ar', J_Ear)
        det_J_E = np.linalg.det(J_Ear)

        O = np.zeros_like(y23)
        B_soE = np.array([[y23, O, y31, O, y12, O], [O, x32, O, x13, O, x21],
                          [x32, y23, x13, y31, x21, y12]])
        B_Eso = np.einsum('soE,E->Eso', B_soE, 1 / det_J_E)
        return B_Eso, det_J_E

    def map_U_to_field(self, U_o):
        print('')
        print('U_o,', U_o)
        U_Eia = U_o[self.o_Eia]
        # coordinate transform to local
        u_Eia = self.xU2u(U_Eia)
        u_Eo = u_Eia.reshape(-1, 6)
        B_Eso, _ = self.B_Eso
        eps_Eso = np.einsum('Eso,Eo->Es', B_Eso, u_Eo)
        print('eps_Eso,', eps_Eso)
        return eps_Eso

    def map_field_to_F(self, sig_Es):
        # print('map_field_to_F:')
        # print('sig_Es:', sig_Es)
        B_Eso, det_J_E = self.B_Eso
        f_Eo = self.integ_factor * np.einsum('Eso,Es,E->Eo', B_Eso, sig_Es,
                                             det_J_E / 2)
        f_Eic = f_Eo.reshape(-1, 3, 2)
        # coordinate transform to global
        f_Eic = self.xf2F(f_Eic)
        _, n_i, n_c = f_Eic.shape
        f_Ei = f_Eic.reshape(-1, n_i * n_c)
        o_E = self.o_Eia.reshape(-1, n_i * n_c)
        return o_E.flatten(), f_Ei.flatten()

    def map_field_to_K(self, D_Est):
        # print('map_field_to_K:')
        #==========================================================================
        B_Eso, det_J_E = self.B_Eso
        k2_ij = self.integ_factor * np.einsum('Eso,Est,Etp,E->Eop', B_Eso,
                                              D_Est, B_Eso, det_J_E / 2)
        K_Eiejf = k2_ij.reshape(-1, 3, 2, 3, 2)
        K_Eicjd = self.xk2K(K_Eiejf)

        _, n_i, n_c, n_j, n_d = K_Eicjd.shape
        K_Eij = K_Eicjd.reshape(-1, n_i * n_c, n_j * n_d)
        o_Ei = self.o_Eia.reshape(-1, n_i * n_c)
        # print('K_Eij:', K_Eij)
        print('o_Ei:', o_Ei)
        return SysMtxArray(mtx_arr=K_Eij, dof_map_arr=o_Ei)

    # =========================================================================
    # Property operators for initial configuration
    # =========================================================================
    F0_normals = tr.Property(tr.Array, depends_on='X, L, F')
    r'''Normal facet vectors.
    '''

    @tr.cached_property
    def _get_F0_normals(self):
        x_F = self.x_0[self.F]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        Fa_normals = np.einsum('Iai,Iaj,ijk->Iak', r_deta[..., 0],
                               r_deta[..., 1], EPS)
        return np.sum(Fa_normals, axis=1)

    sign_normals = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Orientation of the normal in the initial state.
    This array is used to switch the normal vectors of the faces
    to be oriented in the positive sense of the z-axis.
    '''

    @tr.cached_property
    def _get_sign_normals(self):
        return np.sign(self.F0_normals[:, 2])

    F_N = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Counter-clockwise enumeration.
    '''

    @tr.cached_property
    def _get_F_N(self):
        turn_facets = np.where(self.sign_normals < 0)
        F_N = np.copy(self.F)
        F_N[turn_facets, :] = self.F[turn_facets, ::-1]
        return F_N

    F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''

    @tr.cached_property
    def _get_F_normals(self):
        n = self.Fa_normals
        return np.sum(n, axis=1)

    F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''

    @tr.cached_property
    def _get_F_normals_0(self):
        n = self.Fa_normals_0
        return np.sum(n, axis=1)

    norm_F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''

    @tr.cached_property
    def _get_norm_F_normals(self):
        n = self.F_normals
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]

    norm_F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''

    @tr.cached_property
    def _get_norm_F_normals_0(self):
        n = self.F_normals_0
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]
Exemplo n.º 4
0
class PreviewTableEditor(_tui.TabularEditor):
    update_cells = _tr.Event()
Exemplo n.º 5
0
class TriXDomainMITC(XDomainFE):
    name = 'TriXDomainFE'
    '''
    Finite element discretization with dofs and mappings derived from the FE definition
    '''

    mesh = tr.Instance(FETriangularMesh)

    def _mesh_default(self):
        return FETriangularMesh(fets=FETS2DMITC())

    fets = tr.DelegatesTo('mesh')

    tree = ['mesh']

    change = tr.Event(GEO=True)

    plot_backend = 'k3d'

    n_dofs = tr.Property

    def _get_n_dofs(self):
        return len(self.mesh.X_Id) * self.mesh.n_nodal_dofs

    eta_w = tr.Property
    r'''Weight factors for numerical integration.
    '''

    def _get_eta_w(self):
        return self.fets.w_m

    # T_Fab = tr.Property(depends_on='+GEO')
    # @tr.cached_property
    # def _get_T_Fab(self):
    #     return self.F_L_bases[:, 0, :]

    I_Ei = tr.Property

    def _get_I_Ei(self):
        return self.F_N

    # x_Eia = tr.Property(depends_on='+GEO')
    # @tr.cached_property
    # def _get_x_Eia(self):
    #     X_Eia = self.X_Id[self.I_Ei, :]
    #     X_E0a = X_Eia[:, 0, :]
    #     X_Eia -= X_E0a[:, np.newaxis, :]
    #     X_Eic = np.einsum('Eac,Eic->Eia', self.T_Fab, X_Eia)
    #     return X_Eic[...,:-1]

    # def U2u(self, U_Eia):
    #     u0_Eia = U_Eia[...,:-1]
    #     return u0_Eia
    #
    # def xU2u(self, U_Eia):
    #     u1_Eia = np.einsum('Eab,Eib->Eia', self.T_Fab, U_Eia)
    #     # u2_Eie =  np.einsum('ea,Eia->Eie', DELTA23_ab, u1_Eia)
    #     # return u2_Eie
    #     return u1_Eia
    #
    # def f2F(self, f_Eid):
    #     F0_Eia = np.concatenate( [f_Eid, np.zeros_like(f_Eid[...,:1])], axis=-1)
    #     return F0_Eia
    #
    # def xf2F(self, f_Eid):
    #     # F1_Eia = np.einsum('da,Eid->Eia', DELTA23_ab, f_Eid)
    #     F2_Eia = np.einsum('Eab,Eia->Eib', self.T_Fab, f_Eid)
    #     return F2_Eia
    #
    # def k2K(self, K_Eiejf):
    #     K0_Eicjf = np.concatenate([K_Eiejf, np.zeros_like(K_Eiejf[:,:,:1,:,:])], axis=2)
    #     K0_Eicjd = np.concatenate([K0_Eicjf, np.zeros_like(K0_Eicjf[:,:,:,:,:1])], axis=4)
    #     return K0_Eicjd
    #
    # def xk2K(self, K_Eiejf):
    #     # K1_Eiejf = np.einsum('ea,fb,Eiejf->Eiajb', DELTA23_ab, DELTA23_ab, K_Eiejf) # correct
    #     T_Eeafb = np.einsum('Eea,Efb->Eeafb', self.T_Fab, self.T_Fab)
    #     #K_Eab = np.einsum('Eeafb,ef->Eab', T_Eeafb, k_ef)
    #     K2_Eiajb = np.einsum('Eeafb,Eiejf->Eiajb', T_Eeafb, K_Eiejf)
    #     #K2_Eicjd = np.einsum('Eca,Ebd,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
    #     #K2_Eicjd = np.einsum('Eac,Edb,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
    #     return K2_Eiajb

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

        X_Ma = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_xyz = k3d.points(X_Ma)
        pb.plot_fig += self.k3d_fixed_xyz

        X_Ma = X_Ia[self.bc_J_x]
        self.k3d_fixed_x = k3d.points(X_Ma, color=0x22ffff)
        pb.plot_fig += self.k3d_fixed_x

        X_Ma = X_Ia[self.bc_J_F_xyz]
        self.k3d_load_z = k3d.points(X_Ma, color=0xff22ff)
        pb.plot_fig += self.k3d_load_z

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

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

        self.k3d_fixed_xyz.positions = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_x.positions = X_Ia[self.bc_J_x]
        self.k3d_load_z.positions = X_Ia[self.bc_J_F_xyz]

        mesh = self.k3d_mesh
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

    # def _get_du_dr_Fmra(self, U_o):
    #     dh_imr = self.fets.dh_imr
    #     dht_imr = self.fets.dht_imr
    #     _, v1_Fid, v2_Fid = self.v_vectors
    #     a = self.fets.a
    #
    #     # Calculating du_dr
    #     U_o = np.arange(3 * 5) # TODO U_o comes from function arguments, this is just for testing
    #     nodes_num = self.mesh.X_Id.shape[0]
    #     U_Ie = np.reshape(U_o, (nodes_num, self.fets.n_nodal_dofs))
    #     U_Fie = U_Ie[self.mesh.I_Fi]
    #     disp_U_Fia = U_Fie[..., :3]
    #     rot_U_Fib = U_Fie[..., 3:]
    #     du_dr1_Fmria = np.einsum('Fia, imr ->Fmria', disp_U_Fia, dh_imr)
    #     du_dr1_Fmra = np.sum(du_dr1_Fmria, axis=3)
    #
    #     alpha_idx = 0
    #     beta_idx = 1
    #     alpha_Fi1 = rot_U_Fib[..., alpha_idx, np.newaxis]
    #     beta_Fi1 = rot_U_Fib[..., beta_idx, np.newaxis]
    #     v2_alpha_Fid = v2_Fid * alpha_Fi1
    #     v1_beta_Fid = v1_Fid * beta_Fi1
    #     v1_v2_dif_Fid = v1_beta_Fid - v2_alpha_Fid
    #     du_dr2_Fmria = np.einsum('Fia, imr ->Fmria', 0.5 * a * v1_v2_dif_Fid, dht_imr)
    #     du_dr2_Fmra = np.sum(du_dr2_Fmria, axis=3)
    #     du_dr_Fmra = du_dr1_Fmra + du_dr2_Fmra
    #     return du_dr_Fmra

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

    @tr.cached_property
    def _get_v_vectors(self):
        # See P. 472 FEM by Zienkiewicz (ISBN: 1856176339)
        # Calculating v_n (director vector)
        n_Fd = self.norm_F_normals
        el_nodes_num = 3
        Vn_Fid = np.tile(n_Fd,
                         (1, 1, el_nodes_num)).reshape(n_Fd.shape[0],
                                                       n_Fd.shape[1],
                                                       el_nodes_num)

        # Calculating v1 and v2 (vectors perpendicular to director vector)
        # Finding V1 by getting the minimum component of V3 vector according to Zienkiewicz
        min_Fi1 = np.abs(Vn_Fid).argmin(axis=2)
        min_Fi1 = min_Fi1[..., np.newaxis]
        tmp_Fid = np.zeros_like(Vn_Fid, dtype=np.int_)
        tmp_Fid[..., :] = np.arange(3)
        min_mask_Fid = tmp_Fid == min_Fi1
        e_x_min_Fid = min_mask_Fid * 1
        V1_Fid = np.cross(e_x_min_Fid, Vn_Fid)

        # Or take simply e_2 to calculate V1_Fid as in Bathe lecture
        # e_2_Fid = np.zeros_like(Vn_Fid)
        # e_2_Fid[..., 1] = 1
        # V1_Fid = np.cross(e_2_Fid, Vn_Fid)

        V2_Fid = np.cross(Vn_Fid, V1_Fid)
        v1_Fid = self._normalize(V1_Fid)
        v2_Fid = self._normalize(V2_Fid)

        return Vn_Fid, v1_Fid, v2_Fid

    def _normalize(self, V_Fid):
        mag_n = np.sqrt(np.einsum('...i,...i', V_Fid, V_Fid))
        v_Fid = V_Fid / mag_n[:, np.newaxis]
        return v_Fid

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

    @tr.cached_property
    def _get_dx_dr_Fmrd(self):
        Vn_Fid, _, _ = self.v_vectors

        dh_imr = self.fets.dh_imr
        dht_imr = self.fets.dht_imr

        X_Fid = self.X_Id[self.F_N]

        a = self.fets.a  # thickness (TODO, make it available as input)
        dx_dr1_Fmrid = np.einsum('Fid, imr -> Fmrid', X_Fid, dh_imr)
        dx_dr2_Fmrid = np.einsum('Fid, imr -> Fmrid', 0.5 * a * Vn_Fid,
                                 dht_imr)

        dx_dr1_Fmrd = np.sum(dx_dr1_Fmrid, axis=3)
        dx_dr2_Fmrd = np.sum(dx_dr2_Fmrid, axis=3)
        dx_dr_Fmrd = dx_dr1_Fmrd + dx_dr2_Fmrd
        return dx_dr_Fmrd

    # dN_dr_Emrco = tr.Property(depends_on='+GEO')
    # @tr.cached_property
    # def _get_dN_dr_Emrco(self):
    #     # thickness
    #     a = self.fets.a
    #     _, v1_Fid, v2_Fid = self.v_vectors
    #     dh_imr = self.fets.dh_imr
    #     dht_imr = self.fets.dht_imr
    #
    #     element_dofs = 3 * self.fets.n_nodal_dofs
    #     dN_dr_Emrco = np.zeros((*self.dx_dr_Fmrd.shape, element_dofs))
    #     # Filling first 3x3 elements from N matrices
    #     dofs = 5
    #     # Looping r, s, t
    #     for i in range(3):
    #         # Looping h1, h2, h3
    #         for j in range(3):
    #             dN_dr_Emrco[..., i, :, 0 + j * dofs:3 + j * dofs] = np.identity(3) * dh_imr[
    #                 j, 0, i]  # dh1/dr (which is the same for all gaus points)
    #             # Example: dh_imr[2, 1, 0] is dh3/dr for the second Gauss point
    #     N4_Fmrai = np.einsum('Fia, imr ->Fmrai', -0.5 * a * v2_Fid, dht_imr)
    #     N5_Fmrai = np.einsum('Fia, imr ->Fmrai', +0.5 * a * v1_Fid, dht_imr)
    #     dN_dr_Emrco[..., :, [3, 8, 13]] = N4_Fmrai
    #     dN_dr_Emrco[..., :, [4, 9, 14]] = N5_Fmrai
    #     return dN_dr_Emrco

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

    # @tr.cached_property
    def _get_B_Emiabo(self):
        delta35_co = np.zeros((3, 5), dtype='f')
        delta35_co[(0, 1, 2), (0, 1, 2)] = 1
        delta25_vo = np.zeros((2, 5), dtype='f')
        delta25_vo[(0, 1), (3, 4)] = 1

        _, v1_Fid, v2_Fid = self.v_vectors
        V_Ficv = np.zeros((*v2_Fid.shape, 2), dtype='f')
        # TODO, one of these maybe should be multiplied with -1 and they might need to be flipped, see x formula
        V_Ficv[..., 0] = v1_Fid
        V_Ficv[..., 1] = -v2_Fid

        # Thickness a
        a = self.fets.a
        dN_imr = self.fets.dh_imr
        dNt_imr = self.fets.dht_imr

        delta = np.identity(3)
        Diff1_abcd = 0.5 * (np.einsum('ac,bd->abcd', delta, delta) +
                            np.einsum('ad,bc->abcd', delta, delta))

        J_Fmrd = self.dx_dr_Fmrd

        inv_J_Fmrd = np.linalg.inv(J_Fmrd)
        det_J_Fm = np.linalg.det(J_Fmrd)
        B1_Emiabo = np.einsum('abcd, imr, co, Emrd -> Emiabo', Diff1_abcd,
                              dN_imr, delta35_co, inv_J_Fmrd)
        B2_Emiabo = np.einsum('abcd, imr, Eicv, vo, Emrd -> Emiabo',
                              Diff1_abcd, dNt_imr, 0.5 * a * V_Ficv,
                              delta25_vo, inv_J_Fmrd)
        B_Emiabo = B1_Emiabo + B2_Emiabo

        # B_Emiabo = np.flip(B_Emiabo, 2)

        return B_Emiabo, det_J_Fm

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

    # @tr.cached_property
    def _get_B_Empf(self):
        # TODO: here we're eliminating eps_33 because this is the assumption for shell (for eps IN ELEMENT LEVEL)
        #  therefore, B_Empf must be already converted to element coords system and not using the global one
        B_Emiabo, _ = self.B_Emiabo
        # Mapping ab to p (3x3 -> 5)
        B_Emipo = B_Emiabo[:, :, :, (0, 1, 0, 1, 2), (0, 1, 1, 2, 0), :]
        # What follows is to adjust shear strains: [e_xx, e_yy, e_xy + e_yx, e_yz + e_zy, e_zx + e_xz]
        B_Emipo[:, :, :,
                2:, :] = B_Emipo[:, :, :, 2:, :] + B_Emiabo[:, :, :, (1, 2, 0),
                                                            (0, 1, 2), :]
        # B_Emipo[:, :, :, 2:, :] = 2 * B_Emipo[:, :, :, 2:, :]

        E, m, i, p, o = B_Emipo.shape
        B_Empio = np.einsum('Emipo->Empio', B_Emipo)
        B_Empf = B_Empio.reshape((E, m, p, i * o))
        # p: index with max value 5
        # f: index with max value 15
        return B_Empf

    # Transformation matrix, see P. 475 (Zienkiewicz FEM book)
    def get_theta(self):
        J_Fmrd = self.dx_dr_Fmrd
        dx_dr_Fmd = J_Fmrd[..., 0, :]
        dx_ds_Fmd = J_Fmrd[..., 1, :]
        V3_Fmd = np.cross(dx_dr_Fmd, dx_ds_Fmd)

        # Finding V1 by simply selecting e1
        e_2_Fmd = np.zeros_like(V3_Fmd)
        e_2_Fmd[..., 0] = 1
        V1_Fmd = np.cross(e_2_Fmd, V3_Fmd)

        # Finding V1 by getting the minimum component of V3 vector according to Zienkiewicz
        # min_Fm1 = np.abs(V3_Fmd).argmin(axis=2)
        # min_Fm1 = min_Fm1[..., np.newaxis]
        # tmp_Fmd = np.zeros_like(V3_Fmd, dtype=np.int_)
        # tmp_Fmd[..., :] = np.arange(3)
        # min_mask_Fmd = tmp_Fmd == min_Fm1
        # e_x_min_Fmd = min_mask_Fmd * 1
        # V1_Fmd = np.cross(e_x_min_Fmd, V3_Fmd)

        V2_Fmd = np.cross(V3_Fmd, V1_Fmd)

        v1_Fmd = self._normalize(V1_Fmd)
        v2_Fmd = self._normalize(V2_Fmd)
        v3_Fmd = self._normalize(V3_Fmd)

        theta_Fmdj = np.zeros((*v1_Fmd.shape, 3), dtype='f')
        theta_Fmdj = np.einsum('Fmdj->Fmjd', theta_Fmdj)
        theta_Fmdj[..., 0, :] = v1_Fmd
        theta_Fmdj[..., 1, :] = v2_Fmd
        theta_Fmdj[..., 2, :] = v3_Fmd
        return theta_Fmdj

    def map_U_to_field(self, U_o):
        # print('map_U_to_field')
        # # For testing with one element:
        # U_io = np.array([[1, 0, 0, 0, 0],
        #                  [0, 0, 0.5, 0, 0],
        #                  [0, 1, 0, 0, 0]], dtype=np.float_)
        print('U_o:', U_o)

        # TODO: check if the transformation caused by following line is needed
        # U_Eio = U_o[self.o_Eia]
        U_Eio = U_o.reshape((-1, self.fets.n_nodal_dofs))[self.F_N]

        B_Emiabo, _ = self.B_Emiabo

        eps_Emab = np.einsum('Emiabo, Eio -> Emab', B_Emiabo, U_Eio)
        eps_Emp = eps_Emab[:, :, (0, 1, 0, 1, 2), (0, 1, 1, 2, 0)]

        # What follows is to adjust shear strains: [e_xx, e_yy, e_xy + e_yx, e_yz + e_zy, e_zx + e_xz]
        eps_Emp[:, :, 2:] = eps_Emp[:, :, 2:] + eps_Emab[:, :, (1, 2, 0),
                                                         (0, 1, 2)]
        # eps_Emp[:, :, 2:] = 2 * eps_Emp[:, :, 2:]

        return eps_Emp

    def map_field_to_F(self, sig_Ems):
        # print('map_field_to_F')
        print('sig_Es', sig_Ems)

        _, det_J_Fm = self.B_Emiabo

        f_Emf = self.integ_factor * np.einsum(
            'm, Emsf, Ems, Em -> Emf', self.fets.w_m, self.B_Empf, sig_Ems,
            det_J_Fm)
        f_Ef = np.sum(f_Emf, axis=1)

        # o_Ei = self.o_Eia.reshape(-1, 3 * 5)
        # print('o_Ei:', o_Ei)
        o_Ei = self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)

        return o_Ei.flatten(), f_Ef.flatten()

    def map_field_to_K(self, D_Est):
        # print('map_field_to_K')
        w_m = self.fets.w_m  # Gauss points weights
        _, det_J_Fm = self.B_Emiabo

        B_Empf = self.B_Empf
        k2_Emop = self.integ_factor * np.einsum(
            'm, Empf, Ept, Emtq, Em -> Emfq', w_m, B_Empf, D_Est, B_Empf,
            det_J_Fm)
        k2_Eop = np.sum(k2_Emop, axis=1)

        # o_Ei = self.o_Eia.reshape(-1, 3 * 5)
        # print('o_Ei:', o_Ei)
        o_Ei = self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)

        return SysMtxArray(mtx_arr=k2_Eop, dof_map_arr=o_Ei)

    # O_Eo = tr.Property(tr.Array, depends_on='X, L, F')
    # @tr.cached_property
    # def _get_O_Eo(self):
    #     return self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)

    # =========================================================================
    # Property operators for initial configuration
    # =========================================================================
    F0_normals_Fk = tr.Property(tr.Array, depends_on='X, L, F')
    r'''Normals of the facets in the initial state (before reordering indices).'''

    @tr.cached_property
    def _get_F0_normals_Fk(self):
        n0_Fmk = self._get_normals_Fmk(X_Fid=self.X_Id[self.mesh.I_Fi])
        return np.sum(n0_Fmk, axis=1)

    Fa_normals_Fmk = tr.Property
    '''Normals of the facets after reordering indices.'''

    def _get_Fa_normals_Fmk(self):
        n_Fmk = self._get_normals_Fmk(X_Fid=self.X_Id[self.F_N])
        return n_Fmk

    def _get_normals_Fmk(self, X_Fid):
        dh_mri = np.einsum('imr->mri', self.fets.dh_imr)
        # dh_Fimr = self.dh_Fimr ??
        r_deta_Fmdr = np.einsum('mri, Fid->Fmdr', dh_mri, X_Fid)
        return np.einsum('Fmi,Fmj,ijk->Fmk', r_deta_Fmdr[..., 0],
                         r_deta_Fmdr[..., 1], EPS)

    sign_normals_F = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Orientation of the normal in the initial state.
    This array is used to switch the normal vectors of the faces
    to be oriented in the positive sense of the z-axis.
    '''

    @tr.cached_property
    def _get_sign_normals_F(self):
        # TODO: this takes the sign of the z component of the normal, in 3d this could be not sufficient
        return np.sign(self.F0_normals_Fk[:, 2])

    norm_F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets after reordering F indices.'''

    @tr.cached_property
    def _get_norm_F_normals(self):
        Fa_normals_Fmk = self.Fa_normals_Fmk
        n_Fk = np.sum(Fa_normals_Fmk, axis=1)
        mag_n = np.sqrt(np.einsum('...i,...i', n_Fk, n_Fk))
        return n_Fk / mag_n[:, np.newaxis]

    F_N = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Counter-clockwise enumeration.'''

    @tr.cached_property
    def _get_F_N(self):
        # TODO: following test may be not valid in 3D shells case! other condition is to be taken
        turn_facets_F = np.where(self.sign_normals_F < 0)
        F_Fi = np.copy(self.mesh.I_Fi)
        F_Fi[turn_facets_F, :] = self.mesh.I_Fi[turn_facets_F, ::-1]
        # return self.mesh.I_Fi  # for testing..
        return F_Fi

    # TODO: when you get the first benchmark with one element working you may need to enforce these instead of dh_imr..
    dh_Fimr = tr.Property(tr.Array, depends_on=INPUT)

    # @tr.cached_property
    def _get_dh_Fimr(self):
        dh_imr = self.fets.dh_imr
        facets_num = self.F_N.shape[0]
        dh_Fimr = np.copy(np.broadcast_to(dh_imr, (facets_num, *dh_imr.shape)))

        turn_facets = np.where(self.sign_normals_F < 0)
        dh_Fimr[turn_facets, ...] = dh_Fimr[turn_facets, ::-1, ...]
        return dh_Fimr

    dht_Fimr = tr.Property(tr.Array, depends_on=INPUT)

    # @tr.cached_property
    def _get_dht_Fimr(self):
        dht_imr = self.fets.dht_imr
        facets_num = self.F_N.shape[0]
        dht_Fimr = np.copy(
            np.broadcast_to(dht_imr, (facets_num, *dht_imr.shape)))

        turn_facets = np.where(self.sign_normals_F < 0)
        dht_Fimr[turn_facets, ...] = dht_Fimr[turn_facets, ::-1, ...]
        return dht_Fimr
class TriXDomainMITC(XDomainFE):
    name = 'TriXDomainFE'
    '''
    Finite element discretization with dofs and mappings derived from the FE definition
    '''

    mesh = tr.Instance(FETriangularMesh)
    def _mesh_default(self):
        return FETriangularMesh(fets=FETS2DMITC())

    fets = tr.DelegatesTo('mesh')

    tree = ['mesh']

    change = tr.Event(GEO=True)

    plot_backend = 'k3d'

    n_dofs = tr.Property

    def _get_n_dofs(self):
        return len(self.mesh.X_Id) * self.mesh.n_nodal_dofs

    eta_w = tr.Property
    r'''Weight factors for numerical integration.
    '''

    def _get_eta_w(self):
        return self.fets.w_m

    Na_deta = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''
    @tr.cached_property
    def _get_Na_deta(self):
        return np.einsum('imr->mri', self.fets.dh_imr)

    x_0 = tr.Property()
    @tr.cached_property
    def _get_x_0(self):
        return self.mesh.X_Id

    x = tr.Property()
    @tr.cached_property
    def _get_x(self):
        return self.x_0

    F = tr.Property()
    @tr.cached_property
    def _get_F(self):
        return self.mesh.I_Fi

    T_Fab = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_T_Fab(self):
        return self.F_L_bases[:, 0, :]

    I_Ei = tr.Property
    def _get_I_Ei(self):
        return self.F_N

    x_Eia = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_x_Eia(self):
        X_Eia = self.X_Id[self.I_Ei, :]
        X_E0a = X_Eia[:, 0, :]
        X_Eia -= X_E0a[:, np.newaxis, :]
        X_Eic = np.einsum('Eac,Eic->Eia', self.T_Fab, X_Eia)
        return X_Eic[...]

    def U2u(self, U_Eia):
        u0_Eia = U_Eia[...,:-1]
        return u0_Eia

    def xU2u(self, U_Eio):
        u1_Eia = np.einsum('Eab,Eib->Eia', self.T_Fab, U_Eio[..., :-2])
        # u2_Eie =  np.einsum('ea,Eia->Eie', DELTA23_ab, u1_Eia)
        # return u2_Eie

        U1_Eio = np.copy(U_Eio).astype(np.float_)
        U1_Eio[..., :3, :3] = u1_Eia

        return U1_Eio

    def f2F(self, f_Eid):
        F0_Eia = np.concatenate( [f_Eid, np.zeros_like(f_Eid[...,:1])], axis=-1)
        return F0_Eia

    def xf2F(self, f_Eid):
        # F1_Eia = np.einsum('da,Eid->Eia', DELTA23_ab, f_Eid)

        print('f_Eid=', f_Eid)
        # F2_Eia = np.einsum('Eab,Eia->Eib', self.T_Fab, f_Eid)
        F2_Eia = np.einsum('Eab,Eia->Eib', self.T_Fab, f_Eid[..., :-2])
        f1_Eid = np.copy(f_Eid).astype(np.float_)
        f1_Eid[..., :-2] = F2_Eia

        return f1_Eid

    def k2K(self, K_Eiejf):
        K0_Eicjf = np.concatenate([K_Eiejf, np.zeros_like(K_Eiejf[:,:,:1,:,:])], axis=2)
        K0_Eicjd = np.concatenate([K0_Eicjf, np.zeros_like(K0_Eicjf[:,:,:,:,:1])], axis=4)
        return K0_Eicjd

    def xk2K(self, K_Eiejf):
        # K1_Eiejf = np.einsum('ea,fb,Eiejf->Eiajb', DELTA23_ab, DELTA23_ab, K_Eiejf) # correct
        T_Eeafb = np.einsum('Eea,Efb->Eeafb', self.T_Fab, self.T_Fab)
        #K_Eab = np.einsum('Eeafb,ef->Eab', T_Eeafb, k_ef)



        # K2_Eiajb = np.einsum('Eeafb,Eiejf->Eiajb', T_Eeafb, K_Eiejf)

        K2_Eiajb = np.einsum('Eeafb,Eiejf->Eiajb', T_Eeafb, K_Eiejf[:,:,:-2,:,:-2])
        K1_Eiejf = np.copy(K_Eiejf).astype(np.float_)
        K1_Eiejf[:,:,:-2,:,:-2] = K2_Eiajb

        #K2_Eicjd = np.einsum('Eca,Ebd,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        #K2_Eicjd = np.einsum('Eac,Edb,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        return K1_Eiejf

    I_CDij = tr.Property
    def _get_I_CDij(self):
        return self.mesh.I_CDij

    bc_J_F_xyz= tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_F_xyz(self):
        ix2 = int((self.mesh.n_phi_plus) / 2)
        F_I = self.I_CDij[ix2, :, 0, :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        return idx_remap[F_I]

    bc_J_xyz = tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_xyz(self):
        I_M = self.I_CDij[(0, -1), :, (0, -1), :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

    bc_J_x = tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_x(self):
        I_M = self.I_CDij[:, (0, -1), :, (0, -1)].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

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

        X_Ma = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_xyz = k3d.points(X_Ma)
        pb.plot_fig += self.k3d_fixed_xyz

        X_Ma = X_Ia[self.bc_J_x]
        self.k3d_fixed_x = k3d.points(X_Ma, color=0x22ffff)
        pb.plot_fig += self.k3d_fixed_x

        X_Ma = X_Ia[self.bc_J_F_xyz]
        self.k3d_load_z = k3d.points(X_Ma, color=0xff22ff)
        pb.plot_fig += self.k3d_load_z

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

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

        self.k3d_fixed_xyz.positions = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_x.positions = X_Ia[self.bc_J_x]
        self.k3d_load_z.positions = X_Ia[self.bc_J_F_xyz]

        mesh = self.k3d_mesh
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

    def _get_du_dr_Fmra(self, U_o):
        dh_imr = self.fets.dh_imr
        dht_imr = self.fets.dht_imr
        _, v1_Fid, v2_Fid = self.v_vectors
        a = self.fets.a

        # Calculating du_dr
        U_o = np.arange(3 * 5) # TODO U_o comes from function arguments, this is just for testing
        nodes_num = self.mesh.X_Id.shape[0]
        U_Ie = np.reshape(U_o, (nodes_num, self.fets.n_nodal_dofs))
        U_Fie = U_Ie[self.mesh.I_Fi]
        disp_U_Fia = U_Fie[..., :3]
        rot_U_Fib = U_Fie[..., 3:]
        du_dr1_Fmria = np.einsum('Fia, imr ->Fmria', disp_U_Fia, dh_imr)
        du_dr1_Fmra = np.sum(du_dr1_Fmria, axis=3)

        alpha_idx = 0
        beta_idx = 1
        alpha_Fi1 = rot_U_Fib[..., alpha_idx, np.newaxis]
        beta_Fi1 = rot_U_Fib[..., beta_idx, np.newaxis]
        v2_alpha_Fid = v2_Fid * alpha_Fi1
        v1_beta_Fid = v1_Fid * beta_Fi1
        v1_v2_dif_Fid = v1_beta_Fid - v2_alpha_Fid
        du_dr2_Fmria = np.einsum('Fia, imr ->Fmria', 0.5 * a * v1_v2_dif_Fid, dht_imr)
        du_dr2_Fmra = np.sum(du_dr2_Fmria, axis=3)
        du_dr_Fmra = du_dr1_Fmra + du_dr2_Fmra
        return du_dr_Fmra

    v_vectors = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_v_vectors(self):
        # See P. 472 FEM by Zienkiewicz (ISBN: 1856176339)
        # Calculating v_n (director vector)
        n_Fd = self.norm_F_normals
        el_nodes_num = 3
        Vn_Fid = np.tile(n_Fd, (1, 1, el_nodes_num)).reshape(n_Fd.shape[0], n_Fd.shape[1], el_nodes_num)

        # Calculating v1 and v2 (vectors perpendicular to director vector)
        min_Fi1 = np.abs(Vn_Fid).argmin(axis=2)
        min_Fi1 = min_Fi1[..., np.newaxis]
        tmp_Fid = np.zeros_like(Vn_Fid, dtype=np.int_)
        tmp_Fid[..., :] = np.arange(3)
        min_mask_Fid = tmp_Fid == min_Fi1
        e_x_min_Fid = min_mask_Fid * 1
        V1_Fid = np.cross(e_x_min_Fid, Vn_Fid)
        V2_Fid = np.cross(Vn_Fid, V1_Fid)
        v1_Fid = self._normalize(V1_Fid)
        v2_Fid = self._normalize(V2_Fid)

        return Vn_Fid, v1_Fid, v2_Fid

    def _normalize(self, V_Fid):
        mag_n = np.sqrt(np.einsum('...i,...i', V_Fid, V_Fid))
        v_Fid = V_Fid / mag_n[:, np.newaxis]
        return v_Fid

    dx_dr_Fmrd = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_dx_dr_Fmrd(self):
        Vn_Fid, _, _ = self.v_vectors

        dh_imr = self.fets.dh_imr
        dht_imr = self.fets.dht_imr

        # X_Fid = self.X_Id[self.mesh.I_Fi]
        # X_Fid = self.X_Id[self.F_N]

        # get coords transformed to local
        X_Fid = self.x_Eia

        a = self.fets.a  # thickness (TODO, make it available as input)
        dx_dr1_Fmrid = np.einsum('Fid, imr -> Fmrid', X_Fid, dh_imr)
        dx_dr2_Fmrid = np.einsum('Fid, imr -> Fmrid', 0.5 * a * Vn_Fid, dht_imr)

        dx_dr1_Fmrd = np.sum(dx_dr1_Fmrid, axis=3)
        dx_dr2_Fmrd = np.sum(dx_dr2_Fmrid, axis=3)
        dx_dr_Fmrd = dx_dr1_Fmrd + dx_dr2_Fmrd
        return dx_dr_Fmrd

    B_Emiabo = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_B_Emiabo(self):
        delta35_co = np.zeros((3, 5), dtype='f')
        delta35_co[(0, 1, 2), (0, 1, 2)] = 1
        delta25_vo = np.zeros((2, 5), dtype='f')
        delta25_vo[(0, 1), (3, 4)] = 1

        _, v1_Fid, v2_Fid = self.v_vectors
        V_Ficv = np.zeros((*v2_Fid.shape, 2), dtype='f')
        V_Ficv[..., 0] = v1_Fid
        V_Ficv[..., 1] = v2_Fid

        # Thickness a
        a = self.fets.a
        dN_imr = self.fets.dh_imr
        dNt_imr = self.fets.dht_imr

        delta = np.identity(3)
        Diff1_abcd = 0.5 * (
                np.einsum('ac,bd->abcd', delta, delta) +
                np.einsum('ad,bc->abcd', delta, delta)
        )

        J_Fmrd = self.dx_dr_Fmrd

        inv_J_Fmrd = np.linalg.inv(J_Fmrd)
        det_J_Fm = np.linalg.det(J_Fmrd)
        B1_Emiabo = np.einsum('abcd, imr, co, Emrd -> Emiabo', Diff1_abcd, dN_imr, delta35_co, inv_J_Fmrd)
        B2_Emiabo = np.einsum('abcd, imr, Ficv, vo, Emrd -> Emiabo', Diff1_abcd, dNt_imr, 0.5 * a * V_Ficv, delta25_vo,
                              inv_J_Fmrd)
        B_Emiabo = B1_Emiabo + B2_Emiabo

        # B_Emiabo = np.flip(B_Emiabo, 2)

        return B_Emiabo, det_J_Fm

    def _get_B_Empf(self):
        B_Emiabo, _ = self.B_Emiabo
        # Mapping ab to p (3x3 -> 5)
        B_Emipo = B_Emiabo[:, :, :, (0, 1, 0, 1, 2), (0, 1, 1, 2, 0), :]

        E, m, i, p, o = B_Emipo.shape
        B_Empio = np.einsum('Emipo->Empio', B_Emipo)
        B_Empf = B_Empio.reshape((E, m, p, 3 * o))
        # p: index with max value 5
        # f: index with max value 15
        return B_Empf

    def map_U_to_field(self, U_o):
        # print('map_U_to_field')
        # # For testing with one element:
        # U_io = np.array([[1, 0, 0, 0, 0],
        #                  [0, 0, 0.5, 0, 0],
        #                  [0, 1, 0, 0, 0]], dtype=np.float_)
        print('U_o:', U_o)

        # TODO: check if the transformation caused by following line is needed
        U_Eio = U_o[self.o_Eia]
        # print('U_Eio,', U_Eio)
        # U_Eio = U_o.reshape((-1, self.fets.n_nodal_dofs))[self.mesh.I_Fi]
        # U_Eio = U_o.reshape((-1, self.fets.n_nodal_dofs))[self.F_N]

        # transform to local
        U_Eio = self.xU2u(U_Eio)

        print('U_Eio,', U_Eio)

        B_Emiabo, _ = self.B_Emiabo

        eps_Emab = np.einsum('Emiabo, Eio -> Emab', B_Emiabo, U_Eio)

        eps_Emp = eps_Emab[:, :, (0, 1, 0, 1, 0), (0, 1, 1, 2, 2)]
        return eps_Emp

    def map_field_to_F(self, sig_Ems):
        # print('map_field_to_F')
        print('sig_Es', sig_Ems)

        _, det_J_Fm = self.B_Emiabo

        B_Empf = self._get_B_Empf()
        f_Emf = self.integ_factor * np.einsum('m, Emsf, Ems, Em -> Emf', self.fets.w_m, B_Empf, sig_Ems, det_J_Fm)
        f_Ef = np.sum(f_Emf, axis=1)

        # o_Ei = self.o_Eia.reshape(-1, 3 * 5)
        # print('o_Ei:', o_Ei)
        # # o_Ei = self.o_Ia[self.mesh.I_Fi].reshape(-1, 3 * self.fets.n_nodal_dofs)
        # # o_Ei = self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)
        # return o_Ei.flatten(), f_Ef.flatten()


        f_Eic = f_Ef.reshape(-1, 3, 5)
        # coordinate transform to global
        f_Eic = self.xf2F(f_Eic)
        _, n_i, n_c = f_Eic.shape
        f_Ei = f_Eic.reshape(-1, n_i * n_c)
        o_E = self.o_Eia.reshape(-1, n_i * n_c)
        return o_E.flatten(), f_Ei.flatten()


    def map_field_to_K(self, D_Est):
        # print('map_field_to_K')
        w_m = self.fets.w_m  # Gauss points weights
        B_Emiabo, det_J_Fm = self.B_Emiabo

        B_Empf = self._get_B_Empf()
        k2_Emop = self.integ_factor * np.einsum('m, Empf, Ept, Emtq, Em -> Emfq', w_m, B_Empf, D_Est, B_Empf,
                                                   det_J_Fm)
        k2_Eop = np.sum(k2_Emop, axis=1)

        # o_Ei = self.o_Eia.reshape(-1, 3 * 5)
        # print('o_Ei:', o_Ei)
        # # # o_Ei = self.o_Ia[self.mesh.I_Fi].reshape(-1, 3 * self.fets.n_nodal_dofs)
        # # o_Ei = self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)
        #
        # return SysMtxArray(mtx_arr=k2_Eop, dof_map_arr=o_Ei)


        K_Eiejf = k2_Eop.reshape(-1, 3, 5, 3, 5)
        K_Eicjd = self.xk2K(K_Eiejf)
        _, n_i, n_c, n_j, n_d = K_Eicjd.shape
        K_Eij = K_Eicjd.reshape(-1, n_i * n_c, n_j * n_d)
        o_Ei = self.o_Eia.reshape(-1, n_i * n_c)
        return SysMtxArray(mtx_arr=K_Eij, dof_map_arr=o_Ei)


    # O_Eo = tr.Property(tr.Array, depends_on='X, L, F')
    # @tr.cached_property
    # def _get_O_Eo(self):
    #     return self.o_Ia[self.F_N].reshape(-1, 3 * self.fets.n_nodal_dofs)

    # =========================================================================
    # Property operators for initial configuration
    # =========================================================================
    F0_normals = tr.Property(tr.Array, depends_on='X, L, F')
    r'''Normal facet vectors.
    '''
    @tr.cached_property
    def _get_F0_normals(self):
        x_F = self.x_0[self.F]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        Fa_normals = np.einsum('Iai,Iaj,ijk->Iak',
                               r_deta[..., 0], r_deta[..., 1], EPS)
        return np.sum(Fa_normals, axis=1)

    sign_normals = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Orientation of the normal in the initial state.
    This array is used to switch the normal vectors of the faces
    to be oriented in the positive sense of the z-axis.
    '''
    @tr.cached_property
    def _get_sign_normals(self):
        return np.sign(self.F0_normals[:, 2])

    F_N = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Counter-clockwise enumeration.
    '''
    @tr.cached_property
    def _get_F_N(self):
        # TODO: following test may be not valid in 3D shells case! other condition is to be taken
        turn_facets = np.where(self.sign_normals < 0)
        F_N = np.copy(self.F)
        F_N[turn_facets, :] = self.F[turn_facets, ::-1]
        return F_N

    F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals(self):
        n = self.Fa_normals
        return np.sum(n, axis=1)

    F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals_0(self):
        n = self.Fa_normals_0
        return np.sum(n, axis=1)

    norm_F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''
    @tr.cached_property
    def _get_norm_F_normals(self):
        n = self.F_normals
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]

    norm_F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''
    @tr.cached_property
    def _get_norm_F_normals_0(self):
        n = self.F_normals_0
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]

    F_normals_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals_du(self):
        n_du = self.Fa_normals_du
        return np.sum(n_du, axis=1)

    F_area = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the surface area of the facets.
    '''
    @tr.cached_property
    def _get_F_area(self):
        a = self.Fa_area
        A = np.einsum('a,Ia->I', self.eta_w, a)
        return A

    # =========================================================================
    # Potential energy
    # =========================================================================
    F_V = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the total potential energy of gravity for each facet
    '''
    @tr.cached_property
    def _get_F_V(self):
        eta_w = self.eta_w
        a = self.Fa_area
        ra = self.Fa_r
        F_V = np.einsum('a,Ia,Ia->I', eta_w, ra[..., 2], a)
        return F_V

    F_V_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivative of total potential energy of gravity for each facet
    with respect to each node and displacement component [FIi]
    '''
    @tr.cached_property
    def _get_F_V_du(self):
        r = self.Fa_r
        a = self.Fa_area
        a_dx = self.Fa_area_du
        r3_a_dx = np.einsum('Ia,IaJj->IaJj', r[..., 2], a_dx)
        N_eta_ip = self.Na
        r3_dx = np.einsum('aK,KJ,j->aJj', N_eta_ip, DELTA, DELTA[2, :])
        a_r3_dx = np.einsum('Ia,aJj->IaJj', a, r3_dx)
        F_V_du = np.einsum('a,IaJj->IJj', self.eta_w, (a_r3_dx + r3_a_dx))
        return F_V_du

    # =========================================================================
    # Line vectors
    # =========================================================================

    F_L_vectors_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.

    .. math::
        v_{pld} \;\mathrm{where} \; p\in\mathcal{F}, l\in (0,1,2), d\in (0,1,2)

    with the indices :math:`p,l,d` representing the facet, line vector around
    the facet and and vector component, respectively.
    '''
    @tr.cached_property
    def _get_F_L_vectors_0(self):
        F_N = self.F_N  # F_N is cycled counter clockwise
        return self.x_0[F_N[:, (1, 2, 0)]] - self.x_0[F_N[:, (0, 1, 2)]]

    F_L_vectors = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.


    .. math::
        v_{pld} \;\mathrm{where} \; p\in\mathcal{F}, l\in (0,1,2), d\in (0,1,2)

    with the indices :math:`p,l,d` representing the facet, line vector around
    the facet and and vector component, respectively.
    '''
    @tr.cached_property
    def _get_F_L_vectors(self):
        F_N = self.F_N  # F_N is cycled counter clockwise
        return self.x[F_N[:, (1, 2, 0)]] - self.x[F_N[:, (0, 1, 2)]]

    F_L_vectors_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivatives of the line vectors around the facets.

    .. math::
        \pard{v_{pld}}{x_{Ie}} \; \mathrm{where} \;
        p \in \mathcal{F},  \in (0,1,2), d\in (0,1,2), I\in \mathcal{N},
        e \in (0,1,3)

    with the indices :math:`p,l,d,I,e` representing the facet,
    line vector around the facet and and vector component,
    node vector and and its component index,
    respectively.

    This array works essentially as an index function delivering -1
    for the components of the first node in each dimension and +1
    for the components of the second node
    in each dimension.

    For a facet :math:`p` with lines :math:`l` and component :math:`d` return
    the derivatives with respect to the displacement of the node :math:`I`
    in the direction :math:`e`.

    .. math::
        \bm{a}_1 = \bm{x}_2 - \bm{x}_1 \\
        \bm{a}_2 = \bm{x}_3 - \bm{x}_2 \\
        \bm{a}_3 = \bm{x}_1 - \bm{x}_3

    The corresponding derivatives are then

    .. math::
        \pard{\bm{a}_1}{\bm{u}_1} = -1, \;\;\;
        \pard{\bm{a}_1}{\bm{u}_2} = 1 \\
        \pard{\bm{a}_2}{\bm{u}_2} = -1, \;\;\;
        \pard{\bm{a}_2}{\bm{u}_3} = 1 \\
        \pard{\bm{a}_3}{\bm{u}_3} = -1, \;\;\;
        \pard{\bm{a}_3}{\bm{u}_1} = 1 \\

    '''

    def _get_F_L_vectors_du(self):
        return self.L_vectors_du[self.F_L]

    F_L_vectors_dul = tr.Property(tr.Array, depends_on=INPUT)

    def _get_F_L_vectors_dul(self):
        return self.L_vectors_dul[self.F_L]

    norm_F_L_vectors = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.
    '''
    @tr.cached_property
    def _get_norm_F_L_vectors(self):
        v = self.F_L_vectors
        mag_v = np.sqrt(np.einsum('...i,...i', v, v))
        return v / mag_v[..., np.newaxis]

    norm_F_L_vectors_du = tr.Property(tr.Array, depends_on=INPUT)
    '''Get the derivatives of cycled line vectors around the facet
    '''
    @tr.cached_property
    def _get_norm_F_L_vectors_du(self):
        v = self.F_L_vectors
        v_du = self.F_L_vectors_du  # @UnusedVariable
        mag_v = np.einsum('...i,...i', v, v)  # @UnusedVariable
        # @todo: finish the chain rule
        raise NotImplemented

    # =========================================================================
    # Orthonormal basis of each facet.
    # =========================================================================
    F_L_bases = tr.Property(tr.Array, depends_on=INPUT)
    r'''Line bases around a facet.
    '''
    @tr.cached_property
    def _get_F_L_bases(self):
        l = self.norm_F_L_vectors
        n = self.norm_F_normals
        lxn = np.einsum('...li,...j,...kij->...lk', l, n, EPS)
        n_ = n[:, np.newaxis, :] * np.ones((1, 3, 1), dtype='float_')
        T = np.concatenate([l[:, :, np.newaxis, :],
                            -lxn[:, :, np.newaxis, :],
                            n_[:, :, np.newaxis, :]], axis=2)
        return T

    F_L_bases_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Derivatives of the line bases around a facet.
    '''
    @tr.cached_property
    def _get_F_L_bases_du(self):
        '''Derivatives of line bases'''
        raise NotImplemented

    # =========================================================================
    # Sector angles
    # =========================================================================
    F_theta = tr.Property(tr.Array, depends_on=INPUT)
    '''Get the sector angles :math:`\theta`  within a facet.
    '''
    @tr.cached_property
    def _get_F_theta(self):
        v = self.F_L_vectors
        a = -v[:, (2, 0, 1), :]
        b = v[:, (0, 1, 2), :]
        return get_theta(a, b)

    F_theta_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivatives of sector angles :math:`\theta` within a facet.
    '''
    @tr.cached_property
    def _get_F_theta_du(self):
        v = self.F_L_vectors
        v_du = self.F_L_vectors_du

        a = -v[:, (2, 0, 1), :]
        b = v[:, (0, 1, 2), :]
        a_du = -v_du[:, (2, 0, 1), ...]
        b_du = v_du[:, (0, 1, 2), ...]

        return get_theta_du(a, a_du, b, b_du)

    Fa_normals_du = tr.Property
    '''Get the derivatives of the normals with respect
    to the node displacements.
    '''

    def _get_Fa_normals_du(self):
        x_F = self.x[self.F_N]
        N_deta_ip = self.Na_deta
        NN_delta_eps_x1 = np.einsum('aK,aL,KJ,dli,ILl->IaiJd',
                                    N_deta_ip[:, 0, :], N_deta_ip[:, 1, :],
                                    DELTA, EPS, x_F)
        NN_delta_eps_x2 = np.einsum('aK,aL,LJ,kdi,IKk->IaiJd',
                                    N_deta_ip[:, 0, :], N_deta_ip[:, 1, :],
                                    DELTA, EPS, x_F)
        n_du = NN_delta_eps_x1 + NN_delta_eps_x2
        return n_du

    Fa_area_du = tr.Property
    '''Get the derivatives of the facet area with respect
    to node displacements.
    '''

    def _get_Fa_area_du(self):
        a = self.Fa_area
        n = self.Fa_normals
        n_du = self.Fa_normals_du
        a_du = np.einsum('Ia,Iak,IakJd->IaJd', 1 / a, n, n_du)
        return a_du

    Fa_normals = tr.Property
    '''Get normals of the facets.
    '''

    def _get_Fa_normals(self):

        # TODO, check this change
        # x_F = self.x[self.mesh.I_Fi]
        x_F = self.x[self.F_N]

        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        return np.einsum('Iai,Iaj,ijk->Iak',
                         r_deta[..., 0], r_deta[..., 1], EPS)

    Fa_normals_0 = tr.Property
    '''Get normals of the facets.
    '''

    def _get_Fa_normals_0(self):
        x_F = self.x_0[self.F_N]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        return np.einsum('Iai,Iaj,ijk->Iak',
                         r_deta[..., 0], r_deta[..., 1], EPS)

    Fa_area = tr.Property
    '''Get the surface area of the facets.
    '''

    def _get_Fa_area(self):
        n = self.Fa_normals
        a = np.sqrt(np.einsum('Iai,Iai->Ia', n, n))
        return a

    Fa_r = tr.Property
    '''Get the reference vector to integrations point in each facet.
    '''

    def _get_Fa_r(self):
        x_F = self.x[self.F_N]
        N_eta_ip = self.Na
        r = np.einsum('aK,IKi->Iai', N_eta_ip, x_F)
        return r
Exemplo n.º 7
0
class EEGSensor(t.HasTraits):
    preferences = t.Any()
    connected = t.Bool(False)
    serial_port = t.Instance(serial.Serial)

    com_port = t.Int()  # If None, we just search for it.

    def _com_port_default(self):
        """ Get default com port from preferences. """
        if self.preferences:
            return int(
                self.preferences.get('sensor.com_port', COM_SEARCH_START))
        return t.undefined

    def _com_port_changed(self, val):
        """ Save any COM port changes to preferences. """
        if self.preferences:
            return self.preferences.set('sensor.com_port', val)

    channels = t.Int(CHANNELS)
    timeseries = t.Array(dtype='float', value=np.zeros([1, CHANNELS + 1]))
    history = t.List()
    data_changed = t.Event()

    # Below, these separate properties and buttons for each channel are a bit
    #  verbose, but it seems to be the most clear way to implement this.
    channel_1_enabled = t.Bool(False)
    channel_2_enabled = t.Bool(False)
    channel_3_enabled = t.Bool(False)
    channel_4_enabled = t.Bool(False)
    channel_5_enabled = t.Bool(False)
    channel_6_enabled = t.Bool(False)
    channel_7_enabled = t.Bool(False)
    channel_8_enabled = t.Bool(False)

    channel_1_on = t.Button()
    channel_2_on = t.Button()
    channel_3_on = t.Button()
    channel_4_on = t.Button()
    channel_5_on = t.Button()
    channel_6_on = t.Button()
    channel_7_on = t.Button()
    channel_8_on = t.Button()

    channel_1_off = t.Button()
    channel_2_off = t.Button()
    channel_3_off = t.Button()
    channel_4_off = t.Button()
    channel_5_off = t.Button()
    channel_6_off = t.Button()
    channel_7_off = t.Button()
    channel_8_off = t.Button()

    # Properties
    history_length = t.Property(t.Int, depends_on="data_changed")

    def _get_history_length(self):
        return len(self.history)

    timeseries_length = t.Property(t.Int, depends_on="data_changed")

    def _get_timeseries_length(self):
        return self.timeseries.shape[0]

    @t.on_trait_change(','.join(['channel_%d_on' % i for i in range(1, 9)] +
                                ['channel_%d_off' % i for i in range(1, 9)]))
    def toggle_channels(self, name, new):
        if not self.connected:
            return
        deactivate_codes = ['1', '2', '3', '4', '5', '6', '7', '8']
        activate_codes = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i']
        if name.endswith('_off'):
            cmd = deactivate_codes[int(name[-len('_off') - 1]) - 1]
        elif name.endswith('_on'):
            cmd = activate_codes[int(name[-len('_on') - 1]) - 1]
        else:
            raise ValueError()
        self.serial_port.write(cmd + '\n')
        # self.serial_port.write('b\n')
        time.sleep(.100)
        # self.serial_port.flushInput()
        # time.sleep(.50)

    def connect(self):
        if self.connected:
            self.disconnect()

        assert self.serial_port is None

        # If no com port is selected, search for it... this search code could
        #  be drastically sped up by analyzing a listing of actual COM ports.
        try:
            if self.com_port is None:
                for i in range(COM_SEARCH_START, COM_SEARCH_END + 1):
                    try:
                        port = 'COM%d' % i
                        self.serial_port = serial.Serial(
                            port, BAUD, timeout=STARTUP_TIMEOUT)
                        if self.serial_port.read(1) == '':
                            self.serial_port.close()
                            self.serial_port = None
                            continue
                        else:
                            # Assume it's the right one...
                            self.serial_port.write('s\n')  # Reset.
                            self.serial_port.write(
                                'b\n')  # Start sending binary.
                            self.serial_port.read(
                                5)  # Make sure we can read something
                            # Okay, we're convinced.
                            self.com_port = i
                            self.connected = True
                            self.serial_port.timeout = RUN_TIMEOUT
                            break
                    except serial.SerialException, e:
                        logging.warn("Couldn't open %s: %s" % (port, str(e)))
                else:
                    logging.warn("Couldn't find a functioning serial port." %
                                 (port, str(e)))

            else:  # A specific COM port is requested.
Exemplo n.º 8
0
class TriXDomainFE(XDomainFE):
    name = 'TriXDomainFE'
    '''
    Finite element discretization with dofs and mappings derived from the FE definition
    '''

    mesh = tr.Instance(FETriangularMesh, ())
    fets = tr.DelegatesTo('mesh')

    tree = ['mesh']

    change = tr.Event(GEO=True)

    plot_backend = 'k3d'

    n_dofs = tr.Property

    def _get_n_dofs(self):
        return len(self.mesh.X_Id) * self.mesh.n_nodal_dofs

    eta_w = tr.Property
    r'''Weight factors for numerical integration.
    '''

    def _get_eta_w(self):
        return self.fets.w_m

    Na_deta = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''
    @tr.cached_property
    def _get_Na_deta(self):
        return np.einsum('imr->mri', self.fets.dN_imr)

    x_0 = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''
    @tr.cached_property
    def _get_x_0(self):
        return self.mesh.X_Id

    x = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''
    @tr.cached_property
    def _get_x(self):
        return self.x_0

    F = tr.Property()
    r'''Derivatives of the shape functions in the integration points.
    '''
    @tr.cached_property
    def _get_F(self):
        return self.mesh.I_Fi

    T_Fab = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_T_Fab(self):
        return self.F_L_bases[:, 0, :]

    I_Ei = tr.Property
    def _get_I_Ei(self):
        return self.F_N

    x_Eia = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_x_Eia(self):
        X_Eia = self.X_Id[self.I_Ei, :]
        X_E0a = X_Eia[:, 0, :]
        X_Eia -= X_E0a[:, np.newaxis, :]
        X_Eic = np.einsum('Eac,Eic->Eia', self.T_Fab, X_Eia)
        return X_Eic[...,:-1]
        # return X_Eia[..., :-1]

    def U2u(self, U_Eia):
        u0_Eia = U_Eia[...,:-1]
        return u0_Eia

    def xU2u(self, U_Eia):
        u1_Eia = np.einsum('Eab,Eib->Eia', self.T_Fab, U_Eia)
        u2_Eie =  np.einsum('ea,Eia->Eie', DELTA23_ab, u1_Eia)
        # u2_Eie = np.einsum('ea,Eia->Eie', DELTA23_ab, U_Eia)
        return u2_Eie

    def f2F(self, f_Eid):
        F0_Eia = np.concatenate( [f_Eid, np.zeros_like(f_Eid[...,:1])], axis=-1)
        return F0_Eia

    def xf2F(self, f_Eid):
        F1_Eia = np.einsum('da,Eid->Eia', DELTA23_ab, f_Eid)
        F2_Eia = np.einsum('Eab,Eia->Eib', self.T_Fab, F1_Eia)
        return F2_Eia
        # return F1_Eia

    def k2K(self, K_Eiejf):
        K0_Eicjf = np.concatenate( [K_Eiejf, np.zeros_like(K_Eiejf[:,:,:1,:,:])], axis=2)
        K0_Eicjd = np.concatenate( [K0_Eicjf, np.zeros_like(K0_Eicjf[:,:,:,:,:1])], axis=4)
        return K0_Eicjd

    def xk2K(self, K_Eiejf):
        K1_Eiejf = np.einsum('ea,fb,Eiejf->Eiajb', DELTA23_ab, DELTA23_ab, K_Eiejf) # correct
        T_Eeafb = np.einsum('Eea,Efb->Eeafb', self.T_Fab, self.T_Fab)
        #K_Eab = np.einsum('Eeafb,ef->Eab', T_Eeafb, k_ef)
        K2_Eiajb = np.einsum('Eeafb,Eiejf->Eiajb', T_Eeafb, K1_Eiejf)
        #K2_Eicjd = np.einsum('Eca,Ebd,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        #K2_Eicjd = np.einsum('Eac,Edb,Eiajb->Eicjd', self.T_Fab, self.T_Fab, K1_Eicjd)
        return K2_Eiajb
        # return K1_Eiejf

    I_CDij = tr.Property
    def _get_I_CDij(self):
        return self.mesh.I_CDij

    bc_J_F_xyz= tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_F_xyz(self):
        ix2 = int((self.mesh.n_phi_plus) / 2)
        F_I = self.I_CDij[ix2, :, 0, :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        return idx_remap[F_I]

    bc_J_xyz = tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_xyz(self):
        I_M = self.I_CDij[(0, -1), :, (0, -1), :].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

    bc_J_x = tr.Property(depends_on='state_changed')
    @tr.cached_property
    def _get_bc_J_x(self):
        I_M = self.I_CDij[:, (0, -1), :, (0, -1)].flatten()
        _, idx_remap = self.mesh.unique_node_map
        J_M = idx_remap[I_M]
        return J_M

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

        X_Ma = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_xyz = k3d.points(X_Ma)
        pb.plot_fig += self.k3d_fixed_xyz

        X_Ma = X_Ia[self.bc_J_x]
        self.k3d_fixed_x = k3d.points(X_Ma, color=0x22ffff)
        pb.plot_fig += self.k3d_fixed_x

        X_Ma = X_Ia[self.bc_J_F_xyz]
        self.k3d_load_z = k3d.points(X_Ma, color=0xff22ff)
        pb.plot_fig += self.k3d_load_z

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

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

        self.k3d_fixed_xyz.positions = X_Ia[self.bc_J_xyz]
        self.k3d_fixed_x.positions = X_Ia[self.bc_J_x]
        self.k3d_load_z.positions = X_Ia[self.bc_J_F_xyz]

        mesh = self.k3d_mesh
        mesh.vertices = X_Ia
        mesh.indices = I_Fi

    B_Eso = tr.Property(depends_on='+GEO')
    @tr.cached_property
    def _get_B_Eso(self):
        xx_Ei, yy_Ei = np.einsum('...a->a...', self.x_Eia)
        # xx_Ei, yy_Ei = np.einsum('...a->a...', self.X_Id[self.F_N, :-1])
        # xx_Ei, yy_Ei = np.einsum('...a->a...', self.mesh.X_Id[self.mesh.I_Fi, :-1])

        y23 = yy_Ei[:, 1] - yy_Ei[:, 2]
        y31 = yy_Ei[:, 2] - yy_Ei[:, 0]
        y12 = yy_Ei[:, 0] - yy_Ei[:, 1]
        x32 = xx_Ei[:, 2] - xx_Ei[:, 1]
        x13 = xx_Ei[:, 0] - xx_Ei[:, 2]
        x21 = xx_Ei[:, 1] - xx_Ei[:, 0]
        x23 = -x32
        y32 = -y23
        y13 = -y31

        J_Ear = np.array([[x13, y13], [x23, y23]])
        J_Ear = np.einsum('ar...->...ar', J_Ear)
        det_J_E = np.linalg.det(J_Ear)

        O = np.zeros_like(y23)
        B_soE = np.array(
            [
                [y23, O, y31, O, y12, O],
                [O, x32, O, x13, O, x21],
                [x32, y23, x13, y31, x21, y12]
            ]
        )
        B_Eso = np.einsum('soE,E->Eso', B_soE, 1 / det_J_E)
        return B_Eso, det_J_E

    def map_U_to_field(self, U_o):
        print('')
        print('U_o,', U_o)
        U_Eia = U_o[self.o_Eia]
        # coordinate transform to local
        u_Eia = self.xU2u(U_Eia)
        u_Eo = u_Eia.reshape(-1, 6)
        B_Eso, _ = self.B_Eso
        eps_Eso = np.einsum(
            'Eso,Eo->Es',
            B_Eso, u_Eo
        )
        print('eps_Eso,', eps_Eso)
        return eps_Eso

    def map_field_to_F(self, sig_Es):
        # print('map_field_to_F:')
        # print('sig_Es:', sig_Es)
        B_Eso, det_J_E = self.B_Eso
        f_Eo = self.integ_factor * np.einsum(
            'Eso,Es,E->Eo',
            B_Eso, sig_Es, det_J_E / 2
        )
        f_Eic = f_Eo.reshape(-1,3,2)
        # coordinate transform to global
        f_Eic = self.xf2F(f_Eic)
        _, n_i, n_c = f_Eic.shape
        f_Ei = f_Eic.reshape(-1, n_i * n_c)
        o_E = self.o_Eia.reshape(-1, n_i * n_c)
        return o_E.flatten(), f_Ei.flatten()

    def map_field_to_K(self, D_Est):
        # print('map_field_to_K:')
        #==========================================================================
        B_Eso, det_J_E = self.B_Eso
        k2_ij = self.integ_factor * np.einsum('Eso,Est,Etp,E->Eop', B_Eso, D_Est, B_Eso, det_J_E / 2)
        K_Eiejf = k2_ij.reshape(-1, 3, 2, 3, 2)
        K_Eicjd = self.xk2K(K_Eiejf)

        _, n_i, n_c, n_j, n_d = K_Eicjd.shape
        K_Eij = K_Eicjd.reshape(-1, n_i * n_c, n_j * n_d)
        o_Ei = self.o_Eia.reshape(-1, n_i * n_c)
        # print('K_Eij:', K_Eij)
        print('o_Ei:', o_Ei)
        return SysMtxArray(mtx_arr=K_Eij, dof_map_arr=o_Ei)


    # =========================================================================
    # Property operators for initial configuration
    # =========================================================================
    F0_normals = tr.Property(tr.Array, depends_on='X, L, F')
    r'''Normal facet vectors.
    '''
    @tr.cached_property
    def _get_F0_normals(self):
        x_F = self.x_0[self.F]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        Fa_normals = np.einsum('Iai,Iaj,ijk->Iak',
                               r_deta[..., 0], r_deta[..., 1], EPS)
        return np.sum(Fa_normals, axis=1)

    sign_normals = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Orientation of the normal in the initial state.
    This array is used to switch the normal vectors of the faces
    to be oriented in the positive sense of the z-axis.
    '''
    @tr.cached_property
    def _get_sign_normals(self):
        return np.sign(self.F0_normals[:, 2])

    F_N = tr.Property(tr.Array, depends_on='X,L,F')
    r'''Counter-clockwise enumeration.
    '''
    @tr.cached_property
    def _get_F_N(self):
        turn_facets = np.where(self.sign_normals < 0)
        F_N = np.copy(self.F)
        F_N[turn_facets, :] = self.F[turn_facets, ::-1]
        return F_N
        # return self.mesh.I_Fi

    F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals(self):
        n = self.Fa_normals
        return np.sum(n, axis=1)

    F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals_0(self):
        n = self.Fa_normals_0
        return np.sum(n, axis=1)

    norm_F_normals = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''
    @tr.cached_property
    def _get_norm_F_normals(self):
        n = self.F_normals
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]

    norm_F_normals_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normed normals of the facets.
    '''
    @tr.cached_property
    def _get_norm_F_normals_0(self):
        n = self.F_normals_0
        mag_n = np.sqrt(np.einsum('...i,...i', n, n))
        return n / mag_n[:, np.newaxis]

    F_normals_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the normals of the facets.
    '''
    @tr.cached_property
    def _get_F_normals_du(self):
        n_du = self.Fa_normals_du
        return np.sum(n_du, axis=1)

    F_area = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the surface area of the facets.
    '''
    @tr.cached_property
    def _get_F_area(self):
        a = self.Fa_area
        A = np.einsum('a,Ia->I', self.eta_w, a)
        return A

    # =========================================================================
    # Potential energy
    # =========================================================================
    F_V = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the total potential energy of gravity for each facet
    '''
    @tr.cached_property
    def _get_F_V(self):
        eta_w = self.eta_w
        a = self.Fa_area
        ra = self.Fa_r
        F_V = np.einsum('a,Ia,Ia->I', eta_w, ra[..., 2], a)
        return F_V

    F_V_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivative of total potential energy of gravity for each facet
    with respect to each node and displacement component [FIi]
    '''
    @tr.cached_property
    def _get_F_V_du(self):
        r = self.Fa_r
        a = self.Fa_area
        a_dx = self.Fa_area_du
        r3_a_dx = np.einsum('Ia,IaJj->IaJj', r[..., 2], a_dx)
        N_eta_ip = self.Na
        r3_dx = np.einsum('aK,KJ,j->aJj', N_eta_ip, DELTA, DELTA[2, :])
        a_r3_dx = np.einsum('Ia,aJj->IaJj', a, r3_dx)
        F_V_du = np.einsum('a,IaJj->IJj', self.eta_w, (a_r3_dx + r3_a_dx))
        return F_V_du

    # =========================================================================
    # Line vectors
    # =========================================================================

    F_L_vectors_0 = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.

    .. math::
        v_{pld} \;\mathrm{where} \; p\in\mathcal{F}, l\in (0,1,2), d\in (0,1,2)

    with the indices :math:`p,l,d` representing the facet, line vector around
    the facet and and vector component, respectively.
    '''
    @tr.cached_property
    def _get_F_L_vectors_0(self):
        F_N = self.F_N  # F_N is cycled counter clockwise
        return self.x_0[F_N[:, (1, 2, 0)]] - self.x_0[F_N[:, (0, 1, 2)]]

    F_L_vectors = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.

    .. math::
        v_{pld} \;\mathrm{where} \; p\in\mathcal{F}, l\in (0,1,2), d\in (0,1,2)

    with the indices :math:`p,l,d` representing the facet, line vector around
    the facet and and vector component, respectively.
    '''
    @tr.cached_property
    def _get_F_L_vectors(self):
        F_N = self.F_N  # F_N is cycled counter clockwise
        return self.x[F_N[:, (1, 2, 0)]] - self.x[F_N[:, (0, 1, 2)]]

    F_L_vectors_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivatives of the line vectors around the facets.

    .. math::
        \pard{v_{pld}}{x_{Ie}} \; \mathrm{where} \;
        p \in \mathcal{F},  \in (0,1,2), d\in (0,1,2), I\in \mathcal{N},
        e \in (0,1,3)

    with the indices :math:`p,l,d,I,e` representing the facet,
    line vector around the facet and and vector component,
    node vector and and its component index,
    respectively.

    This array works essentially as an index function delivering -1
    for the components of the first node in each dimension and +1
    for the components of the second node
    in each dimension.

    For a facet :math:`p` with lines :math:`l` and component :math:`d` return
    the derivatives with respect to the displacement of the node :math:`I`
    in the direction :math:`e`.

    .. math::
        \bm{a}_1 = \bm{x}_2 - \bm{x}_1 \\
        \bm{a}_2 = \bm{x}_3 - \bm{x}_2 \\
        \bm{a}_3 = \bm{x}_1 - \bm{x}_3

    The corresponding derivatives are then

    .. math::
        \pard{\bm{a}_1}{\bm{u}_1} = -1, \;\;\;
        \pard{\bm{a}_1}{\bm{u}_2} = 1 \\
        \pard{\bm{a}_2}{\bm{u}_2} = -1, \;\;\;
        \pard{\bm{a}_2}{\bm{u}_3} = 1 \\
        \pard{\bm{a}_3}{\bm{u}_3} = -1, \;\;\;
        \pard{\bm{a}_3}{\bm{u}_1} = 1 \\

    '''

    def _get_F_L_vectors_du(self):
        return self.L_vectors_du[self.F_L]

    F_L_vectors_dul = tr.Property(tr.Array, depends_on=INPUT)

    def _get_F_L_vectors_dul(self):
        return self.L_vectors_dul[self.F_L]

    norm_F_L_vectors = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the cycled line vectors around the facet
    The cycle is closed - the first and last vector are identical.
    '''
    @tr.cached_property
    def _get_norm_F_L_vectors(self):
        v = self.F_L_vectors
        mag_v = np.sqrt(np.einsum('...i,...i', v, v))
        return v / mag_v[..., np.newaxis]

    norm_F_L_vectors_du = tr.Property(tr.Array, depends_on=INPUT)
    '''Get the derivatives of cycled line vectors around the facet
    '''
    @tr.cached_property
    def _get_norm_F_L_vectors_du(self):
        v = self.F_L_vectors
        v_du = self.F_L_vectors_du  # @UnusedVariable
        mag_v = np.einsum('...i,...i', v, v)  # @UnusedVariable
        # @todo: finish the chain rule
        raise NotImplemented

    # =========================================================================
    # Orthonormal basis of each facet.
    # =========================================================================
    F_L_bases = tr.Property(tr.Array, depends_on=INPUT)
    r'''Line bases around a facet.
    '''
    @tr.cached_property
    def _get_F_L_bases(self):
        l = self.norm_F_L_vectors
        n = self.norm_F_normals
        lxn = np.einsum('...li,...j,...kij->...lk', l, n, EPS)
        n_ = n[:, np.newaxis, :] * np.ones((1, 3, 1), dtype='float_')
        T = np.concatenate([l[:, :, np.newaxis, :],
                            -lxn[:, :, np.newaxis, :],
                            n_[:, :, np.newaxis, :]], axis=2)
        return T

    F_L_bases_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Derivatives of the line bases around a facet.
    '''
    @tr.cached_property
    def _get_F_L_bases_du(self):
        '''Derivatives of line bases'''
        raise NotImplemented

    # =========================================================================
    # Sector angles
    # =========================================================================
    F_theta = tr.Property(tr.Array, depends_on=INPUT)
    '''Get the sector angles :math:`\theta`  within a facet.
    '''
    @tr.cached_property
    def _get_F_theta(self):
        v = self.F_L_vectors
        a = -v[:, (2, 0, 1), :]
        b = v[:, (0, 1, 2), :]
        return get_theta(a, b)

    F_theta_du = tr.Property(tr.Array, depends_on=INPUT)
    r'''Get the derivatives of sector angles :math:`\theta` within a facet.
    '''
    @tr.cached_property
    def _get_F_theta_du(self):
        v = self.F_L_vectors
        v_du = self.F_L_vectors_du

        a = -v[:, (2, 0, 1), :]
        b = v[:, (0, 1, 2), :]
        a_du = -v_du[:, (2, 0, 1), ...]
        b_du = v_du[:, (0, 1, 2), ...]

        return get_theta_du(a, a_du, b, b_du)

    Fa_normals_du = tr.Property
    '''Get the derivatives of the normals with respect
    to the node displacements.
    '''

    def _get_Fa_normals_du(self):
        x_F = self.x[self.F_N]
        N_deta_ip = self.Na_deta
        NN_delta_eps_x1 = np.einsum('aK,aL,KJ,dli,ILl->IaiJd',
                                    N_deta_ip[:, 0, :], N_deta_ip[:, 1, :],
                                    DELTA, EPS, x_F)
        NN_delta_eps_x2 = np.einsum('aK,aL,LJ,kdi,IKk->IaiJd',
                                    N_deta_ip[:, 0, :], N_deta_ip[:, 1, :],
                                    DELTA, EPS, x_F)
        n_du = NN_delta_eps_x1 + NN_delta_eps_x2
        return n_du

    Fa_area_du = tr.Property
    '''Get the derivatives of the facet area with respect
    to node displacements.
    '''

    def _get_Fa_area_du(self):
        a = self.Fa_area
        n = self.Fa_normals
        n_du = self.Fa_normals_du
        a_du = np.einsum('Ia,Iak,IakJd->IaJd', 1 / a, n, n_du)
        return a_du

    Fa_normals = tr.Property
    '''Get normals of the facets.
    '''

    def _get_Fa_normals(self):
        x_F = self.x[self.F_N]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        return np.einsum('Iai,Iaj,ijk->Iak',
                         r_deta[..., 0], r_deta[..., 1], EPS)

    Fa_normals_0 = tr.Property
    '''Get normals of the facets.
    '''

    def _get_Fa_normals_0(self):
        x_F = self.x_0[self.F_N]
        N_deta_ip = self.Na_deta
        r_deta = np.einsum('ajK,IKi->Iaij', N_deta_ip, x_F)
        return np.einsum('Iai,Iaj,ijk->Iak',
                         r_deta[..., 0], r_deta[..., 1], EPS)

    Fa_area = tr.Property
    '''Get the surface area of the facets.
    '''

    def _get_Fa_area(self):
        n = self.Fa_normals
        a = np.sqrt(np.einsum('Iai,Iai->Ia', n, n))
        return a

    Fa_r = tr.Property
    '''Get the reference vector to integrations point in each facet.
    '''

    def _get_Fa_r(self):
        x_F = self.x[self.F_N]
        N_eta_ip = self.Na
        r = np.einsum('aK,IKi->Iai', N_eta_ip, x_F)
        return r
Exemplo n.º 9
0
class BasicPlot(traits.HasTraits):
    close_signal = traits.Event()
    plot = traits.Instance(chaco.Plot)