Exemple #1
0
class DofGridSlice(CellGridSlice):

    dof_grid = WeakRef(DofCellGrid)

    def __init__(self, dof_grid, **args):
        self.dof_grid = dof_grid
        super(DofGridSlice, self).__init__(**args)

    cell_grid = Property()

    def _get_cell_grid(self):
        return self.dof_grid.cell_grid

    dofs = Property

    def _get_dofs(self):
        _, idx2 = self.idx_tuple
        return self.dof_grid.cell_dof_map[np.ix_(
            self.elems, self.cell_grid.grid_cell[idx2])]
Exemple #2
0
class Exporter(HasTraits):
    '''Base class for visualization objects.
    Each state and operator objects like crease pattern
    or constraint can define provide tailored visualizations
    transferring the information into a view objects shared
    within a particular forming task or a whole forming process.
    '''

    label = Str('default')
    '''Label of the visualization object.
    '''

    forming_task = WeakRef(FormingTask)
    '''Link to the visual object to transform into the 
    forming_task_view3d.
    '''

    forming_task_changed = Event
    '''Event registering changes in the source object.
Exemple #3
0
class DofGridSlice(CellGridSlice):

    dof_grid = WeakRef(DofCellGrid)

    def __init__(self, dof_grid, **args):
        self.dof_grid = dof_grid
        super(DofGridSlice, self).__init__(**args)

    cell_grid = Property(depends_on='dof_grid.+changed_structure')

    @cached_property
    def _get_cell_grid(self):
        return self.dof_grid.cell_grid

    dofs = Property

    def _get_dofs(self):
        idx1, idx2 = self.idx_tuple
        return self.dof_grid.cell_dof_map[ix_(self.elems,
                                              self.cell_grid.grid_cell[idx2])]
Exemple #4
0
class VariedParam(HasTraits):
    """
    Association between the spatial function and material parameter.
    """
    mats_eval = WeakRef(IMATSEval)

    varname = String

    reference_value = Property(Float, depends_on='mats_eval')

    @cached_property
    def _get_reference_value(self):
        return getattr(self.mats_eval, self.varname)

    switch = Enum('constant', 'varied')

    spatial_fn = Instance(MFnNDGrid)

    variable = Property(Float)

    def _get_variable(self):
        return getattr(self.mats_eval, self.varname)

    def _set_variable(self, value):
        setattr(self.mats_eval, self.varname, value)

    def adjust_for_sctx(self, sctx):
        if self.switch == 'varied':
            X_pnt = sctx.fets_eval.get_X_pnt(sctx)
            # X_coord = zeros(3)
            # X_coord[0:2] = X_pnt
            coeff = self.spatial_fn(X_pnt)
            self.variable = self.reference_value * coeff

    traits_view = View(Group(
        Item('varname', style='readonly', show_label=False),
        Item('reference_value', style='readonly'),
        Item('switch', style='custom', show_label=False),
        Item('spatial_fn', style='custom', show_label=False, resizable=True)),
                       resizable=True,
                       height=800)
class FEGridNodeSlice(HasStrictTraits):
    '''General implementation of a slice within the FEGrid
    '''

    fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid')

    grid_slice = Any

    def __repr__(self):
        return repr(self.grid_slice)

    dof_nodes = Property

    def _get_dof_nodes(self):
        return self.fe_grid.dof_grid.cell_grid.point_idx_grid[self.grid_slice]

    dofs = Property

    def _get_dofs(self):
        return self.fe_grid.dof_grid.dofs_Ia[self.dof_nodes, :]

    dof_X = Property

    def _get_dof_X(self):
        return self.fe_grid.dof_grid.cell_grid.point_X_arr[self.dof_nodes, :]

    geo_nodes = Property

    def _get_geo_nodes(self):
        return self.fe_grid.geo_grid.cell_grid.point_idx_grid[self.grid_slice]

    geo_x = Property

    def _get_geo_x(self):
        return self.fe_grid.geo_grid.cell_grid.point_x_arr[self.geo_nodes, :]

    geo_X = Property

    def _get_geo_X(self):
        return self.fe_grid.geo_grid.cell_grid.point_X_arr[self.geo_nodes, :]
Exemple #6
0
class RTraceEval(HasTraits):
    name = Str('unnamed')
    ts = WeakRef(ITStepperEval)

    u_mapping = Callable
    eval = Callable

    def __call__(self, sctx, u, *args, **kw):

        # When crossing the levels - start a mapping
        # This method might have side effects for the context
        # - mapping of global to local values
        #
        args_mapped = []
        kw_mapped = {}

        if self.u_mapping:

            u = self.u_mapping(sctx, u)

            # map everything that has been sent together with u
            # this might be the time derivatives of u or its
            # spatial integrals.
            #
            args_mapped = [self.u_mapping(sctx, u_value)
                           for u_value in args]

            kw_mapped = {}
            for u_name, u_value in kw.items():
                kw_mapped[u_name] = self.u_mapping(sctx, u_value)

        # Invoke the tracer evaluation.
        #
        try:
            val = self.eval(sctx, u, *args_mapped, **kw_mapped)
        except TypeError, e:
            raise TypeError, 'tracer name %s: %s %s' % (
                self.name, e, self.eval)

        return val
class FEGridIdxSlice(HasStrictTraits):
    '''General implementation of a slice within the FEGrid
    '''

    fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid')

    grid_slice = Any

    def __repr__(self):
        return repr(self.grid_slice)

    dof_grid_slice = Property(depends_on='fe_grid.dof_grid')

    @cached_property
    def _get_dof_grid_slice(self):
        return self.fe_grid.dof_grid[self.grid_slice]

    geo_grid_slice = Property(depends_on='fe_grid.geo_grid')

    @cached_property
    def _get_geo_grid_slice(self):
        return self.fe_grid.geo_grid[self.grid_slice]

    elem_grid = Property

    def _get_elem_grid(self):
        return self.dof_grid_slice.elem_grid

    elems = Property

    def _get_elems(self):
        return self.dof_grid_slice.elems

    dof_nodes = Property

    def _get_dof_nodes(self):
        return self.dof_grid_slice.nodes

    dofs = Property

    def _get_dofs(self):
        return self.dof_grid_slice.dofs

    dof_X = Property

    def _get_dof_X(self):
        return self.dof_grid_slice.point_X_arr

    geo_nodes = Property

    def _get_geo_nodes(self):
        return self.geo_grid_slice.nodes

    geo_x = Property

    def _get_geo_x(self):
        return self.geo_grid_slice.point_x_arr

    geo_X = Property

    def _get_geo_X(self):
        return self.geo_grid_slice.point_X_arr
Exemple #8
0
class SimulationStep(HasStrictTraits):
    r"""Class implementing the transition of the formed object 
    from its initial time to the target time :math:`t`.
    """

    forming_task = WeakRef(FormingTask)
    r'''Backward link to the client forming tasks.
    This may be an incremental time stepping SimulationTask
    of a MappingTaks performed in a single iterative step.
    '''

    source_config_changed = Event
    r'''Notification event for changes in the configuration
    of the optimization problem.
    '''

    config = Instance(SimulationConfig)
    r'''Configuration of the optimization problem.
    '''

    # =====================================================================
    # Cached properties derived from configuration and position in the pipe
    # =====================================================================
    cp_state = Property(Instance(CreasePatternState),
                        depends_on='source_config_changed')
    r'''Crease pattern state.
    '''
    @cached_property
    def _get_cp_state(self):
        return self.forming_task.formed_object

    fu = Property(depends_on='source_config_changed')
    r'''Goal function object.
    '''

    @cached_property
    def _get_fu(self):
        self.config.fu.forming_task = self.forming_task
        return self.config.fu

    gu = Property(depends_on='source_config_changed')
    r'''Goal function object.
    '''

    @cached_property
    def _get_gu(self):
        self.config.gu.forming_task = self.forming_task
        return self.config.gu

    gu_lst = Property(depends_on='source_config_changed')
    r'''Equality constraint object.
    '''

    @cached_property
    def _get_gu_lst(self):
        for gu in self.config.gu_lst:
            gu.forming_task = self.forming_task
        return self.config.gu_lst

    hu_lst = Property(depends_on='source_config_changed')
    r'''Inequality constraint object.
    '''

    @cached_property
    def _get_hu_lst(self):
        for hu in self.config.gu_lst:
            hu.forming_task = self.forming_task
        return self.config.hu_lst

    def __str__(self):
        s = ''
        for gu in self.gu_lst:
            s += str(gu)
        return s

    debug_level = DelegatesTo("config")
    # ===========================================================================
    # Configuration parameters for the iterative solver
    # ===========================================================================
    t = Float(1.0, auto_set=False, enter_set=True)
    r'''Target time within the step in the range (0,1).
    '''

    # =========================================================================
    # Output data
    # =========================================================================

    x_0 = Property
    r'''Initial position of all nodes.
    '''

    def _get_x_0(self):
        return self.X_0.reshape(-1, self.n_D)

    U = Property(Array(float))
    r'''Intermediate displacement vector of the cp_state
    '''

    def _get_U(self):
        return self.cp_state.U

    def _set_U(self, value):
        self.cp_state.U = value

    U_t = Property(depends_on='t')
    r'''Final displacement vector :math:`X` at target time :math:`t`.
    '''

    @cached_property
    def _get_U_t(self):
        U_t = self._solve()
        return U_t

    X_t = Property()
    r'''Vector of node positions :math:`U` at target time :math:`t`.
    '''

    def _get_X_t(self):
        return self.X_0[np.newaxis, :] + self.U_t

    x_t = Property()
    r'''Array of node positions :math:`x_{ij}` at target time :math:`t` - [node, dim].
    '''

    def _get_x_t(self):
        n_t = self.X_t.shape[0]
        return self.X_t.reshape(n_t, -1, self.n_D)

    u_t = Property()
    r'''Array of nodal displaceements :math:`x_{ij}`  [node, dim].
    '''

    def _get_u_t(self):
        n_D = self.cp_state.n_D
        return self.U_t.reshape(-1, n_D)

    # ===========================================================================
    # Iterative solvers
    # ===========================================================================

    def _solve(self):
        '''Decide which solver to take and start it.
        '''
        self.config.validate_input()
        if self.config.goal_function_type_ is not None:
            U_t = self._solve_fmin()
        else:
            # no goal function - switch to an implicit time-stepping algorithm
            print('SOLVING NR')
            U_t = self._solve_nr()

        return U_t

    def _solve_nr(self):
        '''Find the solution using the Newton-Raphson procedure.
        '''
        i = 0
        U_save = np.copy(self.U)
        acc = self.config.acc
        max_iter = self.config.MAX_ITER
        while i <= max_iter:
            dR = self.get_G_du_t(self.U)
            R = self.get_G_t(self.U)
            nR = np.linalg.norm(R)
            if nR < acc:
                print('==== converged in ', i, 'iterations ====')
                break
            try:
                d_U = np.linalg.solve(dR, -R)
                self.U += d_U  # in-place increment
                i += 1
            except Exception as inst:
                print('=== Problems solving iteration step %d  ====' % i)
                print('=== Exception message: ', inst)
                self.U = U_save
                raise inst
        else:
            self.U = U_save
            print('==== did not converge in %d iterations ====' % i)

        # update the state object with the new displacement vector
        return self.U

    def _solve_fmin(self):
        '''Solve the problem using the
        Sequential Least Square Quadratic Programming method.
        '''
        print('==== solving with SLSQP optimization ====')
        U_save = np.copy(self.U)
        d0 = self.get_f_t(self.U)
        eps = d0 * 1e-4
        get_f_du_t = None
        get_G_du_t = None
        get_H_t = None
        get_H_du_t = None
        acc = self.config.acc
        max_iter = self.config.MAX_ITER

        if self.config.use_f_du:
            get_f_du_t = self.get_f_du_t
        if self.config.use_G_du:
            get_G_du_t = self.get_G_du_t
        if self.config.has_H:
            get_H_t = self.get_H_t
            if self.config.use_H_du:
                get_H_du_t = self.get_H_du_t

        info = fmin_slsqp(
            self.get_f_t,
            self.U,
            fprime=get_f_du_t,
            f_eqcons=self.get_G_t,
            fprime_eqcons=get_G_du_t,
            #                           f_ieqcons=get_H_t,
            #                           fprime_ieqcons=get_H_du_t,
            acc=acc,
            iter=max_iter,
            iprint=2,
            full_output=True,
            epsilon=eps)
        U, f, n_iter, imode, smode = info
        if imode == 0:
            print('(time: %g, iter: %d, f: %g)' % (self.t, n_iter, f))
        else:
            # no convergence reached.
            self.U = U_save
            print('(time: %g, iter: %d, f: %g, err: %d, %s)' % \
                (self.t, n_iter, f, imode, smode))
        return U

    def clear_iter(self):
        self.u_it_list = []

    record_iter = Bool(False)
    u_it_list = List

    # ==========================================================================
    # Goal function
    # ==========================================================================

    def get_f_t(self, U):
        '''Get the goal function value.
        '''
        if self.record_iter:
            self.u_it_list.append(np.copy(U.reshape(-1, 3)))
        self.cp_state.U = U
        f = self.get_f()
        if self.debug_level > 0:
            print('f:\n', f)
        return f

    def get_f(self):
        return self.fu.get_f(self.t)

    def get_f_du_t(self, U):
        '''Get the goal function derivatives.
        '''
        self.cp_state.U = U
        f_du = self.get_f_du()
        if self.debug_level > 2:
            print('f_du.shape:\n', f_du.shape)
            print('f_du:\n', f_du)
        return f_du

    def get_f_du(self):
        return self.fu.get_f_du(self.t)

    # ==========================================================================
    # Equality constraints
    # ==========================================================================
    def get_G_t(self, U):
        self.cp_state.U = U
        g = self.get_G(self.t)
        if self.debug_level > 1:
            print('G:\n', [g])
        return g

    def get_G(self, t=0):
        g_lst = [gu.get_G(t) for gu in self.gu_lst]
        if (g_lst == []):
            return []
        return np.hstack(g_lst)

    def get_G_du_t(self, U):
        self.cp_state.U = U
        return self.get_G_du(self.t)

    def get_G_du(self, t=0):
        g_du_lst = [gu.get_G_du(t) for gu in self.gu_lst]
        if (g_du_lst == []):
            return []
        g_du = np.vstack(g_du_lst)
        if self.debug_level > 3:
            print('G_du.shape:\n', g_du.shape)
            print('G_du:\n', [g_du])
        return g_du

    # ==========================================================================
    # Inequality constraints
    # ==========================================================================
    def get_H_t(self, U):
        self.cp_state.U = U
        h = self.get_H(self.t)
        if self.debug_level > 1:
            print('H:\n', [h])
        return h

    def get_H(self, t=0):
        h_lst = [hu.get_H(t) for hu in self.hu_lst]
        if (h_lst == []):
            return []
        return np.hstack(h_lst)

    def get_H_du_t(self, U):
        self.cp_state.U = U
        return self.get_H_du(self.t)

    def get_H_du(self, t=0):
        h_du_lst = [hu.get_H_du(t) for hu in self.hu_lst]
        if (h_du_lst == []):
            return []
        h_du = np.vstack(h_du_lst)
        if self.debug_level > 3:
            print('G_du.shape:\n', h_du.shape)
            print('G_du:\n', [h_du])
        return h_du
Exemple #9
0
class BaseComponent(HasStrictTraits):
    """ The most base class of the Enaml component heierarchy.

    All Enaml classes should inherit from this class. This class is not 
    meant to be used directly.

    """
    #: The parent component of this component. It is stored as a weakref
    #: to mitigate issues with reference cycles. A top-level component's
    #: parent is None.
    parent = WeakRef('BaseComponent')

    #: The list of children components for this component. Subclasses
    #: should redefine this trait to restrict which types of children
    #: they allow if necessary. This list should not be manipulated
    #: outside of the ``*_child(...)`` methods so that the ui may be
    #: properly updated when the children change.
    children = List(Instance('BaseComponent'))

    #: The toolkit specific object that implements the behavior of
    #: this component and manages the gui toolkit object. Subclasses
    #: should redefine this trait to specify the specialized type of
    #: abstract_obj that is accepted.
    abstract_obj = Instance(AbstractTkBaseComponent)

    #: A list of hooks that are called during the setup process
    #: that can modify the behavior of the component such as installing
    #: listeners at the appropriate times. Hooks should normally be
    #: appended to this list instead of a list being assigned atomically.
    setup_hooks = List(Instance(AbstractSetupHook))

    #: The toolkit that created this object. This does not need to
    #: be stored weakly because the toolkit does not maintain refs
    #: to the compoents that its constructors create.
    toolkit = Instance(Toolkit)

    #: Whether or not the widget is enabled.
    enabled = Bool(True)

    #: Whether or not the widget is visible.
    visible = Bool(True)

    #: Whether the component has been initialized for not. This will be set to
    #: True after all of the setup() steps defined here are completed. It should
    #: not be changed afterwards. This can be used to trigger certain actions
    #: that need to be called after the component has been set up.
    initialized = Bool(False)

    #: The background color of the widget
    bg_color = Property(ColorTrait,
                        depends_on=['_user_bg_color', '_style_bg_color'])

    #: Private trait holding the user-set background color value
    _user_bg_color = ColorTrait

    #: Private trait holding the background color value from the style
    _style_bg_color = ColorTrait

    #: The foreground color of the widget
    fg_color = Property(ColorTrait,
                        depends_on=['_user_fg_color', '_style_fg_color'])

    #: Private trait holding the user-set foreground color value
    _user_fg_color = ColorTrait

    #: Private trait holding the foreground color value from the style
    _style_fg_color = ColorTrait

    #: The foreground color of the widget
    font = Property(FontTrait, depends_on=['_user_font', '_style_font'])

    #: Private trait holding the user-set foreground color value
    _user_font = FontTrait

    #: Private trait holding the foreground color value from the style
    _style_font = FontTrait

    #: The attributes on this class that can be set by the styling mechanism
    _style_tags = Tuple(Str, ('bg_color', 'fg_color', 'font'))

    #: The optional style identifier for the StyleSheet system.
    style_id = Str

    #: The optional style type for the StyleSheet system. This
    #: is set by default by the constructor object.
    style_type = Str

    #: The optional style class for the StyleSheet system.
    style_class = Str

    #: An optional name to give to this component to assist in finding
    #: it in the tree.
    name = Str

    def add_child(self, child):
        """ Add the child to this component.

        Arguments
        ---------
        child : Instance(BaseComponent)
            The child to add to the component.

        """
        self.children.append(child)

    def remove_child(self, child):
        """ Remove the child from this component.

        Arguments
        ---------
        child : Instance(BaseComponent)
            The child to remove from the container.

        """
        try:
            self.children.remove(child)
        except ValueError:
            raise ValueError('Child %s not in children.' % child)

    def replace_child(self, child, other_child):
        """ Replace a child with another child.

        Arguments
        ---------
        child : Instance(BaseComponent)
            The child being replaced.

        other_child : Instance(BaseComponent)
            The child taking the place of the removed child.

        """
        try:
            idx = self.children.index(child)
        except ValueError:
            raise ValueError('Child %s not in children.' % child)
        self.children[idx] = other_child

    def swap_children(self, child, other_child):
        """ Swap the position of the two children.

        Arguments
        ---------
        child : Instance(BaseComponent)
            The first child in the swap.

        other_child : Instance(BaseComponent)
            The second child in the swap.

        """
        try:
            idx = self.children.index(child)
        except ValueError:
            raise ValueError('Child %s not in children.' % child)
        try:
            other_idx = self.children.index(other_child)
        except ValueError:
            raise ValueError('Child %s not in children.' % other_child)
        self.children[idx] = other_child
        self.children[other_idx] = child

    def traverse(self):
        """ Yields all of the nodes in the tree in breadth first order.

        """
        deq = deque([self])
        while deq:
            item = deq.popleft()
            yield item
            deq.extend(item.children)

    def find_by_name(self, name):
        """ Find a component in this tree by name. 

        This method will traverse the tree of components, breadth first,
        from this point downward, looking for a component with the given
        name. The first one with the given name is returned, or None if
        no component is found.

        Parameters
        ----------
        name : string
            The name of the component for which to search.
        
        Returns
        -------
        result : BaseComponent or None
            The first component found with the given name, or None if 
            no component is found.
        
        """
        for cmpnt in self.traverse():
            if cmpnt.name == name:
                return cmpnt

    def set_style_sheet(self, style_sheet):
        """ Sets the style sheet for this component.

        Arguments
        ---------
        style_sheet : StyleSheet
            The style sheet instance for this component.

        """
        raise NotImplementedError('Implement me!')

    def setup(self, parent=None):
        """ Run the setup process for the ui tree.

        This method splits up the setup process into several passes:
        
        1) The child shell objects are given a reference to their parent
        2) The abstract objects are given a reference to the shell object
        3) The abstract objects create their internal toolkit object
        4) The abstract objects initialize their internal toolkit object
        5) The abstract objects bind their event handlers
        6) The abstract object is added as a listener to the shell object

        Each of these methods are performed top down. Setup hooks are 
        called for items 3, 4, and 5.

        After step 6, the `initialized` trait is set to True.

        Parameters
        ----------
        parent : native toolkit widget, optional
            If embedding this BaseComponent into a non-Enaml GUI, use this
            to pass the appropriate toolkit widget that should be the parent.

        """
        self.set_parent_refs()
        self.set_shell_refs()
        self.create(parent)
        self.initialize()
        self.bind()
        self.set_listeners()

        self.initialized = True

    def set_parent_refs(self):
        """ Assigns a reference to self for every child in children and 
        dispatches the tree top down. This should not normally be called 
        by user code.

        """
        for child in self.children:
            child.parent = self
            child.set_parent_refs()

    def set_shell_refs(self):
        """ Assigns a reference to self to the abstract obj and 
        dispatches the tree top down. This should not normally be 
        called by user code.

        """
        self.abstract_obj.shell_obj = self
        for child in self.children:
            child.set_shell_refs()

    @setup_hook
    def create(self, parent):
        """ A setup method that tells the abstract object to create its
        internal toolkit object. This should not normally be called by 
        user code.

        """
        self.abstract_obj.create(parent)
        # FIXME: toolkit_widget is defined on Component, not BaseComponent.
        # FIXME: technically, we allow toolkit_widget to be something that is
        # not precisely a real toolkit widget (e.g. a QLayout).
        self_widget = self.toolkit_widget
        for child in self.children:
            child.create(self_widget)

    @setup_hook
    def initialize(self):
        """ A setup method that tells the abstract object to initialize
        its internal toolkit object. This should not normally be called 
        by user code.

        """
        self.abstract_obj.initialize()
        for child in self.children:
            child.initialize()

    @setup_hook
    def bind(self):
        """ A setup method that tells the abstract object to bind its
        event handlers to its internal toolkit object. This should not
        normally be called by user code.

        """
        self.abstract_obj.bind()
        for child in self.children:
            child.bind()

    def set_listeners(self):
        """ Sets the abstract object as a traits listener for this
        component with a prefix of 'shell'.

        """
        self.add_trait_listener(self.abstract_obj, 'shell')
        for child in self.children:
            child.set_listeners()

    def _set_bg_color(self, new):
        """ Property setter for the 'bg_color' background color property.
        Set values are pushed to the '_user_bg_color' trait.
        """
        self._user_bg_color = new

    def _get_bg_color(self):
        """ Property sgtter for the 'bg_color' background color property.
        We use the '_user_bg_color' trait unless it is None.
        """
        if self._user_bg_color:
            return self._user_bg_color
        return self._style_bg_color

    def _set_fg_color(self, new):
        """ Property setter for the 'fg_color' foreground color property.
        Set values are pushed to the '_user_fg_color' trait.
        """
        self._user_fg_color = new

    def _get_fg_color(self):
        """ Property sgtter for the 'fg_color' foreground color property.
        We use the '_user_fg_color' trait unless it is None.
        """
        if self._user_fg_color:
            return self._user_fg_color
        return self._style_fg_color

    def _set_font(self, new):
        """ Property setter for the 'fg_color' foreground color property.
        Set values are pushed to the '_user_fg_color' trait.
        """
        self._user_font = new

    def _get_font(self):
        """ Property sgtter for the 'fg_color' foreground color property.
        We use the '_user_fg_color' trait unless it is None.
        """
        if self._user_font:
            return self._user_font
        return self._style_font
Exemple #10
0
class Constructor(HasStrictTraits):
    """ The constructor class to use to populate the toolkit.

    """
    #: A callable object which returns the shell class to use
    #: for the widget.
    shell_loader = Callable

    #: A callable object which returns the abstract implementation class
    #: to use for the widget.
    abstract_loader = Callable

    #: The key with which this constructor was added to the toolkit.
    #: It is set by the toolkit and used as the type name of the
    #: instantiated component.
    style_type = Str

    #: A reference (stored weakly) to the toolkit in which this
    #: constructor is contained. It is used to set the toolkit
    #: attribute on the components as they are created.
    toolkit = WeakRef('Toolkit')

    def __init__(self, shell_loader, abstract_loader):
        """ Initialize a constructor instance.

        Parameters
        ----------
        shell_loader : Callable
            A callable object which returns the shell class to use
            for the widget.

        abstract_loader : Callable
            A callable object which returns the abstract implementation
            class to use for the widget.

        """
        super(Constructor, self).__init__()
        self.shell_loader = shell_loader
        self.abstract_loader = abstract_loader

    def __call__(self, *args, **kwargs):
        """ Calls the loaders and assembles the component.

        Subclasses should override this method to implement custom
        construction behavior if the default is not sufficient.

        """
        shell_cls = self.shell_loader()
        abstract_cls = self.abstract_loader()
        component = shell_cls(style_type=self.style_type,
                              toolkit=self.toolkit,
                              abstract_obj=abstract_cls())
        return component

    def clone(self, shell_loader=None, abstract_loader=None):
        """ Creates a clone of this constructor, optionally changing
        out one or both of the loaders.

        """
        if shell_loader is None:
            shell_loader = self.shell_loader
        if abstract_loader is None:
            abstract_loader = self.abstract_loader
        return Constructor(shell_loader, abstract_loader)
class CellGridSlice(HasTraits):
    '''General implementation of a slice within the FEGrid
    '''
    cell_grid = WeakRef()  # 'ibvpy.mesh.cell_grid.cell_grid.CellGrid' )

    grid_slice = Any

    idx_tuple = Property(Tuple,
                         depends_on='grid_slice,cell_grid.+changed_structure')

    @cached_property
    def _get_idx_tuple(self):
        ''' Classify the index specification
            By default, the idx is assigned to element index
            to handle the case of a single integer index
            '''
        idx1 = self.grid_slice
        # The default for node index is the universal slice
        idx2 = slice(None, None)
        n_dims = self.cell_grid.n_dims
        # If the index is an iterable
        if hasattr(self.grid_slice, '__iter__'):
            # Get the first n_dims of indexes for the cell grid
            idx1 = self.grid_slice[:n_dims]
            # If there are more indexes than n_dims save them for
            # use within the cell to identify the nodes.
            if len(self.grid_slice) > n_dims:
                idx2 = self.grid_slice[n_dims:]
        return (idx1, idx2)

    elem_grid = Property

    def _get_elem_grid(self):
        idx1, _ = self.idx_tuple
        return self.cell_grid.cell_idx_grid[idx1]

    elems = Property

    def _get_elems(self):
        # get the cells affected by the slice @todo - rename cell_grid
        # attribute within CellGrid
        return self.elem_grid.flatten()

    # Get the node map associated with the sliced elements
    #
    nodes = Property

    def _get_nodes(self):
        # get the node map associated with the sliced elements
        idx1, idx2 = self.idx_tuple

        sliced_cell_node_map = self.cell_grid.cell_node_map[self.elems]

        # get the sliced nodes for the sliced cells
        return sliced_cell_node_map[:, self.cell_grid.grid_cell[idx2]]

    points = Property

    def _get_points(self):
        # get the coordinates for the sliced coords
        return self.cell_grid.point_X_arr[self.nodes]

    # Global coordinates of nodes involved in the slice
    # Structured element by element
    #
    point_X_arr = Property

    def _get_point_X_arr(self):
        return self.cell_grid.point_X_arr[self.nodes]

    # Parametric coordinates of nodes involved in the slice
    # Structured element by element
    #
    point_x_arr = Property

    def _get_point_x_arr(self):
        return self.cell_grid.point_x_arr[self.nodes]
class CamMove(HasStrictTraits):
    '''Camera transitions.

    Attach functional mapping depending on time variable
    for azimuth, elevation, distance, focal point and roll angle.
    '''

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

    fta = WeakRef
    ftv = WeakRef
    from_station = WeakRef(CamStation)
    to_station = WeakRef(CamStation)

    changed = Event

    cam_attributes = [
        'azimuth', 'elevation', 'distance', 'fpoint', 'roll']

    azimuth_move = Trait('linear', {'linear': linear_cam_move,
                                    'damped': damped_cam_move})
    elevation_move = Trait('linear', {'linear': linear_cam_move,
                                      'damped': damped_cam_move})
    distance_move = Trait('linear', {'linear': linear_cam_move,
                                     'damped': damped_cam_move})
    fpoint_move = Trait('linear', {'linear': linear_cam_move,
                                   'damped': damped_cam_move})
    roll_move = Trait('linear', {'linear': linear_cam_move,
                                 'damped': damped_cam_move})

    duration = Float(10, label='Duration')

    bts = Property(label='Start time')

    def _get_bts(self):
        return self.from_station.time_stemp

    ets = Property(label='End time')

    def _get_ets(self):
        return self.from_station.time_stemp + self.duration

    n_t = Int(10, input=True)

    cmt = Property(Array('float_'), depends_on='n_t')
    '''Relative camera move time (CMT) running from zero to one.
    '''
    @cached_property
    def _get_cmt(self):
        return np.linspace(0, 1, self.n_t)

    viz_t_move = Property(Array('float_'))
    '''Time line range during the camera move
    '''

    def _get_viz_t_move(self):
        return np.linspace(self.bts, self.ets, self.n_t)

    vot_start = Float(0.0, auto_set=False, enter_set=True, input=True)
    vot_end = Float(1.0, auto_set=False, enter_set=True, input=True)
    vot = Property(Array('float_'), depends_on='n_t,vot_start,vot_end')
    '''Visualization object time (VOT). By default it is the same as the camera time.
    It can be mapped to a different time profile using viz_time_fn
    '''
    @cached_property
    def _get_vot(self):
        return np.linspace(self.vot_start, self.vot_end, self.n_t)

    def _get_vis3d_center_t(self):
        '''Get the center of the object'''
        return self.ftv.get_center_t

    def _get_vis3d_bounding_box_t(self):
        '''Get the bounding box of the object'''
        return self.vis.get_center(self.t_range)

    transition_arr = Property(
        Array(dtype='float_'), depends_on='changed,+input')
    '''Array with azimuth values along the transition 
    '''
    @cached_property
    def _get_transition_arr(self):
        trans_arr = [getattr(self, attr + '_move_')(
            getattr(self.from_station, attr),
            getattr(self.to_station, attr), self.n_t)
            for attr in self.cam_attributes
        ]
        trans_arr.append(self.vot)
        trans_arr.append(self.viz_t_move)
        return trans_arr

    def reset_cam(self, m, a, e, d, f, r):
        m.view(azimuth=a, elevation=e, distance=d, focalpoint=f)
        m.roll(r)

    def take(self, ftv):
        for a, e, d, f, r, vot, viz_t in zip(*self.transition_arr):
            ftv.update(vot, viz_t, force=True)
            self.reset_cam(ftv.mlab, a, float(e), d, f, r)
            sleep(self.fta.anim_delay)

    def render_take(self, ftv, fname_base, format_, idx_offset, figsize_factor):
        im_files = []
        for idx, (a, e, d, f, r, vot, viz_t) \
                in enumerate(zip(*self.transition_arr)):
            ftv.update(vot, viz_t, force=True)
            # @todo: temporary focal point determination - make it optional
            self.reset_cam(ftv.mlab, a, e, d, f, r)
            fname = '%s%03d.%s' % (fname_base, idx + idx_offset, format_)
            ftv.mlab.savefig(fname, size=(
                figsize_factor * 800, figsize_factor * 500))  #
            im_files.append(fname)
        return im_files

    view = View(VGroup(HGroup(InstanceUItem('from_station@', resizable=True),
                              VGroup(Item('azimuth_move'),
                                     Item('elevation_move'),
                                     Item('distance_move'),
                                     Item('roll_move'),
                                     Item('fpoint_move'),
                                     Item('n_t'),
                                     Item('duration'),
                                     ),
                              InstanceUItem('to_station@', resizable=True),
                              ),
                       VGroup(HGroup(UItem('vot_start'),
                                     UItem('vot_end'),
                                     springy=True
                                     ),
                              label='object time range'
                              ),
                       ),
                buttons=['OK', 'Cancel'])
Exemple #13
0
class BaseComponent(HasStrictTraits):
    """ The most base class of the Enaml component hierarchy.

    All declarative Enaml classes should inherit from this class. This 
    class is not meant to be instantiated directly.

    """
    #: A readonly property which returns the current instance of
    #: the component. This allows declarative Enaml components to
    #: access self according to the standard attribute scoping rules.
    self = Property

    #: The parent component of this component. It is stored as a weakref
    #: to mitigate issues with reference cycles. A top-level component's
    #: parent is None.
    parent = WeakRef('BaseComponent')

    #: The list of children for this component. This is a read-only
    #: lazy property that is computed based on the static list of
    #: _subcomponents and the items they return by calling their
    #: 'get_actual' method. This list should not be manipulated by
    #: user code.
    children = LazyProperty(
        List(Instance('BaseComponent')), 
        depends_on='_subcomponents:_actual_updated',
    )

    #: Whether the component has been initialized or not. This will be 
    #: set to True after all of the setup() steps defined here are 
    #: completed. It should not be changed afterwards. This can be used 
    #: to trigger certain actions that need to occur after the component 
    #: has been set up.
    initialized = Bool(False)

    #: An optional name to give to this component to assist in finding
    #: it in the tree. See the 'find_by_name' method.
    name = Str

    #: A reference to the toolkit that was used to create this object.
    toolkit = Instance(Toolkit)

    #: The private dictionary of expression objects that are bound to 
    #: attributes on this component. It should not be manipulated by
    #: user code. Rather, expressions should be bound by calling the 
    #: 'bind_expression' method.
    _expressions = Dict(Str, List(Instance(AbstractExpression)))

    #: The private list of virtual base classes that were used to 
    #: instantiate this component from Enaml source code. The 
    #: EnamlFactory class of the Enaml runtime will directly append
    #: to this list as necessary.
    _bases = List

    #: The private internal list of subcomponents for this component. 
    #: This list should not be manipulated by the user, and should not
    #: be changed after initialization. It can, however, be redefined
    #: by subclasses to limit the type or number of subcomponents.
    _subcomponents = List(Instance('BaseComponent'))

    #: A private event that should be emitted by a component when the 
    #: results of calling get_actual() will result in new values. 
    #: This event is listened to by the parent of subcomponents in order 
    #: to know when to rebuild its list of children. User code will not 
    #: typically interact with this event.
    _actual_updated = EnamlEvent

    #: The HasTraits class defines a class attribute 'set' which is
    #: a deprecated alias for the 'trait_set' method. The problem
    #: is that having that as an attribute interferes with the 
    #: ability of Enaml expressions to resolve the builtin 'set',
    #: since the dynamic attribute scoping takes precedence over
    #: builtins. This resets those ill-effects.
    set = Disallow

    #--------------------------------------------------------------------------
    # Special Methods
    #--------------------------------------------------------------------------
    def __repr__(self):
        """ An overridden repr which returns the repr of the factory 
        from which this component is derived, provided that it is not 
        simply a root constructor. Otherwise, it defaults to the super
        class' repr implementation.

        """
        # If there are any bases, the last one in the list will always 
        # be a constructor. We want to ignore that one and focus on the
        # repr of the virtual base class from which the component was 
        # derived in the Enaml source code.
        bases = self._bases
        if len(bases) >= 2:
            base = bases[0]
            return repr(base)
        return super(BaseComponent, self).__repr__()

    #--------------------------------------------------------------------------
    # Property Getters
    #--------------------------------------------------------------------------
    def _get_self(self):
        """ The property getter for the 'self' attribute.

        """
        return self
        
    def _get_children(self):
        """ The lazy property getter for the 'children' attribute.

        This property getter returns the flattened list of components
        returned by calling 'get_actual()' on each subcomponent.

        """
        return sum([c.get_actual() for c in self._subcomponents], [])
    
    #--------------------------------------------------------------------------
    # Component Manipulation
    #--------------------------------------------------------------------------
    def get_actual(self):
        """ Returns the list of BaseComponent instances which should be
        included as proper children of our parent. By default this 
        simply returns [self]. This method should be reimplemented by 
        subclasses which need to contribute different components to their
        parent's children.

        """
        return [self]
        
    def add_subcomponent(self, component):
        """ Adds the given component as a subcomponent of this object.
        By default, the subcomponent is added to an internal list of 
        subcomponents. This method may be overridden by subclasses to 
        filter or otherwise handle certain subcomponents differently.

        """
        component.parent = self
        self._subcomponents.append(component)
    
    #--------------------------------------------------------------------------
    # Setup Methods 
    #--------------------------------------------------------------------------
    def setup(self, parent=None):
        """ Run the setup process for the ui tree.

        The setup process is fairly complex and involves multiple steps.
        The complexity is required in order to ensure a consistent state
        of the component tree so that default values that are computed 
        from expressions have the necessary information available.

        The setup process is comprised of the following steps:
        
        1)  Abstract objects create their internal toolkit object
        2)  Abstract objects initialize their internal toolkit object
        3)  Bound expression values are explicitly applied
        4)  Abstract objects bind their event handlers
        5)  Abstract objects are added as listeners to the shell object
        6)  Visibility is initialized
        7)  Layout is initialized
        8)  A finalization pass is made
        9)  Nodes are marked as initialized
        
        Many of these setup methods are no-ops, but are defined on this
        BaseComponent for simplicity and continuity. Subclasses that
        need to partake in certain portions of the layout process 
        should re-implement the appropriate setup methods.

        Parameters
        ----------
        parent : native toolkit widget, optional
            If embedding this BaseComponent into a non-Enaml GUI, use 
            this to pass the appropriate toolkit widget that should be 
            the parent toolkit widget for this component.

        """
        self._setup_create_widgets(parent)
        self._setup_init_widgets()
        self._setup_eval_expressions()
        self._setup_bind_widgets()
        self._setup_listeners()
        self._setup_init_visibility()
        self._setup_init_layout()
        self._setup_finalize()
        self._setup_set_initialized()

    def _setup_create_widgets(self, parent):
        """ A setup method that, by default, is a no-op. Subclasses 
        that drive gui toolkit widgets should reimplement this method
        to create the underlying toolkit widget(s).

        """
        for child in self._subcomponents:
            child._setup_create_widgets(parent)

    def _setup_init_widgets(self):
        """ A setup method that, by default, is a no-op. Subclasses 
        that drive gui toolkit widgets should reimplement this method
        to initialize their internal toolkit widget(s).

        """
        for child in self._subcomponents:
            child._setup_init_widgets()

    def _setup_eval_expressions(self):
        """ A setup method that loops over all of bound expressions and
        performs a getattr for those attributes. This ensures that all
        bound attributes are initialized, even if they weren't implicitly
        initialized in any of the previous setup methods.

        """
        for name in self._expressions:
            getattr(self, name)
        for child in self._subcomponents:
            child._setup_eval_expressions()

    def _setup_bind_widgets(self):
        """ A setup method that, by default, is a no-op. Subclasses 
        that drive gui toolkit widgets should reimplement this method
        to bind any event handlers of their internal toolkit widget(s).

        """
        for child in self._subcomponents:
            child._setup_bind_widgets()

    def _setup_listeners(self):
        """ A setup method that, by default, is a no-op. Subclasses 
        that drive gui toolkit widgets should reimplement this method
        to setup an traits listeners necessary to drive their internal
        toolkit widget(s).

        """
        for child in self._subcomponents:
            child._setup_listeners()

    def _setup_init_visibility(self):
        """ A setup method that, by default, is a no-op. Subclasses 
        that drive gui toolkit widgets should reimplement this method
        to initialize the visibility of their widgets.

        """
        for child in self._subcomponents:
            child._setup_init_visibility()

    def _setup_init_layout(self):
        """ A setup method that, by default, is a no-op. Subclasses 
        that manage layout should reimplement this method to initialize
        their underlying layout.

        """
        for child in self._subcomponents:
            child._setup_init_layout()

    def _setup_finalize(self):
        """ A setup method that, by default, is a no-op. Subclasses
        that need to perform process after layout is initialized but
        before a node is marked as fully initialized should reimplement
        this method.

        """
        for child in self._subcomponents:
            child._setup_finalize()

    def _setup_set_initialized(self):
        """ A setup method which updates the initialized attribute of 
        the component to True. This is performed bottom-up.

        """
        for child in self._subcomponents:
            child._setup_set_initialized()
        self.initialized = True

    #--------------------------------------------------------------------------
    # Teardown Methods
    #--------------------------------------------------------------------------
    def destroy(self):
        """ Destroys the component by clearing the list of subcomponents
        and calling 'destroy' on all of the old subcomponents, then gets
        rid of all references to the subcomponents and bound expressions.
        Subclasses that need more control over destruction should 
        reimplement this method.

        """
        for child in self._subcomponents:
            child.destroy()
        del self._subcomponents[:]
        self._expressions.clear()

    #--------------------------------------------------------------------------
    # Layout Stubs
    #--------------------------------------------------------------------------
    def relayout(self):
        """ A method called when the layout of the component's children
        should be refreshed. By default, this method proxies the call up
        the hierarchy until an implementor is found. Any implementors 
        should ensure that the necessary operations take place immediately 
        and are complete before the method returns.

        """
        parent = self.parent
        if parent is not None:
            parent.relayout()

    def request_relayout(self):
        """ A method called when the layout of the component's children
        should be refreshed at some point in the future. By default, this 
        method proxies the call up the hierarchy until an implementor is 
        found. Any implementors should ensure that this method returns 
        immediately, and that relayout occurs at some point in the future.

        """
        parent = self.parent
        if parent is not None:
            parent.request_relayout()

    def refresh(self):
        """ A method called when the positioning of the component's 
        children should be refreshed. By default, this method proxies the 
        call up the hierarchy until an implementor is found. Implementors 
        should ensure that this method takes place immediately, and that
        the refresh is complete before the method returns. 

        Note: This method should perform less work than 'relayout' and 
            should typically only need to be called when the children 
            need to be repositioned, rather than have all of their layout 
            relationships recomputed.

        """
        parent = self.parent
        if parent is not None:
            parent.refresh()
        
    def request_refresh(self):
        """ A method called when the positioning of the component's 
        children should be refreshed at some point in the future. By 
        default, this method proxies the call up the hierarchy until an 
        implementor is found. Implementors should ensure that this method 
        returns immediately, and that the refresh is completed at some 
        time in the future.
        
        Note: This method should perform less work than 'relayout' and 
            should typically only need to be called when the children 
            need to be repositioned, rather than have all of their layout 
            relationships recomputed.

        """
        parent = self.parent
        if parent is not None:
            parent.request_refresh()
        
    def request_relayout_task(self, callback, *args, **kwargs):
        """ Schedule a callback to be executed, followed by a relayout. 
        By default, this method proxies the call up the hierarchy until 
        an implementor is found. Implementors should ensure that the
        callback is executed with given arguments at some point in the
        future and is followed by a relayout. It is suggested that 
        implementors collapse multiple calls to this method which
        results in a single relayout.

        """
        parent = self.parent
        if parent is not None:
            parent.request_relayout_task(callback, *args, **kwargs)
        
    def request_refresh_task(self, callback, *args, **kwargs):
        """ Schedule a callback to be executed, followed by a rerfresh. 
        By default, this method proxies the call up the hierarchy until 
        an implementor is found. Implementors should ensure that the
        callback is executed with given arguments at some point in the
        future and is followed by a relayout. It is suggested that 
        implementors collapse multiple calls to this method which
        results in a single refresh.

        """
        parent = self.parent
        if parent is not None:
            parent.request_refresh_task(callback, *args, **kwargs)

    #--------------------------------------------------------------------------
    # Bound Attribute Handling
    #--------------------------------------------------------------------------
    def add_attribute(self, name, attr_type=object, is_event=False):
        """ Adds an attribute to the base component with the given name
        and ensures that values assigned to this attribute are of a
        given type.

        If the object already has an attribute with the given name,
        an exception will be raised.

        Parameters
        ----------
        name : string
            The name of the attribute to add.
        
        attr_type : type-like object, optional
            An object that behaves like a type for the purposes of a
            call to isinstance. Defaults to object.
        
        is_event : bool, optional
            If True, the added attribute will behave like an event.
            Otherwise, it will behave like a normal attribute. The 
            default is False.

        """
        # Check to see if a trait is already defined. We don't use
        # hasattr here since that might prematurely trigger a trait
        # intialization. We allow overriding traits of type Disallow,
        # UserAttribute, and UserEvent. The first is a consequence of 
        # using HasStrictTraits, where non-existing attributes are 
        # manifested as a Disallow trait. The others allow a custom 
        # derived component to specialize the attribute and event types 
        # of the component from which it is deriving.
        curr = self.trait(name)
        if curr is not None:
            ttype = curr.trait_type
            allowed = (UserAttribute, UserEvent)
            if ttype is not Disallow and not isinstance(ttype, allowed):
                msg = ("Cannot add '%s' attribute. The '%s' attribute on "
                       "the %s object already exists.")
                raise TypeError(msg % (name, name, self))
            
        # At this point we know there are no non-overridable traits 
        # defined for the object, but it is possible that there are 
        # methods or other non-trait attributes using the given name. 
        # We could potentially check for those, but its probably more 
        # useful to allow for overriding such things from Enaml, so we 
        # just go ahead and add the attribute.
        try:
            if is_event:
                self.add_trait(name, UserEvent(attr_type))
            else:
                self.add_trait(name, UserAttribute(attr_type))
        except TypeError:
            msg = ("'%s' is not a valid type for the '%s' attribute "
                   "declaration on %s")
            raise TypeError(msg % (attr_type, name, self))

    def bind_expression(self, name, expression, notify_only=False):
        """ Binds the given expression to the attribute 'name'.
         
        If the attribute does not exist, an exception is raised. A 
        strong reference to the expression object is kept internally.
        If the expression is not notify_only and the object is already
        fully initialized, the value of the expression will be applied
        immediately.

        Parameters
        ----------
        name : string
            The name of the attribute on which to bind the expression.
        
        expression : AbstractExpression
            A concrete implementation of AbstractExpression.
        
        notify_only : bool, optional
            If True, the expression is only a notifier, in which case
            multiple binding is allowed, otherwise the new expression
            overrides any old non-notify expression. Defaults to False.

        """
        curr = self.trait(name)
        if curr is None or curr.trait_type is Disallow:
            msg = "Cannot bind expression. %s object has no attribute '%s'"
            raise AttributeError(msg % (self, name))

        # If this is the first time an expression is being bound to the
        # given attribute, then we hook up a change handler. This ensures
        # that we only get one notification event per bound attribute.
        # We also create the notification entry in the dict, which is 
        # a list with at least one item. The first item will always be
        # the left associative expression (or None) and all following
        # items will be the notify_only expressions.
        expressions = self._expressions
        if name not in expressions:
            self.on_trait_change(self._on_bound_attr_changed, name)
            expressions[name] = [None]

        # There can be multiple notify_only expressions bound to a 
        # single attribute, so they just get appended to the end of
        # the list. Otherwise, the left associative expression gets
        # placed at the zero position of the list, overriding any
        # existing expression.
        if notify_only:
            expressions[name].append(expression)
        else:
            handler = self._on_expression_changed
            old = expressions[name][0]
            if old is not None:
                old.expression_changed.disconnect(handler)
            expression.expression_changed.connect(handler)
            expressions[name][0] = expression
        
            # Hookup support for default value computation.
            if not self.initialized:
                # We only need to add an ExpressionTrait once, since it 
                # will reach back into the _expressions dict as needed
                # and retrieve the bound expression.
                if not isinstance(curr.trait_type, ExpressionTrait):
                    self.add_trait(name, ExpressionTrait(curr))
            else:
                # If the component is already initialized, and the given
                # expression supports evaluation, update the attribute 
                # with the current value.
                val = expression.eval()
                if val is not NotImplemented:
                    setattr(self, name, val)

    def _on_expression_changed(self, expression, name, value):
        """ A private signal callback for the expression_changed signal
        of the bound expressions. It updates the value of the attribute
        with the new value from the expression.

        """
        setattr(self, name, value)
    
    def _on_bound_attr_changed(self, obj, name, old, new):
        """ A private handler which is called when any attribute which
        has a bound signal changes. It calls the notify method on each
        of the expressions bound to that attribute, but only after the
        component has been fully initialized.

        """
        # The check for None is for the case where there are no left 
        # associative expressions bound to the attribute, so the first
        # entry in the list is still None.
        if self.initialized:
            for expr in self._expressions[name]:
                if expr is not None:
                    expr.notify(old, new)

    #--------------------------------------------------------------------------
    # Auxiliary Methods 
    #--------------------------------------------------------------------------
    def when(self, switch):
        """ A method which returns itself or None based on the truth of
        the argument.

        This can be useful to easily turn off the effects of a component
        if various situations such as constraints-based layout.

        Parameters
        ----------
        switch : bool
            A boolean which indicates whether the instance or None 
            should be returned.
        
        Returns
        -------
        result : self or None
            If 'switch' is boolean True, self is returned. Otherwise,
            None is returned.

        """
        if switch:
            return self
    
    def traverse(self, depth_first=False):
        """ Yields all of the nodes in the tree, from this node downward.

        Parameters
        ----------
        depth_first : bool, optional
            If True, yield the nodes in depth first order. If False,
            yield the nodes in breadth first order. Defaults to False.

        """
        if depth_first:
            stack = [self]
            stack_pop = stack.pop
            stack_extend = stack.extend
        else:
            stack = deque([self])
            stack_pop = stack.popleft
            stack_extend = stack.extend

        while stack:
            item = stack_pop()
            yield item
            stack_extend(item.children)
    
    def traverse_ancestors(self, root=None):
        """ Yields all of the nodes in the tree, from the parent of this 
        node updward, stopping at the given root.

        Parameters
        ----------
        root : BaseComponent, optional
            The component at which to stop the traversal. Defaults
            to None

        """
        parent = self.parent
        while parent is not root and parent is not None:
            yield parent
            parent = parent.parent

    def find_by_name(self, name):
        """ Locate and return a named item that exists in the subtree
        which starts at this node.

        This method will traverse the tree of components, breadth first,
        from this point downward, looking for a component with the given
        name. The first one with the given name is returned, or None if
        no component is found.

        Parameters
        ----------
        name : string
            The name of the component for which to search.
        
        Returns
        -------
        result : BaseComponent or None
            The first component found with the given name, or None if 
            no component is found.
        
        """
        for cmpnt in self.traverse():
            if cmpnt.name == name:
                return cmpnt

    def toplevel_component(self):
        """ Walks up the tree of components starting at this node and
        returns the toplevel node, which is the first node encountered
        without a parent.

        """
        cmpnt = self
        while cmpnt is not None:
            res = cmpnt
            cmpnt = cmpnt.parent
        return res
Exemple #14
0
class Engine(HasStrictTraits):
    """ The Mayavi engine base class.
    """

    # The version of this class.  Used for persistence.
    __version__ = 0

    # The scenes associated with this project.
    scenes = List(Scene, record=True)

    # The list to provide to a TreeEditor.  Always add on a AdderNode.
    # TODO: It makes more sense to put the modification of the list
    # in some other UI module, and not here.
    children_ui_list = Property(record=False)

    # Our name.
    name = Str('Mayavi Engine')

    # Current scene.
    current_scene = Property(Instance(Scene), record=False)

    # Current object.
    current_object = Property(record=False)

    # Current selection -- the currently selected object on the tree.
    current_selection = Property(record=False)

    # Has the Engine started?  Use this event to do something after
    # the engine has been started.
    started = Event(record=False)

    # An optional callable that will generate a usable new viewer
    # containing a `tvtk.pyface.TVTKScene` instance. Ideally
    # the viewer should have an interface like
    # `tvtk.pyface.TVTKWindow` -- basically it must
    # implement the `closing` and `activated` events, however, this is
    # not necessary.  The created viewer is used by the `new_scene`
    # method to create a new Viewer.  This is a mechanism to use a
    # user specified scene with the Engine and have the ability to
    # load saved visualizations using the new scene.  Handy for things
    # like off-screen rendering.
    scene_factory = Callable(viewer_factory)

    # Are we running?
    running = Bool(False, record=False)

    # This event is invoked when the engine has been stopped.
    closed = Event()

    # The recorder for script recording.
    recorder = Instance(Recorder, record=False)

    ########################################
    # Private traits.

    _current_scene = WeakRef(Scene, allow_none=True)
    _current_object = WeakRef(HasTraits, allow_none=True)
    _current_selection = WeakRef(HasTraits, allow_none=True)
    _viewer_ref = Dict

    # View related traits.
    current_selection_view = View(Item(name='_current_selection',
                                       enabled_when='_current_selection is not None',
                                       style='custom', springy=True,
                                       show_label=False,),
                                  resizable=True,
                                  scrollable=True
                                  )

    ######################################################################
    # `object` interface
    ######################################################################
    def __init__(self, **traits):
        super(Engine, self).__init__(**traits)

        # FIXME: This is tied to preferences.  It really should not be
        # we need to use bind_preferences here.

        # To remove ref cycle with root preferences helper, the trait change
        # handler is an instance method
        preference_manager.root.on_trait_change(self._show_helper_nodes_changed,
                                                'show_helper_nodes')

    def __get_pure_state__(self):
        d = self.__dict__.copy()
        for x in ['_current_scene', '_current_object',
                  '__sync_trait__', '_viewer_ref',
                  '__traits_listener__']:
            d.pop(x, None)
        return d

    def __set_pure_state__(self, state):
        # Current number of scenes.
        n_scene = len(self.scenes)
        # Number of scenes in saved state.
        n_saved_scene = len(state.scenes)
        # Remove extra ones.
        for i in range(n_scene - n_saved_scene):
            self.close_scene(self.scenes[-1])
        # Add new ones.
        for i in range(n_saved_scene - n_scene):
            self.new_scene()
        # Set the state.
        state_pickler.set_state(self, state)

    def __getstate__(self):
        return state_pickler.dumps(self)

    def __setstate__(self, str_state):
        self.__init__()
        state = state_pickler.loads_state(str_state)
        state_pickler.update_state(state)
        self.__set_pure_state__(state)

    ######################################################################
    # `Engine` interface
    ######################################################################
    def start(self):
        """This is called by the plugin when the plugin actually
        starts."""
        registry.register_engine(self)
        # Notify any listeners that the engine is started.
        self.started = self
        self.running = True

    def stop(self):
        registry.unregister_engine(self)
        self.running = False
        self.closed = True

    @recordable
    def add_source(self, src, scene=None):
        """Adds a source to the pipeline. Uses the current scene unless a
        scene is given in the scene keyword argument."""
        passed_scene = scene
        if scene is not None:
            tvtk_scene = scene.scene
            for sc in self.scenes:
                if sc.scene == tvtk_scene:
                    scene = sc
                    break
            else:
                error('This scene is not managed by mayavi')
                return
        else:
            scene = self.current_scene

        # Create a new scene if none is available.
        if scene is None:
            self.new_scene()
            scene = self.current_scene
        scene.add_child(src)
        self.current_object = src

    @recordable
    def add_filter(self, fil, obj=None):
        """Adds a filter to the pipeline at an appropriate point. Adds it
        to the selected object, or to an object passed as the
        kwarg `obj`.
        """
        passed_obj = obj
        if obj is None:
            obj = self.current_object
        if not isinstance(obj, Base):
            msg = 'No valid current object, '\
                  'please select an active object.'
            error(msg)
            return
        if (obj is not None) and (not isinstance(obj, Scene)):
            if obj.running:
                obj.add_child(fil)
                self.current_object = fil
            else:
                msg = 'Current object is not active, '\
                      'please select an active object.'
                error(msg)
        else:
            if obj is None:
                error('Please create a VTK scene and open some data first.')
            else:
                error('No data: cannot use a Filter/Module/ModuleManager.')

    @recordable
    def add_module(self, mod, obj=None):
        """Adds a module to the pipeline at an appropriate point. Adds it
        to the selected object, or to an object passed through the
        kwarg `obj`.
        """
        self.add_filter(mod, obj=obj)

    @recordable
    def save_visualization(self, file_or_fname):
        """Given a file or a file name, this saves the current
        visualization to the file.
        """
        # Save the state of VTK's global warning display.
        o = vtk.vtkObject
        w = o.GetGlobalWarningDisplay()
        o.SetGlobalWarningDisplay(0) # Turn it off.
        try:
            #FIXME: This is for streamline seed point widget position which
            #does not get serialized correctly
            if is_old_pipeline():
                state_pickler.dump(self, file_or_fname)
            else:
                state = state_pickler.get_state(self)
                st = state.scenes[0].children[0].children[0].children[4]
                l_pos = st.seed.widget.position
                st.seed.widget.position = [pos.item() for pos in l_pos]
                saved_state = state_pickler.dumps(state)
                file_or_fname.write(saved_state)
        except (IndexError, AttributeError):
            state_pickler.dump(self, file_or_fname)
        finally:
            # Reset the warning state.
            o.SetGlobalWarningDisplay(w)

    @recordable
    def load_visualization(self, file_or_fname):
        """Given a file/file name this loads the visualization."""
        # Save the state of VTK's global warning display.
        o = vtk.vtkObject
        w = o.GetGlobalWarningDisplay()
        o.SetGlobalWarningDisplay(0) # Turn it off.
        try:
            # Get the state from the file.
            state = state_pickler.load_state(file_or_fname)
            state_pickler.update_state(state)
            # Add the new scenes.
            for scene_state in state.scenes:
                self.new_scene()
                scene = self.scenes[-1]
                # Disable rendering initially.
                if scene.scene is not None:
                    scene.scene.disable_render = True
                # Update the state.
                state_pickler.update_state(scene_state)
                scene.__set_pure_state__(scene_state)
                # Setting the state will automatically reset the
                # disable_render.
                scene.render()
        finally:
            # Reset the warning state.
            o.SetGlobalWarningDisplay(w)

    @recordable
    def open(self, filename, scene=None):
        """Open a file given a filename if possible in either the
        current scene or the passed `scene`.
        """
        passed_scene = scene
        reader = registry.get_file_reader(filename)
        if reader is None:
            msg = 'No suitable reader found for the file %s'%filename
            error(msg)
        else:
            src = None
            if scene is None:
                scene = self.current_scene
            if scene is None:
                scene = self.new_scene()
            try:
                sc = scene.scene
                if sc is not None:
                    sc.busy = True
                callable = reader.get_callable()
                if reader.factory is None:
                    src = callable()
                    src.initialize(filename)
                else:
                    # Factory functions are passed the filename and a
                    # reference to the engine.
                    src = callable(filename, self)
                if src is not None:
                    self.add_source(src, passed_scene)
            finally:
                if sc is not None:
                    sc.busy = False
            if src is not None:
                return src

    def record(self, msg):
        """This is merely a convenience method to record messages to the
        script recorder.
        """
        r = self.recorder
        if r is not None:
            r.record(msg)

    ######################################################################
    # Scene creation/deletion related methods.
    ######################################################################
    def add_scene(self, scene, name=None):
        """Add given `scene` (a `pyface.tvtk.scene.Scene` instance) to
        the mayavi engine so that mayavi can manage the scene.  This
        is used when the user creates a scene.  Note that for the
        `EnvisageEngine` this is automatically taken care of when you
        create a new scene using the TVTK scene plugin.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene`

          The scene that needs to be managed from mayavi.

         name - `str`
          The name assigned to the scene.  It tries to determine the
          name of the scene from the passed scene instance.  If this
          is not possible it defaults to 'Mayavi Scene'.

        """
        if name is None:
            if hasattr(scene, 'name'):
                name = scene.name
            else:
                name = 'Mayavi Scene %d'%next(scene_id_generator)

        s = Scene(scene=scene, name=name, parent=self)
        s.start()
        # We don't want the startup setup to be recorded.
        recorder = self.recorder
        self.scenes.append(s)
        self.current_scene = s
        if recorder is not None:
            recorder.register(s)

    @recordable
    def remove_scene(self, scene, **kwargs):
        """Remove a given `scene` (a `pyface.tvtk.scene.Scene`
        instance) from the mayavi engine if it is already being
        managed by mayavi.  Note that for the `EnvisageEngine` this is
        automatically taken care of when you close a scene started
        using the TVTK scene plugin.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene`

          The scene that needs to be removed from mayavi.
        """
        s = None
        for index, x in enumerate(self.scenes):
            if x.scene is scene:
                s = x
                break
        if s is not None:
            s.stop()
            self.scenes.remove(s)
            # Don't record it shutting down.  To do this we must
            # unregister it here so we don't record unnecessary calls.
            recorder = self.recorder
            if recorder is not None:
                recorder.unregister(s)

        # Remove the reference to the viewer if any.
        if scene in self._viewer_ref:
            del self._viewer_ref[scene]

        # Clear the current scene if it has been removed.
        if scene is self._current_scene:
            self._current_scene = None

    @recordable
    def new_scene(self, viewer=None, name=None, **kwargs):
        """Create or manage a new VTK scene window.  If no `viewer`
        argument is provided, the method creates a new viewer using
        `self.scene_factory`.  If `self.scene_factory` is `None` then
        it creates an `ivtk` viewer.  This code requires that the
        `viewer` has a `scene` attribute/trait that is a
        `pyface.tvtk.scene.Scene`.  It also works best if the viewer
        supports `closing` and `activated` events.

        The method returns the created viewer.

        Parameters:
        -----------

         viewer - The viewer object, if None, one is created for you.

         name - The name attribute of the viewer

         ``**kwargs`` - The extra keyword arguments are passed along to
         the scene factory.

        """
        if viewer is None:
            factory_kwargs = {}
            factory_kwargs_names = get_args(self.scene_factory)
            for arg, value in kwargs.items():
                if arg in factory_kwargs_names:
                    factory_kwargs[arg] = value

            viewer = self.scene_factory(**factory_kwargs)
            process_ui_events()

        if name is not None:
            viewer.name = name
        # Hang on to a reference to this viewer, if not done this will cause a
        # crash with Qt4.  This because the viewer will be closed and gc'd if
        # there isn't a reference to it.  When the viewer is gc'd the scene is
        # also closed and the engine will have a dead scene causing a crash.
        self._viewer_ref[viewer.scene] = viewer

        self.add_scene(viewer.scene)
        if hasattr(viewer, 'on_trait_change'):
            viewer.on_trait_change(self._on_scene_closed, 'closing')
            viewer.on_trait_change(self._on_scene_activated, 'activated')
            if hasattr(viewer, 'title'):
                self.current_scene.sync_trait('name', viewer, 'title')
        return viewer


    @recordable
    def close_scene(self, scene):
        """Given a scene created from new_scene, this method closes it
        and removes the scene from the list of scenes we manage.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene` or an object that holds a
         reference to a `pyface.tvtk.scene.Scene` in a `scene`
         attribute.
        """
        viewer = self.get_viewer(scene)
        self.remove_scene(scene.scene)
        if hasattr(scene, 'close'):
            scene.close()
        elif scene.scene is not None:
            scene.scene.close()
        if viewer is not None and hasattr(viewer, 'close'):
            viewer.close()

    def get_viewer(self, scene):
        """Return the viewer associated with a given scene.

        Parameters:
        -----------
         scene - An `mayavi.core.scene.Scene` instance.
        """
        return self._viewer_ref.get(scene.scene)

    def dialog_view(self):
        """ Default dialog view for Engine objects.
        """
        return None

    ######################################################################
    # Non-public interface
    ######################################################################
    def _on_select(self, object):
        """Called by the EngineTree when an object on the view is
        selected.  This basically sets the current object and current
        scene."""
        self.current_selection = object
        self._current_object = object
        try:
            scene = object.scene
            for s in self.scenes:
                if s.scene == scene:
                    self._current_scene = s
                    break
        except AttributeError:
            pass

    def _get_current_scene(self):
        n_scene = len(self.scenes)
        if n_scene == 0:
            return None
        elif n_scene == 1:
            return self.scenes[0]
        elif self._current_scene is not None:
            return self._current_scene
        elif n_scene > 1:
            return self.scenes[-1]
        else:
            return None

    def _set_current_scene(self, scene):
        old = self._current_scene
        self._current_scene = scene
        self.trait_property_changed('current_scene', old, scene)

    def _get_current_object(self):
        if self._current_object is not None:
            return self._current_object
        elif self.current_scene is not None:
            return self.current_scene
        else:
            return None

    def _set_current_object(self, object):
        old = self._current_object
        self._current_object = object
        self.trait_property_changed('current_object', old, object)

    def _get_current_selection(self):
        return self._current_selection

    def _set_current_selection(self, object):
        old = self._current_selection
        if not isinstance(object, (Base, AdderNode)):
            object = None
        self._current_selection = object
        self.trait_property_changed('current_selection', old, object)

    def _on_scene_closed(self, obj, name, old, new):
        self.remove_scene(obj.scene)

    def _on_scene_activated(self, obj, name, old, new):
        for scene in self.scenes:
            if scene.scene is obj.scene:
                self.current_scene = scene
                break

    def _closed_fired(self):
        """ When the engine is closed, clear the viewer ref which otherwise
            stores references to scenes to prevent crash on QT4.
            See: self.new_scene and MlabSceneModel._closed_fired
        """
        self._viewer_ref.clear()
        self.scenes = []
        preference_manager.root.on_trait_change(self._show_helper_nodes_changed,
                                                'show_helper_nodes',
                                                remove=True)
        registry.unregister_engine(self)

    def _show_helper_nodes_changed(self):
        self.trait_property_changed('children_ui_list', [],
                                    self.children_ui_list)

    def _get_children_ui_list(self):
        """ Trait getter for children_ui_list Property.
        """
        if preference_manager.root.show_helper_nodes \
                    and len(self.scenes) == 0:
            return [SceneAdderNode(object=self)]
        else:
            return self.scenes

    @on_trait_change('scenes[]')
    def _trigger_children_ui_list(self, old, new):
        """ Trigger a children_ui_list change when scenes changed.
        """
        self.trait_property_changed('children_ui_list', old, new)

    def _recorder_changed(self, old, new):
        if new is not None:
            new.record('# Recorded script from Mayavi2')
            new.record('from numpy import array')
            new.record('try:')
            new.record('    engine = mayavi.engine')
            new.record('except NameError:')
            new.record('    from mayavi.api import Engine')
            new.record('    engine = Engine()')
            new.record('    engine.start()')
            new.record('if len(engine.scenes) == 0:')
            new.record('    engine.new_scene()')
            new.record('# ------------------------------------------- ')
        elif old is not None:
            old.record('# ------------------------------------------- ')
            old.record('from mayavi.tools.show import show')
            old.record('show()')
Exemple #15
0
class Base(TreeNodeObject):
    # The version of this class.  Used for persistence.
    __version__ = 0

    ########################################
    # Traits

    # The scene (RenderWindow) associated with this component.
    scene = Instance(TVTKScene, record=False)

    # Is this object running as part of the mayavi pipeline.
    running = Property(Bool, record=False)

    # The object's name.
    name = Str('')

    # The default icon.
    icon = 'module.ico'

    # The human readable type for this object
    type = Str('', record=False)

    # Is this object visible or not.
    visible = Bool(True, desc='if the object is visible')

    # Extend the children list with an AdderNode when a TreeEditor needs it.
    children_ui_list = Property(depends_on=['children'], record=False)

    # The parent of this object, i.e. self is an element of the parents
    # children.  If there is no notion of a parent/child relationship
    # this trait is None.
    parent = WeakRef(record=False)

    # A helper for the right click menus, context sensitivity etc.
    menu_helper = Instance(HasTraits, record=False)

    # Our recorder.
    recorder = Instance(Recorder, record=False)

    ##################################################
    # Private traits
    _is_running = Bool(False)

    # This is used to save the state of the object when it is not
    # running.  When the object "starts", the state is loaded.  This
    # is done because a stopped object will not have a meaningful VTK
    # pipeline setup, so setting its state will lead to all kinds of
    # errors.
    _saved_state = Any('')

    # Hide and show actions
    _HideShowAction = Instance(
        Action,
        kw={
            'name': 'Hide/Show',
            'action': 'object._hideshow'
        },
    )

    # The menu shown on right-click for this.
    _menu = Instance(Menu, transient=True)

    # Path to the icon for this object.
    _icon_path = Str()

    # Adder node: a dialog to add children to this object
    _adder_node_class = None

    # Name of the file that may host the hand-crafted view
    _view_filename = Str(transient=True)

    # Hand crafted view.
    _module_view = Instance(View, transient=True)

    # Work around problem with HasPrivateTraits.
    __ = Python

    ##################################################

    ######################################################################
    # `object` interface
    ######################################################################
    def __get_pure_state__(self):
        """Method used by the state_pickler.
        """
        d = self.__dict__.copy()
        for attr in ('scene', '_is_running', '__sync_trait__',
                     '__traits_listener__', '_icon_path', '_menu',
                     '_HideShowAction', 'menu_helper', 'parent', 'parent_',
                     '_module_view', '_listener_cache', '_view_filename',
                     'mlab_source'):
            d.pop(attr, None)
        return d

    def __getstate__(self):
        """Allows standard pickle to work via the state_pickler.
        """
        return state_pickler.dumps(self)

    def __setstate__(self, str_state):
        """Allows standard pickle to work via the state_pickler.
        """
        self.__init__()
        # Get the state from the string and update it.
        state = state_pickler.loads_state(str_state)
        state_pickler.update_state(state)
        # Save the state and load it if we are running.
        self._saved_state = pickle.dumps(state)
        if self.running:
            self._load_saved_state()

    def __deepcopy__(self, memo):
        """Method used by copy.deepcopy().  This also uses the
        state_pickler to work correctly.
        """
        # Create a new instance.
        new = self.__class__()
        # If we have a saved state, use it for the new instance.  If
        # not, get our state and save that.
        saved_state = self._saved_state
        if len(saved_state) == 0:
            state = state_pickler.get_state(self)
            #FIXME: This is for streamline seed point widget position which
            #does not get serialized correctly
            if not is_old_pipeline():
                try:
                    st = state.children[0].children[4]
                    l_pos = st.seed.widget.position
                    st.seed.widget.position = [pos.item() for pos in l_pos]
                except (IndexError, AttributeError):
                    pass
            saved_state = pickle.dumps(state)
        new._saved_state = saved_state
        # In the unlikely case that a new instance is running, load
        # the saved state.
        if new.running:
            new._load_saved_state()
        return new

    ######################################################################
    # `Base` interface
    ######################################################################
    def start(self):
        """Invoked when this object is added to the mayavi pipeline.
        """
        self.running = True
        self._load_saved_state()

    def stop(self):
        """Invoked when this object is removed from the mayavi
        pipeline.
        """
        self.running = False

    def add_child(self, child):
        """This method intelligently adds a child to this object in
        the MayaVi pipeline.
        """
        raise NotImplementedError

    def remove_child(self, child):
        """Remove specified child from our children.
        """
        raise NotImplementedError()

    def remove(self):
        """Remove ourselves from the mayavi pipeline.
        """
        if self.parent is not None:
            e = get_engine(self)
            self.parent.remove_child(self)
            if e.current_object is self:
                e.current_object = self.parent

    def render(self):
        """Invokes render on the scene, this in turn invokes Render on
        the VTK pipeline.
        """
        s = self.scene
        if s is not None:
            s.render()

    def dialog_view(self):
        """ Returns a view with an icon and a title.
        """
        view = self.trait_view()
        icon = self._icon_path + os.sep + 'images' + os.sep \
                            + self.icon
        view.icon = ImageResource(icon)
        view.title = "Edit%s: %s" % (self.type, self.name)
        view.buttons = ['OK', 'Cancel']
        return view

    def trait_view(self, name=None, view_element=None):
        """ Gets or sets a ViewElement associated with an object's class.

        Overridden here to search for a separate file in the same directory
        for the view to use for this object. The view should be declared in
        the file named <class name>_view. If a file with this name is not
        found, the trait_view method on the base class will be called.
        """

        # If a name is specified, then call the HasTraits trait_view method
        # which will return (or assign) the *view_element* associated with
        # *name*.
        if name:
            return super(Base, self).trait_view(name, view_element)

        view = self._load_view_cached(name, view_element)
        # Uncomment this when developping views.
        #view = self._load_view_non_cached(name, view_element)
        return view

    ######################################################################
    # `TreeNodeObject` interface
    ######################################################################
    def tno_get_label(self, node):
        """Gets the label to display for a specified object.
        """
        if self.name == '':
            self.name = self.__class__.__name__
        return self.name

    def tno_get_view(self, node):
        """Gets the View to use when editing an object.
        """
        view = self.trait_view()
        view.kind = 'subpanel'
        return view

    def tno_confirm_delete(self, node):
        """Confirms that a specified object can be deleted or not.
        """
        if preference_manager.root.confirm_delete:
            return None
        else:
            return True

    def tno_get_menu(self, node):
        """ Returns the contextual pop-up menu.
        """
        if self._menu is None:
            return super(Base, self).tno_get_menu(node)
        return self._menu

    def tno_get_icon(self, node, is_expanded):
        return self.icon

    def tno_get_icon_path(self, node):
        return self._icon_path

    def tno_delete_child(self, node, index):
        if len(self.children_ui_list) > len(self.children):
            del self.children[index - 1]
        else:
            del self.children[index]

    def tno_append_child(self, node, child):
        """ Appends a child to the object's children.
        """
        self.children.append(child)

    def tno_insert_child(self, node, index, child):
        """ Inserts a child into the object's children.
        """
        if len(self.children_ui_list) > len(self.children):
            idx = index - 1
        else:
            idx = index
        self.children[idx:idx] = [child]

    ######################################################################
    # Non-public interface
    ######################################################################
    def _get_running(self):
        return self._is_running

    def _set_running(self, new):
        if self._is_running == new:
            return
        else:
            old = self._is_running
            self._is_running = new
            self.trait_property_changed('running', old, new)

    def _get_children_ui_list(self):
        """ Getter for Traits Property children_ui_list.

        For the base class, do not add anything to the children list.
        """
        if ((not preference_manager.root.show_helper_nodes
             or len(self.children) > 0) or self._adder_node_class is None or
            (not self.type == ' scene' and 'none' in self.output_info.datasets)
                # We can't use isinstance, as we would have circular
                # imports
            ):
            return self.children
        else:
            return [
                self._adder_node_class(object=self),
            ]

    @on_trait_change('children[]')
    def _trigger_children_ui_list(self, old, new):
        """ Trigger a children_ui_list change when scenes changed.
        """
        self.trait_property_changed('children_ui_list', old, new)

    def _visible_changed(self, value):
        # A hack to set the name when the tree view is not active.
        # `self.name` is set only when tno_get_label is called and this
        # is never called when the tree view is not shown leading to an
        # empty name.
        if len(self.name) == 0:
            self.tno_get_label(None)
        if value:
            #self._HideShowAction.name = "Hide"
            self.name = self.name.replace(' [Hidden]', '')
        else:
            #self._HideShowAction.name = "Show"
            n = self.name
            if ' [Hidden]' not in n:
                self.name = "%s [Hidden]" % n

    def _load_view_cached(self, name, view_element):
        """ Use a cached view for the object, for faster refresh.
        """
        if self._module_view is not None:
            view = self._module_view
        else:
            logger.debug(
                "No view found for [%s] in [%s]. "
                "Using the base class trait_view instead.", self,
                self._view_filename)
            view = super(Base, self).trait_view(name, view_element)
        return view

    def _load_view_non_cached(self, name, view_element):
        """ Loads the view by execing a file. Useful when tweaking
            views.
        """
        result = {}
        view_filename = self._view_filename
        try:
            exec(compile(open(view_filename).read(), view_filename, 'exec'),
                 {}, result)
            view = result['view']
        except IOError:
            logger.debug(
                "No view found for [%s] in [%s]. "
                "Using the base class trait_view instead.", self,
                view_filename)
            view = super(Base, self).trait_view(name, view_element)
        return view

    def _hideshow(self):
        if self.visible:
            self.visible = False
        else:
            self.visible = True

    def _load_saved_state(self):
        """Load the saved state (if any) of this object.
        """
        saved_state = self._saved_state
        if len(saved_state) > 0:
            state = pickle.loads(saved_state)
            if hasattr(self, '__set_pure_state__'):
                self.__set_pure_state__(state)
            else:
                state_pickler.set_state(self, state)
            self._saved_state = ''

    def __view_filename_default(self):
        """ The name of the file that will host the view.
        """
        module = self.__module__.split('.')
        class_filename = module[-1] + '.py'
        module_dir_name = module[1:-1]
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        view_filename = os.path.join(*([base_dir] + module_dir_name \
                                        + UI_DIR_NAME + [class_filename]))
        return view_filename

    def __module_view_default(self):
        """ Try to load a view for this object.
        """
        view_filename = self._view_filename
        try:
            result = imp.load_module('view', open(view_filename, 'r'),
                                     view_filename, ('.py', 'U', 1))
            view = result.view
        except:
            view = None
        return view

    def __menu_default(self):
        extras = []
        if self.menu_helper is not None:
            extras = self.menu_helper.actions + self._extra_menu_items()
        menu_actions = [Separator()] + extras + \
                       [Separator(), self._HideShowAction, Separator()] + \
                       deepcopy(standard_menu_actions)
        return Menu(*menu_actions)

    def __icon_path_default(self):
        return resource_path()

    def _extra_menu_items(self):
        """Override this to generate any new menu actions you want on
        the right click menu."""
        return []
Exemple #16
0
class AStrategyBase(HasTraits):
    '''
    Interface for adaptive computations.
    
    There are four positions in the algorithm where adaptive changes to the
    spatial and temporal discretization may be applied:
    (1) before the computation starts
    (2) in every iteration (ihandler)
    (3) on failure of the iteration loop (fhandler)
    (4) on equilibrium (ehandler)
    A total of 9 methods is implemented to allow for an flexible adjustment
    of the global algorithm. The default implementation is neutral regarding
    the behavior of the global algorithm. 
    '''

    tloop = WeakRef()

    tstepper = Property()

    def _get_tstepper(self):
        return self.tloop.tstepper

    # (1) INITIALIZE
    #
    def initialize_strategy(self):
        '''Called before the computation starts.'''
        pass

    # (2) LOAD STEP
    #
    def begin_time_step(self, t):
        '''Prepare a new load step
        '''
        pass

    def end_time_step(self, t):
        '''Prepare a new load step
        '''
        pass

    # (2) ITERATION (ihandler)
    #
    def ihandler_needed(self):
        '''Decides whether the iteration must be adapted.
        True means that ihandler_invoke is called and the time step is restarted.
        False deaginctivates any further ihandler actions.
        '''
        return False

    def ihandler_invoke(self):
        '''Handle the iteration.'''
        pass

    def ihandler_get_scale(self):
        '''Scale time step with a factor.'''
        return 1.0

    # (3) EQUILIBRIUM (ehandler)
    #
    def ehandler_needed(self):
        '''Decide whether to invoke the equilibrium handler.'''
        return False

    def ehandler_accept(self):
        '''Decide whether to accept or redo the current time step.'''
        return True

    def ehandler_invoke(self):
        '''
        Extend algorithm when equilibrium is found
        (but before potentially accepting the state).
        '''
        pass

    def ehandler_get_scale(self):
        '''Scale time step with a factor.'''
        return 1.0

    # (4) FAILURE (fhandler)
    #
    def fhandler_get_scale(self):
        '''
        Handle failure of iteration loop.
        Default: Half the time step.
        '''
        return 1 / 2.
Exemple #17
0
class DOTSListEval(TStepperEval):

    '''
    Domain with uniform FE-time-step-eval.
    '''
    implements(ITStepperEval)

    sdomain = WeakRef('ibvpy.mesh.fe_domain.FEDomain')

    dots_list = List

    def new_cntl_var(self):
        return zeros(self.sdomain.n_dofs, float_)

    def new_resp_var(self):
        return zeros(self.sdomain.n_dofs, float_)

    K = Property

    @cached_property
    def _get_K(self):
        return SysMtxAssembly()

    F_int = Property(depends_on='sdomain.changed_structure')

    @cached_property
    def _get_F_int(self):
        n_dofs = self.sdomain.n_dofs
        return zeros(n_dofs, 'float_')

    def setup(self, sctx):
        print 'DEPRECATED CALL TO SETUP'

    def get_state_array_size(self):
        return 0

    def apply_constraints(self, K):
        for dots_eval in self.dots_list:
            dots_eval.apply_constraints(K)

    def get_corr_pred(self, sctx, U, d_U, tn, tn1, *args, **kw):

        K = self.K
        K.reset()

        F_int = self.F_int
        F_int[:] = 0.0

        U = self.tstepper.U_k
        d_U = self.tstepper.d_U

        for dots_eval in self.dots_list:

            K_mtx_arr = dots_eval.get_corr_pred(sctx, U, d_U, tn, tn1,
                                                self.F_int, *args, **kw)

            K.sys_mtx_arrays.append(K_mtx_arr)

        return self.F_int, self.K

    rte_dict = Property(Dict, depends_on='dots_list')

    @cached_property
    def _get_rte_dict(self):
        rte_dict = {}

        dots_rte_dicts = []
        rte_keys = []
        for dots_eval in self.dots_list:
            dots_rte_dict = {}
            for key, eval in dots_eval.rte_dict.items():
                # add the mapping here
                #
                if key not in rte_keys:
                    rte_keys.append(key)
                dots_rte_dict[key] = eval
            dots_rte_dicts.append(dots_rte_dict)

        # Get the union of all available rte keys
        for key in rte_keys:
            rte_list = []
            for rte_dict in dots_rte_dicts:
                rte_list.append(rte_dict.get(key, None))
            rte_dict[key] = tuple(rte_list)

        return rte_dict

    traits_view = View()
Exemple #18
0
class RTraceDomain(HasTraits):
    '''
    Trace encompassing the whole spatial domain.
    '''
    label = Str('RTraceDomainField')
    fets_eval = Delegate('sd')
    dots = Delegate('sd')
    position = Enum('nodes', 'int_pnts')
    sd = WeakRef(ISDomain)

    point_offset = Int
    cell_offset = Int

    # Tag that can be used to skip the domain when gathering the vtk data
    #
    skip_domain = Bool(False)

    #-------------------------ts_eval-----------------------------------------
    # Visualization pipelines
    #-------------------------------------------------------------------------
    mvp_mgrid_geo = Trait(MVUnstructuredGrid)

    def _mvp_mgrid_geo_default(self):
        return MVUnstructuredGrid(name='Response tracer mesh',
                                  warp=False,
                                  warp_var='')

    def redraw(self):
        '''
        '''
        # self.mvp_mgrid_geo.redraw()

        print 'REDRAWING', self.sd.name

        self.mvp_mgrid_geo.rebuild_pipeline(self.vtk_node_structure)

    vtk_node_structure = Property(Instance(tvtk.UnstructuredGrid),
                                  depends_on='sd.changed_structure')

    @cached_property
    def _get_vtk_node_structure(self):
        self.position = 'nodes'
        return self.vtk_structure

    vtk_ip_structure = Property(Instance(tvtk.UnstructuredGrid),
                                depends_on='sd.changed_structure')

    @cached_property
    def _get_vtk_ip_structure(self):
        self.position = 'int_pnts'
        return self.vtk_structure

    vtk_structure = Property(Instance(tvtk.UnstructuredGrid))

    def _get_vtk_structure(self):
        ug = tvtk.UnstructuredGrid()

        if self.skip_domain:
            return ug

        cell_array, cell_offsets, cell_types = self.vtk_cell_data

        n_cells = cell_types.shape[0]
        ug.points = self.vtk_X

        vtk_cell_array = tvtk.CellArray()
        vtk_cell_array.set_cells(n_cells, cell_array)
        ug.set_cells(cell_types, cell_offsets, vtk_cell_array)
        return ug

# vtk_X = Property(Array) #TODO: cleanup
#    def _get_vtk_X(self):
#        '''Get the discretization points based on the fets_eval
#        associated with the current domain.
#        '''
#        if self.position == 'int_pnts':
#            ip_arr = self.fets_eval.ip_coords
#
#        pts = []
#        dim_slice = self.fets_eval.dim_slice
#        for e in self.sd.elements:
#            X = e.get_X_mtx()
#            if dim_slice:
#                X = X[:,dim_slice]
#                if self.position == 'int_pnts':
#                    ip_arr = ip_arr[:,dim_slice]
#            if self.position == 'nodes':
#                pts += list( self.fets_eval.get_vtk_r_glb_arr( X ) )
#            elif self.position == 'int_pnts':
#                pts += list( self.fets_eval.get_vtk_r_glb_arr( X, ip_arr) )
#        pts_array = array(pts, dtype = 'float_' )
#        return pts_array

    vtk_X = Property(Array)  # TODO: cleanup

    def _get_vtk_X(self):
        return self.dots.get_vtk_X(self.position)

