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

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

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

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

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

    t = tr.DelegatesTo('tline', 'val')
    t_max = tr.DelegatesTo('tline', 'max')
コード例 #2
0
class Conjoint(Model):
    # The imput data for calculation
    owner_ref = _traits.WeakRef()
    # design = DataSet()
    design = _traits.DelegatesTo('owner_ref')
    design_vars = _traits.List(_traits.Str())
    liking = DataSet()
    # consumers = DataSet()
    consumers = _traits.DelegatesTo('owner_ref')
    consumers_vars = _traits.List(_traits.Str())

    # Conjoint settings
    model_struct = _traits.Enum('Struct 1', 'Struct 2', 'Struct 3')

    # Conjoint calculation state
    ccs = _traits.Instance(ConjointCalcState, ())
    cm = _traits.Instance(ConjointMachine)

    # depends_on
    res = _traits.Property(
        depends_on='design_vars, consumers_vars, model_struct')

    def _cm_default(self):
        try:
            return ConjointMachine()
        except RNotFoundException:
            self.ccs.messages = ("Was not able to find and start R.\n"
                                 "You have to check the installation of R")
            self.ccs.edit_traits(kind='livemodal')

    @_traits.on_trait_change('owner_ref.model_struct')
    def _struc_altered(self, new):
        self.model_struct = new

    @_traits.on_trait_change('owner_ref.sel_design_var')
    def _des_var_altered(self, new):
        self.design_vars = new

    @_traits.on_trait_change('owner_ref.sel_cons_char')
    def _cons_char_altered(self, new):
        self.consumers_vars = new

    @_traits.cached_property
    def _get_res(self):
        if not self.cm.run_state:
            self.cm.run_state = self.ccs

        model = {
            'Struct 1': 1,
            'Struct 2': 2,
            'Struct 3': 3
        }[self.model_struct]

        self.cm.schedule_calculation(self.design, sorted(self.design_vars),
                                     self.liking, model, self.consumers,
                                     sorted(self.consumers_vars))
        self.ccs.edit_traits(kind='livemodal')
        return self.cm.get_result()
コード例 #3
0
ファイル: sim_controler.py プロジェクト: simvisage/bmcs
class Hist(tr.HasStrictTraits):

    model = tr.Instance(IModel)
    tstep = tr.DelegatesTo('model')

    def init_state(self):
        pass
コード例 #4
0
class Simulator(BMCSTreeNode, TLineMixIn):
    r'''Base class for simulators included in the BMCS Tool Suite.
    It implements the state dependencies within the simulation tree.
    It handles also the communication between the simulation and
    the user interface in several modes of interaction.
    '''
    name = 'simulator'

    @tr.observe('state_changed')
    def _model_structure_changed(self, event=None):
        self.tloop.restart = True

    #=========================================================================
    # TIME LOOP
    #=========================================================================

    tloop = Property(Instance(ITLoop), depends_on='state_changed')
    r'''Time loop constructed based on the current model.
    '''

    @cached_property
    def _get_tloop(self):
        return self.tstep.tloop_type(tstep=self.tstep, tline=self.tline)

    def __init__(self, tstep, *args, **kw):
        self.tstep = tstep
        super(Simulator, self).__init__(*args, **kw)

    tstep = tr.WeakRef(ITStep)

    hist = tr.Property

    def _get_hist(self):
        return self.tstep.hist

    def run(self):
        r'''Run a thread if it does not exist - do nothing otherwise
        '''
        self.tloop()
        return

    interrupt = tr.DelegatesTo('tloop')

    def reset(self):
        self.tloop.reset()

    ipw_view = bu.View(
        run_method='tloop',
        reset_method='reset',
        interrupt_var='interrupt',
        time_var='t',
        time_max='t_max',
    )
コード例 #5
0
class BMCSStudy(ReportStudy):
    '''Combine the simulater with specification of outputs
    '''

    model = tr.Instance(IModel)

    sim = tr.Property(depends_on='model')

    def _get_sim(self):
        return self.model.sim

    '''Model of the studied phoenomenon.
    '''

    viz_sheet = tr.Instance(BMCSVizSheet, ())
    '''Sheet for 2d visualization.
    '''

    input = tr.Property

    def _get_input(self):
        return self.sim

    output = tr.Property

    def _get_output(self):
        return self.viz_sheet

    offline = tr.DelegatesTo('viz_sheet')
    n_cols = tr.DelegatesTo('viz_sheet')

    def _sim_changed(self):
        self.sim.set_ui_recursively(self)
        tline = self.sim.tline
        self.viz_sheet.time_range_changed(tline.max)
        self.viz_sheet.time_changed(tline.val)

    running = tr.Bool(False)
    enable_run = tr.Bool(True)
    enable_pause = tr.Bool(False)
    enable_stop = tr.Bool(False)

    def _running_changed(self):
        '''If the simulation is running disable the run botton,
        enable the pause button and disable changes in all 
        input parameters.
        '''
        self.enable_run = not self.running
        self.enable_pause = self.running
        self.sim.set_traits_with_metadata(self.enable_run, disable_on_run=True)

    start_event = tr.Event
    '''Event announcing the start of the calculation
    '''

    def _start_event_fired(self):
        self.viz_sheet.run_started()

    finish_event = tr.Event
    '''Event announcing the start of the calculation
    '''

    def _finish_event_fired(self):
        self.viz_sheet.run_finished()

    def run(self):
        self.sim.run_thread()
        self.enable_stop = True

    def join(self):
        self.sim.join_thread()

    def pause(self):
        self.sim.pause()

    def stop(self):
        self.sim.stop()
        self.enable_stop = False

    def report_tex(self):
        r = Reporter(report_name=self.sim.name,
                     input=self.sim,
                     output=self.viz_sheet)
        r.write()
        r.show_tex()

    def report_pdf(self):
        r = Reporter(studies=[self])
        r.write()
        r.show_tex()
        r.run_pdflatex()
        r.show_pdf()

    def add_viz2d(self, clname, name, **kw):
        self.sim.add_viz2d(clname, name, **kw)
コード例 #6
0
ファイル: viz2d_field.py プロジェクト: simvisage/bmcs
class Vis2DField(Vis2D):

    model = tr.DelegatesTo('sim')

    x_file = tr.File
    file_list = tr.List(tr.File)

    var = tr.Str('<unnamed>')

    dir = tr.Directory

    def new_dir(self):
        self.dir = tempfile.mkdtemp()

    def setup(self):
        self.new_dir()
        # make a loop over the DomainState
        fe_domain = self.sim.tstep.fe_domain
        domain = fe_domain[2]
        xdomain = domain.xdomain
        r_Eia = np.einsum(
            'Eira,Eia->Eir',
            xdomain.T_Emra[..., :xdomain.x_Eia.shape[-1]], xdomain.x_Eia
        )

        file_name = 'slice_x_%s' % (self.var,)
        target_file = os.path.join(
            self.dir, file_name.replace('.', '_') + '.npy'
        )
        #print('r', r_Eia[..., :-1])
        np.save(target_file, r_Eia[..., :-1])
        self.x_file = target_file

    def get_x_Eir(self):
        return np.load(self.x_file)

    def update(self):
        ts = self.sim.tstep
        fe_domain = self.sim.tstep.fe_domain
        domain = fe_domain[2]
        xdomain = domain.xdomain
        U = ts.U_k
        t = ts.t_n1
        s_Emr = xdomain.map_U_to_field(U)

        var_function = domain.tmodel.var_dict.get(self.var, None)
        if var_function == None:
            raise ValueError('no such variable' % self.var)

        state_k = copy.deepcopy(domain.state_n)
        var_k = var_function(s_Emr, ts.t_n1, **state_k)

        target_file = self.filename(t)

        #np.save(target_file, s_Emr)
        np.save(target_file, var_k)
        self.file_list.append(target_file)

    def filename(self, t):
        file_name = 'slice_%s_step_%008.4f' % (self.var, t)
        target_file = os.path.join(
            self.dir, file_name.replace('.', '_')
        ) + '.npy'
        return target_file
コード例 #7
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]
コード例 #8
0
ファイル: sim_controler.py プロジェクト: simvisage/bmcs
class SimControler(BMCSTreeNode, TLineMixIn):

    model = tr.Instance(IModel)
    hist = tr.DelegatesTo('model')
    tstep = tr.DelegatesTo('model')
    tloop = tr.DelegatesTo('model')
コード例 #9
0
class EnsembleTrainer(t.HasStrictTraits):
    def __init__(self, config={}, **kwargs):
        trainer_template = Trainer(**config)
        super().__init__(trainer_template=trainer_template,
                         config=config,
                         **kwargs)

    config: dict = t.Dict()

    trainer_template: Trainer = t.Instance(Trainer)
    trainers: ty.List[Trainer] = t.List(t.Instance(Trainer))

    n_folds = t.Int(5)

    dl_test: DataLoader = t.DelegatesTo("trainer_template")
    data_spec: dict = t.DelegatesTo("trainer_template")
    cuda: bool = t.DelegatesTo("trainer_template")
    device: str = t.DelegatesTo("trainer_template")
    loss_func: str = t.DelegatesTo("trainer_template")
    batch_size: int = t.DelegatesTo("trainer_template")
    win_len: int = t.DelegatesTo("trainer_template")
    has_null_class: bool = t.DelegatesTo("trainer_template")
    predict_null_class: bool = t.DelegatesTo("trainer_template")
    name: str = t.Str()

    def _name_default(self):
        import time

        modelstr = "Ensemble"
        timestr = time.strftime("%Y%m%d-%H%M%S")
        return f"{modelstr}_{timestr}"

    X_folds = t.Tuple(transient=True)
    ys_folds = t.Tuple(transient=True)

    def _trainers_default(self):
        # Temp trainer for grabbing datasets, etc
        tt = self.trainer_template
        tt.init_data()

        # Combine official train & val sets
        X = torch.cat(
            [tt.dl_train.dataset.tensors[0], tt.dl_val.dataset.tensors[0]])
        ys = [
            torch.cat([yt, yv]) for yt, yv in zip(
                tt.dl_train.dataset.tensors[1:], tt.dl_val.dataset.tensors[1:])
        ]
        # make folds
        fold_len = int(np.ceil(len(X) / self.n_folds))
        self.X_folds = torch.split(X, fold_len)
        self.ys_folds = [torch.split(y, fold_len) for y in ys]

        trainers = []
        for i_val_fold in range(self.n_folds):
            trainer = Trainer(
                validation_fold=i_val_fold,
                name=f"{self.name}/{i_val_fold}",
                **self.config,
            )

            trainer.dl_test = tt.dl_test

            trainers.append(trainer)

        return trainers

    model: models.BaseNet = t.Instance(torch.nn.Module, transient=True)

    def _model_default(self):
        model = models.FilterNetEnsemble()
        model.set_models([trainer.model for trainer in self.trainers])
        return model

    model_path: str = t.Str()

    def _model_path_default(self):
        return f"saved_models/{self.name}/"

    def init_data(self):
        # Initiate loading of datasets, model
        pass
        # for trainer in self.trainers:
        #     trainer.init_data()

    def init_train(self):
        pass
        # for trainer in self.trainers:
        #     trainer.init_train()

    def train(self, max_epochs=50):
        """ A pretty standard training loop, constrained to stop in `max_epochs` but may stop early if our
        custom stopping metric does not improve for `self.patience` epochs. Always checkpoints
        when a new best stopping_metric is achieved. An alternative to using
        ray.tune for training."""

        for trainer in self.trainers:
            # Add data to trainer

            X_train = torch.cat([
                arr for i, arr in enumerate(self.X_folds)
                if i != trainer.validation_fold
            ])
            ys_train = [
                torch.cat([
                    arr for i, arr in enumerate(y)
                    if i != trainer.validation_fold
                ]) for y in self.ys_folds
            ]

            X_val = torch.cat([
                arr for i, arr in enumerate(self.X_folds)
                if i == trainer.validation_fold
            ])
            ys_val = [
                torch.cat([
                    arr for i, arr in enumerate(y)
                    if i == trainer.validation_fold
                ]) for y in self.ys_folds
            ]

            trainer.dl_train = DataLoader(
                TensorDataset(torch.Tensor(X_train), *ys_train),
                batch_size=trainer.batch_size,
                shuffle=True,
            )
            trainer.data_spec = self.trainer_template.data_spec
            trainer.epoch_iters = self.trainer_template.epoch_iters
            trainer.dl_val = DataLoader(
                TensorDataset(torch.Tensor(X_val), *ys_val),
                batch_size=trainer.batch_size,
                shuffle=False,
            )

            # Now clear local vars to save ranm
            X_train = ys_train = X_val = ys_val = None

            trainer.init_data()
            trainer.init_train()
            trainer.train(max_epochs=max_epochs)

            # Clear trainer train and val datasets to save ram
            trainer.dl_train = t.Undefined
            trainer.dl_val = t.Undefined

            print(f"RESTORING TO best model")
            trainer._restore()
            trainer._save()

            trainer.print_train_summary()

            em = EvalModel(trainer=trainer)

            em.run_test_set()
            em.calc_metrics()
            em.calc_ward_metrics()
            print(em.classification_report_df.to_string(float_format="%.3f"))
            em._save()

    def print_train_summary(self):
        for trainer in self.trainers:
            trainer.print_train_summary()

    def _save(self, checkpoint_dir=None, save_model=True, save_trainer=True):
        """ Saves/checkpoints model state and training state to disk. """
        if checkpoint_dir is None:
            checkpoint_dir = self.model_path
        else:
            self.model_path = checkpoint_dir

        os.makedirs(checkpoint_dir, exist_ok=True)

        # save model params
        model_path = os.path.join(checkpoint_dir, "model.pth")
        trainer_path = os.path.join(checkpoint_dir, "trainer.pth")

        if save_model:
            torch.save(self.model.state_dict(), model_path)
        if save_trainer:
            with open(trainer_path, "wb") as f:
                pickle.dump(self, f)

        return checkpoint_dir

    def _restore(self, checkpoint_dir=None):
        """ Restores model state and training state from disk. """

        if checkpoint_dir is None:
            checkpoint_dir = self.model_path

        model_path = os.path.join(checkpoint_dir, "model.pth")
        trainer_path = os.path.join(checkpoint_dir, "trainer.pth")

        # Reconstitute old trainer and copy state to this trainer.
        with open(trainer_path, "rb") as f:
            other_trainer = pickle.load(f)

        self.__setstate__(other_trainer.__getstate__())

        # Load sub-models
        for trainer in self.trainers:
            trainer._restore()

        # Load model (after loading state in case we need to re-initialize model from config)
        self.model.load_state_dict(
            torch.load(model_path, map_location=self.device))