#    debug_cell_data = Bool(True)

# vtk_cell_data = Property(Array, depends_on = 'point_offset,cell_offset' )#TODO:check the dependencies
#    def _get_vtk_cell_data(self):
#
#        if self.position == 'nodes':
#            subcell_offsets, subcell_lengths, subcells, subcell_types = self.dots.vtk_node_cell_data
#        elif self.position == 'int_pnts':
#            subcell_offsets, subcell_lengths, subcells, subcell_types = self.fets_eval.vtk_ip_cell_data
#
#        self.debug_cell_data = True
#
#        if self.debug_cell_data:
#            print 'subcell_offsets'
#            print subcell_offsets
#            print 'subcell_lengths'
#            print subcell_lengths
#            print 'subcells'
#            print subcells
#            print 'subcell_types'
#            print subcell_types
#
#        n_subcells = subcell_types.shape[0]
#        n_cell_points = self.n_cell_points
#        subcell_size = subcells.shape[0] + n_subcells
#
#        if self.debug_cell_data:
#            print 'n_cell_points', n_cell_points
#            print 'n_cells', self.n_cells
#
#        vtk_cell_array = zeros( (self.n_cells, subcell_size), dtype = int )
#
#        idx_cell_pnts = repeat( True, subcell_size )
#
#        if self.debug_cell_data:
#            print 'idx_cell_pnts'
#            print idx_cell_pnts
#
#        idx_cell_pnts[ subcell_offsets ] = False
#
#        if self.debug_cell_data:
#            print 'idx_cell_pnts'
#            print idx_cell_pnts
#
#        idx_lengths = idx_cell_pnts == False
#
#        if self.debug_cell_data:
#            print 'idx_lengths'
#            print idx_lengths
#
#        point_offsets = arange( self.n_cells ) * n_cell_points
#        point_offsets += self.point_offset
#
#        if self.debug_cell_data:
#            print 'point_offsets'
#            print point_offsets
#
#        vtk_cell_array[:,idx_cell_pnts] = point_offsets[:,None] + subcells[None,:]
#        vtk_cell_array[:,idx_lengths] = subcell_lengths[None,:]
#
#        if self.debug_cell_data:
#            print 'vtk_cell_array'
#            print vtk_cell_array
#
#        active_cells = self.sd.idx_active_elems
#
#        if self.debug_cell_data:
#            print 'active cells'
#            print active_cells
#
#        cell_offsets = active_cells * subcell_size
#        cell_offsets += self.cell_offset
#        vtk_cell_offsets = cell_offsets[:,None] + subcell_offsets[None,:]
#
#        if self.debug_cell_data:
#            print 'vtk_cell_offsets'
#            print vtk_cell_offsets
#
#        vtk_cell_types = zeros( self.n_cells * n_subcells, dtype = int ).reshape( self.n_cells,
#                                                                                  n_subcells )
#        vtk_cell_types += subcell_types[None,:]
#
#        if self.debug_cell_data:
#            print 'vtk_cell_types'
#            print vtk_cell_types
#
# return vtk_cell_array.flatten(), vtk_cell_offsets.flatten(),
# vtk_cell_types.flatten()