コード例 #10
0
ファイル: shear_zone.py プロジェクト: simvisage/bmcs
class XDomain(BMCSLeafNode, Vis2D):
    '''Represent the current crack state.
    This objects represents the discretization of the domain using 
    segments / finite elements
    '''
    node_name = 'crack domain'

    ts = tr.WeakRef

    #=========================================================================
    # Primary state variables
    #=========================================================================
    x_rot_1 = tr.Float(50, state_changed=True)
    theta = tr.Float(0, state_changed=True)

    @tr.on_trait_change('+state_changed')
    def set_state_changed(self):
        self.state_changed = True
    #=========================================================================
    # Discretization parameters
    #=========================================================================
    n_J = tr.Int(10)
    '''Number of nodes along the uncracked zone
    '''
    n_m = tr.Int(8)
    '''Number of integration points within a segment
    '''

    tree_view = ui.View(
        ui.Item('L_fps', style='readonly'),
        ui.Item('n_m', style='readonly'),
        ui.Item('n_J', style='readonly'),
        ui.Item('theta', style='readonly'),
        ui.Item('x_tip_a', style='readonly'),
        ui.Item('x_rot_a', style='readonly'),
    )
    eta = tr.DelegatesTo('ts')
    #=========================================================================
    # Geometry - used for visualization and constraits of crack propagation.
    #=========================================================================
    x_Ca = tr.Property(depends_on='input_changed')
    '''Corner coordinates'''
    @tr.cached_property
    def _get_x_Ca(self):
        L = self.ts.L
        H = self.ts.H
        return np.array([[0, L, L, 0],
                         [0, 0, H, H]], dtype=np.float_).T

    C_Li = tr.Property(depends_on='input_changed')
    '''Lines'''
    @tr.cached_property
    def _get_C_Li(self):
        return np.array([[0, 1], [1, 2], [2, 3], [3, 0]], dtype=np.int_)

    #=========================================================================
    # Discretization / state domain
    #=========================================================================
    state_changed = tr.Event
    '''Register the state change event to trigger recalculation.
    '''

    x_t_Ia = tr.Array(dtype=np.float_, value=[])

    def _x_t_Ia_default(self):
        return np.array([[self.ts.initial_crack_position, 0]],
                        dtype=np.float_)

    x_Ia = tr.Property(depends_on='state_changed')
    '''Nodes along the crack path including the fps segment'''
    @tr.cached_property
    def _get_x_Ia(self):
        return np.vstack([self.x_t_Ia, self.x_tip_a[np.newaxis, :]])

    I_Li = tr.Property(depends_on='state_changed')
    '''Crack segments'''
    @tr.cached_property
    def _get_I_Li(self):
        N_I = np.arange(len(self.x_Ia))
        I_Li = np.array([N_I[:-1], N_I[1:]], dtype=np.int_).T
        return I_Li

    x_Ja = tr.Property(depends_on='state_changed')
    '''Uncracked vertical section'''
    @tr.cached_property
    def _get_x_Ja(self):
        x_J_1 = np.linspace(self.x_Ia[-1, 1], self.x_Ca[-1, 1], self.n_J)
        return np.c_[self.x_Ia[-1, 0] * np.ones_like(x_J_1), x_J_1]

    xx_Ka = tr.Property(depends_on='state_changed')
    '''Integrated section'''
    @tr.cached_property
    def _get_xx_Ka(self):
        return np.concatenate([self.x_Ia, self.x_Ja[1:]], axis=0)

    x_Ka = tr.Property(depends_on='state_changed')
    '''Integration points'''
    @tr.cached_property
    def _get_x_Ka(self):
        eta_m = np.linspace(0, 1, self.n_m)
        d_La = self.xx_Ka[1:] - self.xx_Ka[:-1]
        d_Kma = np.einsum('Ka,m->Kma', d_La, eta_m)
        x_Kma = self.xx_Ka[:-1, np.newaxis, :] + d_Kma
        return np.vstack([x_Kma[:, :-1, :].reshape(-1, 2), self.xx_Ka[[-1], :]])

    K_Li = tr.Property(depends_on='state_changed')
    '''Crack segments'''
    @tr.cached_property
    def _get_K_Li(self):
        N_K = np.arange(len(self.x_Ka))
        K_Li = np.array([N_K[:-1], N_K[1:]], dtype=np.int_).T
        return K_Li

    x_Lb = tr.Property(depends_on='state_changed')
    '''Midpoints'''
    @tr.cached_property
    def _get_x_Lb(self):
        return np.sum(self.x_Ka[self.K_Li], axis=1) / 2

    L_fps = tr.Property(tr.Float)
    '''Length of the fracture process segment.
    '''

    def _get_L_fps(self):
        return self.ts.mm.L_fps

    #=========================================================================
    # Fracture process segment
    #=========================================================================
    T_fps_a = tr.Property(tr.Array, depends_on='state_changed')
    '''Orientation matrix of the crack propagation segment
    '''
    @tr.cached_property
    def _get_T_fps_a(self):
        return np.array([-np.sin(self.theta), np.cos(self.theta)],
                        dtype=np.float_)

    x_tip_a = tr.Property(tr.Array, depends_on='state_changed')
    '''Unknown position of the crack tip. Depends on the sought
    fracture process segment orientation $\theta$
    '''
    @tr.cached_property
    def _get_x_tip_a(self):
        return self.x_fps_a + self.L_fps * self.T_fps_a

    da_fps = tr.Property(tr.Float, depends_on='input_changed')
    '''Crack length increment contoling the calculation
    '''
    @tr.cached_property
    def _get_da_fps(self):
        return self.eta * self.L_fps

    x1_fps_a = tr.Property(tr.Array, depends_on='state_changed')
    '''Shifted position of the fracture process hot spot. This 
    is the point that is required to achieve the tensile strength
    '''
    @tr.cached_property
    def _get_x1_fps_a(self):
        x1_fps_a = self.x_fps_a + self.da_fps * self.T_fps_a
        x1_fps_a[1] = np.min([x1_fps_a[1], self.ts.H])
        return x1_fps_a

    x_fps_a = tr.Property(tr.Array, depends_on='state_changed')
    '''Position of the starting point of the fracture process segment.
    '''
    @tr.cached_property
    def _get_x_fps_a(self):
        return self.x_t_Ia[-1, :]

    #=========================================================================
    # Transformation relative to the crack path
    #=========================================================================

    norm_n_vec_L = tr.Property(depends_on='state_changed')
    '''Unit line vector
    '''
    @tr.cached_property
    def _get_norm_n_vec_L(self):
        K_Li = self.K_Li
        x_Lia = self.x_Ka[K_Li]
        n_vec_La = x_Lia[:, 1, :] - x_Lia[:, 0, :]
        return np.sqrt(np.einsum('...a,...a->...', n_vec_La, n_vec_La))

    T_Lab = tr.Property(depends_on='state_changed')
    '''Unit line vector
    '''
    @tr.cached_property
    def _get_T_Lab(self):
        K_Li = self.K_Li
        x_Lia = self.x_Ka[K_Li]
        line_vec_La = x_Lia[:, 1, :] - x_Lia[:, 0, :]
        norm_line_vec_L = np.sqrt(np.einsum('...a,...a->...',
                                            line_vec_La, line_vec_La))
        normed_line_vec_La = np.einsum('...a,...->...a',
                                       line_vec_La, 1. / norm_line_vec_L)
        t_vec_La = np.einsum('ijk,...j,k->...i',
                             EPS[:-1, :-1, :], normed_line_vec_La, Z)
        T_bLa = np.array([t_vec_La, normed_line_vec_La])
        T_Lab = np.einsum('bLa->Lab', T_bLa)
        return T_Lab

    plot_scale = tr.Float(1.0, auto_set=False, enter_set=True)

    x_n_ejaM = tr.Property(depends_on='state_changed')
    '''Unit line vector
    '''
    @tr.cached_property
    def _get_x_n_ejaM(self):
        K_Li = self.K_Li
        T_Lab = self.T_Lab
        x_Lia = self.x_Ka[K_Li]
        x_n0_Mea = np.einsum(
            'e,Ma->Mae', np.ones((2,)), np.sum(x_Lia, axis=1) / 2)
        x_n_jMea = np.array(
            [x_n0_Mea, x_n0_Mea + self.plot_scale * T_Lab]
        )
        return np.einsum('jMae->ejaM', x_n_jMea)

    def get_rot_mtx(self, phi):
        return np.array(
            [[np.cos(phi), -np.sin(phi)],
             [np.sin(phi), np.cos(phi)]], dtype=np.float_)

    x_rot_a = tr.Property(depends_on='state_changed')
    '''Center of rotation.
    '''
    @tr.cached_property
    def _get_x_rot_a(self):
        x_rot_0 = self.x_tip_a[0]
        x_rot_1 = self.x_rot_1
        return np.array([x_rot_0, x_rot_1], dtype=np.float_)

    def rotate(self, x_Ka, phi):
        rot_mtx = self.get_rot_mtx(phi)
        u_Ib = np.einsum(
            'ab,...b->...a',
            rot_mtx, x_Ka - self.x_rot_a
        )
        return u_Ib + self.x_rot_a

    w_f_t = tr.DelegatesTo('sim')

    #=========================================================================
    # Unknown variables
    #=========================================================================
    phi = tr.Property(tr.Float, depends_on='state_changed')
    '''Rotation of the right-hand part of the shear zone.
    '''
    @tr.cached_property
    def _get_phi(self):
        x_fps_a = self.x_fps_a
        w_f_t = self.ts.w_f_t
        x_rot_a = self.x_rot_a
        n_fps = len(self.x_t_Ia) - 1
        n_m_fps = n_fps * (self.n_m - 1) + 3
        T_ab1 = self.T_Lab[n_m_fps, ...]
        theta = self.theta
        T_ab = np.array([[np.cos(theta), np.sin(theta)],
                         [-np.sin(theta), np.cos(theta)]])
        phi = get_phi(T_ab, x_fps_a, x_rot_a, w_f_t)
        return phi

    def get_moved_Ka(self, x_Ka):
        return self.rotate(x_Ka=x_Ka, phi=self.phi)

    x1_Ia = tr.Property(depends_on='state_changed')
    '''Displaced segment nodes'''
    @tr.cached_property
    def _get_x1_Ia(self):
        return self.get_moved_Ka(self.x_Ia)

    x1_Ka = tr.Property(depends_on='state_changed')
    '''Displaced integration points'''
    @tr.cached_property
    def _get_x1_Ka(self):
        return self.get_moved_Ka(self.x_Ka)

    x1_Ca = tr.Property(depends_on='state_changed')
    '''Diplaced corner nodes'''
    @tr.cached_property
    def _get_x1_Ca(self):
        return self.get_moved_Ka(self.x_Ca)

    #=========================================================================
    # Control and state variables
    #=========================================================================
    v = tr.Property(depends_on='state_changed')
    '''Vertical displacement on the right hand side
    '''
    @tr.cached_property
    def _get_v(self):
        x_rot_0 = self.x_rot_a[0]
        phi = self.phi
        L_rot_distance = self.ts.L - x_rot_0
        v = L_rot_distance * np.sin(phi)
        return v

    #=========================================================================
    # Kinematics
    #=========================================================================

    u_Lib = tr.Property(depends_on='state_changed')
    '''Displacement of lines in local coordinates'''
    @tr.cached_property
    def _get_u_Lib(self):
        K_Li = self.K_Li
        u_Ka = self.x1_Ka - self.x_Ka
        u_Lia = u_Ka[K_Li]
        T_Lab = self.T_Lab
        u_Lib = np.einsum(
            'Lia,Lab->Lib', u_Lia, T_Lab
        )
        return u_Lib

    u_Lb = tr.Property(depends_on='state_changed')
    '''Displacement of the segment midpoints '''
    @tr.cached_property
    def _get_u_Lb(self):
        K_Li = self.K_Li
        u_Ka = self.x1_Ka - self.x_Ka
        u_Lia = u_Ka[K_Li]
        u_La = np.sum(u_Lia, axis=1) / 2
        T_Lab = self.T_Lab
        u_Lb = np.einsum(
            'La,Lab->Lb', u_La, T_Lab
        )
        return u_Lb

    get_u0 = tr.Property(depends_on='state_changed')
    '''Get an interpolator function returning horizontal displacement 
    component for a specified vertical coordinate of a ligmant.
    '''
    @tr.cached_property
    def _get_get_u0(self):
        return interp1d(self.x_Lb[:, 1], self.u_Lb[:, 0],
                        fill_value='extrapolate')

    u_ejaM = tr.Property(depends_on='state_changed')
    '''Transformed displacement at the line midpoints '''
    @tr.cached_property
    def _get_u_ejaM(self):
        return self.get_f_ejaM(self.u_Lb)

    def get_f_ejaM(self, f_Lb, scale=1.0):
        K_Li = self.K_Li
        T_Lab = self.T_Lab
        x_Lia = self.x_Ka[K_Li]
        x_n0_Mea = np.einsum(
            'e,Ma->Mae', np.ones((2,)), np.sum(x_Lia, axis=1) / 2)
        f_base_Lab = np.einsum(
            'Lb, Lab->Lab', f_Lb, scale * T_Lab
        )
        x_n_jMea = np.array(
            [x_n0_Mea, x_n0_Mea + f_base_Lab]
        )
        return np.einsum('jMae->ejaM', x_n_jMea)

    cmod = tr.Property(depends_on='state_changed')
    '''Crack mouth opening displacement .
    '''
    @tr.cached_property
    def _get_cmod(self):
        return self.u_Lb[0, 0]

    xn_m_fps = tr.Property(depends_on='state_changed')
    '''Get the index of the material point at which the orientation is 
    evaluated
    '''
    @tr.cached_property
    def _get_xn_m_fps(self):
        n_tip = len(self.x_t_Ia) - 1
        n_m_tip = n_tip * (self.n_m - 1) + int(self.n_m * 2)
        return n_m_tip

    x_sig_fps_1 = tr.Property(depends_on='state_changed')
    '''Get the vertical position of the orientation segment 
    '''
    @tr.cached_property
    def _get_x_sig_fps_1(self):
        return self.x_tip_a[1]

    #=========================================================================
    # Plot current state
    #=========================================================================
    def plot_x_Ka(self, ax):
        x_aK = self.x_Ka.T
        ax.plot(*x_aK, 'bo', color='blue', markersize=8)

    def plot_x_tip_a(self, ax):
        '''Show the current crack tip.
        '''
        x, y = self.x_tip_a
        ax.plot(x, y, 'bo', color='red', markersize=10)

    def plot_x_rot_a(self, ax):
        '''Show the current center of rotation.
        '''
        x, y = self.x_rot_a

        ax.annotate('center of rotation', xy=(x, y), xytext=(x + 50, y + 20),
                    arrowprops=dict(facecolor='black', width=1, shrink=0.1),
                    )

        ax.plot([0, self.ts.L], [y, y],
                color='black', linestyle='--')
        ax.plot(x, y, 'bo', color='blue', markersize=10)

    def plot_x_fps_a(self, ax):
        x, y = self.x_fps_a
        ax.plot(x, y, 'bo', color='green', markersize=10)

    def plot_sz0(self, ax):
        x_Ia = self.x_Ia
        x_Ca = self.x_Ca
        x_aI = x_Ia.T
        x_LL = x_Ca[0]
        x_LU = x_Ca[3]
        x_RL = self.x_Ka[0]
        x_RU = self.x_Ka[-1]
        x_Da = np.array([x_LL, x_RL, x_RU, x_LU])
        D_Li = np.array([[0, 1], [2, 3], [3, 0]], dtype=np.int_)
        x_aiD = np.einsum('Dia->aiD', x_Da[D_Li])
        ax.plot(*x_aiD, color='black')
        ax.plot(*x_aI, lw=2, color='black')

    def plot_sz1(self, ax):
        x_Ia = self.x1_Ia
        x_Ca = self.x1_Ca
        x_aI = x_Ia.T
        x_LL = self.x1_Ka[0]
        x_LU = self.x1_Ka[-1]
        x_RL = x_Ca[1]
        x_RU = x_Ca[2]
        x_Da = np.array([x_LL, x_RL, x_RU, x_LU])
        D_Li = np.array([[0, 1], [1, 2], [2, 3], ], dtype=np.int_)
        x_aiD = np.einsum('Dia->aiD', x_Da[D_Li])
        ax.plot(*x_aiD, color='black')
        ax.set_title(r'Simulated crack path')
        ax.set_xlabel(r'Horizontal position $x$ [mm]')
        ax.set_ylabel(r'Vertical position $z$ [mm]')
        ax.plot(*x_aI, lw=2, color='black')

    def plot_sz_fill(self, ax):
        x_Ca = self.x1_Ca
        x_Da = np.vstack([
            x_Ca[:1],
            self.x_Ia,
            self.x1_Ia[::-1],
            x_Ca[1:, :],
        ])
        ax.fill(*x_Da.T, color='gray', alpha=0.2)

    def plot_reinf(self, ax):
        for z in self.ts.z_f:
            # left part
            ax.plot([0, self.ts.L], [z, z], color='maroon', lw=5)

    def plot_sz_state(self, ax, vot=1.0):
        self.plot_sz1(ax)
        self.plot_sz0(ax)
        self.plot_sz_fill(ax)
        self.plot_x_tip_a(ax)
        self.plot_x_rot_a(ax)
        self.plot_x_fps_a(ax)
        self.plot_reinf(ax)
        ax.axis('off')
        ax.axis('equal')

    def plot_T_Lab(self, ax):
        K_Li = self.K_Li
        x_n_ejaM = self.x_n_ejaM
        x_n_eajM = np.einsum('ejaM->eajM', x_n_ejaM)
        ax.plot(*x_n_eajM.reshape(-1, 2, len(K_Li)), color='orange', lw=3)

    def plot_u_T_Lab(self, ax):
        u_ejaM = self.u_ejaM
        u_eajM = np.einsum('ejaM->eajM', u_ejaM)
        ax.plot(*u_eajM.reshape(-1, 2, len(self.K_Li)), color='orange', lw=3)

    def plot_hlines(self, ax, h_min, h_max):
        _, y_tip = self.x_tip_a
        _, y_rot = self.x_rot_a
        _, z_fps = self.x_fps_a
        ax.plot([h_min, h_max], [y_tip, y_tip],
                color='black', linestyle=':')
        ax.plot([h_min, h_max], [y_rot, y_rot],
                color='black', linestyle='--')
        ax.plot([h_min, h_max], [z_fps, z_fps],
                color='black', linestyle='-.')

    def plot_u_Lc(self, ax, u_Lc, idx=0, color='black', label=r'$w$ [mm]'):
        x_La = self.x_Lb
        u_Lb_min = np.min(u_Lc[:, idx])
        u_Lb_max = np.max(u_Lc[:, idx])
        self.plot_hlines(ax, u_Lb_min, u_Lb_max)
        ax.plot(u_Lc[:, idx], x_La[:, 1], color=color, label=label)
        ax.fill_betweenx(x_La[:, 1], u_Lc[:, idx], 0, color=color, alpha=0.1)
        ax.set_xlabel(label)
        ax.legend(loc='lower left')

    def plot_u_L0(self, ax, vot=1):
        self.plot_u_Lc(ax, self.u_Lb, 0,
                       label=r'$w$ [mm]')
        ax.set_xlabel(r'effective horizontal COD $w$ [mm]')