# TODO:check the dependencies

    vtk_cell_data = Property(
        Array, depends_on='point_offset,cell_offset,sd.changed_structure')

    def _get_vtk_cell_data(self):

        #        cell_array, cell_offsets, cell_types = self.dots.get_vtk_cell_data(self.position)
        #        cell_array += self.point_offset
        #        cell_offsets += self.cell_offset
        return self.dots.get_vtk_cell_data(self.position, self.point_offset,
                                           self.cell_offset)

    # number of points
    n_points = Property(Int, depends_on='vtk_X, sd.changed_structure')

    @cached_property
    def _get_n_points(self):
        if self.skip_domain:
            return 0
        return self.vtk_X.shape[0]

#    debug_cell_data = Bool( False )

#    n_cells = Property( Int )
#    def _get_n_cells(self):
#        '''Return the total number of cells'''
#        return self.sd.n_active_elems
#
#    n_cell_points = Property( Int )
#    def _get_n_cell_points(self):
#        '''Return the number of points defining one cell'''
#        return self.fets_eval.n_vtk_r

    def clear(self):
        pass

    view = View(HSplit(
        VSplit(VGroup(Item('refresh_button', show_label=False), ), ), ),
                resizable=True)
Exemple #19
0
class MousePickDispatcher(HasTraits):
    """ An event dispatcher to send pick event on mouse clicks.

        This objects wires VTK observers so that picking callbacks
        can be bound to mouse click without movement.

        The object deals with adding and removing the VTK-level
        callbacks.
    """

    # The scene events are wired to.
    scene = WeakRef(Scene)

    # The list of callbacks, with the picker type they should be using,
    # and the mouse button that triggers them.
    callbacks = List(Tuple(
                        Callable,
                        Enum('cell', 'point', 'world'),
                        Enum('Left', 'Middle', 'Right'),
                        ),
                    help="The list of callbacks, with the picker type they "
                         "should be using, and the mouse button that "
                         "triggers them. The callback is passed "
                         "as an argument the tvtk picker."
                    )

    #--------------------------------------------------------------------------
    # Private traits
    #--------------------------------------------------------------------------

    # Whether the mouse has moved after the button press
    _mouse_no_mvt = Int

    # The button that has been pressed
    _current_button = Enum('Left', 'Middle', 'Right')

    # The various picker that are used when the mouse is pressed
    _active_pickers = Dict

    # The VTK callback numbers corresponding to our callbacks
    _picker_callback_nbs = Dict(value_trait=Int)

    # The VTK callback numbers corresponding to mouse movement
    _mouse_mvt_callback_nb = Int

    # The VTK callback numbers corresponding to mouse press
    _mouse_press_callback_nbs = Dict

    # The VTK callback numbers corresponding to mouse release
    _mouse_release_callback_nbs = Dict

    #--------------------------------------------------------------------------
    # Callbacks management
    #--------------------------------------------------------------------------

    @on_trait_change('callbacks_items')
    def dispatch_callbacks_change(self, name, trait_list_event):
        for item in trait_list_event.added:
            self.callback_added(item)
        for item in trait_list_event.removed:
            self.callback_removed(item)


    def callback_added(self, item):
        """ Wire up the different VTK callbacks.
        """
        callback, type, button = item
        picker = getattr(self.scene.scene.picker, '%spicker' % type)
        self._active_pickers[type] = picker

        # Register the pick callback
        if not type in self._picker_callback_nbs:
            self._picker_callback_nbs[type] = \
                            picker.add_observer("EndPickEvent",
                                                self.on_pick)

        # Register the callbacks on the scene interactor
        if VTK_VERSION>5:
            move_event = "RenderEvent"
        else:
            move_event = 'MouseMoveEvent'
        if not self._mouse_mvt_callback_nb:
            self._mouse_mvt_callback_nb = \
                self.scene.scene.interactor.add_observer(move_event,
                                                self.on_mouse_move)
        if not button in self._mouse_press_callback_nbs:
            self._mouse_press_callback_nbs[button] = \
                self.scene.scene.interactor.add_observer(
                                    '%sButtonPressEvent' % button,
                                    self.on_button_press)
        if VTK_VERSION>5:
            release_event = "EndInteractionEvent"
        else:
            release_event = '%sButtonReleaseEvent' % button
        if not button in self._mouse_release_callback_nbs:
            self._mouse_release_callback_nbs[button] = \
                self.scene.scene.interactor.add_observer(
                                    release_event,
                                    self.on_button_release)


    def callback_removed(self, item):
        """ Clean up the unecessary VTK callbacks.
        """
        callback, type, button = item

        # If the picker is no longer needed, clean up its observers.
        if not [t for c, t, b in self.callbacks if t == type]:
            picker = self._active_pickers[type]
            picker.remove_observer(self._picker_callback_nbs[type])
            del self._active_pickers[type]

        # If there are no longer callbacks on the button, clean up
        # the corresponding observers.
        if not [b for c, t, b in self.callbacks if b == button]:
            self.scene.scene.interactor.remove_observer(
                    self._mouse_press_callback_nbs[button])
            self.scene.scene.interactor.remove_observer(
                    self._mouse_release_callback_nbs[button])
        if len(self.callbacks) == 0 and self._mouse_mvt_callback_nb:
            self.scene.scene.interactor.remove_observer(
                            self._mouse_mvt_callback_nb)
            self._mouse_mvt_callback_nb = 0


    def clear_callbacks(self):
        while self.callbacks:
            self.callbacks.pop()

    #--------------------------------------------------------------------------
    # Mouse movement dispatch mechanism
    #--------------------------------------------------------------------------

    def on_button_press(self, vtk_picker, event):
        self._current_button = event[:-len('ButtonPressEvent')]
        self._mouse_no_mvt = 2


    def on_mouse_move(self, vtk_picker, event):
        if self._mouse_no_mvt:
            self._mouse_no_mvt -= 1


    def on_button_release(self, vtk_picker, event):
        """ If the mouse has not moved, pick with our pickers.
        """
        if self._mouse_no_mvt:
            x, y = vtk_picker.GetEventPosition()
            for picker in self._active_pickers.values():
                try:
                    picker.pick((x, y, 0), self.scene.scene.renderer)
                except TypeError:
                    picker.pick(x, y, 0, self.scene.scene.renderer)
        self._mouse_no_mvt = 0


    def on_pick(self, vtk_picker, event):
        """ Dispatch the pick to the callback associated with the
            corresponding mouse button.
        """
        picker = tvtk.to_tvtk(vtk_picker)
        for event_type, event_picker in self._active_pickers.items():
            if picker is event_picker:
                for callback, type, button in self.callbacks:
                    if ( type == event_type
                                    and button == self._current_button):
                        callback(picker)
                break

    #--------------------------------------------------------------------------
    # Private methods
    #--------------------------------------------------------------------------

    def __del__(self):
        self.clear_callbacks()