コード例 #11
0
ファイル: evalmodel.py プロジェクト: showkeyjar/FilterNet
class EvalModel(t.HasStrictTraits):
    trainer: Trainer = t.Any()
    model: mo.BaseNet = t.DelegatesTo("trainer")
    dl_test: DataLoader = t.DelegatesTo("trainer")
    data_spec: dict = t.DelegatesTo("trainer")
    cuda: bool = t.DelegatesTo("trainer")
    device: str = t.DelegatesTo("trainer")
    loss_func: str = t.DelegatesTo("trainer")
    model_path: str = t.DelegatesTo("trainer")
    has_null_class: bool = t.DelegatesTo("trainer")
    predict_null_class: bool = t.DelegatesTo("trainer")

    # 'prediction' mode employs overlap and reconstructs signal
    #   as a contiguous timeseries w/ optional windowing.
    #   It aims for best accuracy/f1 by using overlap, and will
    #   typically outperform 'training' mode.
    # 'training' mode does not average repeated point and does
    #   not window; it should product acc/loss/f1 similar to
    #   training mode.
    run_mode: str = t.Enum(["prediction", "training"])
    window: str = t.Enum(["hanning", "boxcar"])
    eval_batch_size: int = t.Int(100)

    target_names: ty.List[str] = t.ListStr()

    def _target_names_default(self):
        target_names = self.data_spec["output_spec"][0]["classes"]

        if self.has_null_class:
            assert target_names[0] in ("", "Null")

            if not self.predict_null_class:
                target_names = target_names[1:]

        return target_names

    def _run_model_on_batch(self, data, targets):
        targets = torch.stack(targets)

        if self.cuda:
            data, targets = data.cuda(), targets.cuda()

        output = self.model(data)

        _targets = self.model.transform_targets(targets, one_hot=False)
        if self.loss_func == "cross_entropy":
            _losses = [F.cross_entropy(o, t) for o, t in zip(output, _targets)]
            loss = sum(_losses)
        elif self.loss_func == "binary_cross_entropy":
            _targets_onehot = self.model.transform_targets(targets,
                                                           one_hot=True)
            _losses = [
                F.binary_cross_entropy_with_logits(o, t)
                for o, t in zip(output, _targets_onehot)
            ]
            loss = sum(_losses)
        else:
            raise NotImplementedError(self.loss)

        # Assume only 1 output:

        return loss, output[0], _targets[0], _losses[0]

    def run_test_set(self, dl=None):
        """ Runs `self.model` on `self.dl_test` (or a provided dl) and stores results for subsequent evaluation. """
        if dl is None:
            dl = self.dl_test

        if self.cuda:
            self.model.cuda()
        self.model.eval()
        if self.eval_batch_size:
            dl = DataLoader(dl.dataset,
                            batch_size=self.eval_batch_size,
                            shuffle=False)
        #
        #     # Xc, yc = data.get_x_y_contig('test')
        X, *ys = dl.dataset.tensors
        # X: [N, input_chans, win_len]
        step = int(X.shape[2] / 2)
        assert torch.equal(X[0, :, step], X[1, :, 0])

        losses = []
        outputsraw = []
        outputs = []
        targets = []

        with Timer("run", log_output=False) as tr:
            with Timer("infer", log_output=False) as ti:
                for batch_idx, (data, *target) in enumerate(dl):
                    (
                        batch_loss,
                        batch_output,
                        batch_targets,
                        train_losses,
                    ) = self._run_model_on_batch(data, target)

                    losses.append(batch_loss.detach().cpu().item())
                    outputsraw.append(batch_output.detach().cpu().data.numpy())
                    outputs.append(
                        torch.argmax(batch_output, 1,
                                     False).detach().cpu().data.numpy())
                    targets.append(batch_targets.detach().cpu().data.numpy())
            self.infer_time_s_cpu = ti.interval_cpu
            self.infer_time_s_wall = ti.interval_wall

            self.loss = np.mean(losses)
            targets = np.concatenate(targets, axis=0)  # [N, out_win_len]
            outputsraw = np.concatenate(
                outputsraw, axis=0)  # [N, n_out_classes, out_win_len]
            outputs = np.concatenate(outputs,
                                     axis=0)  # [N, n_out_classes, out_win_len]

            # win_len = toutputsraw[0].shape[-1]
            if (self.model.output_type == "many_to_one_takelast"
                    or self.run_mode == "training"):
                self.targets = np.concatenate(targets, axis=-1)  # [N,]
                self.outputsraw = np.concatenate(
                    outputsraw, axis=-1)  # [n_out_classes, N,]
                self.outputs = np.concatenate(outputs, axis=-1)  # [N,]

            elif self.run_mode == "prediction":
                n_segments, n_classes, out_win_len = outputsraw.shape

                output_step = int(out_win_len / 2)

                if self.window == "hanning":
                    EPS = 0.001  # prevents divide-by-zero
                    arr_window = (1 - EPS) * np.hanning(out_win_len) + EPS
                elif self.window == "boxcar":
                    arr_window = np.ones((out_win_len, ))
                else:
                    raise ValueError()

                # Allocate space for merged predictions
                if self.has_null_class and not self.predict_null_class:
                    outputsraw2 = np.zeros(
                        (n_segments + 1, n_classes - 1, output_step, 2))
                    window2 = np.zeros(
                        (n_segments + 1, n_classes - 1, output_step,
                         2))  # [N+1, out_win_len/2, 2]
                    # Drop in outputs/window vals in the two layers
                    outputsraw = outputsraw[:, 1:, :]
                else:
                    outputsraw2 = np.zeros(
                        (n_segments + 1, n_classes, output_step, 2))
                    window2 = np.zeros((n_segments + 1, n_classes, output_step,
                                        2))  # [N+1, out_win_len/2, 2]

                # Drop in outputs/window vals in the two layers
                outputsraw2[:-1, :, :, 0] = outputsraw[:, :, :output_step]
                outputsraw2[1:, :, :,
                            1] = outputsraw[:, :, output_step:output_step * 2]
                window2[:-1, :, :, 0] = arr_window[:output_step]
                window2[1:, :, :, 1] = arr_window[output_step:output_step * 2]

                merged_outputsraw = (outputsraw2 * window2).sum(
                    axis=3) / (window2).sum(axis=3)
                softmaxed_merged_outputsraw = softmax(merged_outputsraw,
                                                      axis=1)
                merged_outputs = np.argmax(softmaxed_merged_outputsraw, 1)

                self.outputsraw = np.concatenate(merged_outputsraw, axis=-1)
                self.outputs = np.concatenate(merged_outputs, axis=-1)
                self.targets = np.concatenate(
                    np.concatenate(
                        [
                            targets[:, :output_step],
                            targets[[-1], output_step:output_step * 2],
                        ],
                        axis=0,
                    ),
                    axis=-1,
                )
            else:
                raise ValueError()

        if self.has_null_class and not self.predict_null_class:
            not_null_mask = self.targets > 0
            self.outputsraw = self.outputsraw[..., not_null_mask]
            self.outputs = self.outputs[not_null_mask]
            self.targets = self.targets[not_null_mask]
            self.targets -= 1

        self.n_samples_in = np.prod(dl.dataset.tensors[1].shape)
        self.n_samples_out = len(self.outputs)
        self.infer_samples_per_s = self.n_samples_in / self.infer_time_s_wall
        self.run_time_s_cpu = tr.interval_cpu
        self.run_time_s_wall = tr.interval_wall

    loss: float = t.Float()
    targets: np.ndarray = t.Array()
    outputsraw: np.ndarray = t.Array()
    outputs: np.ndarray = t.Array()
    n_samples_in: int = t.Int()
    n_samples_out: int = t.Int()
    infer_samples_per_s: float = t.Float()

    infer_time_s_cpu: float = t.Float()
    infer_time_s_wall: float = t.Float()
    run_time_s_cpu: float = t.Float()
    run_time_s_wall: float = t.Float()

    extra: dict = t.Dict({})

    acc: float = t.Float()
    f1: float = t.Float()
    f1_mean: float = t.Float()
    event_f1: float = t.Float()
    classification_report_txt: str = t.Str()
    classification_report_dict: dict = t.Dict()
    classification_report_df: pd.DataFrame = t.Property(
        t.Instance(pd.DataFrame))
    confusion_matrix: np.ndarray = t.Array()

    nonull_acc: float = t.Float()
    nonull_f1: float = t.Float()
    nonull_f1_mean: float = t.Float()
    nonull_classification_report_txt: str = t.Str()
    nonull_classification_report_dict: dict = t.Dict()
    nonull_classification_report_df: pd.DataFrame = t.Property(
        t.Instance(pd.DataFrame))
    nonull_confusion_matrix: np.ndarray = t.Array()

    def calc_metrics(self):

        self.acc = sklearn.metrics.accuracy_score(self.targets, self.outputs)
        self.f1 = sklearn.metrics.f1_score(self.targets,
                                           self.outputs,
                                           average="weighted")
        self.f1_mean = sklearn.metrics.f1_score(self.targets,
                                                self.outputs,
                                                average="macro")

        self.classification_report_txt = sklearn.metrics.classification_report(
            self.targets,
            self.outputs,
            digits=3,
            labels=np.arange(len(self.target_names)),
            target_names=self.target_names,
        )
        self.classification_report_dict = sklearn.metrics.classification_report(
            self.targets,
            self.outputs,
            digits=3,
            output_dict=True,
            labels=np.arange(len(self.target_names)),
            target_names=self.target_names,
        )
        self.confusion_matrix = sklearn.metrics.confusion_matrix(
            self.targets, self.outputs)

        # Now, ignoring the null/none class:
        if self.has_null_class and self.predict_null_class:
            # assume null class comes fistnonull_mask = self.targets > 0
            nonull_mask = self.targets > 0
            nonull_targets = self.targets[nonull_mask]
            # nonull_outputs = self.outputs[nonull_mask]
            nonull_outputs = self.outputsraw[1:, :].argmax(
                axis=0)[nonull_mask] + 1

            self.nonull_acc = sklearn.metrics.accuracy_score(
                nonull_targets, nonull_outputs)
            self.nonull_f1 = sklearn.metrics.f1_score(nonull_targets,
                                                      nonull_outputs,
                                                      average="weighted")
            self.nonull_f1_mean = sklearn.metrics.f1_score(nonull_targets,
                                                           nonull_outputs,
                                                           average="macro")
            self.nonull_classification_report_txt = sklearn.metrics.classification_report(
                nonull_targets,
                nonull_outputs,
                digits=3,
                labels=np.arange(len(self.target_names)),
                target_names=self.target_names,
            )
            self.nonull_classification_report_dict = sklearn.metrics.classification_report(
                nonull_targets,
                nonull_outputs,
                digits=3,
                output_dict=True,
                labels=np.arange(len(self.target_names)),
                target_names=self.target_names,
            )
            self.nonull_confusion_matrix = sklearn.metrics.confusion_matrix(
                nonull_targets, nonull_outputs)
        else:
            self.nonull_acc = self.acc
            self.nonull_f1 = self.f1
            self.nonull_f1_mean = self.f1_mean
            self.nonull_classification_report_txt = self.classification_report_txt
            self.nonull_classification_report_dict = self.classification_report_dict
            self.nonull_confusion_matrix = self.confusion_matrix

    ward_metrics: WardMetrics = t.Instance(WardMetrics)

    def calc_ward_metrics(self):
        """ Do event-wise metrics, using the `wardmetrics` package which implements metrics from:

         [1]    J. A. Ward, P. Lukowicz, and H. W. Gellersen, “Performance metrics for activity recognition,”
                    ACM Trans. Intell. Syst. Technol., vol. 2, no. 1, pp. 1–23, Jan. 2011.
        """

        import wardmetrics

        # Must be in prediction mode -- otherwise, data is not contiguous, ward metrics will be bogus
        assert self.run_mode == "prediction"

        targets = self.targets
        predictions = self.outputs

        wmetrics = WardMetrics()

        targets_events = wardmetrics.frame_results_to_events(targets)
        preds_events = wardmetrics.frame_results_to_events(predictions)

        for i, class_name in enumerate(self.target_names):
            class_wmetrics = ClassWardMetrics()

            t = targets_events.get(str(i), [])
            p = preds_events.get(str(i), [])
            # class_wmetrics['t'] = t
            # class_wmetrics['p'] = p

            try:
                assert len(t) and len(p)
                (
                    twoset_results,
                    segments_with_scores,
                    segment_counts,
                    normed_segment_counts,
                ) = wardmetrics.eval_segments(t, p)
                class_wmetrics.segment_twoset_results = twoset_results

                (
                    gt_event_scores,
                    det_event_scores,
                    detailed_scores,
                    standard_scores,
                ) = wardmetrics.eval_events(t, p)
                class_wmetrics.event_detailed_scores = detailed_scores
                class_wmetrics.event_standard_scores = standard_scores
            except (AssertionError, ZeroDivisionError) as e:
                class_wmetrics.segment_twoset_results = {}
                class_wmetrics.event_detailed_scores = {}
                class_wmetrics.event_standard_scores = {}
                # print("Empty Results or targets for a class.")
                # raise ValueError("Empty Results or targets for a class.")

            wmetrics.class_ward_metrics.append(class_wmetrics)

        tt = []
        pp = []
        for i, class_name in enumerate(self.target_names):
            # skip null class for combined eventing:
            if class_name in ("", "Null"):
                continue

            if len(tt) or len(pp):
                offset = np.max(tt + pp) + 2
            else:
                offset = 0
            [(a + offset, b + offset) for (a, b) in t]

            t = targets_events.get(str(i), [])
            p = preds_events.get(str(i), [])

            tt += [(a + offset, b + offset) for (a, b) in t]
            pp += [(a + offset, b + offset) for (a, b) in p]

        t = tt
        p = pp

        class_wmetrics = ClassWardMetrics()
        assert len(t) and len(p)
        (
            twoset_results,
            segments_with_scores,
            segment_counts,
            normed_segment_counts,
        ) = wardmetrics.eval_segments(t, p)
        class_wmetrics.segment_twoset_results = twoset_results

        (
            gt_event_scores,
            det_event_scores,
            detailed_scores,
            standard_scores,
        ) = wardmetrics.eval_events(t, p)
        class_wmetrics.event_detailed_scores = detailed_scores
        class_wmetrics.event_standard_scores = standard_scores

        # Reformat as dataframe for easier calculations
        df = pd.DataFrame(
            [cm.event_standard_scores for cm in wmetrics.class_ward_metrics],
            index=self.target_names,
        )
        df.loc["all_nonull"] = class_wmetrics.event_standard_scores

        # Calculate F1's to summarize recall/precision for each class
        df["f1"] = (2 * (df["precision"] * df["recall"]) /
                    (df["precision"] + df["recall"]))
        df["f1 (weighted)"] = (
            2 * (df["precision (weighted)"] * df["recall (weighted)"]) /
            (df["precision (weighted)"] + df["recall (weighted)"]))

        # Load dataframes into dictionary output
        wmetrics.df_event_scores = df
        wmetrics.df_event_detailed_scores = pd.DataFrame(
            [cm.event_detailed_scores for cm in wmetrics.class_ward_metrics],
            index=self.target_names,
        )
        wmetrics.df_segment_2set_results = pd.DataFrame(
            [cm.segment_twoset_results for cm in wmetrics.class_ward_metrics],
            index=self.target_names,
        )
        wmetrics.overall_ward_metrics = class_wmetrics

        self.ward_metrics = wmetrics
        self.event_f1 = self.ward_metrics.df_event_scores.loc["all_nonull",
                                                              "f1"]

    def _get_classification_report_df(self):
        df = pd.DataFrame(self.classification_report_dict).T

        # Include Ward-metrics-derived "Event F1 (unweighted by length)"
        if self.ward_metrics:
            df["event_f1"] = self.ward_metrics.df_event_scores["f1"]
        else:
            df["event_f1"] = np.nan

            # Calculate various summary averages
        df.loc["macro avg", "event_f1"] = df["event_f1"].iloc[:-3].mean()
        df.loc["weighted avg", "event_f1"] = (
            df["event_f1"].iloc[:-3] *
            df["support"].iloc[:-3]).sum() / df["support"].iloc[:-3].sum()

        df["support"] = df["support"].astype(int)

        return df

    def _get_nonull_classification_report_df(self):
        target_names = self.target_names
        if not (target_names[0] in ("", "Null")):
            return None

        df = pd.DataFrame(self.nonull_classification_report_dict).T

        df["support"] = df["support"].astype(int)

        return df

    def _save(self, checkpoint_dir=None):
        """ Saves/checkpoints model state and training state to disk. """
        if checkpoint_dir is None:
            checkpoint_dir = self.model_path

        os.makedirs(checkpoint_dir, exist_ok=True)

        # save model params
        evalmodel_path = os.path.join(checkpoint_dir, "evalmodel.pth")

        with open(evalmodel_path, "wb") as f:
            pickle.dump(self, f)

        return checkpoint_dir

    def _restore(self, checkpoint_dir=None):
        """ Restores model state and training state from disk. """

        if checkpoint_dir is None:
            checkpoint_dir = self.model_path

        evalmodel_path = os.path.join(checkpoint_dir, "evalmodel.pth")

        # Reconstitute old trainer and copy state to this trainer.
        with open(evalmodel_path, "rb") as f:
            other_evalmodel = pickle.load(f)

        self.__setstate__(other_evalmodel.__getstate__())

        self.trainer._restore(checkpoint_dir)
コード例 #12
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
コード例 #13
0
class ModelController(_traitsui.Controller):
    '''MVController base class for stat analysis model'''
    id = _traits.DelegatesTo('model')
    name = _traits.Str()
    plot_uis = _traits.List()
    win_handle = _traits.Any()

    # def init(self, info):
    #     super(ModelController, self).init(info)
    #     self.win_handle = info.ui.control

    def _name_default(self):
        raise NotImplementedError('_name_default')

    def __eq__(self, other):
        return self.id == other

    def __ne__(self, other):
        return self.id != other

    def get_result(self):
        return self.model.res

    def open_window(self, viewable, view_loop):
        """Expected viewable is by now:
          + Plot subtype
          + DataSet type
        """
        if isinstance(viewable, PCScatterPlot):
            res = self.get_result()
            plot_control = PCPlotControl(viewable)

            win = SinglePlotWindow(
                plot=plot_control,
                res=res,
                view_loop=view_loop,
            )

            self._show_plot_window(win)
        elif isinstance(viewable, _chaco.DataView):
            res = self.get_result()
            plot_control = NoPlotControl(viewable)

            win = SinglePlotWindow(
                plot=plot_control,
                res=res,
                view_loop=view_loop,
            )

            self._show_plot_window(win)
        elif isinstance(viewable, DataSet):
            table = DSTableViewer(viewable)
            table.edit_traits(view=table.get_view(),
                              kind='live',
                              parent=self.win_handle)
        else:
            raise NotImplementedError("Do not know how to open this")

    def _show_plot_window(self, plot_window):
        # FIXME: Setting parent forcing main ui to stay behind plot windows
        # plot_window.mother_ref = self
        if sys.platform == 'linux2':
            self.plot_uis.append(
                # plot_window.edit_traits(parent=self.win_handle, kind='live')
                plot_window.edit_traits(kind='live'))
        # elif sys.platform == 'win32':
        else:
            # FIXME: Investigate more here
            self.plot_uis.append(
                plot_window.edit_traits(parent=self.win_handle, kind='live')
                # plot_window.edit_traits(kind='live')
            )

    def _wind_title(self, res):
        mn = res.method_name
        return "{0} | Overview - ConsumerCheck".format(mn)