Exemple #20
0
class Container(LayoutTaskHandler, PaddingConstraints, ConstraintsWidget):
    """ A Component subclass that provides functionality for laying out 
    constrainable children according to their system of constraints.

    """
    #: A read-only cached property which returns the constraints layout
    #: manager for this container, or None if the layout is being managed
    #: by a parent container.
    layout_manager = Property(
        Instance(ConstraintsLayout),
        depends_on='owns_layout',
    )

    #: The list of children that can participate in constraints based
    #: layout. This list is composed of components in the list of
    #: children that are instances of Constrainable.
    constraints_children = Property(
        List(Instance(Constrainable)),
        depends_on='children',
    )

    #: A boolean which indicates whether or not to allow the layout
    #: ownership of this container to be transferred to an ancestor.
    #: This is False by default, which means that every container
    #: get its own layout solver. This improves speed and reduces
    #: memory use (by keeping a solver's internal tableaux small)
    #: but at the cost of not being able to share constraints
    #: across Container boundaries. This flag must be explicitly
    #: marked as True to enable sharing.
    share_layout = Bool(False)

    #: A read-only property which returns True if this container owns
    #: its layout and is responsible for setting the geometry of its
    #: children, or False if that responsibility has been transferred
    #: to another component in the hierarchy.
    owns_layout = Property(Bool, depends_on='_layout_owner')

    #: A container has a default padding of 10 on all sides.
    padding = Box(10, 10, 10, 10)

    #: Containers freely exapnd in width and height. The size hint
    #: constraints for a Container are used when the container is
    #: not sharing its layout. In these cases, expansion of the
    #: container is typically desired.
    hug_width = 'ignore'
    hug_height = 'ignore'

    #: A private trait which stores a weak reference to the owner of
    #: the layout for this container, or None if this container owns
    #: its layout.
    _layout_owner = WeakRef(allow_none=True)

    #: A private cached property which computes the size hint whenever
    #: the size_hint_updated event is fired.
    _size_hint = Property(Instance(Size), depends_on='size_hint_updated')

    #: Overridden parent class trait.
    abstract_obj = Instance(AbstractTkContainer)

    #--------------------------------------------------------------------------
    # Property Getters
    #--------------------------------------------------------------------------
    @cached_property
    def _get_constraints_children(self):
        """ Cached property getter for the 'constraints_children' 
        attribute. This getter returns the sublist of children that are 
        instances of ConstraintsWidget.

        """
        flt = lambda child: isinstance(child, Constrainable)
        return filter(flt, self.children)

    @cached_property
    def _get_layout_manager(self):
        """ The property getter for the 'layout_manager' attribute.

        """
        if self.owns_layout:
            return ConstraintsLayout()

    def _get_owns_layout(self):
        """ The property getter for the 'owns_layout' attribute.

        """
        return self._layout_owner is None

    @cached_property
    def _get__size_hint(self):
        """ The property getter for the '_size_hint' attribtue.

        """
        # XXX we can probably do better than the min size. Maybe have
        # the layout manager compute a preferred size, or something
        # similar to a preferred size. But I don't know at the moment
        # what it would actually mean to have a preferred size from
        # a set of constraints.
        return self.compute_min_size()

    #--------------------------------------------------------------------------
    # Change Handlers
    #--------------------------------------------------------------------------
    def _constraints_children_changed(self):
        """ A handler which requests a relayout when the constraints
        children change, provided that the container is initialized.

        """
        if self.initialized:
            self.request_relayout()

    #--------------------------------------------------------------------------
    # Setup Methods
    #--------------------------------------------------------------------------
    def _setup_init_layout(self):
        """ A reimplemented parent class setup method that performs any
        layout initialization necessary for the component. The layout is
        initialized from the bottom up.

        """
        super(Container, self)._setup_init_layout()
        self.initialize_layout()

    #--------------------------------------------------------------------------
    # Layout Handling
    #--------------------------------------------------------------------------
    def initialize_layout(self):
        """ A reimplemented parent class method that initializes the 
        layout manager for the first time, and binds the relevant
        layout change handlers.

        """
        # We only need to initialize the manager if we own the layout.
        if self.owns_layout:
            constraints = self.compute_constraints()
            self.layout_manager.initialize(constraints)
            # We fire off a size hint updated event here since, if
            # for some reason, the size hint was computed before
            # the layout was initialized, the value will be wrong
            # and cached. So we need to clear the cache so that it
            # will be recomputed by the next consumer that needs it.
            self.size_hint_updated()

    def relayout(self):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            super(Container, self).relayout()
        else:
            self._layout_owner.relayout()

    def refresh(self):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            super(Container, self).refresh()
        else:
            self._layout_owner.refresh()

    def request_relayout(self):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            super(Container, self).request_relayout()
        else:
            self._layout_owner.request_relayout()

    def request_refresh(self):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            super(Container, self).request_refresh()
        else:
            self._layout_owner.request_refresh()

    def request_relayout_task(self, callback, *args, **kwargs):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            sup = super(Container, self)
            sup.request_relayout_task(callback, *args, **kwargs)
        else:
            self._layout_owner.request_relayout_task(callback, *args, **kwargs)

    def request_refresh_task(self, callback, *args, **kwargs):
        """ A reimplemented parent class method which forwards the call
        to the layout owner if necessary.

        """
        if self.owns_layout:
            sup = super(Container, self)
            sup.request_refresh_task(callback, *args, **kwargs)
        else:
            self._layout_owner.request_refresh_task(callback, *args, **kwargs)

    def do_relayout(self):
        """ A reimplemented LayoutTaskHandler handler method which will
        actually perform the layout.

        """
        # At this point, we know that we own the layout since the
        # calls that trigger the call to this method would have
        # already been forwarded on to the layout owner. So, at
        # this point, we just have to recompute the constraints
        # and do a refresh.

        # TODO There is a big opportunity here to optimize by calling
        # .update_constraints() on the layout manager instead of just
        # recomputing everything from scratch, but that will require
        # tracking the created constraints so for now we just punt.
        self.layout_manager.initialize(self.compute_constraints())
        self.do_refresh()

        # We emit the size hint updated event at this point since
        # we are still inside a freeze context. This means that if
        # our parent is a toplevel window, it can set the new size
        # of the window before leaving the context which helps
        # eleminate flicker during the resize.
        self.size_hint_updated()

    def do_refresh(self):
        """ A reimplemented LayoutTaskHandler handler method which will
        actually perform the refresh.

        """
        # At this point, we know that we own the layout since the
        # calls that trigger the call to this method would have
        # already been forwarded on to the layout owner. So, at
        # this point, we just have to do a refresh.
        width = self.width
        height = self.height
        size = self.size()
        self.layout_manager.layout(self.apply_layout, width, height, size)

    def apply_layout(self):
        """ The callback invoked by the layout manager when there are
        new layout values available. This traverses the constraints 
        children for which this container has layout ownership and 
        applies the geometry updates.

        """
        stack = [((0, 0), self.constraints_children)]
        pop = stack.pop
        push = stack.append
        while stack:
            offset, children = pop()
            for child in children:
                new_offset = child.update_layout_geometry(*offset)
                if isinstance(child, Container):
                    if child._layout_owner is self:
                        push((new_offset, child.constraints_children))

    #--------------------------------------------------------------------------
    # Constraints Computation
    #--------------------------------------------------------------------------
    def compute_constraints(self):
        """ Descends the tree for all containers and children for which
        this container can manage layout, and aggregates all of their
        constraints into a single list.

        """
        expand = expand_constraints

        cns = []
        cns_extend = cns.extend

        # We don't care about the size hint constraints for a container
        # which manages a layout because the actual size is the input
        # to the solver.
        cns_extend(expand(self, self.hard_constraints()))
        cns_extend(expand(self, self.padding_constraints()))
        cns_extend(expand(self, self.user_constraints()))
        cns_extend(expand(self, self.component_constraints()))

        stack = list(self.constraints_children)
        stack_pop = stack.pop
        stack_extend = stack.extend
        while stack:
            child = stack_pop()
            if isinstance(child, Container):
                # When we take over layout ownership of a container we
                # don't care about its size hint constraints since we
                # deal with its children directly.
                if child.transfer_layout_ownership(self):
                    cns_extend(expand(child, child.hard_constraints()))
                    cns_extend(expand(child, child.padding_constraints()))
                    cns_extend(expand(child, child.user_constraints()))
                    cns_extend(expand(child, child.component_constraints()))
                    stack_extend(child.constraints_children)
                else:
                    # If we aren't taking over layout ownership, then we
                    # don't care about any of the container's internal
                    # constraints.
                    cns_extend(expand(child, child.hard_constraints()))
                    cns_extend(expand(child, child.size_hint_constraints()))
            else:
                cns_extend(expand(child, child.hard_constraints()))
                cns_extend(expand(child, child.size_hint_constraints()))
                cns_extend(expand(child, child.user_constraints()))
                cns_extend(expand(child, child.component_constraints()))
                if isinstance(child, PaddingConstraints):
                    cns_extend(expand(child, child.padding_constraints()))

        return cns

    def default_user_constraints(self):
        """ Constraints to use if the constraints trait is an empty list.
        The default container behavior is to put the layout children into
        a vertical box layout.

        """
        from ..layout.layout_helpers import vbox
        return [vbox(*self.constraints_children)]

    #--------------------------------------------------------------------------
    # Overrides
    #--------------------------------------------------------------------------
    def size_hint(self):
        """ Overridden parent class method to return the size hint of 
        the container from the layout manager. If the container does not
        own its layout, or if the layout has not been initialized, then
        this method will return (-1, -1). This method does not rely on 
        the size hint computation of the underlying toolkit widget. Thus,
        the underlying widget may call back into this method as needed
        to get a size hint from the layout manager.

        """
        # Since this may be called very often by user code, especially
        # if the toolkit widget is using it as a replacement for its
        # internal size hint computation, we must cache the value or
        # it will be too expensive to use under heavy resize loads.
        # This returns the value from the cached property which is
        # updated whenver the size_hint_updated event is fired.
        return self._size_hint

    #--------------------------------------------------------------------------
    # Auxiliary Methods
    #--------------------------------------------------------------------------
    def transfer_layout_ownership(self, owner):
        """ A method which can be called by other components in the
        hierarchy to gain ownership responsibility for the layout 
        of the children of this container. 

        Whether or not to allow the transfer is determined  by the 
        boolean value of the 'share_layout' flag.

        Parameters
        ----------
        owner : BaseComponent
            The component which has taken ownership responsibility
            for laying out the children of this component. All 
            relayout and refresh requests will be forwarded to this
            component.
        
        Returns
        -------
        results : bool
            True if the transfer was allowed, False otherwise.
        
        """
        if not self.share_layout:
            return False
        self._layout_owner = owner
        return True

    def compute_min_size(self):
        """ Calculates the minimum size of the container which would 
        allow all constraints to be satisfied. If this container does
        not own its layout, or if the layout has not been initialized,
        then this method will return (-1, -1).

        """
        if self.owns_layout and self.layout_manager.initialized:
            width = self.width
            height = self.height
            w, h = self.layout_manager.get_min_size(width, height)
            res = Size(int(round(w)), int(round(h)))
        else:
            res = Size(-1, -1)
        return res

    def compute_max_size(self):
        """ Calculates the maximum size of the container which would 
        allow all constraints to be satisfied. If this container does
        not own its layout, or if the layout has not been initialized
        then this method will return (-1, -1).

        """
        if self.owns_layout and self.layout_manager.initialized:
            width = self.width
            height = self.height
            w, h = self.layout_manager.get_max_size(width, height)
            res = Size(int(round(w)), int(round(h)))
        else:
            res = Size(-1, -1)
        return res
Exemple #21
0
class FEGrid(FEGridActivationMap):

    '''Structured FEGrid consisting of potentially independent
    dof_grid and geo_grid.

    For isoparametric element formulations, the dof_grid and geo_grid may
    share a single cell_grid to save memory.

    Structure of the grid
    ---------------------
    The structure of the grid is defined at two levels:
    1) within a cell specify the distribution of points
    (for dof_r and for geo_r).

    2) the cells are repeated in respective dimension by n_elem
    number of elements

    Services
    --------
    1) For a given element number return the nodal coordinates respecting
       the specification in the geo_r
    2) For a given element number return the array of dof numbers respecting
       the specification in the geo_r
    3) For a given CellSpec return a CellGrid respecting the geometric
       parameters of the FEGrid (applicable for response trace fields
       with finer distribution of nodes.
    4) For a given subdivision of the cell return CellGrid usable as
       visualization field.(where to specify the topology? - probably in
       the CellSpec?
    '''

    # Grid geometry specification
    #
    coord_min = Array(Float, value=[0., 0., 0.])
    coord_max = Array(Float, value=[1., 1., 1.])

    geo_transform = Callable

    # number of elements in the individual dimensions
    shape = Array(Int, value=[1, 1, 1], changes_ndofs=True)

    changed_structure = Event

    @on_trait_change('+changed_structure')
    def set_changed_structure(self):
        self.changed_structure = True

    _tree_label = 'subgrid'

    fets_eval = Instance(IFETSEval)

    # identifier within the refinement level
    idx = Int()

    # links within the dependency
    prev_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid', allow_none=True)
    next_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid', allow_none=True)

    _name = Str('')
    name = Property

    def _get_name(self):
        '''Return the name within the level
        '''
        if self._name == '':
            return 'grid ' + str(self.idx)
        else:
            return self._name

    def _set_name(self, value):
        self._name = value

    def __repr__(self):
        return self.name

    # dof offset within the global enumeration
    dof_offset = Property(
        Int, depends_on='prev_grid.dof_offset,level.dof_offset')
    # cached_property

    def _get_dof_offset(self):
        if self.prev_grid:
            return self.prev_grid.dof_offset + self.prev_grid.n_dofs
        elif self.level:
            return self.level.dof_offset
        else:
            return 0

    _level = WeakRef(
        'ibvpy.mesh.fe_refinement_grid.FERefinementGrid', links_changed=True)
    # parent domain
    level = Property

    def _set_level(self, value):
        'reset the parent of this domain'
        if self._level and self._level != value:
            # remove the grid from the level
            self._level._fe_subgrids.remove(self)
            # unlink it from previous and next
            if self.prev_grid:
                self.prev_grid.next_grid = self.next_grid
            if self.next_grid:
                self.next_grid.prev_grid = self.prev_grid
        # set the new parent
        self._level = value
        # link the subgrid within the level