コード例 #14
0
class MainWindow(tr.HasStrictTraits):

    forming_process_view = tr.Instance(FormingProcessView, ())
    forming_task_scene = tr.Instance(FormingTaskView3D, ())

    forming_process = tr.Property

    def _get_forming_process(self):
        return self.forming_process_view.forming_process

    def _set_forming_process(self, fp):
        self.forming_process_view.forming_process = fp

    def _selected_node_changed(self):
        self.selected_node.ui = self

    def get_vot_range(self):
        return self.forming_task_scene.get_vot_range()

    vot = tr.DelegatesTo('forming_task_scene')

    data_changed = tr.Event

    replot = tr.Button

    def _replot_fired(self):
        self.figure.clear()
        self.selected_node.plot(self.figure)
        self.data_changed = True

    clear = tr.Button()

    def _clear_fired(self):
        self.figure.clear()
        self.data_changed = True

    view = tu.View(tu.HSplit(
        tu.VGroup(
            tu.Item(
                'forming_process_view@',
                id='oricreate.hsplit.left.tree.id',
                resizable=True,
                show_label=False,
                width=300,
            ),
            id='oricreate.hsplit.left.id',
        ),
        tu.VGroup(
            tu.Item(
                'forming_task_scene@',
                show_label=False,
                resizable=True,
                id='oricreate.hsplit.viz3d.notebook.id',
            ),
            id='oricreate.hsplit.viz3d.id',
            label='viz sheet',
        ),
        id='oricreate.hsplit.id',
    ),
                   id='oricreate.id',
                   width=1.0,
                   height=1.0,
                   title='OriCreate',
                   resizable=True,
                   handler=TreeViewHandler(),
                   key_bindings=key_bindings,
                   toolbar=tu.ToolBar(*toolbar_actions,
                                      image_size=(32, 32),
                                      show_tool_names=False,
                                      show_divider=True,
                                      name='view_toolbar'),
                   menubar=tu.MenuBar(
                       Menu(menu_exit, Separator(), name='File'), ))
コード例 #15
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

    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
コード例 #16
0
class MKappa(InteractiveModel, InjectSymbExpr):
    """Class returning the moment curvature relationship."""
    name = 'Moment-Curvature'

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

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

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

    DEPSTR = 'state_changed'

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

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

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

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

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

    kappa_slider = Float(0.0000001)

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

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

    apply_material_safety_factors = tr.Bool(False)

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

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

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

    z_j = tr.Property

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

    A_j = tr.Property

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

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

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

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

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

    # SOLVER: Get eps_bot to render zero force

    # num_of_trials = tr.Int(30)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    kappa_norm = tr.Property()

    def _get_kappa_norm(self):
        return self.kappa_cr

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def get_mk(self):
        return self.M_t / self.M_scale, self.kappa_t
コード例 #17
0
ファイル: interfaces.py プロジェクト: ranveeraggarwal/dipy
class ShmTrackingInterface(T.HasStrictTraits):

    dwi_images = T.DelegatesTo('all_inputs')
    all_inputs = T.Instance(InputData, args=())
    min_signal = T.DelegatesTo('all_inputs')
    seed_roi = nifti_file
    seed_density = T.Array(dtype='int', shape=(3, ), value=[1, 1, 1])

    smoothing_kernel_type = T.Enum(None, all_kernels.keys())
    smoothing_kernel = T.Instance(T.HasTraits)

    @T.on_trait_change('smoothing_kernel_type')
    def set_smoothing_kernel(self):
        if self.smoothing_kernel_type is not None:
            kernel_factory = all_kernels[self.smoothing_kernel_type]
            self.smoothing_kernel = kernel_factory()
        else:
            self.smoothing_kernel = None

    interpolator = T.Enum('NearestNeighbor', all_interpolators.keys())
    model_type = T.Enum('SlowAdcOpdf', all_shmodels.keys())
    sh_order = T.Int(4)
    Lambda = T.Float(0, desc="Smoothing on the odf")
    sphere_coverage = T.Int(5)
    min_peak_spacing = T.Range(0., 1, np.sqrt(.5), desc="as a dot product")
    min_relative_peak = T.Range(0., 1, .25)

    probabilistic = T.Bool(False, label='Probabilistic (Residual Bootstrap)')
    bootstrap_input = T.Bool(False)
    bootstrap_vector = T.Array(dtype='int', value=[])

    # integrator = Enum('Boundry', all_integrators.keys())
    seed_largest_peak = T.Bool(False,
                               desc="Ignore sub-peaks and start follow "
                               "the largest peak at each seed")
    start_direction = T.Array(dtype='float',
                              shape=(3, ),
                              value=[0, 0, 1],
                              desc="Prefered direction from seeds when "
                              "multiple directions are available. "
                              "(Mostly) doesn't matter when 'seed "
                              "largest peak' and 'track two directions' "
                              "are both True",
                              label="Start direction (RAS)")
    track_two_directions = T.Bool(False)
    fa_threshold = T.Float(1.0)
    max_turn_angle = T.Range(0., 90, 0)

    stop_on_target = T.Bool(False)
    targets = T.List(nifti_file, [])

    # will be set later
    voxel_size = T.Array(dtype='float', shape=(3, ))
    affine = T.Array(dtype='float', shape=(4, 4))
    shape = T.Tuple((0, 0, 0))

    # set for io
    save_streamlines_to = T.File('')
    save_counts_to = nifti_file

    # io methods
    def save_streamlines(self, streamlines, save_streamlines_to):
        trk_hdr = empty_header()
        voxel_order = orientation_to_string(nib.io_orientation(self.affine))
        trk_hdr['voxel_order'] = voxel_order
        trk_hdr['voxel_size'] = self.voxel_size
        trk_hdr['vox_to_ras'] = self.affine
        trk_hdr['dim'] = self.shape
        trk_tracks = ((ii, None, None) for ii in streamlines)
        write(save_streamlines_to, trk_tracks, trk_hdr)
        pickle.dump(self, open(save_streamlines_to + '.p', 'wb'))

    def save_counts(self, streamlines, save_counts_to):
        counts = density_map(streamlines, self.shape, self.voxel_size)
        if counts.max() < 2**15:
            counts = counts.astype('int16')
        nib.save(nib.Nifti1Image(counts, self.affine), save_counts_to)

    # tracking methods
    def track_shm(self, debug=False):
        if self.sphere_coverage > 7 or self.sphere_coverage < 1:
            raise ValueError("sphere coverage must be between 1 and 7")
        verts, edges, faces = create_half_unit_sphere(self.sphere_coverage)
        verts, pot = disperse_charges(verts, 10, .3)

        data, voxel_size, affine, fa, bvec, bval = self.all_inputs.read_data()
        self.voxel_size = voxel_size
        self.affine = affine
        self.shape = fa.shape

        model_type = all_shmodels[self.model_type]
        model = model_type(self.sh_order, bval, bvec, self.Lambda)
        model.set_sampling_points(verts, edges)

        data = np.asarray(data, dtype='float', order='C')
        if self.smoothing_kernel is not None:
            kernel = self.smoothing_kernel.get_kernel()
            convolve(data, kernel, out=data)

        normalize_data(data, bval, self.min_signal, out=data)
        dmin = data.min()
        data = data[..., lazy_index(bval > 0)]
        if self.bootstrap_input:
            if self.bootstrap_vector.size == 0:
                n = data.shape[-1]
                self.bootstrap_vector = np.random.randint(n, size=n)
            H = hat(model.B)
            R = lcr_matrix(H)
            data = bootstrap_data_array(data, H, R, self.bootstrap_vector)
            data.clip(dmin, out=data)

        mask = fa > self.fa_threshold
        targets = [read_roi(tgt, shape=self.shape) for tgt in self.targets]
        if self.stop_on_target:
            for target_mask in targets:
                mask = mask & ~target_mask

        seed_mask = read_roi(self.seed_roi, shape=self.shape)
        seeds = seeds_from_mask(seed_mask, self.seed_density, voxel_size)

        if ((self.interpolator == 'NearestNeighbor' and not self.probabilistic
             and not debug)):
            using_optimze = True
            peak_finder = NND_ClosestPeakSelector(model, data, mask,
                                                  voxel_size)
        else:
            using_optimze = False
            interpolator_type = all_interpolators[self.interpolator]
            interpolator = interpolator_type(data, voxel_size, mask)
            peak_finder = ClosestPeakSelector(model, interpolator)

        # Set peak_finder parameters for start steps
        peak_finder.angle_limit = 90
        model.peak_spacing = self.min_peak_spacing
        if self.seed_largest_peak:
            model.min_relative_peak = 1
        else:
            model.min_relative_peak = self.min_relative_peak

        data_ornt = nib.io_orientation(self.affine)
        best_start = reorient_vectors(self.start_direction, 'ras', data_ornt)
        start_steps = closest_start(seeds, peak_finder, best_start)

        if self.probabilistic:
            interpolator = ResidualBootstrapWrapper(interpolator,
                                                    model.B,
                                                    min_signal=dmin)
            peak_finder = ClosestPeakSelector(model, interpolator)
        elif using_optimze and self.seed_largest_peak:
            peak_finder.reset_cache()

        # Reset peak_finder parameters for tracking
        peak_finder.angle_limit = self.max_turn_angle
        model.peak_spacing = self.min_peak_spacing
        model.min_relative_peak = self.min_relative_peak

        integrator = BoundryIntegrator(voxel_size, overstep=.1)
        streamlines = generate_streamlines(peak_finder, integrator, seeds,
                                           start_steps)
        if self.track_two_directions:
            start_steps = -start_steps
            streamlinesB = generate_streamlines(peak_finder, integrator, seeds,
                                                start_steps)
            streamlines = merge_streamlines(streamlines, streamlinesB)

        for target_mask in targets:
            streamlines = target(streamlines, target_mask, voxel_size)

        return streamlines