#        prev_grid = self._level.last_subgrid
#        if prev_grid:
#            self.prev_grid = prev_grid
#            prev_grid.next_grid = self
        # add to the level
        self.idx = len(self._level._fe_subgrids)
        self._level._fe_subgrids.append(self)

    def _get_level(self):
        return self._level

    def __del__(self):
        ''' Release the grid from the dependency structure
        '''
        if self.prev_grid:
            self.prev_grid.next_grid = self.next_grid
        if self.next_grid:
            self.next_grid.prev_grid = self.prev_grid

    dots = Property

    @cached_property
    def _get_dots(self):
        '''Construct and return a new instance of domain
        time stepper.
        '''
        return self.fets_eval.dots_class(sdomain=self)

    dof_r = Property

    def _get_dof_r(self):
        return self.fets_eval.dof_r

    geo_r = Property

    def _get_geo_r(self):
        return self.fets_eval.geo_r

    n_nodal_dofs = Property

    def _get_n_nodal_dofs(self):
        return self.fets_eval.n_nodal_dofs

    #-------------------------------------------------------------------------
    # Derived properties
    #-------------------------------------------------------------------------
    # dof point distribution within the cell converted into the CellSpec format
    # CellSpec can derive the shape of the single grid cell, i.e.
    # the number of points specified in the individual directions.
    #
    dof_grid_spec = Property(Instance(CellSpec), depends_on='fets_eval.dof_r')

    def _get_dof_grid_spec(self):
        return CellSpec(node_coords=self.dof_r)

    # geo point distribution within the cell ... the same as above ...
    #
    geo_grid_spec = Property(Instance(CellSpec), depends_on='fets_eval.geo_r')

    def _get_geo_grid_spec(self):
        return CellSpec(node_coords=self.geo_r)

    # dof_grid is represented by the DofCellGrid class maintaining
    # the internal data structure to retrieve the element mappings
    # within the grid.
    #
    dof_grid = Property(Instance(DofCellGrid),
                        depends_on='fets_eval.dof_r,shape,coord_min,coord_max')

    def _get_dof_grid(self):
        return self._grids[0]

    dof_vertex_X_grid = Property

    def _get_dof_vertex_X_grid(self):
        return self.dof_grid.cell_grid.vertex_X_grid

    intg_elem_grid = Property

    def _get_intg_elem_grid(self):
        return self.dof_grid.cell_grid.cell_idx_grid

    ls_mask = Property

    def _get_ls_mask(self):
        return zeros(self.intg_elem_grid.shape, dtype=bool)

    ls_elem_grid = Property

    def _get_ls_elem_grid(self):
        return self.dof_grid.cell_grid.cell_idx_grid

    intg_elem_grid_dof_map = Property

    def _get_intg_elem_grid_dof_map(self):
        return self.dof_grid.cell_grid_dof_map

    def get_cell_point_X_arr(self, elem):
        return self.geo_grid.get_cell_point_X_arr(elem)

    # geo_grid is represented by the GeoCellGrid class maintaining
    # the internal data structure to retrieve the element mappings
    # within the grid.
    #
    geo_grid = Property(Instance(GeoCellGrid),
                        depends_on='fets_eval.geo_r,shape,coord_min,coord_max')

    def _get_geo_grid(self):
        return self._grids[1]

    traits_view = View(  # Include( 'assemb_view' ),
        #                        Group(
        #                               Item( 'shape' ),
        #                               Item( 'coord_min' ),
        #                               Item( 'coord_max' ),
        #                               Item( 'n_nodal_dofs' ),
        #                               Item( 'fe_cell_array' ),
        #                               label = 'Geometry data' ),
        Group(
            Item('n_dofs'),
            Item('dof_offset'),
            label='DOF data'),
        resizable=True,
        scrollable=True,
    )

    I = Property

    def _get_I(self):
        return ISliceProperty(fe_grid=self)

    def __getitem__(self, idx):
        if isinstance(idx, tuple) or isinstance(idx, int):
            return FEGridIdxSlice(fe_grid=self, grid_slice=idx)
        elif isinstance(idx, str):
            return FEGridLevelSetSlice(fe_grid=self, ls_function_eval=idx)
        else:
            raise TypeError(type(idx), 'is unsupported type for slicing')

    #-------------------------------------------------------------
    # Implement the FEDomain interface
    #-------------------------------------------------------------

    n_dofs = Property(Int)

    def _get_n_dofs(self):
        return self.dof_grid.n_dofs

    def get_cell_offset(self, idx_tuple):
        return self.dof_grid.get_cell_offset(idx_tuple)
    #-------------------------------------------------------------------------
    # Implement the parent interface
    #-------------------------------------------------------------------------

    def deactivate(self, idx):
        '''Exclude the specified element from the integration.
        '''
        if isinstance(idx, tuple):
            self.inactive_elems.append(self.dof_grid.get_cell_offset(idx))
        elif isinstance(idx, int):
            self.inactive_elems.append(idx)
        if self.level:
            self.level.set_changed_structure()

    #-------------------------------------------------------------
    # Implement the IFEUniformDomain interface
    #-------------------------------------------------------------

    # Full elem dof map ignoring masked elements.
    ls_elem_dof_map = Property

    def _get_ls_elem_dof_map(self):
        return self.dof_grid.elem_dof_map

    # Full elem dof map ignoring masked elements.
    elem_dof_map_unmasked = Property

    def _get_elem_dof_map_unmasked(self):
        return self.dof_grid.elem_dof_map

    elem_dof_map = Property(
        depends_on='fets_eval.dof_r,shape,dof_offset, changed_structure')

    def _get_elem_dof_map(self):
        elem_dof_map = self.dof_grid.elem_dof_map[self.activation_map, :]
        return elem_dof_map

    elem_X_map = Property(
        depends_on='fets_eval.geo_r,shape,coord_min,coord_max,n_nodal_dofs, changed_structure')

    @cached_property
    def _get_elem_X_map(self):
        elem_X_map = self.geo_grid.elem_X_map[self.activation_map, :].copy()
        return elem_X_map

    ls_elem_X_map = Property

    def _get_ls_elem_X_map(self):
        return self.geo_grid.elem_X_map.copy()

    elem_X_map_unmasked = Property

    def _get_elem_X_map_unmasked(self):
        return self.geo_grid.elem_X_map.copy()

    elem_x_map = Property(
        depends_on='fets_eval.geo_r,shape,coord_min,coord_max,n_nodal_dofs, changed_structure')

    @cached_property
    def _get_elem_x_map(self):
        elem_x_map = self.geo_grid.elem_x_map[self.activation_map, :].copy()
        return copy(elem_x_map)

    elem_x_map_unmasked = Property

    def _get_elem_x_map_unmasked(self):
        return self.geo_grid.elem_x_map.copy()

    ls_elem_x_map = Property

    def _get_ls_elem_x_map(self):
        return self.geo_grid.elem_x_map.copy()

    X_dof_arr = Property

    def _get_X_dof_arr(self):
        return self.dof_grid.cell_grid.point_X_arr

    elem_node_map = Property

    def _get_elem_node_map(self):
        return self.dof_grid.cell_grid.cell_node_map

    n_grid_elems = Property

    def _get_n_grid_elems(self):
        return reduce(lambda x, y: x * y, self.shape)

    n_active_elems = Property

    def _get_n_active_elems(self):
        return self.elem_dof_map.shape[0]

    elements = Property(
        List, depends_on='fets_eval.dof_r,fets_eval.geo_r,shape+,coord_min,coord_max,fets_eval.n_nodal_dofs,dof_offset, changed_structure')

    @cached_property
    def _get_elements(self):
        return [MElem(dofs=dofs,
                      point_X_arr=point_X_arr,
                      point_x_arr=point_x_arr)
                for dofs, point_X_arr, point_x_arr
                in zip(self.elem_dof_map,
                       self.elem_X_map,
                       self.elem_x_map)]

    dof_Eid = Property
    '''Mapping of Element, Node, Dimension -> DOF 
    '''

    def _get_dof_Eid(self):
        return self.dof_grid.dof_Eid

    dofs = Property

    def _get_dofs(self):
        return self.dof_grid.dofs

    I_Ei = Property
    '''For a given element and its node number return the global index
    of the node'''

    def _get_I_Ei(self):
        return self.geo_grid.cell_grid.cell_node_map

    X_Id = Property()
    '''Array of containing the coordinate d \in (0,1,3)
    of a node I
    '''

    def _get_X_Id(self):
        return self.geo_grid.point_X_arr

    def apply_on_ip_grid(self, fn, ip_mask):
        '''
        Apply the function fn over the first dimension of the array.
        @param fn: function to apply for each ip from ip_mask and each element.
        @param ip_mask: specifies the local coordinates within the element.
        '''
        X_el = self.elem_X_map
        # test call to the function with single output - to get the shape of
        # the result.
        out_single = fn(ip_mask[0], X_el[0])
        out_grid_shape = (X_el.shape[0], ip_mask.shape[0],) + out_single.shape
        out_grid = zeros(out_grid_shape)

        for el in range(X_el.shape[0]):
            for ip in range(ip_mask.shape[0]):
                out_grid[el, ip, ...] = fn(ip_mask[ip], X_el[el])

        return out_grid

    def apply_on_ip_grid_unmasked(self, fn, ip_mask):
        '''
        Apply the function fn over the first dimension of the array.
        @param fn: function to apply for each ip from ip_mask and each element.
        @param ip_mask: specifies the local coordinates within the element.
        '''
        X_el = self.geo_grid.elem_X_map
        # test call to the function with single output - to get the shape of
        # the result.
        out_single = fn(ip_mask[0], X_el[0])
        out_grid_shape = (X_el.shape[0], ip_mask.shape[0],) + out_single.shape
        out_grid = zeros(out_grid_shape)

        for el in range(X_el.shape[0]):
            for ip in range(ip_mask.shape[0]):
                out_grid[el, ip, ...] = fn(ip_mask[ip], X_el[el])

        return out_grid

    #-------------------------------------------------------------
    # Queries
    #-------------------------------------------------------------
    # The delegation had to be replaced with explicit calls to functions.
    # The reason is lost binding if a method
    #
    # fe_domain.get_left_dofs
    #
    # If the discretization parameter changed, then a new dof_grid was generated
    # but the call fe_domain.get_left_dofs went to the original dof_grid.
    #
    # get_all_dofs   = DelegatesTo('dof_grid')
    def get_all_dofs(self): return self.dof_grid.get_all_dofs()
    # get_left_dofs   = Delegate('dof_grid')

    def get_left_dofs(self): return self.dof_grid.get_left_dofs()
    # get_right_dofs  = Delegate('dof_grid')

    def get_right_dofs(self): return self.dof_grid.get_right_dofs()
    # get_top_dofs    = Delegate('dof_grid')

    def get_top_dofs(self): return self.dof_grid.get_top_dofs()
    # get_bottom_dofs = Delegate('dof_grid')

    def get_bottom_dofs(self): return self.dof_grid.get_bottom_dofs()
    # get_front_dofs  = Delegate('dof_grid')

    def get_front_dofs(self): return self.dof_grid.get_front_dofs()
    # get_back_dofs   = Delegate('dof_grid')

    def get_back_dofs(self): return self.dof_grid.get_back_dofs()
    # get_bottom_left_dofs   = Delegate('dof_grid')

    def get_bottom_left_dofs(self): return self.dof_grid.get_bottom_left_dofs()
    # get_bottom_front_dofs   = Delegate('dof_grid')

    def get_bottom_front_dofs(
        self): return self.dof_grid.get_bottom_front_dofs()
    # get_bottom_back_dofs   = Delegate('dof_grid')

    def get_bottom_back_dofs(self): return self.dof_grid.get_bottom_back_dofs()
    # get_top_left_dofs   = Delegate('dof_grid')

    def get_top_left_dofs(self): return self.dof_grid.get_top_left_dofs()
    # get_bottom_right_dofs  = Delegate('dof_grid')

    def get_bottom_right_dofs(
        self): return self.dof_grid.get_bottom_right_dofs()
    # get_top_right_dofs  = Delegate('dof_grid')

    def get_top_right_dofs(self): return self.dof_grid.get_top_right_dofs()
    # get_bottom_middle_dofs  = Delegate('dof_grid')

    def get_bottom_middle_dofs(
        self): return self.dof_grid.get_bottom_middle_dofs()
    # get_top_middle_dofs  = Delegate('dof_grid')

    def get_top_middle_dofs(self): return self.dof_grid.get_top_middle_dofs()
    # get_left_middle_dofs  = Delegate('dof_grid')

    def get_left_middle_dofs(self): return self.dof_grid.get_left_middle_dofs()
    # get_right_middle_dofs  = Delegate('dof_grid')

    def get_right_middle_dofs(
        self): return self.dof_grid.get_right_middle_dofs()
    # get_left_front_bottom_dof  = Delegate('dof_grid')

    def get_left_front_bottom_dofs(
        self): return self.dof_grid.get_left_front_bottom_dofs()
    # get_left_front_middle_dof  = Delegate('dof_grid')

    def get_left_front_middle_dofs(
        self): return self.dof_grid.get_left_front_middle_dofs()

    #-------------------------------------------------------------
    # LevelSet Methods
    #-------------------------------------------------------------

    def get_lset_subdomain(self, lset_function):
        '''@TODO - implement the subdomain selection method
        '''
        raise NotImplementedError

    def get_boundary(self, side=None):
        '''@todo:-implement the boundary extraction
        '''
        raise NotImplementedError

    def get_interior(self):
        '''@todo:-implement the boundary extraction
        '''
        raise NotImplementedError

    def get_ls_mask(self, ls_mask_function):
        '''
        Return a boolean array indicating masked nodes.
        '''
        return self.geo_grid.get_ls_mask(ls_mask_function)

    def get_intersected_elems(self, ls_function, ls_limits=None):
        '''
        Return elems intersected by specified domain.

        Requirements on the e_domain
        - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        The e_domain must be at most n - 1 dimensionality of the grid.

        Two methods must be supported by the e_domain
        1 ) The e_domain must be discretizable within the current grid. That means
           it should return arrays with points on grid lines that intersect the
           e_domain's boundaries
        2) With the list of intersections, it is possible to identify the intersected
           elements.
        These two methods would avoid a full search through all the grid points.

        Applicable:
        -----------
        Boundary conditions including the shape function coefficient
        Local - element enrirchments (XFEM)

        Method result
        -------------
        The method returns the

        '''
        return self.geo_grid.get_intersected_cells(ls_function, ls_limits)

    def get_negative_elems(self, ls_function):
        return self.geo_grid.get_negative_cells(ls_function)

    def get_affected_elems(self, e_domain):
        '''
        3) It should have a notion of inside / outside to decide whether or not
           a point is to be included or not.
        '''

    def get_enclosed_nodes(self, e_domain):
        '''
        Get elements that are inside of the e_domain.
        The elements are not intersected by the boundaries of the e_domain.

        The e_domain must be at least the same dimension as the grid.
        The e_domain must have an operator inside/outside to decide about
        whether or not a point is included.

        The method returns an array of element numbers within the specified e_domain.
        '''

    def get_subgrid(self, bounding_box):
        '''
        Return subgrid with base_node number
        '''

    def get_ls_value(self, X_pnt):
        # @TODO: make it work for 3d
        return self.ls_function.level_set_fn(X_pnt[0], X_pnt[1])

    _grids = Property(
        depends_on='fets_eval.dof_r,fets_eval.geo_r,shape+,coord_min+,coord_max+,fets_eval.n_nodal_dofs,dof_offset,geo_transform')

    @cached_property
    def _get__grids(self):
        '''Regenerate grids based on the specification
        '''
        # Check if the specifiers for both grids are identical
        #
        cell_grid = CellGrid(
            grid_cell_spec=self.dof_grid_spec,
            geo_transform=self.geo_transform,
            shape=self.shape,
            coord_min=self.coord_min,
            coord_max=self.coord_max
        )
        _xdof_grid = DofCellGrid(
            cell_grid=cell_grid,
            n_nodal_dofs=self.n_nodal_dofs,
            dof_offset=self.dof_offset
        )

        # if self.dof_r.all() != self.geo_r.all():
        #
        # @todo check whether this is universally valid (tolerance of coordinate values)
        #
        if not array_equal(self.dof_r, self.geo_r):
            #        if (self.dof_r.shape[0] != self.geo_r.shape[0])or\
            #           ((self.dof_r.shape == self.geo_r.shape) and\
            #            linalg.norm(self.dof_r - self.geo_r) > 1.e-3) :
            # If the geometry grid differs from the dof_grid specifier
            # construct a separate cell grid, otherwise reuse the dof_grid
            cell_grid = CellGrid(grid_cell_spec=self.geo_grid_spec,
                                 geo_transform=self.geo_transform,
                                 shape=self.shape,
                                 coord_min=self.coord_min,
                                 coord_max=self.coord_max)
        _xgeo_grid = GeoCellGrid(cell_grid=cell_grid)
        return (_xdof_grid, _xgeo_grid)

    traits_view = View(Item('name'),
                       Item('n_dofs'),
                       Item('geo_transform@'),
                       Item('dof_offset'),
                       Item('fe_cell_array'),
                       Item('prev_grid'),
                       Item('fets_eval@', resizable=True),
                       resizable=True,
                       scrollable=True,
                       width=0.5,
                       height=0.5,
                       )
Exemple #22
0
class RTraceDomainList(HasTraits):

    label = Str('RTraceDomainField')
    sd = WeakRef(ISDomain)
    position = Enum('nodes', 'int_pnts')
    subfields = List

    def redraw(self):
        '''Delegate the calculation to the pipeline
        '''
        # self.mvp_mgrid_geo.redraw() # 'label_scalars')
        self.mvp_mgrid_geo.rebuild_pipeline(self.vtk_node_structure)

    vtk_node_structure = Property(Instance(tvtk.UnstructuredGrid))

    #@cached_property

    def _get_vtk_node_structure(self):
        self.position = 'nodes'
        return self.vtk_structure

    vtk_ip_structure = Property(Instance(tvtk.UnstructuredGrid))

    #@cached_property

    def _get_vtk_ip_structure(self):
        self.position = 'int_pnts'
        return self.vtk_structure

    vtk_structure = Property(Instance(tvtk.UnstructuredGrid))

    def _get_vtk_structure(self):
        ug = tvtk.UnstructuredGrid()
        cell_array, cell_offsets, cell_types = self.vtk_cell_data
        n_cells = cell_types.shape[0]
        ug.points = self.vtk_X
        vtk_cell_array = tvtk.CellArray()
        vtk_cell_array.set_cells(n_cells, cell_array)
        ug.set_cells(cell_types, cell_offsets, vtk_cell_array)
        return ug

    vtk_X = Property

    def _get_vtk_X(self):
        point_arr_list = []
        for sf in self.subfields:
            if sf.skip_domain:
                continue
            sf.position = self.position
            sf_vtk_X = sf.vtk_X
            if sf_vtk_X.shape[0] == 0:  # all elem are deactivated
                continue
            point_arr_list.append(sf_vtk_X)
        if len(point_arr_list) > 0:
            # print 'point_arr_list ', point_arr_list
            return vstack(point_arr_list)
        else:
            return zeros((0, 3), dtype='float_')

    # point offset to use when more fields are patched together within
    # RTDomainList

    point_offset = Int(0)

    # cell offset to use when more fields are patched together within
    # RTDomainList

    cell_offset = Int(0)

    vtk_cell_data = Property

    def _get_vtk_cell_data(self):
        cell_array_list = []
        cell_offset_list = []
        cell_types_list = []
        point_offset = self.point_offset
        cell_offset = self.cell_offset
        for sf in self.subfields:
            if sf.skip_domain:
                continue
            sf.position = self.position
            sf.point_offset = point_offset
            sf.cell_offset = cell_offset
            cell_array, cell_offsets, cell_types = sf.vtk_cell_data
            cell_array_list.append(cell_array)
            cell_offset_list.append(cell_offsets)
            cell_types_list.append(cell_types)
            point_offset += sf.n_points
            cell_offset += cell_array.shape[0]
        if len(cell_array_list) > 0:
            cell_array = hstack(cell_array_list)
            cell_offsets = hstack(cell_offset_list)
            cell_types = hstack(cell_types_list)
        else:
            cell_array = array([], dtype='int_')
            cell_offsets = array([], dtype='int_')
            cell_types = array([], dtype='int_')
        return (cell_array, cell_offsets, cell_types)

    #-------------------------------------------------------------------------
    # Visualization pipelines
    #-------------------------------------------------------------------------

    mvp_mgrid_geo = Trait(MVUnstructuredGrid)

    #    def _mvp_mgrid_geo_default(self):
    #        return MVUnstructuredGrid( name = 'Response tracer mesh',
    #                                   points = self.vtk_r,
    #                                   cell_data = self.vtk_cell_data,
    #                                    )

    def _mvp_mgrid_geo_default(self):
        return MVUnstructuredGrid(name='Response tracer mesh',
                                  warp=False,
                                  warp_var='')

    view = View(resizable=True)