コード例 #18
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
コード例 #19
0
class WBTessellation5PV2(WBNumTessellation):
    name = 'WBTessellation5PV2'

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

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

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

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

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

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

        sol_num = self.sol_num

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

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

        cos_psi1 = ((b**2 - a**2) - a * sqrt(a**2 + b**2) * cos(beta)) / (
            b * sqrt(a**2 + b**2) * sin(beta))
        sin_psi1 = sqrt(a**2 * (3 * b**2 - a**2) + 2 * a *
                        (b**2 - a**2) * sqrt(a**2 + b**2) * cos(beta) -
                        (a**2 + b**2)**2 * cos(beta)**2) / (
                            b * sqrt(a**2 + b**2) * sin(beta))
        cos_psi5 = (sqrt(a**2 + b**2) * cos(beta) -
                    a * cos(2 * gamma)) / (b * sin(2 * gamma))
        sin_psi5 = sqrt(
            b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma) -
            (a**2 + b**2) *
            (cos(beta)**2 + cos(2 * gamma)**2)) / (b * sin(2 * gamma))
        cos_psi6 = (a - sqrt(a**2 + b**2) * cos(beta) * cos(2 * gamma)) / (
            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        sin_psi6 = sqrt(b**2 + 2 * a * sqrt(a**2 + b**2) * cos(beta) *
                        cos(2 * gamma) - (a**2 + b**2) *
                        (cos(beta)**2 + cos(2 * gamma)**2)) / (
                            sqrt(a**2 + b**2) * sin(beta) * sin(2 * gamma))
        cos_psi1plus6 = cos_psi1 * cos_psi6 - sin_psi1 * sin_psi6
        sin_psi1plus6 = sin_psi1 * cos_psi6 + cos_psi1 * sin_psi6

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return -np.array(rhos), -np.array(sigmas)
コード例 #20
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]
コード例 #21
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
コード例 #22
0
class InelStateEvolution(bu.InteractiveModel):
    name = 'State evolution'

    slider_exp = tr.WeakRef(bu.InteractiveModel)

    t_slider = bu.Float(0)
    t_max = bu.Float(1.001)

    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')

    ipw_view = bu.View(bu.Item('t_max', latex=r't_{\max}', readonly=True),
                       time_editor=bu.HistoryEditor(var='t_slider',
                                                    low=0,
                                                    max_var='t_max',
                                                    n_steps=50))

    def plot_omega_NT(self, ax, **kw):
        s_x_pi_, s_y_pi_, w_pi_, z_, alpha_x_, alpha_y_, omega_T_, omega_N_ = self.Eps_arr.T
        ax.plot(omega_N_, omega_T_, **kw)
        ax.set_xlabel(r'$\omega_\mathrm{N}$')
        ax.set_ylabel(r'$\omega_\mathrm{T}$')

    def plot_Sig_Eps(self, axes):
        ax1, ax11, ax2, ax22, ax3, ax33, ax4 = axes
        colors = ['blue', 'red', 'green', 'black', 'magenta']
        t = self.t_arr
        s_x_pi_, s_y_pi_, w_pi_, z_, alpha_x_, alpha_y_, omega_T_, omega_N_ = self.Eps_arr.T
        tau_x_pi_, tau_y_pi_, sig_pi_, Z_, X_x_, X_y_, Y_T_, Y_N_ = self.Sig_arr.T
        n_step = len(s_x_pi_)

        idx = np.argmax(self.t_slider < self.t_arr)

        # slip path in 2d
        def get_cum_s(s_x, s_y):
            d_s_x, d_s_y = s_x[1:] - s_x[:-1], s_y[1:] - s_y[:-1]
            d_s = np.hstack([0, np.sqrt(d_s_x**2 + d_s_y**2)])
            return cumtrapz(d_s, initial=0)

        s_t = get_cum_s(self.s_x_t, self.s_y_t)
        s_pi_t = get_cum_s(s_x_pi_, s_y_pi_)
        w_t = self.w_t
        w_pi_t = w_pi_
        tau_pi = np.sqrt(tau_x_pi_**2 + tau_y_pi_**2)

        ax1.set_title('stress - displacement')
        ax1.plot(t, tau_pi, '--', color='darkgreen', label=r'$||\tau||$')
        ax1.fill_between(t, tau_pi, 0, color='limegreen', alpha=0.1)
        ax1.plot(t, sig_pi_, '--', color='olivedrab', label=r'$\sigma$')
        ax1.fill_between(t, sig_pi_, 0, color='olivedrab', alpha=0.1)
        ax1.set_ylabel(r'$|| \tau ||, \sigma$')
        ax1.set_xlabel('$t$')
        ax1.plot(t[idx], 0, marker='H', color='red')
        ax1.legend()
        ax11.plot(t, s_t, color='darkgreen', label=r'$||s||$')
        ax11.plot(t, s_pi_t, '--', color='orange', label=r'$||s^\pi||$')
        ax11.plot(t, w_t, color='olivedrab', label=r'$w$')
        ax11.plot(t, w_pi_t, '--', color='chocolate', label=r'$w^\pi$')
        ax11.set_ylabel(r'$|| s ||, w$')
        ax11.legend()
        mpl_align_yaxis(ax1, 0, ax11, 0)

        ax2.set_title('energy release rate - damage')
        ax2.plot(t, Y_N_, '--', color='darkgray', label=r'$Y_N$')
        ax2.fill_between(t, Y_N_, 0, color='darkgray', alpha=0.15)
        ax2.plot(t, Y_T_, '--', color='darkslategray', label=r'$Y_T$')
        ax2.fill_between(t, Y_T_, 0, color='darkslategray', alpha=0.05)
        ax2.set_xlabel('$t$')
        ax2.set_ylabel('$Y$')
        ax2.plot(t[idx], 0, marker='H', color='red')
        ax2.legend()
        ax22.plot(t, omega_N_, color='darkgray', label=r'$\omega_N$')
        ax22.plot(t, omega_T_, color='darkslategray', label=r'$\omega_T$')
        ax22.set_ylim(ymax=1)
        ax22.set_ylabel(r'$\omega$')
        ax22.legend()

        ax3.set_title('hardening force - displacement')
        alpha_t = np.sqrt(alpha_x_**2 + alpha_y_**2)
        X_t = np.sqrt(X_x_**2 + X_y_**2)
        ax3.plot(t, Z_, '--', color='darkcyan', label=r'$Z$')
        ax3.fill_between(t, Z_, 0, color='darkcyan', alpha=0.05)
        ax3.plot(t, X_t, '--', color='darkslateblue', label=r'$X$')
        ax3.fill_between(t, X_t, 0, color='darkslateblue', alpha=0.05)
        ax3.set_ylabel(r'$Z, X$')
        ax3.set_xlabel('$t$')
        ax3.plot(t[idx], 0, marker='H', color='red')
        ax3.legend()
        ax33.plot(t, z_, color='darkcyan', label=r'$z$')
        ax33.plot(t, alpha_t, color='darkslateblue', label=r'$\alpha$')
        ax33.set_ylabel(r'$z, \alpha$')
        ax33.legend(loc='lower left')

        slide_model = self.slider_exp.slide_model
        slide_model.plot_f_state(ax4, self.Eps_arr[idx, :],
                                 self.Sig_arr[idx, :])

    @staticmethod
    def subplots(fig):
        ((ax1, ax2), (ax3, ax4)) = fig.subplots(2, 2)
        ax11 = ax1.twinx()
        ax22 = ax2.twinx()
        ax33 = ax3.twinx()
        return ax1, ax11, ax2, ax22, ax3, ax33, ax4

    def update_plot(self, axes):
        self.plot_Sig_Eps(axes)
コード例 #23
0
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')
コード例 #24
0
ファイル: sim_controler.py プロジェクト: simvisage/bmcs
class TStep(tr.HasStrictTraits):

    model = tr.Instance(IModel)
    sim = tr.DelegatesTo('model')

    t_n = tr.Float(0.0)
    t_n1 = tr.Float(0.0)

    def init_state(self):
        self.t_n = 0
        self.t_n1 = 0
        self.model.init_state()
        self.linalg_sys.register_constraints(0, 1)
        for var, state in self.model.S.items():
            state[...] = 0

    trial_state_changed = tr.Event

    R_dR_dU = tr.Property(depends_on='trial_state_changed, t_n1')

    @tr.cached_property
    def _get_R_dR_dU(self):
        R = self.R
        dR_dU = self.dR_dU
        return R, dR_dU

    R = tr.Property(depends_on='trial_state_changed, t_n1')

    @tr.cached_property
    def _get_R(self):
        bc = self.model.bc
        R = self.model.F - self.t_n1
        return R

    R_norm = tr.Property(depends_on='trial_state_changed, t_n1')

    @tr.cached_property
    def _get_R_norm(self):
        R = self.R
        return np.sqrt(np.einsum('...i,...i', R, R))

    dR_dU = tr.Property(depends_on='trial_state_changed')

    @tr.cached_property
    def _get_dR_dU(self):
        return self.model.d_F_U

    linalg_sys = tr.Instance(LinAlgSys, ())

    def make_iter(self):
        R, dR_dU = self.R_dR_dU
        self.linalg_sys.A = dR_dU
        self.linalg_sys.b_0 = R
        self.linalg_sys.apply_constraints(
            self.step_flag, self.t_n, self.t_n1
        )

        d_U = self.linalg_sys.solve()
        #d_U = -R / dR_dU
        self.model.U_k += d_U
        self.trial_state_changed = True

    def make_incr(self, t_n1):
        self.model.U_n[...] = self.model.U_k
        self.t_n1 = t_n1
コード例 #25
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
コード例 #26
0
ファイル: sim_controler.py プロジェクト: simvisage/bmcs
class TLoop(tr.HasTraits):

    tstep = tr.Instance(ITStep)
    sim = tr.DelegatesTo('tstep')
    tline = tr.Property

    def _get_tline(self):
        return self.sim.tline

    k_max = tr.Int(100, enter_set=True, auto_set=False)

    acc = tr.Float(1e-4, enter_set=True, auto_set=False)

    verbose = tr.Bool(False, enter_set=True, auto_set=False)

    paused = tr.Bool(False)

    restart = tr.Bool(True)

    user_wants_abort = tr.Property

    def _get_user_wants_abort(self):
        return self.restart or self.paused

    def init(self):
        if self.paused:
            self.paused = False
        if self.restart:
            self.tline.val = self.tline.min
            self.tstep.init_state()
            self.restart = False

    def eval(self):
        t_n1 = self.tline.val
        t_max = self.tline.max
        dt = self.tline.step

        if self.verbose:
            print('t:', end='')

        while t_n1 <= (t_max + 1e-8):
            if self.verbose:
                print('\t%5.2f' % t_n1, end='')
            k = 0
            self.tstep.t_n1 = t_n1
            while (k < self.k_max) and (not self.user_wants_abort):
                R_norm = self.tstep.R_norm
                if R_norm < self.acc:
                    if self.verbose:
                        print('(%g), ' % k, end='\n')
                    break
                try:
                    self.tstep.make_iter()
                except RuntimeError as e:
                    raise(e)
                k += 1
            else:  # handle unfinished iteration loop
                if k >= self.k_max:  # maximum number of restarts exceeded
                    # no success abort the simulation
                    self.restart = True
                    print('')
                    raise StopIteration('Warning: '
                                        'convergence not reached in %g iterations' % k)
                else:  # reduce the step size
                    dt /= 2
                    continue

            # accept the time step and record the state in history
            self.tstep.make_incr(t_n1)
            # update the line - launches notifiers to subscribers
            self.tline.val = min(t_n1, self.tline.max)
            # set a new target time
            t_n1 += dt
            self.tstep.t_n1 = t_n1
        return