Exemple #23
0
class TStepperEval(HasTraits):

    """
    Base class for time stepper evaluators (TSE).

    Each time stepper classes implement the methods evaluating the
    governing equations of the simulated problem at a discrete time
    instance t. The time-steppers can be chained in order to reflect
    the hierarchical structure of spatial integration starting from
    the domain and using embedded loops over the finite elements,
    layers and material points.

    Furthermore, the time-step-evals specify the variables that can be
    evaluated for the provided spatial context and state array
    (rte_dict) attribute.
    """
    implements(ITStepperEval)

    tstepper = WeakRef('ibvpy.core.tstepper.TStepper')

    def get_state_array_size(self):
        '''
        Get the array representing the physical state of an object.
        '''
        return 0

    def setup(self, sctx):
        '''
        Setup the spatial context and state array  
        '''
        pass

    def apply_constraints(self, K):
        '''
        Apply implicity constraints associated with internal mappings
        within the time stepper evaluator.

        This method is used by hierarchical representation of the domain
        with local refinements and subdomains.
        '''
        pass

    def get_corr_pred(self, sctx, u, tn, tn1):
        '''
        Get the corrector and predictor.
        '''
        raise NotImplementedError

    def identify_parameters(self):
        '''
        Extract the traits that are floating points and can be associated 
        with a statistical distribution.
        '''
        params = {}
        for name, trait in self.traits().items():
            if trait.trait_type.__class__ is Float:
                params[name] = trait
        return params

    def register_mv_pipelines(self, e):
        ''' Register the visualization pipelines in mayavi engine
            (empty by default)
        '''
        pass

    # Resp-trace-evals
    #
    rte_dict = Trait(Dict)

    def _rte_dict_default(self):
        return {}
Exemple #24
0
class Viz3D(HasStrictTraits):
    '''Base class for visualization objects.
    Each state and operator objects like crease pattern
    or constraint can define provide tailored visualizations
    transferring the information into a view objects shared
    within a particular forming task or a whole forming process.
    '''

    label = Str('default')
    '''Label of the visualization object.
    '''

    anim_t_start = PrototypedFrom('vis3d')
    anim_t_end = PrototypedFrom('vis3d')

    order = Int(1, auto_set=False, enter_set=True)
    '''Deprecated -- is only here to have a control parameter
    that avoids text visualization at the beginning of the time line
    because then mlab fails. 
    '''

    vis3d = WeakRef(Visual3D)
    '''Link to the visual object to transform into the 
    forming_task_view3d.
    '''

    vis3d_changed = Event
    '''Event registering changes in the source object.
    '''

    ftv = WeakRef
    '''Folding task view3d object. 
    '''

    pipes = Dict()

    def register(self, ftv):
        '''Construct the visualization within the forming task view3d object.
        '''
        ftv.viz3d_dict[self.label] = self
        return

    def plot(self):
        '''Plot the object within ftv
        '''
        return

    hidden = Bool(False)

    def _show(self):
        if self.hidden == True:
            self.show()
            self.hidden = False

    def _hide(self):
        if self.hidden == False:
            self.hide()
            self.hidden = True

    def hide(self):
        for pipe in list(self.pipes.values()):
            pipe.visible = False

    def show(self):
        for pipe in list(self.pipes.values()):
            pipe.visible = True

    def update_t(self, anim_t=0.0, vot=0.0):
        '''Update with regard to the global time line.
        '''
        if anim_t >= self.anim_t_start and anim_t <= self.anim_t_end \
                or self.anim_t_end < 0.0:
            self._show()
            self.update(vot)
        else:
            self._hide()

    def update(self, vot=0.0):
        '''Update the visualization within the view3d object.
        '''
        return

    min_max = Property
    '''Bounding box limits set to none by default. 
    '''

    def _get_min_max(self):
        return None, None

    viz3d_view = tui.Group(tui.Item('hode', resizable=True))
class FEGridLevelSetSlice(HasTraits):

    # Link to the source grid to slice
    #
    fe_grid = WeakRef('ibvpy.mesh.fe_grid.FEGrid')

    # Specification of further methods to be provided by the fe_grid_cell
    #
    # @todo -
    # 1) describe the functionality of the methods
    # 2) decide where to implement the details of the method
    #    Generally, the lookup for elements should be directed
    #    to the geo_grid, while the lookup for individual degrees
    #    of freedom should go into the dof_grid
    #
    ls_function_eval = Str

    ls_spec_list = Property(Tuple, depends_on='ls_function_eval')

    @cached_property
    def _get_ls_spec_list(self):
        fn_list = self.ls_function_eval.split(';')
        fns = []
        lim = []
        for fn in fn_list:
            try:
                afn, limits = fn.split('@')
            except:  # convenience - of there is just one fn, user does not have to add region
                afn = fn
                limits = '1'
            fns.append(afn.upper())  # transform to uppercase
            lim.append(limits)  # limits.upper() )
        return fns, lim

    ls_fns = Property

    def _get_ls_fns(self):
        return self.ls_spec_list[0]

    ls_lims = Property

    def _get_ls_lims(self):
        return self.ls_spec_list[1]

    def ls_function(self, X=None, Y=None, Z=None):
        '''
        get_value() evaluates the value for all functions as defined in set_fns, 
        will be 0 if grid point is not defined in any function
        '''
        val = 0.0
        for fn, limit in zip(self.ls_fns, self.ls_lims):
            if eval(limit):
                val = eval(fn)
        return val

    def ls_mask_function(self, X=None, Y=None, Z=None):
        '''Return True if the point is within one of the ranges of the 
        supplied ls functions.
        '''
        masked = True
        for limit in self.ls_lims:
            if eval(limit):
                if masked == False:
                    raise ValueError(
                        'overlapping level set limits - two level sets defined'
                    )
                masked = False
        return masked

    #
    tip_elems = Property(Array(int))

    def _get_tip_elems(self):
        '''Get the elements at the tip of the level set function

        The current implementation does not consider the association
        between the ls_mask function and the ls_function.

        This is probably not correct.

        The tip elems should be removed from the level set alone.

        '''
        ls_mask = self.fe_grid.get_ls_mask(self.ls_mask_function)
        # get the nodes having True value

        masked_nodes = np.argwhere(ls_mask)
        # find out the element associated with these nodes

        neighbour_idx = np.array([[-1, -1], [-1, 0], [0, -1], [0, 0]],
                                 dtype=int)

        # get for each node the neighbors
        e_arr = masked_nodes[:, None, :] + neighbour_idx[None, :, :]

        # get for each node the corresponding neighbors
        e_flattened = [
            e_arr[:, :, idx].flatten() for idx in range(e_arr.shape[2])
        ]
        e_masked = np.c_[tuple(e_flattened)]

        # find the intersection with the level set
        intersected_elem_offsets = self.fe_grid.get_intersected_elems(
            self.ls_function)

        # get the indices of the intersected by the level set
        # this should be done more efficiently
        e_ls = np.array([
            self.fe_grid.geo_grid.cell_grid.get_cell_idx(offset)
            for offset in intersected_elem_offsets
        ],
                        dtype=int)

        # make a broadcasted comparison between the masked elements
        # (elements containing a node outside outside the level set domain)
        # and elements intersected by the level set.
        #
        broadcasted_compare = (e_ls == e_masked[:, None, :])

        # along the axis 2 - there is a comparison of the coordinates
        # - if all are True, then all coordinates are equal - this
        # is detected by the boolean product (logical and along axis 2)
        #
        logical_and_along_ax2 = broadcasted_compare.prod(axis=2, dtype=bool)

        # along the axis 0 - there is a each-with-each comparison
        # - if one of the entries is True, then the second list contains
        #   the same entry as well. This is detected by the sum operator
        #  (logical or along axis 0)
        logical_or_along_ax0 = logical_and_along_ax2.sum(axis=0, dtype=bool)

        #res = ( e_ls == e_masked[:, None, :] ).prod( axis = 2, dtype = bool ).sum( axis = 0, dtype = bool )

        return e_ls[logical_or_along_ax0]

    elems = Property(Array(int))

    def _get_elems(self):
        # 1. for each ls_function identify the intersected elements
        # 2. choose those lying within the ls_domain - including the tip
        # elements.
        return self.fe_grid.get_intersected_elems(self.ls_function,
                                                  self.ls_mask_function)

    neg_elems = Property(Array(Int))

    def _get_neg_elems(self):
        return self.fe_grid.get_negative_elems(self.ls_function)

    pos_elems = Property(Array(Int))

    def _get_pos_elems(self):
        # return self.fe_grid.get_positive_elems()
        raise NotImplementedError

    dof_nodes_values = Property(Array(Float))

    @cached_property
    def _get_dof_nodes_values(self):
        #nodes = self.dof_grid.cell_node_map[ self.get_intersected_elems() ]
        # print "map ", nodes
        #coords = self.dof_grid.get_cell_points(self.get_intersected_elems())
        coords = self.dof_X

        # @todo - make the dimensional dependency
        #
        ls_fn = np.frompyfunc(self.ls_function, 2, 1)
        o_shape = coords.shape  # save shape for later
        if coords.shape[2] == 1:
            X = coords.reshape(o_shape[0] * o_shape[1], o_shape[2]).T
            Y = np.zeros_like(X)
        elif coords.shape[2] == 2:
            X, Y = coords.reshape(o_shape[0] * o_shape[1], o_shape[2]).T
        values = ls_fn(X, Y)
        return values.reshape(o_shape[0], o_shape[1])

    geo_X = Property

    def _get_geo_X(self):
        '''Get global coordinates geometrical points of the intersected elements.
        '''
        return self.fe_grid.geo_grid.get_cell_point_X_arr(self.elems)

    geo_x = Property

    def _get_geo_x(self):
        return self.fe_grid.geo_grid.get_cell_point_x_arr(self.elems)

    dof_X = Property

    def _get_dof_X(self):
        '''Get global coordinates field points of the intersected elements.
        '''
        return self.fe_grid.dof_grid.get_cell_point_X_arr(self.elems)

    r_i = Property(Array(float))  # 2 points/element

    @cached_property
    def _get_r_i(self):
        '''
        Return local coordinates of the intersection points
        Assuming rectangular parametric coordinates
        Works for 1D and 2D
        '''
        i_elements = self.fe_grid.get_intersected_elems(self.ls_function)
        el_pnts = []
        for elem in i_elements:
            inter_pts = []
            # X_mtx = self.elements[elem].get_X_mtx() # skips deactivated
            # elements
            X_mtx = self.fe_grid.geo_grid.get_cell_point_X_arr(elem)
            dim = X_mtx.shape[1]  # TODO:merge 1 and 2d
            if dim == 1:
                r_coord = self._get_intersect_pt(self.ls_fn_r, (0., X_mtx))
                if r_coord != None:
                    inter_pts.append([r_coord])
            elif dim == 2:
                for c_coord in [-1., 1.]:
                    args = (c_coord, X_mtx)
                    s_coord = self._get_intersect_pt(self.ls_fn_s, args)
                    r_coord = self._get_intersect_pt(self.ls_fn_r, args)
                    if s_coord != None:
                        inter_pts.append([c_coord, s_coord])
                    if r_coord != None:
                        inter_pts.append([r_coord, c_coord])
            elif dim == 3:
                raise NotImplementedError('not available for 3D yet')
            el_pnts.append(inter_pts)
        return np.array(el_pnts)

    dofs = Property

    def _get_dofs(self):
        '''Get number of affected DOFs

        @todo - reshape dofs so that they are of dimension
        ( elem, node, dim )
        '''
        dofs = []
        for elem in self.elems:
            dof_map = self.fe_grid.dof_grid.get_cell_dofs(elem).flatten()
            dofs.append(dof_map)
        return np.array(dofs)

    i_neg_dofs = Property

    def _get_i_neg_dofs(self):
        '''Get intersected dofs with negative value of the level set at their position.
        '''
        shape = self.dofs.shape
        n_node_dofs = shape[1] / self.dof_X.shape[1]
        return self.dofs.reshape(shape[0], self.dof_X.shape[1],
                                 n_node_dofs)[self.dof_nodes_values < 0.]

    neg_dofs = Property

    def _get_neg_dofs(self):
        '''Get dofs with negative value of the level set at their position.
        '''
        dofs = []
        for elem in self.neg_elems:
            dof_map = self.fe_grid.dof_grid.get_cell_dofs(elem).flatten()
            dofs.append(dof_map)
        if dofs == []:
            return np.array(dofs)
        return np.hstack(dofs)

    def _get_intersect_pt(self, fn, args):
        try:
            return brentq(fn, -1, 1, args=args)
        except ValueError:
            return

    def ls_fn_r(self, r, s, X_mtx):  # TODO:dimensionless treatment
        X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx)
        if X_pnt.shape[0] == 1:
            Y = 0.
        else:
            Y = X_pnt[1]
        return self.ls_function(X_pnt[0], Y)

    def ls_fn_s(self, s, r, X_mtx):
        X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx)
        return self.ls_function(X_pnt[0], X_pnt[1])

    def ls_fn_X(self, X, Y):
        return self.ls_function(X, Y)
Exemple #26
0
class StyleManager(HasStrictTraits):

    implements(IStyleNodeData)

    component = WeakRef('enaml.widgets.component.Component')

    style_sheet = DelegatesTo('parent')

    style_node = DelegatesTo('parent')

    listen = Bool(False)

    @on_trait_change('style_sheet')
    def sheet_changed(self, sheet):
        if self.listen:
            self.apply()

    @on_trait_change('style_sheet:updated')
    def sheet_updated(self, tags):
        if self.listen:
            self.update_tags(tags)

    def apply(self, set_listen=True):
        style_sheet = self.style_sheet
        tags = set(style_sheet.get_tags())
        self.update_tags(tags)
        if set_listen:
            self.listen = True

    def update_tags(self, tags):
        style_sheet = self.style_sheet
        style_node = self.style_node
        for tag in tags:
            if hasattr(style_node, tag):
                val = style_sheet.get_property(tag, self)
                if val is not NO_STYLE:
                    setattr(style_node, tag, val)

    #--------------------------------------------------------------------------
    # IStyleNodeData interface
    #--------------------------------------------------------------------------
    def node_id(self):
        """ Returns the id of the element being styled as a string.

        """
        return self.component.identifier

    def node_classes(self):
        """ Returns the classes of the element being styled as a space
        separated string.

        """
        return self.style_node.style_class

    def node_type(self):
        """ Returns the type of the element being styled as a string.

        """
        return self.component.type_name

    def parent_node(self):
        """ Returns an IStyleNodeData object for the parent element of the
        element being styled.

        """
        return self.component.parent.style_manager
class ISliceProperty(HasStrictTraits):

    fe_grid = WeakRef()

    def __getitem__(self, idx):
        return FEGridNodeSlice(fe_grid=self.fe_grid, grid_slice=idx)
Exemple #28
0
class TestClass(HasTraits):
    atr = WeakRef(Fruit)
    atr2 = WeakRef(Fruit())
    atr3 = WeakRef("Fruit")