class MotionProfiler(ConfigLoadable):
# ===========================================================================
# configable parameters
# ===========================================================================
    velocity_tol = Float(0.5, enter_set=True, auto_set=False)
    acceleration_tol = Float(0.5, enter_set=True, auto_set=False)
    deceleration_tol = Float(0.05, enter_set=True, auto_set=False)

    max_velocity = Float(4, enter_set=True, auto_set=False)
    min_velocity = Float(0.05, enter_set=True, auto_set=False)

    min_acceleration = Float(0.05)
    min_deceleration = Float(0.05)

    min_acceleration_time = Float(0.2, enter_set=True, auto_set=False)
    max_transit_time = Float(5, enter_set=True, auto_set=False)
# ===============================================================================
# computed parameters
# ===============================================================================
    atime = Float
    dtime = Float
    cvtime = Float

    adisp = Float
    ddisp = Float
    cvdisp = Float

# ===============================================================================
# error flags
# ===============================================================================
    max_transit_err = Bool
    velocity_err = Bool
    min_acceleration_err = Bool
    trapezoidal_err = Bool

    @on_trait_change(','.join(ATTRS))
    def save_parameter(self, obj, name, old, new):

        config = self.get_configuration(self.config_path)
        config.set('General', name, new)
        self.write_configuration(config, self.config_path)

    def load(self, p):
        self.config_path = p
        if os.path.isfile(p):
            config = self.get_configuration(self.config_path)
            for attr in ATTRS:
                self.set_attribute(config, attr, 'General', attr, cast='float')
        else:
            # create a new config file with default values
            config = self.configparser_factory()
            config.add_section('General')
            for attr in ATTRS:
                config.set('General', attr, getattr(self, attr))
            self.write_configuration(config)

    def traits_view(self):
        v = View(
                Group(
                    Item('velocity_tol'),
                    Item('acceleration_tol'),
                    Item('deceleration_tol'),
                    Item('min_velocity'),
                    Item('max_velocity'),
                    Item('max_transit_time'),
                    Item('min_acceleration_time'),
                    label='Tolerances'
                     ),
                 Group(
                       HGroup(
                               Group(
                                     Item('atime', format_str='%0.4f', style='readonly'),
                                     Item('dtime', format_str='%0.4f', style='readonly'),
                                     Item('cvtime', format_str='%0.4f', style='readonly'),
                                     ),
                               spring,
                               Group(
                                     Item('adisp', format_str='%0.4f', style='readonly'),
                                     Item('ddisp', format_str='%0.4f', style='readonly'),
                                     Item('cvdisp', format_str='%0.4f', style='readonly'),
                                     )
                              ),
                       Group(
                             Item('max_transit_err', style='custom'),
                             Item('velocity_err', style='custom'),
                             Item('min_acceleration_err', style='custom'),
                             Item('trapezoidal_err', style='custom'),
                             ),
                       label='Results'
                       )
               )
        return v

    def check_motion(self, displacement, obj):
        ac = obj.nominal_acceleration
        dc = obj.nominal_deceleration
        v = obj.nominal_velocity

        mv = obj.velocity
        mac = obj.acceleration
        mdc = obj.deceleration

        nv, nac, ndc = self.calculate_corrected_parameters(displacement, v, ac, dc)
        dv = abs(nv - mv)
        dac = abs(nac - mac)
        ddc = abs(ndc - mdc)
        change = (
                  dv / mv > self.velocity_tol or
                  dac / mac > self.acceleration_tol or
                  ddc / mdc > self.deceleration_tol)
        return change, nv, max(0.1, nac), max(0.1, ndc)

    def calculate_transit_parameters(self, displacement, v, ac, dc):
        '''
            return the time spent accelerating, decelerating, and at speed
            and respective displacements
        '''
        # time to velocity
        atime = v / ac
        dtime = v / dc

        # acceleration distance
        acd = 0.5 * ac * math.pow(atime, 2)

        # decel distance
        dcd = 0.5 * dc * math.pow(dtime, 2)

        # constant velocity distance
        cvd = displacement - acd - dcd

        cvtime = cvd / v

        self.atime = atime
        self.dtime = dtime
        self.cvtime = cvtime

        self.adisp = acd
        self.ddisp = dcd
        self.cvdisp = cvd

        return (atime, dtime, cvtime), (acd, dcd, cvd),


    def calculate_corrected_parameters(self, displacement, velocity, ac, dc):
        self.velocity_err = False
        self.min_acceleration_err = False
        self.max_transit_err = False
        self.trapezoidal_err = False
        ac = float(ac)
        dc = float(dc)
#        force = False
        acdc_param = 1 / ac + 1 / dc
        '''
            trapezodail movement
            calculate velocity so that atime=dtime=1/2vtime
        '''

        cv = (displacement * ac / 2.0) ** 0.5
        times, _distances = self.calculate_transit_parameters(displacement, cv, ac, dc)

        oac = ac
        odc = dc
        if sum(times) > self.max_transit_time:
            self.max_transit_err = True
            self.debug('max transit error. {} > {}'.format(sum(times), self.max_transit_time))
            # calculate the min velocity required for max_transit_time
            # given ac and dc
            A = 0.5 * acdc_param
            B = -self.max_transit_time
            C = displacement
            det = B ** 2 - 4 * A * C
            while det < 0:
                ac *= 1.01
                dc *= 1.01
                acdc_param = 1 / ac + 1 / dc
                A = 0.5 * acdc_param
                B = -self.max_transit_time
                C = displacement
                det = B ** 2 - 4 * A * C

#             A = 0.5 * acdc_param
#             B = -self.max_transit_time
#             C = displacement
#             det = B ** 2 - 4 * A * C
#
#             i = 1
#             p = 0.01
#             while det < 0:
#                 ac = oac * (1 + p * i)
#                 dc = odc * (1 + p * i)
#                 acdc_param = 1 / ac + 1 / dc
#
#                 A = 0.5 * acdc_param
#                 det = B ** 2 - 4 * A * C
#                 if i > 1000:
#                     p += 0.01

            cv = (-B + math.sqrt(B ** 2 - 4 * A * C)) / (2 * A)

        cv = min(self.max_velocity, cv)

        if times[0] < self.min_acceleration_time:
            self.min_acceleration_err = True
#            #calculate new acceleration for fixed accel time
            ac = cv / self.min_acceleration_time
            self.debug('minimum acceleration time err. {} > {} new accel= {}'.format(times[0],
                                                                                     self.min_acceleration_time, ac))
            dc = ac
#            cv = self.min_acceleration_time * ac
            times, _distances = self.calculate_transit_parameters(displacement, cv, ac, dc)

            if _distances[2] < 0:
                self.trapezoidal_err = True
                ac = displacement / (2 * self.min_acceleration_time ** 2)
                dc = ac
                ncv = ac * self.min_acceleration_time

                self.debug('trapezoidal err. negative velocity displacement velocity= {} new velocity= {}'.format(cv, ncv))
                cv = ncv

#                cv = self.min_velocity
#                ac = self.min_acceleration
#                dc = self.min_acceleration
                # ncv, ac, dc = self.find_min(displacement, cv, ac, dc)
#            force = True

#            times, _distances = self.calculate_transit_parameters(displacement, cv, ac, dc)
        return cv, ac, dc
Example #2
0
class Vis2D(HasStrictTraits):
    '''Each state and operator object can be associated with 
    several visualization objects with a shortened class name Viz3D. 
    In order to introduce a n independent class subsystem into 
    the class structure, objects supporting visualization inherit 
    from Visual3D which introduces a dictionary viz3d objects.
    '''
    def setup(self):
        pass

    sim = WeakRef
    '''Root of the simulation to extract the data
    '''

    vot = Float(0.0, time_change=True)
    '''Visual object time
    '''

    viz2d_classes = Dict
    '''Visualization classes applicable to this object. 
    '''

    viz2d_class_names = Property(List(Str), depends_on='viz2d_classes')
    '''Keys of the viz2d classes
    '''

    @cached_property
    def _get_viz2d_class_names(self):
        return list(self.viz2d_classes.keys())

    selected_viz2d_class = Str

    def _selected_viz2d_class_default(self):
        if len(self.viz2d_class_names) > 0:
            return self.viz2d_class_names[0]
        else:
            return ''

    add_selected_viz2d = Button(label='Add plot viz2d')

    def _add_selected_viz2d_fired(self):
        viz2d_class_name = self.selected_viz2d_class
        self.add_viz2d(viz2d_class_name, '<unnamed>')

    def add_viz2d(self, class_name, name, **kw):
        if name == '':
            name = class_name
        viz2d_class = self.viz2d_classes[class_name]
        viz2d = viz2d_class(name=name, vis2d=self, **kw)
        self.viz2d.append(viz2d)
        if hasattr(self, 'ui') and self.ui:
            self.ui.viz_sheet.viz2d_list.append(viz2d)

    viz2d = List(Viz2D)

    actions = HGroup(
        UItem('add_selected_viz2d'),
        UItem('selected_viz2d_class',
              springy=True,
              editor=EnumEditor(name='object.viz2d_class_names', )),
    )

    def plt(self, name, label=None):
        return Viz2DPlot(plot_fn=name, label=label, vis2d=self)

    view = View(Include('actions'), resizable=True)
Example #3
0
class IntegratorSectorTime(TimeInOut):
    """
    Provides an Integrator in the time domain.
    """

    #: :class:`~acoular.grids.RectGrid` object that provides the grid locations.
    grid = Trait(RectGrid, desc="beamforming grid")

    #: List of sectors in grid
    sectors = List()

    #: Clipping, in Dezibel relative to maximum (negative values)
    clip = Float(-350.0)

    #: Number of channels in output (= number of sectors).
    numchannels = Property(depends_on=[
        'sectors',
    ])

    # internal identifier
    digest = Property(
        depends_on = ['sectors', 'clip', 'grid.digest', 'source.digest', \
        '__class__'],
        )

    @cached_property
    def _get_digest(self):
        return digest(self)

    @cached_property
    def _get_numchannels(self):
        return len(self.sectors)

    def result(self, num=1):
        """
        Python generator that yields the source output integrated over the given 
        sectors, block-wise.
        
        Parameters
        ----------
        num : integer, defaults to 1
            This parameter defines the size of the blocks to be yielded
            (i.e. the number of samples per block).
        
        Returns
        -------
        Samples in blocks of shape (num, :attr:`numchannels`). 
        :attr:`numchannels` is the number of sectors.
        The last block may be shorter than num.
        """
        inds = [self.grid.indices(*sector) for sector in self.sectors]
        gshape = self.grid.shape
        o = empty((num, self.numchannels), dtype=float)  # output array
        for r in self.source.result(num):
            ns = r.shape[0]
            mapshape = (ns, ) + gshape
            rmax = r.max()
            rmin = rmax * 10**(self.clip / 10.0)
            r = where(r > rmin, r, 0.0)
            i = 0
            for ind in inds:
                h = r[:].reshape(mapshape)[(s_[:], ) + ind]
                o[:ns, i] = h.reshape(h.shape[0], -1).sum(axis=1)
                i += 1
            yield o[:ns]
Example #4
0
class FEQ4T(FETSEval):

    debug_on = True

    # Material parameters
    #
    k = Float(1.0, label='conductivity')

    # Dimensional mapping
    #
    dim_slice = slice(0, 2)

    # System mapping parameters
    #
    n_e_dofs = Int(4)
    n_nodal_dofs = Int(1)

    # Order of node positions for the formulation of shape function
    # (isoparametric formulation)
    # Order of node positions for the formulation of shape function
    #
    dof_r = [[-1, -1], [1, -1], [1, 1], [-1, 1]]
    geo_r = [[-1, -1], [1, -1], [1, 1], [-1, 1]]

    # Integration parameters
    #
    ngp_r = 2
    ngp_s = 2

    # Field visualization attributes
    #
    field_entity_type = 'quad'

    # 4 corner nodes, 4 edge nodes and 1 interior nodes
    vtk_r = [[-1., -1.], [0., -1.], [1., -1.], [-1., 0.], [0., 0.], [1., 0.],
             [-1., 1.], [0., 1.], [1., 1.]]
    vtk_cells = [[0, 1, 4, 3], [1, 2, 5, 4], [3, 4, 7, 6], [4, 5, 8, 7]]
    vtk_cell_types = 'Quad'

    #---------------------------------------------------------------------
    # Method required to represent the element geometry
    #---------------------------------------------------------------------
    def get_N_geo_mtx(self, r_pnt):
        '''
        Return the value of shape functions for the specified local coordinate r
        '''
        cx = array(self.geo_r, dtype='float_')
        Nr = array([[
            1 / 4. * (1 + r_pnt[0] * cx[i, 0]) * (1 + r_pnt[1] * cx[i, 1])
            for i in range(0, 4)
        ]])
        return Nr

    def get_dNr_geo_mtx(self, r_pnt):
        '''
        Return the matrix of shape function derivatives.
        Used for the conrcution of the Jacobi matrix.

        @TODO - the B matrix is used
        just for uniaxial bar here with a trivial differential
        operator.
        '''
        cx = array(self.geo_r, dtype='float_')
        dNr_geo = array([[
            1 / 4. * cx[i, 0] * (1 + r_pnt[1] * cx[i, 1]) for i in range(0, 4)
        ], [
            1 / 4. * cx[i, 1] * (1 + r_pnt[0] * cx[i, 0]) for i in range(0, 4)
        ]])
        return dNr_geo

    #---------------------------------------------------------------------
    # Method delivering the shape functions for the field variables and their derivatives
    #---------------------------------------------------------------------
    def get_N_mtx(self, r_pnt):
        return self.get_N_geo_mtx(r_pnt)

    def get_dNr_mtx(self, r_pnt):
        '''
        Return the derivatives of the shape functions
        '''
        return self.get_dNr_geo_mtx(r_pnt)

    def get_B_mtx(self, r_pnt, X_mtx):
        J_mtx = self.get_J_mtx(r_pnt, X_mtx)
        dNr_mtx = self.get_dNr_mtx(r_pnt)
        dNx_mtx = dot(inv(J_mtx), dNr_mtx)
        return dNx_mtx

    def get_mp_state_array_size(self, sctx):
        return 0

    def setup(self, sctx):
        return

    def get_mtrl_corr_pred(self,
                           sctx,
                           eps_eng,
                           d_eps_eng,
                           tn,
                           tn1,
                           eps_avg=None):
        D_mtx = array([[self.k, 0], [0, self.k]])
        sig_mtx = dot(D_mtx, eps_eng)
        return sig_mtx, D_mtx

    rte_dict = Trait(Dict)

    def _rte_dict_default(self):
        '''
        RTraceEval dictionary with standard field variables.
        '''
        rte_dict = self._debug_rte_dict()
        rte_dict.update({
            'eps_app':
            RTraceEvalElemFieldVar(eval=self.get_eps_mtx33),
            'eps0_app':
            RTraceEvalElemFieldVar(eval=self.get_eps0_mtx33),
            'eps1t_app':
            RTraceEvalElemFieldVar(eval=self.get_eps1t_mtx33),
            'u':
            RTraceEvalElemFieldVar(eval=self.get_u)
        })
        return rte_dict
Example #5
0
class BeamProfiler(DisplayPlugin):

    # These traits control the calculation of the Gaussian fit
    background_percentile = Range(0.0, 100.0, 15.0)
    num_crops = Range(0, 5, 1)
    crop_radius = Range(1.0, 4.0, 1.5)  # in beam diameters

    # These are the results of the calculation
    _centroid = Tuple(Float(), Float())
    _minor_axis = Float()
    _major_axis = Float()
    _angle = Float()
    _ellipticity = Float()
    _baseline = Float()
    _include_radius = Float()

    # These control the visualization
    num_points = Int(40)
    color = ColorTrait('white')

    view = View(
        VGroup(Item('active'),
               Item('background_percentile'),
               Item('num_crops', label='Crop # times'),
               Item('crop_radius'),
               label='Beam Profiler',
               show_border=True))

    def __init__(self, **traits):
        super(BeamProfiler, self).__init__(**traits)
        self.screen.data_store['centroid_x'] = N.array([])
        self.screen.data_store['centroid_y'] = N.array([])
        self.screen.data_store['ellipse_x'] = N.array([])
        self.screen.data_store['ellipse_y'] = N.array([])
        renderers = self.screen.plot.plot(('centroid_x', 'centroid_y'),
                                          type='scatter',
                                          marker_size=2.0,
                                          color=self.color,
                                          marker='circle')
        self._centroid_patch = renderers[0]
        self._centroid_patch.visible = self.active
        renderers = self.screen.plot.plot(('ellipse_x', 'ellipse_y'),
                                          type='line',
                                          color=self.color)
        self._ellipse_patch = renderers[0]
        self._ellipse_patch.visible = self.active

        # Connect handlers
        self.on_trait_change(self._move_centroid, '_centroid', dispatch='ui')
        self.on_trait_change(self._redraw_ellipse,
                             '_centroid,_width,_height,_angle',
                             dispatch='ui')
        self.on_trait_change(
            self._update_hud,
            '_centroid,_width,_height,_angle,_ellipticity,_baseline,'
            '_include_radius',
            dispatch='ui')

    def _move_centroid(self):
        self.screen.data_store['centroid_x'] = N.array([self._centroid[0]])
        self.screen.data_store['centroid_y'] = N.array([self._centroid[1]])

    def _redraw_ellipse(self):
        # Draw an N-point ellipse at the 1/e radius of the Gaussian fit
        # Using a parametric equation in t
        t = N.linspace(0, 2 * N.pi, self.num_points)
        angle = N.radians(self._angle)
        x0, y0 = self._centroid
        sin_t, cos_t = N.sin(t), N.cos(t)
        sin_angle, cos_angle = N.sin(angle), N.cos(angle)
        r_a = self._major_axis / 2.0
        r_b = self._minor_axis / 2.0
        x = x0 + r_a * cos_t * cos_angle - r_b * sin_t * sin_angle
        y = y0 + r_a * cos_t * sin_angle + r_b * sin_t * cos_angle
        self.screen.data_store['ellipse_x'] = x
        self.screen.data_store['ellipse_y'] = y

    def _update_hud(self):
        self.screen.hud(
            'profiler',
            'Centroid: {0._centroid[0]:.1f}, {0._centroid[1]:.1f}\n'
            'Major axis: {0._major_axis:.1f}\n'
            'Minor axis: {0._minor_axis:.1f}\n'
            u'Rotation: {0._angle:.1f}°\n'
            'Ellipticity: {0._ellipticity:.3f}\n'
            'Baseline: {0._baseline:.1f}\n'
            'Inclusion radius: {0._include_radius:.1f}'.format(self))

    def _process(self, frame):
        bw = (len(frame.shape) == 2)
        if not bw:
            # Use standard NTSC conversion formula
            frame = N.array(0.2989 * frame[..., 0] + 0.5870 * frame[..., 1] +
                            0.1140 * frame[..., 2])

        # Calibrate the background
        background = N.percentile(frame, self.background_percentile)
        frame -= background
        #N.clip(frame, 0.0, frame.max(), out=frame)

        m00, m10, m01, m20, m02, m11 = _calculate_moments(frame)

        bc, lc = 0, 0
        for count in range(self.num_crops):
            include_radius, dlc, dbc, drc, dtc, frame = _crop(
                frame, self.crop_radius, m00, m10, m01, m20, m02, m11)
            lc += dlc
            bc += dbc

            # Recalibrate the background and recalculate the moments
            new_bkg = N.percentile(frame, self.background_percentile)
            frame -= new_bkg
            background += new_bkg
            #N.clip(frame, 0.0, frame.max(), out=frame)

            m00, m10, m01, m20, m02, m11 = _calculate_moments(frame)

        m10 += lc
        m01 += bc

        # Calculate Gaussian boundary
        q = N.sqrt((m20 - m02)**2 + 4 * m11**2)
        self._major_axis = 2**1.5 * N.sqrt(m20 + m02 + q)
        self._minor_axis = 2**1.5 * N.sqrt(m20 + m02 - q)
        self._angle = N.degrees(0.5 * N.arctan2(2 * m11, m20 - m02))
        self._ellipticity = self._minor_axis / self._major_axis

        self._centroid = (m10, m01)
        self._baseline = background
        self._include_radius = include_radius

    def activate(self):
        self._centroid_patch.visible = self._ellipse_patch.visible = True

    def deactivate(self):
        self.screen.hud('profiler', None)
        self._centroid_patch.visible = self._ellipse_patch.visible = False
Example #6
0
class IVTKWithCrustAndBrowser(SplitApplicationWindow):
    """ Provides an Scene along with an embedded PyCrust Python shell.
    In the shell, 'scene' and 's' are bound to the Scene."""

    # The ratio of the size of the left/top pane to the right/bottom pane.
    ratio = Float(0.7)

    # The direction in which the panel is split.
    direction = Str('horizontal')

    # The `Scene` instance into which VTK renders.
    scene = Instance(Scene)

    # The `PipelineBrowser` instance.
    browser = Instance(PipelineBrowser)

    # The ordered split window to use.
    browser_scene = Instance(SceneWithBrowser)

    # The `PythonShell` instance.
    python_shell = Instance(PythonShell)

    ###########################################################################
    # 'object' interface.
    ###########################################################################
    def __init__(self, **traits):
        """ Creates a new window. """

        # Base class constructor.
        super(IVTKWithCrustAndBrowser, self).__init__(**traits)
        self.title = 'TVTK Scene'
        # Create the window's menu bar.
        self.menu_bar_manager = create_ivtk_menu(self)

    ###########################################################################
    # `IWindow` interface.
    ###########################################################################
    def close(self):
        if self.scene is not None:
            self.scene.close()
        super(IVTKWithCrustAndBrowser, self).close()

    ###########################################################################
    # Protected 'SplitApplicationWindow' interface.
    ###########################################################################

    # The icon of the window
    icon = Instance(ImageResource, scene_icon)

    def _create_lhs(self, parent):
        """ Creates the left hand side or top depending on the style. """
        self.browser_scene = SceneWithBrowser(parent)
        self.scene = self.browser_scene.scene
        self.browser = self.browser_scene.browser
        return self.browser_scene.control

    def _create_rhs(self, parent):
        """ Creates the right hand side or bottom depending on the
        style.  's' and 'scene' are bound to the Scene instance."""

        self.python_shell = PythonShell(parent)
        self.python_shell.bind('scene', self.scene)
        self.python_shell.bind('s', self.scene)
        self.python_shell.bind('browser', self.browser)
        self.python_shell.bind('b', self.browser)
        self.python_shell.bind('tvtk', tvtk)

        return self.python_shell.control
Example #7
0
class ScatterPlot(BaseXYPlot):
    """
    Renders a scatter plot, given an index and value arrays.
    """

    # The CompiledPath to use if **marker** is set to "custom". This attribute
    # must be a compiled path for the Kiva context onto which this plot will
    # be rendered.  Usually, importing kiva.GraphicsContext will do
    # the right thing.
    custom_symbol = Any

    #------------------------------------------------------------------------
    # Styles on a ScatterPlot
    #------------------------------------------------------------------------

    # The type of marker to use.  This is a mapped trait using strings as the
    # keys.
    marker = MarkerTrait

    # The pixel size of the markers, not including the thickness of the outline.
    # Default value is 4.0.
    # TODO: for consistency, there should be a size data source and a mapper
    marker_size = Either(Float, Array)

    # The function which actually renders the markers
    render_markers_func = Callable(render_markers)

    # The thickness, in pixels, of the outline to draw around the marker.  If
    # this is 0, no outline is drawn.
    line_width = Float(1.0)

    # The fill color of the marker.
    color = black_color_trait

    # The color of the outline to draw around the marker.
    outline_color = black_color_trait

    # The RGBA tuple for rendering lines.  It is always a tuple of length 4.
    # It has the same RGB values as color_, and its alpha value is the alpha
    # value of self.color multiplied by self.alpha.
    effective_color = Property(Tuple, depends_on=['color', 'alpha'])

    # The RGBA tuple for rendering the fill.  It is always a tuple of length 4.
    # It has the same RGB values as outline_color_, and its alpha value is the
    # alpha value of self.outline_color multiplied by self.alpha.
    effective_outline_color = Property(Tuple,
                                       depends_on=['outline_color', 'alpha'])

    # Traits UI View for customizing the plot.
    traits_view = ScatterPlotView()

    #------------------------------------------------------------------------
    # Selection and selection rendering
    # A selection on the lot is indicated by setting the index or value
    # datasource's 'selections' metadata item to a list of indices, or the
    # 'selection_mask' metadata to a boolean array of the same length as the
    # datasource.
    #------------------------------------------------------------------------

    show_selection = Bool(True)

    selection_marker = MarkerTrait

    selection_marker_size = Float(4.0)

    selection_line_width = Float(1.0)

    selection_color = ColorTrait("yellow")

    selection_outline_color = black_color_trait

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

    _cached_selected_pts = Trait(None, None, Array)
    _cached_selected_screen_pts = Array
    _cached_point_mask = Array
    _cached_selection_point_mask = Array
    _selection_cache_valid = Bool(False)

    #------------------------------------------------------------------------
    # Overridden PlotRenderer methods
    #------------------------------------------------------------------------

    def map_screen(self, data_array):
        """ Maps an array of data points into screen space and returns it as
        an array.

        Implements the AbstractPlotRenderer interface.
        """
        # data_array is Nx2 array
        if len(data_array) == 0:
            return []

        # XXX: For some reason, doing the tuple unpacking doesn't work:
        #        x_ary, y_ary = transpose(data_array)
        # There is a mysterious error "object of too small depth for
        # desired array".  However, if you catch this exception and
        # try to execute the very same line of code again, it works
        # without any complaints.
        #
        # For now, we just use slicing to assign the X and Y arrays.
        data_array = asarray(data_array)
        if len(data_array.shape) == 1:
            x_ary = data_array[0]
            y_ary = data_array[1]
        else:
            x_ary = data_array[:, 0]
            y_ary = data_array[:, 1]

        sx = self.index_mapper.map_screen(x_ary)
        sy = self.value_mapper.map_screen(y_ary)
        if self.orientation == "h":
            return transpose(array((sx, sy)))
        else:
            return transpose(array((sy, sx)))

    def map_data(self, screen_pt, all_values=True):
        """ Maps a screen space point into the "index" space of the plot.

        Overrides the BaseXYPlot implementation, and always returns an
        array of (index, value) tuples.
        """
        x, y = screen_pt
        if self.orientation == 'v':
            x, y = y, x
        return array(
            (self.index_mapper.map_data(x), self.value_mapper.map_data(y)))

    def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \
                  index_only = False):
        """ Maps a screen space point to an index into the plot's index array(s).

        Overrides the BaseXYPlot implementation..
        """
        if index_only and self.index.sort_order != "none":
            data_pt = self.map_data(screen_pt)[0]
            # The rest of this was copied out of BaseXYPlot.
            # We can't just used BaseXYPlot.map_index because
            # it expect map_data to return a value, not a pair.
            if ((data_pt < self.index_mapper.range.low) or \
                (data_pt > self.index_mapper.range.high)) and outside_returns_none:
                return None
            index_data = self.index.get_data()
            value_data = self.value.get_data()

            if len(value_data) == 0 or len(index_data) == 0:
                return None

            try:
                ndx = reverse_map_1d(index_data, data_pt,
                                     self.index.sort_order)
            except IndexError, e:
                # if reverse_map raises this exception, it means that data_pt is
                # outside the range of values in index_data.
                if outside_returns_none:
                    return None
                else:
                    if data_pt < index_data[0]:
                        return 0
                    else:
                        return len(index_data) - 1

            if threshold == 0.0:
                # Don't do any threshold testing
                return ndx

            x = index_data[ndx]
            y = value_data[ndx]
            if isnan(x) or isnan(y):
                return None
            sx, sy = self.map_screen([x, y])
            if ((threshold == 0.0) or (screen_pt[0] - sx) < threshold):
                return ndx
            else:
                return None
        else:
Example #8
0
class PhiFnStrainHardening(PhiFnBase):
    '''
    Damage function.
    '''

    implements(IPhiFn)

    Epp = Float(5.9e-05,
                desc='microplane strain at the onset of damage',
                enter_set=True,
                auto_set=False)
    Efp = Float(1.91e-04,
                desc='microplane strain at totaly damaged state',
                enter_set=True,
                auto_set=False)
    Dfp = Float(0.4,
                desc='asymptotic damage level',
                enter_set=True,
                auto_set=False)
    Elimit = Float(8.00e-02,
                   desc='microplane strain at ultimate failure',
                   enter_set=True,
                   auto_set=False)

    def identify_parameters(self):
        return ['Epp', 'Efp', 'Dfp', 'Elimit']

    def get_plot_range(self):
        return 0.0, self.Elimit * 1.2

    @on_trait_change('Epp,Efp,Dfp,Elimit')
    def refresh_plot(self):
        super(PhiFnStrainHardening, self).refresh_plot()

    def _polar_discr_changed(self):
        self.polar_discr.regularization = False

    def get_integ(self, e_max, *c_list):
        '''
        OBSOLETE method - was used for decoupled evaluation of fracture
        energy contribution of the microplane.

        The method returns the value of the following integral:
        int( Phi(e_max~)**2 * e_max~, e_max~ = 0..e_max )
        The value corresponds to the fracture energy of the considered microplane
        divided by E. (Note: For a damage function Phi(e_max) the microplane stress-strain curve
        evaluates to s = Phi(e_max)*E*Phi(e_max)*e_max.)
        '''
        if len(c_list) == 0:
            c_list = [1., 1.]
        Epp = self.Epp * c_list[0]
        Efp = self.Efp * c_list[1]
        Dfp = self.Dfp * c_list[2]
        Elimit = self.Elimit * c_list[3]
        # @todo: modify this for the case tension stiffening
        if e_max <= Epp:
            return 0
        else:
            return -0.5 * Epp * (-Epp - 2.0 * Efp + 2.0 * Efp * exp(((-e_max + Epp) / Efp))) \
                   - 0.5 * e_max * Epp * exp(-((e_max - Epp) / Efp))

    def get_value(self, e_max, *c_list):
        '''
        Evaluate the integrity of a particular microplane.
        '''
        #        print 'x3c_list', c_list
        if len(c_list) == 0:
            c_list = [1., 1., 1., 1.]
#        print 'x4c_list ', c_list

#        print 'self.Epp used for TensionStiffening:', self.Epp
#        print 'self.Efp used for TensionStiffening:', self.Efp
#        print 'self.Dfp used for TensionStiffening:', self.Dfp
#        print 'self.Elimit used for TensionStiffening:', self.Elimit

        Epp = self.Epp * c_list[0]
        Efp = self.Efp * c_list[1]
        Dfp = self.Dfp * c_list[2]
        Elimit = self.Elimit * c_list[3]
        #
        if e_max <= Epp:
            return 1.0
        elif e_max >= Elimit:
            return 1.0e-100


# @todo: check if this is neccessary:
# if values smaller then 1.e-310 are returned
# a zero division error occures otherwise for Dfp=0!
#        elif (e_max-Epp)/Efp >= 50:
#            return Dfp
        else:
            return (1 - Dfp) * sqrt(
                Epp / e_max * exp(-(e_max - Epp) / Efp)) + Dfp

    # Default TraitsUI view
    traits_view = View(HGroup(Group(
        Item('Epp'),
        Item('Efp'),
        Item('Dfp'),
        Item('Elimit'),
        springy=True,
    ),
                              Item('mfn', show_label=False, editor=mfn_editor),
                              label='Damage function',
                              show_border=True),
                       buttons=['OK', 'Cancel'],
                       resizable=True,
                       width=800,
                       height=400)
Example #9
0
class PhiFnStrainHardeningBezier(PhiFnBase):
    '''Fitted polynomial'''

    epsilon_0 = Float(0.00004)
    epsilon_b = Float(0.0005)
    epsilon_f = Float(0.02)
    omega_b = Float(0.63)
    omega_f = Float(0.27)
    omega_t = Float(0.27)

    def identify_parameters(self):
        return [
            'epsilon_0', 'epsilon_b', 'epsilon_f', 'omega_b', 'omega_f',
            'omega_t'
        ]

    def _polar_discr_changed(self):
        self.polar_discr.regularization = False

    def get_plot_range(self):
        return 0.0, self.epsilon_f * 1.5

    def get_value(self, epsilon, *c_list):

        if len(c_list) == 0:
            c_list = ones((6, ), dtype=float)

        epsilon_0 = self.epsilon_0 * c_list[0]
        epsilon_b = self.epsilon_b * c_list[1]
        epsilon_f = self.epsilon_f * c_list[2]
        omega_b = self.omega_b * c_list[3]
        omega_f = self.omega_f * c_list[4]
        omega_t = self.omega_t * c_list[5]

        if epsilon <= epsilon_0:
            return 1.0
        elif epsilon_0 < epsilon and epsilon <= (epsilon_0 + epsilon_b):
            return 1 - omega_b / epsilon_b * (epsilon - epsilon_0)
        elif (epsilon_0 + epsilon_b
              ) < epsilon and epsilon <= epsilon_0 + epsilon_b + epsilon_f:
            MapleGenVar1 = (pow(
                1.0 - (epsilon_b * omega_t -
                       sqrt(epsilon_b * epsilon_b * omega_t * omega_t -
                            2.0 * epsilon_b * omega_t * epsilon * omega_b +
                            2.0 * epsilon_b * omega_t * epsilon_0 * omega_b +
                            2.0 * epsilon_b * epsilon_b * omega_t * omega_b +
                            epsilon_f * omega_b * omega_b * epsilon -
                            epsilon_f * omega_b * omega_b * epsilon_0 -
                            epsilon_f * omega_b * omega_b * epsilon_b)) /
                (2.0 * epsilon_b * omega_t - epsilon_f * omega_b), 2.0) *
                            omega_b)
            MapleGenVar3 = (
                2.0 * (1.0 -
                       (epsilon_b * omega_t -
                        sqrt(epsilon_b * epsilon_b * omega_t * omega_t -
                             2.0 * epsilon_b * omega_t * epsilon * omega_b +
                             2.0 * epsilon_b * omega_t * epsilon_0 * omega_b +
                             2.0 * epsilon_b * epsilon_b * omega_t * omega_b +
                             epsilon_f * omega_b * omega_b * epsilon -
                             epsilon_f * omega_b * omega_b * epsilon_0 -
                             epsilon_f * omega_b * omega_b * epsilon_b)) /
                       (2.0 * epsilon_b * omega_t - epsilon_f * omega_b)) *
                (epsilon_b * omega_t -
                 sqrt(epsilon_b * epsilon_b * omega_t * omega_t -
                      2.0 * epsilon_b * omega_t * epsilon * omega_b +
                      2.0 * epsilon_b * omega_t * epsilon_0 * omega_b +
                      2.0 * epsilon_b * epsilon_b * omega_t * omega_b +
                      epsilon_f * omega_b * omega_b * epsilon -
                      epsilon_f * omega_b * omega_b * epsilon_0 -
                      epsilon_f * omega_b * omega_b * epsilon_b)) /
                (2.0 * epsilon_b * omega_t - epsilon_f * omega_b) *
                (omega_b + omega_t))
            MapleGenVar4 = (
                pow(
                    epsilon_b * omega_t -
                    sqrt(epsilon_b * epsilon_b * omega_t * omega_t -
                         2.0 * epsilon_b * omega_t * epsilon * omega_b +
                         2.0 * epsilon_b * omega_t * epsilon_0 * omega_b +
                         2.0 * epsilon_b * epsilon_b * omega_t * omega_b +
                         epsilon_f * omega_b * omega_b * epsilon -
                         epsilon_f * omega_b * omega_b * epsilon_0 -
                         epsilon_f * omega_b * omega_b * epsilon_b), 2.0) /
                pow(2.0 * epsilon_b * omega_t - epsilon_f * omega_b, 2.0) *
                (omega_b + omega_f))
            MapleGenVar2 = MapleGenVar3 + MapleGenVar4
            return 1 - (MapleGenVar1 + MapleGenVar2)
        elif epsilon > epsilon_0 + epsilon_b + epsilon_f:
            eps_r = epsilon_0 + epsilon_b + epsilon_f
            omega = 1 - \
                (omega_b + omega_f + omega_b / epsilon_b * (epsilon - eps_r))
            if omega <= 0.001:
                return 0.001
            else:
                return omega

    # Default TraitsUI view
    traits_view = View(Group(Item('mfn', show_label=False, editor=mfn_editor),
                             label='Damage law',
                             show_border=True),
                       buttons=['OK', 'Cancel'],
                       resizable=True,
                       width=800,
                       height=800)
Example #10
0
class PhiFnStrainSoftening(PhiFnBase):
    '''
    Damage function.
    '''

    implements(IPhiFn)

    G_f = Float(0.001117,
                label='G_f',
                desc='fracture energy',
                auto_set=False,
                enter_set=True)
    f_t = Float(2.8968,
                label='f_t',
                desc='tensile strength',
                auto_set=False,
                enter_set=True)
    md = Float(
        0.0,
        label='md',
        desc=
        'factor affecting the compresive strength (explain more precisely)',
        auto_set=False,
        enter_set=True)
    h = Float(1.0,
              label='h',
              desc='element size to norm the fracture energy',
              auto_set=False,
              enter_set=True)

    Epp = Float(desc='strain at the onset of damage',
                enter_set=True,
                auto_set=False)
    Efp = Float(desc='strain at total damaged', enter_set=True, auto_set=False)

    @on_trait_change('G_f,f_t,md,h,polar_discr.E')
    def fit_microplane_params(self):
        '''
        Calculate the parameters of the damage function
        '''
        if self.polar_discr == None:
            return
        E = self.polar_discr.E
        G_f = self.G_f
        f_t = self.f_t
        md = self.md
        h = self.h

        gamma = (E * G_f) / (h * f_t**2)
        if gamma < 2.0:
            print 'WARNING: elements too big -> refine, h should be at maximum only half of the characteristic length'
            print 'in FIT PARAMS: gamma set to 2.0'
            gamma = 2.0

        Epp = f_t / \
            ((E * (1 - md) ** 2) * (1.95 - 0.95 / (gamma - 1) ** (0.5)))
        Efp = (G_f / ((1 - md) * h * E * Epp) +
               (2.13 - 1.13 * md) * Epp) / (2.73 - md) - Epp
        self.Epp = Epp
        self.Efp = Efp
        # @todo - plotting must be done separately
        # self.refresh_plot()

    def _polar_discr_changed(self):
        self.polar_discr.regularization = True

    def get_plot_range(self):
        return 0, self.Epp * 20.

    def get_integ(self, e_max, *c_list):
        '''
        OBSOLETE method - was used for decoupled evaluation of fracture
        energy contribution of the microplane.

        The method returns the value of the following integral:
        int( Phi(e_max~)**2 * e_max~, e_max~ = 0..e_max )
        The value corresponds to the fracture energy of the considered microplane
        divided by E. (Note: For a damage function Phi(e_max) the microplane stress-strain curve
        evaluates to s = Phi(e_max)*E*Phi(e_max)*e_max.)
        '''
        if len(c_list) == 0:
            c_list = [1., 1.]
        Epp = self.Epp * c_list[0]
        Efp = self.Efp * c_list[1]
        #
        # cf. derivation in Maple 'relation_between_Gf-ft_and_Epp-Efp
        # devide the result by E, i.e. the returned value does NOT include E
        if e_max <= Epp:
            return 0
        else:
            return -0.5 * Epp * (-Epp - 2.0 * Efp + 2.0 * Efp * exp(((-e_max + Epp) / Efp))) \
                   - 0.5 * e_max * Epp * exp(-((e_max - Epp) / Efp))

    def get_value(self, e_max, *c_list):
        '''
        Evaluate the integrity of a particular microplane.
        '''
        if len(c_list) == 0:
            c_list = [1., 1.]
        Epp = self.Epp * c_list[0]
        Efp = self.Efp * c_list[1]
        if e_max <= Epp:
            return 1.0
# @todo: check if this is necessary:
# if values smaller then 1.e-310 are returned
# a zero division error occures otherwise!
#        elif (e_max-Epp)/Efp >= 50:
#            return 1e-200
        else:
            return sqrt(Epp / e_max * exp(-(e_max - Epp) / Efp))

    # Default TraitsUI view
    traits_view = View(HGroup(
        VGroup(
            Group(
                Item('G_f'),
                Item('f_t'),
                Item('md'),
                Item('h'),
                show_border=True,
                label='Macroscopic damage parameters',
                springy=True,
            ),
            Group(
                Item('Epp', style='readonly'),
                Item('Efp', style='readonly'),
                show_border=True,
                label='Microplane damage parameters',
                springy=True,
            ),
            springy=True,
        ),
        Group(
            Item('mfn', show_label=False, editor=mfn_editor),
            show_border=True,
            label='Damage function',
        ),
    ),
                       buttons=['OK', 'Cancel'],
                       resizable=True,
                       width=800,
                       height=500)
Example #11
0
class PhiFnStrainHardeningLinear(PhiFnBase):
    '''
    Damage function which leads to a piecewise linear stress strain response.
    '''

    implements(IPhiFn)

    E_f = Float(70e+3,
                desc='E-Modulus of the fibers',
                enter_set=True,
                auto_set=False,
                modified=True)
    E_m = Float(34e+3,
                desc='E-Modulus of the matrix',
                enter_set=True,
                auto_set=False,
                modified=True)
    rho = Float(0.03,
                desc='reinforcement ratio',
                enter_set=True,
                auto_set=False,
                modified=True)
    sigma_0 = Float(5.0,
                    desc='asymptotic damage level',
                    enter_set=True,
                    auto_set=False,
                    modified=True)
    alpha = Float(0.0,
                  desc='Slope of the strain hardening curve in section II',
                  enter_set=True,
                  auto_set=False,
                  modified=True)
    beta = Float(0.0,
                 desc='Slope of the strain hardening curve in section III',
                 enter_set=True,
                 auto_set=False,
                 modified=True)
    Elimit = Float(0.006,
                   desc='microplane strain at ultimate failure',
                   enter_set=True,
                   auto_set=False,
                   modified=True)

    def identify_parameters(self):
        return ['E_f', 'E_m', 'rho', 'sigma_0', 'alpha', 'beta', 'Elimit']

    def get_plot_range(self):
        return 0.0, self.Elimit * 1.2

    @on_trait_change('+modified')
    def refresh_plot(self):
        print 'refreshing'
        super(PhiFnStrainHardeningLinear, self).refresh_plot()

    def _polar_discr_changed(self):
        print 'regularizatoin set to False'
        self.polar_discr.regularization = False

    def get_value(self, e_max, *c_list):
        '''
        Evaluate the integrity of a particular microplane.
        '''
        #        print 'x3c_list', c_list
        if len(c_list) == 0:
            c_list = [1., 1., 1., 1., 1., 1., 1.]
#        print 'x4c_list ', c_list

        E_f = self.E_f * c_list[0]
        E_m = self.E_m * c_list[1]
        rho = self.rho * c_list[2]
        sigma_0 = self.sigma_0 * c_list[3]
        alpha = self.alpha * c_list[4]
        beta = self.beta * c_list[5]
        Elimit = self.Elimit * c_list[6]
        #
        E_c = E_m * (1 - rho) + E_f * rho

        epsilon_0 = sigma_0 / E_c

        if e_max <= epsilon_0:
            return 1.0
        elif e_max >= Elimit:
            print '********** microplane reached maximum strain *********'
            return 1e-100

        epsilon_1 = sigma_0 * (-rho * E_f - E_m + rho * E_m + rho * E_f *
                               alpha + beta * E_m - beta * E_m * rho) / \
                              (rho * E_f + E_m - rho * E_m) / rho / E_f / \
                              (alpha - 1.0)

        epsilon = e_max

        if epsilon < epsilon_1:
            return sqrt(
                1.0 +
                (sigma_0 * rho * E_f + sigma_0 * E_m - sigma_0 * rho * E_m +
                 rho * rho * E_f * E_f * alpha * epsilon + rho * E_f * alpha *
                 epsilon * E_m - rho * rho * E_f * alpha * epsilon * E_m -
                 rho * E_f * alpha * sigma_0 - epsilon * rho * rho * E_f *
                 E_f - 2.0 * epsilon * rho * E_f * E_m + 2.0 * epsilon * rho *
                 rho * E_f * E_m - epsilon * E_m * E_m + 2.0 * epsilon * rho *
                 E_m * E_m - epsilon * rho * rho * E_m * E_m) /
                pow(rho * E_f + E_m - rho * E_m, 2.0) / epsilon)
        else:
            return sqrt(
                1.0 + E_m *
                (-E_f * rho * epsilon + epsilon * rho * rho * E_f +
                 beta * sigma_0 - beta * sigma_0 * rho - epsilon * E_m +
                 2.0 * epsilon * rho * E_m - epsilon * rho * rho * E_m) /
                pow(rho * E_f + E_m - rho * E_m, 2.0) / epsilon)

    # Default TraitsUI view
    traits_view = View(HGroup(Group(
        Item('E_f'),
        Item('E_m'),
        Item('rho'),
        Item('sigma_0'),
        Item('alpha'),
        Item('beta'),
        Item('Elimit'),
        springy=True,
    ),
                              Item('mfn', show_label=False, editor=mfn_editor),
                              label='Damage function',
                              show_border=True),
                       buttons=['OK', 'Cancel'],
                       resizable=True,
                       width=800,
                       height=400)
Example #12
0
class MagnetScan(SpectrometerTask):
    #    graph = Any
    detectors = DelegatesTo('spectrometer')
    integration_time = DelegatesTo('spectrometer')

    reference_detector = Any
    additional_detectors = List

    start_mass = Float(36)
    stop_mass = Float(40)
    step_mass = Float(1)
    normalize = Bool(True)

    verbose = False

    def _scan_dac(self, values):

        if self.spectrometer.simulation:
            self._peak_generator = psuedo_peak(values[len(values) / 2] + 0.001,
                                               values[0], values[-1],
                                               len(values))
            #self._peak_generator= multi_peak_generator(values)

        gen = (vi for vi in values)
        evt = Event()
        intensities = []
        mag = self.spectrometer.magnet

        invoke_in_main_thread(self._iter_dac, mag, gen.next(), gen, evt,
                              intensities)

        while not evt.isSet():
            time.sleep(0.01)

        return True

    def _iter_dac(self, mag, di, gen, evt, intensities):

        mag.set_dac(di, verbose=self.verbose)

        d = self._magnet_step_hook()

        self._graph_hook(di, d)

        intensities.append(d)

        try:
            di = gen.next()
        except StopIteration:
            di = None

        if di is not None and self.isAlive():
            p = int(self.integration_time * 1000 * 0.9)
            do_after(p, self._iter_dac, mag, di, gen, evt, intensities)
        else:
            evt.set()

    def _update_graph_data(self, plot, di, intensity, **kw):
        """
            add and scale scans
        """
        def set_data(k, v):
            plot.data.set_data(k, v)

        def get_data(k):
            return plot.data.get_data(k)

        R = None
        r = None
        mi, ma = Inf, -Inf
        for i, v in enumerate(intensity):
            oys = None
            k = 'odata{}'.format(i)
            if hasattr(plot, k):
                oys = getattr(plot, k)

            oys = array([v]) if oys is None else hstack((oys, v))
            setattr(plot, k, oys)

            if i == 0:
                # calculate ref range
                miR = min(oys)
                maR = max(oys)
                R = maR - miR
            else:
                mir = min(oys)
                mar = max(oys)
                r = mar - mir

            if r and R and self.normalize:
                oys = (oys - mir) * R / r + miR

            xs = get_data('x{}'.format(i))
            xs = hstack((xs, di))
            set_data('x{}'.format(i), xs)
            set_data('y{}'.format(i), oys)
            mi, ma = min(mi, min(oys)), max(ma, max(oys))

        self.graph.set_y_limits(min_=mi,
                                max_=ma,
                                pad='0.05',
                                pad_style='upper')

    def _graph_hook(self, di, intensity, **kw):
        graph = self.graph
        if graph:
            plot = graph.plots[0]
            self._update_graph_data(plot, di, intensity)

    def _magnet_step_hook(self):
        spec = self.spectrometer
        ds = [str(self.reference_detector)] + self.additional_detectors
        intensity = spec.get_intensity(ds)

        # debug
        if globalv.experiment_debug:
            from numpy import array, random, ones

            v = self._peak_generator.next()
            v = array([v])

            r = ones(len(ds))
            r = r * v
            if len(r) > 1:
                r[1] *= 0.5
                if len(r) > 2:
                    r[2] *= 0.1

            intensity = r

        return intensity

    def _execute(self):
        sm = self.start_mass
        em = self.stop_mass
        stm = self.step_mass

        self.verbose = True
        if abs(sm - em) > stm:
            self._do_scan(sm, em, stm)
            self._alive = False
            self._post_execute()

        self.verbose = False

    def _do_scan(self, sm, em, stm, directions=None, map_mass=True):
        self.debug('_do_scan')
        # default to forward scan
        if directions is None:
            directions = [1]
        elif isinstance(directions, str):
            if directions == 'Decrease':
                directions = [-1]
            elif directions == 'Oscillate':

                def oscillate():
                    i = 0
                    while 1:
                        if i % 2 == 0:
                            yield 1
                        else:
                            yield -1
                        i += 1

                directions = oscillate()
            else:
                directions = [1]

        spec = self.spectrometer
        mag = spec.magnet
        if map_mass:
            detname = self.reference_detector.name
            ds = spec.correct_dac(self.reference_detector,
                                  mag.map_mass_to_dac(sm, detname))
            de = spec.correct_dac(self.reference_detector,
                                  mag.map_mass_to_dac(em, detname))

            massdev = abs(sm - em)
            dacdev = abs(ds - de)

            stm = stm / float(massdev) * dacdev
            sm, em = ds, de

        for di in directions:
            if not self._alive:
                return

            if di == -1:
                sm, em = em, sm
            values = self._calc_step_values(sm, em, stm)

            if not self._scan_dac(values):
                return

        return True

    def _post_execute(self):
        self.debug('scan finished')

    def _reference_detector_default(self):
        return self.detectors[0]

    def edit_view(self):
        v = self.traits_view()
        v.title = self.title
        v.buttons = ['OK', 'Cancel']
        return v

    def traits_view(self):
        v = View(
            Group(Item('reference_detector',
                       editor=EnumEditor(name='detectors')),
                  Item('start_mass',
                       label='Start Mass',
                       tooltip='Start scan at this mass'),
                  Item('stop_mass',
                       label='Stop Mass',
                       tooltip='Stop scan when magnet reaches this mass'),
                  Item('step_mass',
                       label='Step Mass',
                       tooltip='Step from Start to Stop by this amount'),
                  Item('integration_time', label='Integration (s)'),
                  HGroup(
                      spring,
                      Item('execute_button',
                           editor=ButtonEditor(label_value='execute_label'),
                           show_label=False)),
                  label='Magnet Scan',
                  show_border=True))

        return v


#============= EOF =============================================
Example #13
0
class Axis(ConfigLoadable):
    '''
    '''
    id = Int
    #    name = Str
    position = Float
    negative_limit = Float
    positive_limit = Float
    pdir = Str
    parent = Any(transient=True)
    calculate_parameters = Bool(True)
    drive_ratio = Float(1)

    velocity = Property(depends_on='_velocity')
    _velocity = Float(enter_set=True, auto_set=False)

    acceleration = Property(depends_on='_acceleration')
    _acceleration = Float(enter_set=True, auto_set=False)

    deceleration = Property(depends_on='_deceleration')
    _deceleration = Float(enter_set=True, auto_set=False)

    machine_velocity = Float
    machine_acceleration = Float
    machine_deceleration = Float
    # sets handled by child class

    nominal_velocity = Float
    nominal_acceleration = Float
    nominal_deceleration = Float

    sign = CInt(1)

    def _get_velocity(self):
        return self._velocity

    def _get_acceleration(self):
        return self._acceleration

    def _get_deceleration(self):
        return self._deceleration

    def upload_parameters_to_device(self):
        pass

    @on_trait_change('_velocity, _acceleration, _deceleration')
    def update_machine_values(self, obj, name, old, new):
        setattr(self, 'machine{}'.format(name), new)

    def _calibration_changed(self):
        self.parent.update_axes()

    def simple_view(self):
        v = View(
            Item('calculate_parameters'),
            Item('velocity',
                 format_str='%0.3f',
                 enabled_when='not calculate_parameters'),
            Item('acceleration',
                 format_str='%0.3f',
                 enabled_when='not calculate_parameters'),
            Item('deceleration',
                 format_str='%0.3f',
                 enabled_when='not calculate_parameters'), Item('drive_ratio'))
        return v

    def full_view(self):
        return self.simple_view()

    def dump(self):
        '''
        '''
        pass
#        self.loaded = False
#
#        p = os.path.join(self.pdir, '.%s' % self.name)
#        with open(p, 'w') as f:
#            pickle.dump(self, f)
#    def load_parameters_from_config(self, path):
#        self.config_path = path
#        self._load_parameters_from_config(path)
#
#    def load_parameters(self, pdir):
#        '''
#        '''
# #        self.pdir = pdir
# #        p = os.path.join(pdir, '.%s' % self.name)
# #
# #        if os.path.isfile(p):
# #            return p
# #        else:
#        self.load(pdir)

    def save(self):
        pass

    def ask(self, cmd):
        return self.parent.ask(cmd)

    def _get_parameters(self, path):
        '''
  
        '''
        #        cp = ConfigParser.ConfigParser()
        #        cp.read())
        params = []
        #        if path is None:
        if not os.path.isfile(path):
            path = os.path.join(path, '{}axis.cfg'.format(self.name))

        cp = self.get_configuration(path)
        if cp:
            params = [item for s in cp.sections() for item in cp.items(s)]

#        for ai in a:
#            print ai
#
#        for s in cp.sections():
#            for i in cp.items(s):
#                params.append(i)
        return params

    def _validate_float(self, v):
        try:
            v = float(v)
            return v
        except ValueError:
            pass
Example #14
0
class PointObject(Object):
    """Represent a group of individual points in a mayavi scene."""

    label = Bool(False)
    label_scale = Float(0.01)
    projectable = Bool(False)  # set based on type of points
    orientable = Property(depends_on=['project_to_points'])
    text3d = List
    point_scale = Float(10, label='Point Scale')

    # projection onto a surface
    project_to_points = Array(float, shape=(None, 3))
    project_to_tris = Array(int, shape=(None, 3))
    project_to_surface = Bool(False,
                              label='Project',
                              desc='project points '
                              'onto the surface')
    orient_to_surface = Bool(False,
                             label='Orient',
                             desc='orient points '
                             'toward the surface')
    scale_by_distance = Bool(False,
                             label='Dist.',
                             desc='scale points by '
                             'distance from the surface')
    mark_inside = Bool(False,
                       label='Mark',
                       desc='mark points inside the '
                       'surface in a different color')
    inside_color = RGBColor((0., 0., 0.))

    glyph = Instance(Glyph)
    resolution = Int(8)

    view = View(
        HGroup(Item('visible', show_label=False),
               Item('color', show_label=False), Item('opacity')))

    def __init__(self, view='points', has_norm=False, *args, **kwargs):
        """Init.

        Parameters
        ----------
        view : 'points' | 'cloud'
            Whether the view options should be tailored to individual points
            or a point cloud.
        has_norm : bool
            Whether a norm can be defined; adds view options based on point
            norms (default False).
        """
        assert view in ('points', 'cloud', 'arrow')
        self._view = view
        self._has_norm = bool(has_norm)
        super(PointObject, self).__init__(*args, **kwargs)

    def default_traits_view(self):  # noqa: D102
        color = Item('color', show_label=False)
        scale = Item('point_scale',
                     label='Size',
                     width=_SCALE_WIDTH,
                     editor=laggy_float_editor_headscale)
        orient = Item('orient_to_surface',
                      enabled_when='orientable and not project_to_surface',
                      tooltip='Orient points toward the surface')
        dist = Item('scale_by_distance',
                    enabled_when='orientable and not project_to_surface',
                    tooltip='Scale points by distance from the surface')
        mark = Item('mark_inside',
                    enabled_when='orientable and not project_to_surface',
                    tooltip='Mark points inside the surface using a different '
                    'color')
        if self._view == 'arrow':
            visible = Item('visible', label='Show', show_label=False)
            return View(HGroup(visible, scale, 'opacity', 'label', Spring()))
        elif self._view == 'points':
            visible = Item('visible', label='Show', show_label=True)
            views = (visible, color, scale, 'label')
        else:
            assert self._view == 'cloud'
            visible = Item('visible', show_label=False)
            views = (visible, color, scale)

        if not self._has_norm:
            return View(HGroup(*views))

        group2 = HGroup(dist,
                        Item('project_to_surface',
                             show_label=True,
                             enabled_when='projectable',
                             tooltip='Project points onto the surface '
                             '(for visualization, does not affect '
                             'fitting)'),
                        orient,
                        mark,
                        Spring(),
                        show_left=False)
        return View(HGroup(HGroup(*views), group2))

    @on_trait_change('label')
    def _show_labels(self, show):
        _toggle_mlab_render(self, False)
        while self.text3d:
            text = self.text3d.pop()
            text.remove()

        if show and len(self.src.data.points) > 0:
            fig = self.scene.mayavi_scene
            if self._view == 'arrow':  # for axes
                x, y, z = self.src.data.points[0]
                self.text3d.append(
                    text3d(x,
                           y,
                           z,
                           self.name,
                           scale=self.label_scale,
                           color=self.color,
                           figure=fig))
            else:
                for i, (x, y, z) in enumerate(np.array(self.src.data.points)):
                    self.text3d.append(
                        text3d(x,
                               y,
                               z,
                               ' %i' % i,
                               scale=self.label_scale,
                               color=self.color,
                               figure=fig))
        _toggle_mlab_render(self, True)

    @on_trait_change('visible')
    def _on_hide(self):
        if not self.visible:
            self.label = False

    @on_trait_change('scene.activated')
    def _plot_points(self):
        """Add the points to the mayavi pipeline"""
        if self.scene is None:
            return
        if hasattr(self.glyph, 'remove'):
            self.glyph.remove()
        if hasattr(self.src, 'remove'):
            self.src.remove()

        _toggle_mlab_render(self, False)
        x, y, z = self.points.T
        fig = self.scene.mayavi_scene
        scatter = pipeline.scalar_scatter(x, y, z, fig=fig)
        if not scatter.running:
            # this can occur sometimes during testing w/ui.dispose()
            return
        # fig.scene.engine.current_object is scatter
        mode = 'arrow' if self._view == 'arrow' else 'sphere'
        glyph = pipeline.glyph(scatter,
                               color=self.color,
                               figure=fig,
                               scale_factor=self.point_scale,
                               opacity=1.,
                               resolution=self.resolution,
                               mode=mode)
        glyph.actor.property.backface_culling = True
        glyph.glyph.glyph.vector_mode = 'use_normal'
        glyph.glyph.glyph.clamping = False
        if mode == 'arrow':
            glyph.glyph.glyph_source.glyph_position = 'tail'

        glyph.actor.mapper.color_mode = 'map_scalars'
        glyph.actor.mapper.scalar_mode = 'use_point_data'
        glyph.actor.mapper.use_lookup_table_scalar_range = False

        self.src = scatter
        self.glyph = glyph

        self.sync_trait('point_scale', self.glyph.glyph.glyph, 'scale_factor')
        self.sync_trait('color', self.glyph.actor.property, mutual=False)
        self.sync_trait('visible', self.glyph)
        self.sync_trait('opacity', self.glyph.actor.property)
        self.sync_trait('mark_inside', self.glyph.actor.mapper,
                        'scalar_visibility')
        self.on_trait_change(self._update_points, 'points')
        self._update_marker_scaling()
        self._update_marker_type()
        self._update_colors()
        _toggle_mlab_render(self, True)
        # self.scene.camera.parallel_scale = _scale

    # don't put project_to_tris here, just always set project_to_points second
    @on_trait_change('points,project_to_points,project_to_surface,mark_inside')
    def _update_projections(self):
        """Update the styles of the plotted points."""
        if not hasattr(self.src, 'data'):
            return
        if self._view == 'arrow':
            self.src.data.point_data.normals = self.nn
            self.src.data.point_data.update()
            return
        # projections
        if len(self.project_to_points) <= 1 or len(self.points) == 0:
            return

        # Do the projections
        pts = self.points
        surf = dict(rr=np.array(self.project_to_points),
                    tris=np.array(self.project_to_tris))
        method = 'accurate' if len(surf['rr']) <= 20484 else 'nearest'
        proj_pts, proj_nn = _project_onto_surface(pts,
                                                  surf,
                                                  project_rrs=True,
                                                  return_nn=True,
                                                  method=method)[2:4]
        vec = pts - proj_pts  # point to the surface
        if self.project_to_surface:
            pts = proj_pts
            nn = proj_nn
        else:
            nn = vec.copy()
            _normalize_vectors(nn)
        if self.mark_inside and not self.project_to_surface:
            scalars = _points_outside_surface(pts, surf).astype(int)
        else:
            scalars = np.ones(len(pts))
        # With this, a point exactly on the surface is of size point_scale
        dist = np.linalg.norm(vec, axis=-1, keepdims=True)
        self.src.data.point_data.normals = (250 * dist + 1) * nn
        self.src.data.point_data.scalars = scalars
        self.glyph.actor.mapper.scalar_range = [0., 1.]
        self.src.data.points = pts  # projection can change this
        self.src.data.point_data.update()

    @on_trait_change('color,inside_color')
    def _update_colors(self):
        if self.glyph is None:
            return
        # inside_color is the surface color, let's try to get far
        # from that
        inside = np.array(self.inside_color)
        # if it's too close to gray, just use black:
        if np.mean(np.abs(inside - 0.5)) < 0.2:
            inside.fill(0.)
        else:
            inside = 1 - inside
        colors = np.array([tuple(inside) + (1, ),
                           tuple(self.color) + (1, )]) * 255.
        self.glyph.module_manager.scalar_lut_manager.lut.table = colors

    @on_trait_change('project_to_surface,orient_to_surface')
    def _update_marker_type(self):
        # not implemented for arrow
        if self.glyph is None or self._view == 'arrow':
            return
        defaults = DEFAULTS['coreg']
        gs = self.glyph.glyph.glyph_source
        res = getattr(gs.glyph_source, 'theta_resolution',
                      getattr(gs.glyph_source, 'resolution', None))
        if self.project_to_surface or self.orient_to_surface:
            gs.glyph_source = tvtk.CylinderSource()
            gs.glyph_source.height = defaults['eegp_height']
            gs.glyph_source.center = (0., -defaults['eegp_height'], 0)
            gs.glyph_source.resolution = res
        else:
            gs.glyph_source = tvtk.SphereSource()
            gs.glyph_source.phi_resolution = res
            gs.glyph_source.theta_resolution = res

    @on_trait_change('scale_by_distance,project_to_surface')
    def _update_marker_scaling(self):
        if self.glyph is None:
            return
        if self.scale_by_distance and not self.project_to_surface:
            self.glyph.glyph.scale_mode = 'scale_by_vector'
        else:
            self.glyph.glyph.scale_mode = 'data_scaling_off'

    def _resolution_changed(self, new):
        if not self.glyph:
            return
        gs = self.glyph.glyph.glyph_source.glyph_source
        if isinstance(gs, tvtk.SphereSource):
            gs.phi_resolution = new
            gs.theta_resolution = new
        elif isinstance(gs, tvtk.CylinderSource):
            gs.resolution = new
        else:  # ArrowSource
            gs.tip_resolution = new
            gs.shaft_resolution = new

    @cached_property
    def _get_orientable(self):
        return (len(self.project_to_points) > 0
                and len(self.project_to_tris) > 0)
Example #15
0
class BeadCalibrationOp(HasStrictTraits):
    """
    Calibrate arbitrary channels to molecules-of-fluorophore using fluorescent
    beads (eg, the Spherotech RCP-30-5A rainbow beads.)
    
    To use, set the `beads_file` property to an FCS file containing the beads'
    events; specify which beads you ran by setting the `beads_type` property
    to match one of the values of BeadCalibrationOp.BEADS; and set the
    `units` dict to which channels you want calibrated and in which units.
    Then, call `estimate()` and check the peak-finding with 
    `default_view().plot()`.  If the peak-finding is wacky, try adjusting
    `bead_peak_quantile` and `bead_brightness_threshold`.  When the peaks are
    successfully identified, call apply() on your experimental data set. 
    
    If you can't make the peak finding work, please submit a bug report!
    
    This procedure works best when the beads file is very clean data.  It does
    not do its own gating (maybe a future addition?)  In the meantime, 
    I recommend gating the *acquisition* on the FSC/SSC channels in order
    to get rid of debris, cells, and other noise.
    
    Finally, because you can't have a negative number of fluorescent molecules
    (MEFLs, etc) (as well as for math reasons), this module filters out
    negative values.
    
    
    Attributes
    ----------
    name : Str
        The operation name (for UI representation.)

    units : Dict(Str, Str)
        A dictionary specifying the channels you want calibrated (keys) and
        the units you want them calibrated in (values).  The units must be
        keys of the `beads` attribute.       
        
    beads_file : File
        A file containing the FCS events from the beads.  Must be set to use
        `estimate()`.  This isn't persisted by `pickle()`.

    beads : Dict(Str, List(Float))
        The beads' characteristics.  Keys are calibrated units (ie, MEFL or
        MEAP) and values are ordered lists of known fluorophore levels.  Common
        values for this dict are included in BeadCalibrationOp.BEADS.
        Must be set to use `estimate()`.
        
    bead_peak_quantile : Int
        The quantile threshold used to choose bead peaks.  Default == 80.
        Must be set to use `estimate()`.
        
    bead_brightness_threshold : Float
        How bright must a bead peak be to be considered?  Default == 100.
        Must be set to use `estimate()`.
        
    bead_brightness_cutoff : Float
        If a bead peak is above this, then don't consider it.  Takes care of
        clipping saturated detection.  Defaults to 70% of the detector range.
        
    force_linear : Bool(False)
        A linear fit in log space doesn't always go through the origin, which 
        means that the calibration function isn't strictly a multiplicative
        scaling operation.  Set `force_linear` to force the such
        behavior.  Keep an eye on the diagnostic plot, though, to see how much
        error you're introducing!
        
        
    Metadata
    --------
    bead_calibration_fn : Callable (pandas.Series --> pandas.Series)
        The function to calibrate raw data to bead units
        
    bead_units : String
        The units this channel was calibrated to
        
        
    Notes
    -----
    The peak finding is rather sophisticated.  
    
    For each channel, a 256-bin histogram is computed on the log-transformed
    bead data, and then the histogram is smoothed with a Savitzky-Golay 
    filter (with a window length of 5 and a polynomial order of 1).  
    
    Next, a wavelet-based peak-finding algorithm is used: it convolves the
    smoothed histogram with a series of wavelets and looks for relative 
    maxima at various length-scales.  The parameters of the smoothing 
    algorithm were arrived at empircally, using beads collected at a wide 
    range of PMT voltages.
    
    Finally, the peaks are filtered by height (the histogram bin has a quantile
    greater than `bead_peak_quantile`) and intensity (brighter than 
    `bead_brightness_threshold`).
    
    How to convert from a series of peaks to mean equivalent fluorochrome?
    If there's one peak, we assume that it's the brightest peak.  If there
    are two peaks, we assume they're the brightest two.  If there are n >=3
    peaks, we check all the contiguous n-subsets of the bead intensities
    and find the one whose linear regression (in log space!) has the smallest
    norm (square-root sum-of-squared-residuals.)
    
    There's a slight subtlety in the fact that we're performing the linear
    regression in log-space: if the relationship in log10-space is Y=aX + b,
    then the same relationship in linear space is x = 10**X, y = 10**y, and
    y = (10**b) * (x ** a).
    
    One more thing.  Because the beads are (log) evenly spaced across all
    the channels, we can directly compute the fluorophore equivalent in channels
    where we wouldn't usually measure that fluorophore: for example, you can
    compute MEFL (mean equivalent fluorosceine) in the PE-Texas Red channel,
    because the bead peak pattern is the same in the PE-Texas Red channel
    as it would be in the FITC channel.
    
    Examples
    --------
    >>> bead_op = flow.BeadCalibrationOp()
    >>> bead_op.beads = flow.BeadCalibrationOp.BEADS["Spherotech RCP-30-5A Lot AA01-AA04, AB01, AB02, AC01, GAA01-R"]
    >>> bead_op.units = {"Pacific Blue-A" : "MEFL",
                         "FITC-A" : "MEFL",
                         "PE-Tx-Red-YG-A" : "MEFL"}
    >>>
    >>> bead_op.beads_file = "beads.fcs"
    >>> bead_op.estimate(ex3)
    >>>
    >>> bead_op.default_view().plot(ex3)  
    >>> # check the plot!
    >>>
    >>> ex4 = bead_op.apply(ex3)  
    """

    # traits
    id = Constant('edu.mit.synbio.cytoflow.operations.beads_calibrate')
    friendly_id = Constant("Bead Calibration")

    name = Constant("Bead Calibration")
    units = Dict(Str, Str)

    beads_file = File(exists=True)
    bead_peak_quantile = Int(80)

    bead_brightness_threshold = Float(100)
    bead_brightness_cutoff = Float(Undefined)

    # TODO - bead_brightness_threshold should probably be different depending
    # on the data range of the input.

    force_linear = Bool(False)

    beads = Dict(Str, List(Float))

    _calibration_functions = Dict(Str, Callable, transient=True)
    _peaks = Dict(Str, Any, transient=True)
    _mefs = Dict(Str, Any, transient=True)

    def estimate(self, experiment, subset=None):
        """
        Estimate the calibration coefficients from the beads file.
        """
        if not experiment:
            raise util.CytoflowOpError("No experiment specified")

        if not self.beads_file:
            raise util.CytoflowOpError("No beads file specified")

        if not set(self.units.keys()) <= set(experiment.channels):
            raise util.CytoflowOpError(
                "Specified channels that weren't found in "
                "the experiment.")

        if not set(self.units.values()) <= set(self.beads.keys()):
            raise util.CytoflowOpError("Units don't match beads.")

        # make a little Experiment
        check_tube(self.beads_file, experiment)
        beads_exp = ImportOp(
            tubes=[Tube(file=self.beads_file)],
            channels={
                experiment.metadata[c]["fcs_name"]: c
                for c in experiment.channels
            },
            name_metadata=experiment.metadata['name_metadata']).apply()

        channels = self.units.keys()

        for channel in channels:
            data = beads_exp.data[channel]

            # TODO - this assumes the data is on a linear scale.  check it!
            data_range = experiment.metadata[channel]['range']

            if self.bead_brightness_cutoff is Undefined:
                cutoff = 0.7 * data_range
            else:
                cutoff = self.bead_brightness_cutoff

            # bin the data on a log scale

            hist_bins = np.logspace(1,
                                    math.log(data_range, 2),
                                    num=256,
                                    base=2)
            hist = np.histogram(data, bins=hist_bins)

            # mask off-scale values
            hist[0][0] = 0
            hist[0][-1] = 0

            # smooth it with a Savitzky-Golay filter
            hist_smooth = scipy.signal.savgol_filter(hist[0], 5, 1)

            # find peaks
            peak_bins = scipy.signal.find_peaks_cwt(
                hist_smooth,
                widths=np.arange(3, 20),
                max_distances=np.arange(3, 20) / 2)

            # filter by height and intensity
            peak_threshold = np.percentile(hist_smooth,
                                           self.bead_peak_quantile)
            peak_bins_filtered = \
                [x for x in peak_bins if hist_smooth[x] > peak_threshold
                 and hist[1][x] > self.bead_brightness_threshold
                 and hist[1][x] < cutoff]

            peaks = [hist_bins[x] for x in peak_bins_filtered]
            mef_unit = self.units[channel]

            if not mef_unit in self.beads:
                raise util.CytoflowOpError(
                    "Invalid unit {0} specified for channel {1}".format(
                        mef_unit, channel))

            # "mean equivalent fluorochrome"
            mef = self.beads[mef_unit]

            if len(peaks) == 0:
                raise util.CytoflowOpError(
                    "Didn't find any peaks for channel {}; "
                    "check the diagnostic plot".format(channel))
            elif len(peaks) > len(self.beads):
                raise util.CytoflowOpError(
                    "Found too many peaks for channel {}; "
                    "check the diagnostic plot".format(channel))
            elif len(peaks) == 1:
                # if we only have one peak, assume it's the brightest peak
                a = mef[-1] / peaks[0]
                self._peaks[channel] = peaks
                self._mefs[channel] = [mef[-1]]
                self._calibration_functions[channel] = lambda x, a=a: a * x
            elif len(peaks) == 2:
                # if we have only two peaks, assume they're the brightest two
                self._peaks[channel] = peaks
                self._mefs[channel] = [mef[-2], mef[-1]]
                a = (mef[-1] - mef[-2]) / (peaks[1] - peaks[0])
                print a
                self._calibration_functions[channel] = lambda x, a=a: a * x
            else:
                # if there are n > 2 peaks, check all the contiguous n-subsets
                # of mef for the one whose linear regression with the peaks
                # has the smallest (norm) sum-of-residuals.

                # do it in log10 space because otherwise the brightest peaks
                # have an outsized influence.

                best_resid = np.inf
                for start, end in [(x, x + len(peaks))
                                   for x in range(len(mef) - len(peaks) + 1)]:
                    mef_subset = mef[start:end]

                    # linear regression of the peak locations against mef subset
                    lr = np.polyfit(np.log10(peaks),
                                    np.log10(mef_subset),
                                    deg=1,
                                    full=True)

                    resid = lr[1][0]
                    if resid < best_resid:
                        best_lr = lr[0]
                        best_resid = resid
                        self._peaks[channel] = peaks
                        self._mefs[channel] = mef_subset

                if self.force_linear:
                    # if we're forcing a linear scale for the calibration
                    # function, find that scale with an optimization.  (we can't
                    # use this above, to find the MEFs from the peaks, because
                    # when i tried it mis-identified the proper subset.)

                    # even though this keeps things a linear scale, it can
                    # actually introduce *more* errors because "blank" beads
                    # still fluoresce.

                    def s(x):
                        p = np.multiply(self._peaks[channel], x)
                        return np.sum(
                            np.abs(np.subtract(p, self._mefs[channel])))

                    res = scipy.optimize.minimize(s, [1])

                    print res
                    a = res.x[0]
                    self._calibration_functions[channel] = \
                        lambda x, a=a: a * x

                else:
                    # remember, these (linear) coefficients came from logspace, so
                    # if the relationship in log10 space is Y = aX + b, then in
                    # linear space the relationship is x = 10**X, y = 10**Y,
                    # and y = (10**b) * x ^ a

                    # also remember that the result of np.polyfit is a list of
                    # coefficients with the highest power first!  so if we
                    # solve y=ax + b, coeff #0 is a and coeff #1 is b

                    a = best_lr[0]
                    b = 10**best_lr[1]
                    self._calibration_functions[channel] = \
                        lambda x, a=a, b=b: b * np.power(x, a)

    def apply(self, experiment):
        """Applies the bleedthrough correction to an experiment.
        
        Parameters
        ----------
        old_experiment : Experiment
            the experiment to which this op is applied
            
        Returns
        -------
            a new experiment calibrated in physical units.
        """
        if not experiment:
            raise util.CytoflowOpError("No experiment specified")

        channels = self.units.keys()

        if not self.units:
            raise util.CytoflowOpError("No channels to calibrate.")

        if not self._calibration_functions:
            raise util.CytoflowOpError("Calibration not found. "
                                       "Did you forget to call estimate()?")

        if not set(channels) <= set(experiment.channels):
            raise util.CytoflowOpError(
                "Module units don't match experiment channels")

        if set(channels) != set(self._calibration_functions.keys()):
            raise util.CytoflowOpError("Calibration doesn't match units. "
                                       "Did you forget to call estimate()?")

        # two things.  first, you can't raise a negative value to a non-integer
        # power.  second, negative physical units don't make sense -- how can
        # you have the equivalent of -5 molecules of fluoresceine?  so,
        # we filter out negative values here.

        new_experiment = experiment.clone()

        for channel in channels:
            new_experiment.data = \
                new_experiment.data[new_experiment.data[channel] > 0]

        new_experiment.data.reset_index(drop=True, inplace=True)

        for channel in channels:
            calibration_fn = self._calibration_functions[channel]

            new_experiment[channel] = calibration_fn(new_experiment[channel])
            new_experiment.metadata[channel][
                'bead_calibration_fn'] = calibration_fn
            new_experiment.metadata[channel]['bead_units'] = self.units[
                channel]
            if 'range' in experiment.metadata[channel]:
                new_experiment.metadata[channel]['range'] = calibration_fn(
                    experiment.metadata[channel]['range'])

        new_experiment.history.append(
            self.clone_traits(transient=lambda t: True))
        return new_experiment

    def default_view(self, **kwargs):
        """
        Returns a diagnostic plot to see if the bleedthrough spline estimation
        is working.
        
        Returns
        -------
            IView : An IView, call plot() to see the diagnostic plots
        """

        return BeadCalibrationDiagnostic(op=self, **kwargs)

    BEADS = {
        # from http://www.spherotech.com/RCP-30-5a%20%20rev%20H%20ML%20071712.xls
        "Spherotech RCP-30-5A Lot AG01, AF02, AD04 and AAE01": {
            "MECSB": [216, 464, 1232, 2940, 7669, 19812, 35474],
            "MEBFP": [861, 1997, 5776, 15233, 45389, 152562, 396759],
            "MEFL": [792, 2079, 6588, 16471, 47497, 137049, 271647],
            "MEPE": [531, 1504, 4819, 12506, 36159, 109588, 250892],
            "MEPTR": [233, 669, 2179, 5929, 18219, 63944, 188785],
            "MECY": [1614, 4035, 12025, 31896, 95682, 353225, 1077421],
            "MEPCY7": [14916, 42336, 153840, 494263],
            "MEAP": [373, 1079, 3633, 9896, 28189, 79831, 151008],
            "MEAPCY7": [2864, 7644, 19081, 37258]
        },
        # from http://www.spherotech.com/RCP-30-5a%20%20rev%20G.2.xls
        "Spherotech RCP-30-5A Lot AA01-AA04, AB01, AB02, AC01, GAA01-R": {
            "MECSB": [179, 400, 993, 3203, 6083, 17777, 36331],
            "MEBFP": [700, 1705, 4262, 17546, 35669, 133387, 412089],
            "MEFL": [692, 2192, 6028, 17493, 35674, 126907, 290983],
            "MEPE": [505, 1777, 4974, 13118, 26757, 94930, 250470],
            "MEPTR": [207, 750, 2198, 6063, 12887, 51686, 170219],
            "MECY": [1437, 4693, 12901, 36837, 76621, 261671, 1069858],
            "MEPCY7": [32907, 107787, 503797],
            "MEAP": [587, 2433, 6720, 17962, 30866, 51704, 146080],
            "MEAPCY7": [718, 1920, 5133, 9324, 14210, 26735]
        }
    }
class MATSEvalMicroplaneFatigue(HasTraits):
    #--------------------------
    # material model parameters
    #--------------------------

    E = Float(35000.,
              label="E",
              desc="Young modulus",
              enter_set=True,
              auto_set=False)

    nu = Float(0.2,
               label="nu",
               desc="poission ratio",
               enter_set=True,
               auto_set=False)

    #---------------------------------------
    # Tangential constitutive law parameters
    #---------------------------------------
    gamma_T = Float(80000.,
                    label="Gamma",
                    desc=" Tangential Kinematic hardening modulus",
                    enter_set=True,
                    auto_set=False)

    K_T = Float(10000.0,
                label="K",
                desc="Tangential Isotropic harening",
                enter_set=True,
                auto_set=False)

    S = Float(0.000010,
              label="S",
              desc="Damage strength",
              enter_set=True,
              auto_set=False)

    r = Float(1.21,
              label="r",
              desc="Damage cumulation parameter",
              enter_set=True,
              auto_set=False)

    c = Float(1.85,
              label="c",
              desc="Damage cumulation parameter",
              enter_set=True,
              auto_set=False)

    tau_pi_bar = Float(5.0,
                       label="Tau_bar",
                       desc="Reversibility limit",
                       enter_set=True,
                       auto_set=False)

    a = Float(0.001,
              label="a",
              desc="Lateral pressure coefficient",
              enter_set=True,
              auto_set=False)

    #-------------------------------------------
    # Normal_Tension constitutive law parameters
    #-------------------------------------------
    Ad = Float(6000.0,
               label="a",
               desc="brittleness coefficient",
               enter_set=True,
               auto_set=False)

#     eps_f = Float(0.0001,
#                   label="a",
#                   desc="brittleness coefficient",
#                   enter_set=True,
#                   auto_set=False)

    eps_0 = Float(8.0e-5,
                  label="a",
                  desc="threshold strain",
                  enter_set=True,
                  auto_set=False)

    #-----------------------------------------------
    # Normal_Compression constitutive law parameters
    #-----------------------------------------------
    K_N = Float(5000.,
                label="K_N",
                desc=" Normal isotropic harening",
                enter_set=True,
                auto_set=False)

    gamma_N = Float(20000.,
                    label="gamma_N",
                    desc="Normal kinematic hardening",
                    enter_set=True,
                    auto_set=False)

    sigma_0 = Float(18.,
                    label="sigma_0",
                    desc="Yielding stress",
                    enter_set=True,
                    auto_set=False)

    #--------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD) (full thermodynamic)
    #--------------------------------------------------------------
    def get_normal_Law(self, eps, sctx):

        E_N = self.E / (1.0 - 2.0 * self.nu)

        w_N = sctx[:, 0]
        z_N = sctx[:, 1]
        alpha_N = sctx[:, 2]
        r_N = sctx[:, 3]
        eps_N_p = sctx[:, 4]

        pos = eps > 1e-6
        H = 1.0 * pos

        sigma_n_trial = (1. - H * w_N) * E_N * (eps - eps_N_p)
        Z = self.K_N * r_N
        X = self.gamma_N * alpha_N

        h = self.sigma_0 + Z
        pos_iso = h > 1e-6
        f_trial = abs(sigma_n_trial - X) - h * pos_iso

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
            (E_N + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p = eps_N_p + delta_lamda * sign(sigma_n_trial - X)
        r_N = r_N + delta_lamda
        alpha_N = alpha_N + delta_lamda * sign(sigma_n_trial - X)

        def Z_N(z_N): return 1.0 / self.Ad * (-z_N) / (1.0 + z_N)
        Y_N = 0.5 * H * E_N * eps ** 2.0
        Y_0 = 0.5 * E_N * self.eps_0 ** 2.0
        f = Y_N - (Y_0 + Z_N(z_N))

        thres_2 = f > 1e-6

        def f_w(Y): return 1.0 - 1.0 / (1.0 + self.Ad * (Y - Y_0))
        w_N = f_w(Y_N) * thres_2
        z_N = - w_N * thres_2

        new_sctx = zeros((28, 5))

        new_sctx[:, 0] = w_N
        new_sctx[:, 1] = z_N
        new_sctx[:, 2] = alpha_N
        new_sctx[:, 3] = r_N
        new_sctx[:, 4] = eps_N_p
        return new_sctx

    #--------------------------------------------------------------
    # microplane constitutive law (normal behavior CP + TD)
    #--------------------------------------------------------------
    def get_normal_Law_2(self, eps, sctx):

        E_N = self.E / (1.0 - 2.0 * self.nu)

        w_N = sctx[:, 0]
        eps_max = sctx[:, 1]
        alpha_N = sctx[:, 2]
        r_N = sctx[:, 3]
        eps_N_p = sctx[:, 4]
        #eps_N_p_cum = sctx[:, 5]

        pos = eps > 1e-6
        H = 1.0 * pos

        sigma_n_trial = (1. - H * w_N) * E_N * (eps - eps_N_p)
        Z = self.K_N * r_N
        X = self.gamma_N * alpha_N

        h = self.sigma_0 + Z
        pos_iso = h > 1e-6
        f_trial = abs(sigma_n_trial - X) - h * pos_iso

        thres_1 = f_trial > 1e-6

        delta_lamda = f_trial / \
            (E_N + abs(self.K_N) + self.gamma_N) * thres_1
        eps_N_p = eps_N_p + delta_lamda * sign(sigma_n_trial - X)
        r_N = r_N + delta_lamda
        alpha_N = alpha_N + delta_lamda * sign(sigma_n_trial - X)

        idx = np.where(eps_max >= self.eps_0)
        #eps_N_p_cum += eps_N_p

        w_N[idx] = (1. - sqrt(
            (self.eps_0 / eps_max[idx]) * np.exp(- (eps_max[idx] - self.eps_0) / (self.eps_f - self.eps_0))))

        eps_max = np.maximum(eps, eps_max)

        # print eps_max

#         def Z_N(z_N): return 1. / self.Ad * (-z_N) / (1. + z_N)
#         Y_N = 0.5 * H * E_N * eps ** 2.
#         Y_0 = 0.5 * E_N * self.eps_0 ** 2.
#         f = Y_N - (Y_0 + Z_N(z_N))
#
#         thres_2 = f > 1e-6
#
#         def f_w(Y): return 1. - 1. / (1. + self.Ad * (Y - Y_0))
#         w_N = f_w(Y_N) * thres_2
#         z_N = - w_N * thres_2

        new_sctx = zeros((28, 5))

        new_sctx[:, 0] = w_N
        new_sctx[:, 1] = eps_max
        new_sctx[:, 2] = alpha_N
        new_sctx[:, 3] = r_N
        new_sctx[:, 4] = eps_N_p
        #new_sctx[:, 5] = eps_N_p_cum
        return new_sctx

    #-------------------------------------------------------------------------
    # microplane constitutive law (Tangential CSD)-(Pressure sensitive cumulative damage)
    #-------------------------------------------------------------------------
    def get_tangential_Law(self, e_T, sctx, sigma_kk):

        E_T = self.E / (1. + self.nu)

        w_T = sctx[:, 5]
        z_T = sctx[:, 6]
        alpha_T = sctx[:, 7:10]
        eps_T_pi = sctx[:, 10:13]
        eps_T_pi_cum = sctx[:, 13]

        sig_pi_trial = E_T * (e_T - eps_T_pi)
        Z = self.K_T * z_T
        X = self.gamma_T * alpha_T
        norm_1 = sqrt(
            einsum('nj,nj -> n', (sig_pi_trial - X), (sig_pi_trial - X)))

        f = norm_1 - self.tau_pi_bar - \
            Z + self.a * sigma_kk / 3.0

        plas_1 = f > 1e-6
        elas_1 = f < 1e-6

        delta_lamda = f / \
            (E_T / (1.0 - w_T) + self.gamma_T + self.K_T) * plas_1

        norm_2 = 1.0 * elas_1 + sqrt(
            einsum('nj,nj -> n', (sig_pi_trial - X), (sig_pi_trial - X))) * plas_1

        eps_T_pi[:, 0] = eps_T_pi[:, 0] + plas_1 * delta_lamda * \
            ((sig_pi_trial[:, 0] - X[:, 0]) / (1.0 - w_T)) / norm_2
        eps_T_pi[:, 1] = eps_T_pi[:, 1] + plas_1 * delta_lamda * \
            ((sig_pi_trial[:, 1] - X[:, 1]) / (1.0 - w_T)) / norm_2
        eps_T_pi[:, 2] = eps_T_pi[:, 2] + plas_1 * delta_lamda * \
            ((sig_pi_trial[:, 2] - X[:, 2]) / (1.0 - w_T)) / norm_2

        Y = 0.5 * E_T * \
            einsum('nj,nj -> n', (e_T - eps_T_pi), (e_T - eps_T_pi))

        w_T += ((1 - w_T) ** self.c) * \
            (delta_lamda * (Y / self.S) ** self.r) * \
            (self.tau_pi_bar / (self.tau_pi_bar - self.a * sigma_kk / 3.0))

        alpha_T[:, 0] = alpha_T[:, 0] + plas_1 * delta_lamda *\
            (sig_pi_trial[:, 0] - X[:, 0]) / norm_2
        alpha_T[:, 1] = alpha_T[:, 1] + plas_1 * delta_lamda *\
            (sig_pi_trial[:, 1] - X[:, 1]) / norm_2
        alpha_T[:, 2] = alpha_T[:, 2] + plas_1 * delta_lamda *\
            (sig_pi_trial[:, 2] - X[:, 2]) / norm_2

        z_T = z_T + delta_lamda
        eps_T_pi_cum = eps_T_pi_cum + np.linalg.norm(eps_T_pi)

        new_sctx = zeros((28, 9))
        new_sctx[:, 0] = w_T
        new_sctx[:, 1] = z_T
        new_sctx[:, 2:5] = alpha_T
        new_sctx[:, 5:8] = eps_T_pi
        new_sctx[:, 8] = np.linalg.norm(eps_T_pi)
        return new_sctx
class IrrigationArea(HasTraits):
    name = Str
    surface = Float(desc='Surface [ha]')
    crop = Enum('Alfalfa', 'Wheat', 'Cotton')
Example #18
0
class MATS1DPlastic(MATS1DEval):

    '''
    Scalar Damage Model.
    '''

    E = Float(1.,  # 34e+3,
              label="E",
              desc="Young's Modulus",
              enter_set=True,
              auto_set=False)

    sigma_y = Float(1.,
                    label="sigma_y",
                    desc="Yield stress",
                    enter_set=True,
                    auto_set=False)

    K_bar = Float(0.1,  # 191e-6,
                  label="K",
                  desc="Plasticity modulus",
                  enter_set=True,
                  auto_set=False)

    H_bar = Float(0.1,  # 191e-6,
                  label="H",
                  desc="Hardening modulus",
                  enter_set=True,
                  auto_set=False)

    #--------------------------------------------------------------------------
    # View specification
    #--------------------------------------------------------------------------

    traits_view = View(Group(Group(Item('E'),
                                   Item('sigma_y'),
                                   Item('K_bar'),
                                   Item('H_bar'),
                                   label='Material parameters',
                                   show_border=True),
                             Group(Item('stiffness', style='custom'),
                                   Spring(resizable=True),
                                   label='Configuration parameters',
                                   show_border=True,
                                   ),
                             layout='tabbed'
                             ),
                       resizable=True
                       )

    #-------------------------------------------------------------------------
    # Setup for computation within a supplied spatial context
    #-------------------------------------------------------------------------

    def get_state_array_size(self):
        '''
        Give back the nuber of floats to be saved
        @param sctx:spatial context

        eps_p_n - platic strain 
        alpha_n - hardening
        q_n - back stress  

        '''
        return 3

    def new_cntl_var(self):
        return np.zeros(1, np.float_)

    def new_resp_var(self):
        return np.zeros(1, np.float_)

    #-------------------------------------------------------------------------
    # Evaluation - get the corrector and predictor
    #-------------------------------------------------------------------------
    def get_corr_pred(self, sctx, eps_app_eng, d_eps, tn, tn1, eps_avg=None):
        '''
        Corrector predictor computation.
        @param eps_app_eng input variable - engineering strain
        '''
        eps_n1 = float(eps_app_eng)
        E = self.E

        if eps_avg == None:
            eps_avg = eps_n1

        if sctx.update_state_on:
            eps_n = eps_avg - float(d_eps)
            sctx.mats_state_array[:] = self._get_state_variables(sctx, eps_n)

        eps_p_n, q_n, alpha_n = sctx.mats_state_array
        sigma_trial = self.E * (eps_n1 - eps_p_n)
        xi_trial = sigma_trial - q_n
        f_trial = abs(xi_trial) - (self.sigma_y + self.K_bar * alpha_n)

        sig_n1 = np.zeros((1,), dtype='float_')
        D_n1 = np.zeros((1, 1), dtype='float_')
        if f_trial <= 1e-8:
            sig_n1[0] = sigma_trial
            D_n1[0, 0] = E
        else:
            d_gamma = f_trial / (self.E + self.K_bar + self.H_bar)
            sig_n1[0] = sigma_trial - d_gamma * self.E * sign(xi_trial)
            D_n1[0, 0] = (self.E * (self.K_bar + self.H_bar)) / \
                (self.E + self.K_bar + self.H_bar)

        return sig_n1, D_n1

    #--------------------------------------------------------------------------
    # Subsidiary methods realizing configurable features
    #--------------------------------------------------------------------------

    def _get_state_variables(self, sctx, eps_n):

        eps_p_n, q_n, alpha_n = sctx.mats_state_array

        # Get the characteristics of the trial step
        #
        sig_trial = self.E * (eps_n - eps_p_n)
        xi_trial = sig_trial - q_n
        f_trial = abs(xi_trial) - (self.sigma_y + self.K_bar * alpha_n)

        if f_trial > 1e-8:

            #
            # Tha last equilibrated step was inelastic. Here the
            # corresponding state variables must be calculated once
            # again. This might be expensive for 2D and 3D models. Then,
            # some kind of caching should be considered for the state
            # variables determined during iteration. In particular, the
            # computation of d_gamma should be outsourced into a separate
            # method that can in general perform an iterative computation.
            #
            d_gamma = f_trial / (self.E + self.K_bar + self.H_bar)
            eps_p_n += d_gamma * sign(xi_trial)
            q_n += d_gamma * self.H_bar * sign(xi_trial)
            alpha_n += d_gamma

        newarr = np.array([eps_p_n, q_n, alpha_n], dtype='float_')

        return newarr

    #-----------------------------------------------------------
    # Response trace evaluators
    #--------------------------------------------------------------------------
    def get_eps_p(self, sctx, eps_app_eng):
        return np.array([sctx.mats_state_array[0]])

    def get_q(self, sctx, eps_app_eng):
        return np.array([sctx.mats_state_array[1]])

    def get_alpha(self, sctx, eps_app_eng):
        return np.array([sctx.mats_state_array[2]])

    # Declare and fill-in the rte_dict - it is used by the clients to
    # assemble all the available time-steppers.
    #
    rte_dict = Trait(Dict)

    def _rte_dict_default(self):
        return {'sig_app': self.get_sig_app,
                'eps_app': self.get_eps_app,
                'eps_p': self.get_eps_p,
                'q': self.get_q,
                'alpha': self.get_alpha}

    def _get_explorer_config(self):
        from ibvpy.api import TLine, BCDof, RTDofGraph
        c = super(MATS1DPlastic, self)._get_explorer_config()
        # overload the default configuration
        c['bcond_list'] = [BCDof(var='u',
                                 dof=0, value=2.0,
                                 time_function=lambda t: sin(t))]
        c['rtrace_list'] = [
            RTDofGraph(name='strain - stress',
                       var_x='eps_app', idx_x=0,
                       var_y='sig_app', idx_y=0,
                       record_on='update'),
            RTDofGraph(name='time - plastic_strain',
                       var_x='time', idx_x=0,
                       var_y='eps_p', idx_y=0,
                       record_on='update'),
            RTDofGraph(name='time - back stress',
                       var_x='time', idx_x=0,
                       var_y='q', idx_y=0,
                       record_on='update'),
            RTDofGraph(name='time - hardening',
                       var_x='time', idx_x=0,
                       var_y='alpha', idx_y=0,
                       record_on='update')
        ]
        c['tline'] = TLine(step=0.3, max=10)
        return c
class DataRange1D(BaseDataRange):
    """ Represents a 1-D data range.
    """

    # The actual value of the lower bound of this range (overrides
    # AbstractDataRange). To set it, use **low_setting**.
    low = Property
    # The actual value of the upper bound of this range (overrides
    # AbstractDataRange). To set it, use **high_setting**.
    high = Property

    # Property for the lower bound of this range (overrides AbstractDataRange).
    #
    # * 'auto': The lower bound is automatically set at or below the minimum
    #   of the data.
    # * 'track': The lower bound tracks the upper bound by **tracking_amount**.
    # * CFloat: An explicit value for the lower bound
    low_setting = Property(Trait('auto', 'auto', 'track', CFloat))
    # Property for the upper bound of this range (overrides AbstractDataRange).
    #
    # * 'auto': The upper bound is automatically set at or above the maximum
    #   of the data.
    # * 'track': The upper bound tracks the lower bound by **tracking_amount**.
    # * CFloat: An explicit value for the upper bound
    high_setting = Property(Trait('auto', 'auto', 'track', CFloat))

    # Do "auto" bounds imply an exact fit to the data? If False,
    # they pad a little bit of margin on either side.
    tight_bounds = Bool(True)

    # A user supplied function returning the proper bounding interval.
    # bounds_func takes (data_low, data_high, margin, tight_bounds)
    # and returns (low, high)
    bounds_func = Callable

    # The amount of margin to place on either side of the data, expressed as
    # a percentage of the full data width
    margin = Float(0.05)

    # The minimum percentage difference between low and high.  That is,
    # (high-low) >= epsilon * low.
    # Used to be 1.0e-20 but chaco cannot plot at such a precision!
    epsilon = CFloat(1.0e-10)

    # When either **high** or **low** tracks the other, track by this amount.
    default_tracking_amount = CFloat(20.0)

    # The current tracking amount. This value changes with zooming.
    tracking_amount = default_tracking_amount

    # Default tracking state. This value is used when self.reset() is called.
    #
    # * 'auto': Both bounds reset to 'auto'.
    # * 'high_track': The high bound resets to 'track', and the low bound
    #   resets to 'auto'.
    # * 'low_track': The low bound resets to 'track', and the high bound
    #   resets to 'auto'.
    default_state = Enum('auto', 'high_track', 'low_track')

    # FIXME: this attribute is not used anywhere, is it safe to remove it?
    # Is this range dependent upon another range?
    fit_to_subset = Bool(False)

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

    # The "_setting" attributes correspond to what the user has "set"; the
    # "_value" attributes are the actual numerical values for the given
    # setting.

    # The user-specified low setting.
    _low_setting = Trait('auto', 'auto', 'track', CFloat)
    # The actual numerical value for the low setting.
    _low_value = CFloat(-inf)
    # The user-specified high setting.
    _high_setting = Trait('auto', 'auto', 'track', CFloat)
    # The actual numerical value for the high setting.
    _high_value = CFloat(inf)

    # A list of attributes to persist
    # _pickle_attribs = ("_low_setting", "_high_setting")

    #------------------------------------------------------------------------
    # AbstractRange interface
    #------------------------------------------------------------------------

    def clip_data(self, data):
        """ Returns a list of data values that are within the range.

        Implements AbstractDataRange.
        """
        return compress(self.mask_data(data), data)

    def mask_data(self, data):
        """ Returns a mask array, indicating whether values in the given array
        are inside the range.

        Implements AbstractDataRange.
        """
        return ((data.view(ndarray) >= self._low_value) &
                (data.view(ndarray) <= self._high_value))

    def bound_data(self, data):
        """ Returns a tuple of indices for the start and end of the first run
        of *data* that falls within the range.

        Implements AbstractDataRange.
        """
        mask = self.mask_data(data)
        runs = arg_find_runs(mask, "flat")
        # Since runs of "0" are also considered runs, we have to cycle through
        # until we find the first run of "1"s.
        for run in runs:
            if mask[run[0]] == 1:
                # arg_find_runs returns 1 past the end
                return run[0], run[1] - 1
        return (0, 0)

    def set_bounds(self, low, high):
        """ Sets all the bounds of the range simultaneously.

        Implements AbstractDataRange.
        """
        if low == 'track':
            # Set the high setting first
            result_high = self._do_set_high_setting(high, fire_event=False)
            result_low = self._do_set_low_setting(low, fire_event=False)
            result = result_low or result_high
        else:
            # Either set low first or order doesn't matter
            result_low = self._do_set_low_setting(low, fire_event=False)
            result_high = self._do_set_high_setting(high, fire_event=False)
            result = result_high or result_low
        if result:
            self.updated = result

    def scale_tracking_amount(self, multiplier):
        """ Sets the **tracking_amount** to a new value, scaled by *multiplier*.
        """
        self.tracking_amount = self.tracking_amount * multiplier
        self._do_track()

    def set_tracking_amount(self, amount):
        """ Sets the **tracking_amount** to a new value, *amount*.
        """
        self.tracking_amount = amount
        self._do_track()

    def set_default_tracking_amount(self, amount):
        """ Sets the **default_tracking_amount** to a new value, *amount*.
        """
        self.default_tracking_amount = amount

    #------------------------------------------------------------------------
    # Public methods
    #------------------------------------------------------------------------

    def reset(self):
        """ Resets the bounds of this range, based on **default_state**.
        """
        # need to maintain 'track' setting
        if self.default_state == 'auto':
            self._high_setting = 'auto'
            self._low_setting = 'auto'
        elif self.default_state == 'low_track':
            self._high_setting = 'auto'
            self._low_setting = 'track'
        elif self.default_state == 'high_track':
            self._high_setting = 'track'
            self._low_setting = 'auto'
        self._refresh_bounds()
        self.tracking_amount = self.default_tracking_amount

    def refresh(self):
        """ If any of the bounds is 'auto', this method refreshes the actual
        low and high values from the set of the view filters' data sources.
        """
        if ('auto' in (self._low_setting, self._high_setting)) or \
            ('track' in (self._low_setting, self._high_setting)):
            # If the user has hard-coded bounds, then refresh() doesn't do
            # anything.
            self._refresh_bounds()
        else:
            return

    #------------------------------------------------------------------------
    # Private methods (getters and setters)
    #------------------------------------------------------------------------

    def _get_low(self):
        return float(self._low_value)

    def _set_low(self, val):
        return self._set_low_setting(val)

    def _get_low_setting(self):
        return self._low_setting

    def _do_set_low_setting(self, val, fire_event=True):
        """
        Returns
        -------
        If fire_event is False and the change would have fired an event, returns
        the tuple of the new low and high values.  Otherwise returns None.  In
        particular, if fire_event is True, it always returns None.
        """
        new_values = None
        if self._low_setting != val:

            # Save the new setting.
            self._low_setting = val

            # If val is 'auto' or 'track', get the corresponding numerical
            # value.
            if val == 'auto':
                if len(self.sources) > 0:
                    val = min(
                        [source.get_bounds()[0] for source in self.sources])
                else:
                    val = -inf
            elif val == 'track':
                if len(self.sources) > 0 or self._high_setting != 'auto':
                    val = self._high_value - self.tracking_amount
                else:
                    val = -inf

            # val is now a numerical value.  If it is the same as the current
            # value, there is nothing to do.
            if self._low_value != val:
                self._low_value = val
                if self._high_setting == 'track':
                    self._high_value = val + self.tracking_amount
                if fire_event:
                    self.updated = (self._low_value, self._high_value)
                else:
                    new_values = (self._low_value, self._high_value)

        return new_values

    def _set_low_setting(self, val):
        self._do_set_low_setting(val, True)

    def _get_high(self):
        return float(self._high_value)

    def _set_high(self, val):
        return self._set_high_setting(val)

    def _get_high_setting(self):
        return self._high_setting

    def _do_set_high_setting(self, val, fire_event=True):
        """
        Returns
        -------
        If fire_event is False and the change would have fired an event, returns
        the tuple of the new low and high values.  Otherwise returns None.  In
        particular, if fire_event is True, it always returns None.
        """
        new_values = None
        if self._high_setting != val:

            # Save the new setting.
            self._high_setting = val

            # If val is 'auto' or 'track', get the corresponding numerical
            # value.
            if val == 'auto':
                if len(self.sources) > 0:
                    val = max(
                        [source.get_bounds()[1] for source in self.sources])
                else:
                    val = inf
            elif val == 'track':
                if len(self.sources) > 0 or self._low_setting != 'auto':
                    val = self._low_value + self.tracking_amount
                else:
                    val = inf

            # val is now a numerical value.  If it is the same as the current
            # value, there is nothing to do.
            if self._high_value != val:
                self._high_value = val
                if self._low_setting == 'track':
                    self._low_value = val - self.tracking_amount
                if fire_event:
                    self.updated = (self._low_value, self._high_value)
                else:
                    new_values = (self._low_value, self._high_value)

        return new_values

    def _set_high_setting(self, val):
        self._do_set_high_setting(val, True)

    def _refresh_bounds(self):
        null_bounds = False
        if len(self.sources) == 0:
            null_bounds = True
        else:
            bounds_list = [source.get_bounds() for source in self.sources \
                              if source.get_size() > 0]

            if len(bounds_list) == 0:
                null_bounds = True

        if null_bounds:
            # If we have no sources and our settings are "auto", then reset our
            # bounds to infinity; otherwise, set the _value to the corresponding
            # setting.
            if (self._low_setting in ("auto", "track")):
                self._low_value = -inf
            else:
                self._low_value = self._low_setting
            if (self._high_setting in ("auto", "track")):
                self._high_value = inf
            else:
                self._high_value = self._high_setting
            return
        else:
            mins, maxes = list(zip(*bounds_list))

            low_start, high_start = \
                     calc_bounds(self._low_setting, self._high_setting,
                                 mins, maxes, self.epsilon,
                                 self.tight_bounds, margin=self.margin,
                                 track_amount=self.tracking_amount,
                                 bounds_func=self.bounds_func)

        if (self._low_value != low_start) or (self._high_value != high_start):
            self._low_value = low_start
            self._high_value = high_start
            self.updated = (self._low_value, self._high_value)
        return

    def _do_track(self):
        changed = False
        if self._low_setting == 'track':
            new_value = self._high_value - self.tracking_amount
            if self._low_value != new_value:
                self._low_value = new_value
                changed = True
        elif self._high_setting == 'track':
            new_value = self._low_value + self.tracking_amount
            if self._high_value != new_value:
                self._high_value = new_value
                changed = True
        if changed:
            self.updated = (self._low_value, self._high_value)

    #------------------------------------------------------------------------
    # Event handlers
    #------------------------------------------------------------------------

    def _sources_items_changed(self, event):
        self.refresh()
        for source in event.removed:
            source.on_trait_change(self.refresh, "data_changed", remove=True)
        for source in event.added:
            source.on_trait_change(self.refresh, "data_changed")

    def _sources_changed(self, old, new):
        self.refresh()
        for source in old:
            source.on_trait_change(self.refresh, "data_changed", remove=True)
        for source in new:
            source.on_trait_change(self.refresh, "data_changed")

    #------------------------------------------------------------------------
    # Serialization interface
    #------------------------------------------------------------------------

    def _post_load(self):
        self._sources_changed(None, self.sources)
Example #20
0
class VUMeter(Component):

    # Value expressed in dB
    db = Property(Float)

    # Value expressed as a percent.
    percent = Range(low=0.0)

    # The maximum value to be display in the VU Meter, expressed as a percent.
    max_percent = Float(150.0)

    # Angle (in degrees) from a horizontal line through the hinge of the
    # needle to the edge of the meter axis.
    angle = Float(45.0)

    # Values of the percentage-based ticks; these are drawn and labeled along
    # the bottom of the curve axis.
    percent_ticks = List(list(range(0, 101, 20)))

    # Text to write in the middle of the VU Meter.
    text = Str("VU")

    # Font used to draw `text`.
    text_font = KivaFont("modern 48")

    # Font for the db tick labels.
    db_tick_font = KivaFont("modern 16")

    # Font for the percent tick labels.
    percent_tick_font = KivaFont("modern 12")

    # beta is the fraction of the of needle that is "hidden".
    # beta == 0 puts the hinge point of the needle on the bottom
    # edge of the window.  Values that result in a decent looking
    # meter are 0 < beta < .65.
    # XXX needs a better name!
    _beta = Float(0.3)

    # _outer_radial_margin is the radial extent beyond the circular axis
    # to include  in calculations of the space required for the meter.
    # This allows room for the ticks and labels.
    _outer_radial_margin = Float(60.0)

    # The angle (in radians) of the span of the curve axis.
    _phi = Property(Float, depends_on=['angle'])

    # This is the radius of the circular axis (in screen coordinates).
    _axis_radius = Property(Float, depends_on=['_phi', 'width', 'height'])

    #---------------------------------------------------------------------
    # Trait Property methods
    #---------------------------------------------------------------------

    def _get_db(self):
        db = percent_to_db(self.percent)
        return db

    def _set_db(self, value):
        self.percent = db_to_percent(value)

    def _get__phi(self):
        phi = math.pi * (180.0 - 2 * self.angle) / 180.0
        return phi

    def _get__axis_radius(self):
        M = self._outer_radial_margin
        beta = self._beta
        w = self.width
        h = self.height
        phi = self._phi

        R1 = w / (2 * math.sin(phi / 2)) - M
        R2 = (h - M) / (1 - beta * math.cos(phi / 2))
        R = min(R1, R2)
        return R

    #---------------------------------------------------------------------
    # Trait change handlers
    #---------------------------------------------------------------------

    def _anytrait_changed(self):
        self.request_redraw()

    #---------------------------------------------------------------------
    # Component API
    #---------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):

        beta = self._beta
        phi = self._phi

        w = self.width

        M = self._outer_radial_margin
        R = self._axis_radius

        # (ox, oy) is the position of the "hinge point" of the needle
        # (i.e. the center of rotation).  For beta > ~0, oy is negative,
        # so this point is below the visible region.
        ox = self.x + self.width // 2
        oy = -beta * R * math.cos(phi / 2) + 1

        left_theta = math.radians(180 - self.angle)
        right_theta = math.radians(self.angle)

        # The angle of the 100% position.
        nominal_theta = self._percent_to_theta(100.0)

        # The color of the axis for percent > 100.
        red = (0.8, 0, 0)

        with gc:
            gc.set_antialias(True)

            # Draw everything relative to the center of the circles.
            gc.translate_ctm(ox, oy)

            # Draw the primary ticks and tick labels on the curved axis.
            gc.set_fill_color((0, 0, 0))
            gc.set_font(self.db_tick_font)
            for db in [-20, -10, -7, -5, -3, -2, -1, 0, 1, 2, 3]:
                db_percent = db_to_percent(db)
                theta = self._percent_to_theta(db_percent)
                x1 = R * math.cos(theta)
                y1 = R * math.sin(theta)
                x2 = (R + 0.3 * M) * math.cos(theta)
                y2 = (R + 0.3 * M) * math.sin(theta)
                gc.set_line_width(2.5)
                gc.move_to(x1, y1)
                gc.line_to(x2, y2)
                gc.stroke_path()

                text = str(db)
                if db > 0:
                    text = '+' + text
                self._draw_rotated_label(gc, text, theta, R + 0.4 * M)

            # Draw the secondary ticks on the curve axis.
            for db in [-15, -9, -8, -6, -4, -0.5, 0.5]:
                ##db_percent = 100 * math.pow(10.0, db / 20.0)
                db_percent = db_to_percent(db)
                theta = self._percent_to_theta(db_percent)
                x1 = R * math.cos(theta)
                y1 = R * math.sin(theta)
                x2 = (R + 0.2 * M) * math.cos(theta)
                y2 = (R + 0.2 * M) * math.sin(theta)
                gc.set_line_width(1.0)
                gc.move_to(x1, y1)
                gc.line_to(x2, y2)
                gc.stroke_path()

            # Draw the percent ticks and label on the bottom of the
            # curved axis.
            gc.set_font(self.percent_tick_font)
            gc.set_fill_color((0.5, 0.5, 0.5))
            gc.set_stroke_color((0.5, 0.5, 0.5))
            percents = self.percent_ticks
            for tick_percent in percents:
                theta = self._percent_to_theta(tick_percent)
                x1 = (R - 0.15 * M) * math.cos(theta)
                y1 = (R - 0.15 * M) * math.sin(theta)
                x2 = R * math.cos(theta)
                y2 = R * math.sin(theta)
                gc.set_line_width(2.0)
                gc.move_to(x1, y1)
                gc.line_to(x2, y2)
                gc.stroke_path()

                text = str(tick_percent)
                if tick_percent == percents[-1]:
                    text = text + "%"
                self._draw_rotated_label(gc, text, theta, R - 0.3 * M)

            if self.text:
                gc.set_font(self.text_font)
                tx, ty, tw, th = gc.get_text_extent(self.text)
                gc.set_fill_color((0, 0, 0, 0.25))
                gc.set_text_matrix(affine.affine_from_rotation(0))
                gc.set_text_position(-0.5 * tw, (0.75 * beta + 0.25) * R)
                gc.show_text(self.text)

            # Draw the red curved axis.
            gc.set_stroke_color(red)
            w = 10
            gc.set_line_width(w)
            gc.arc(0, 0, R + 0.5 * w - 1, right_theta, nominal_theta)
            gc.stroke_path()

            # Draw the black curved axis.
            w = 4
            gc.set_line_width(w)
            gc.set_stroke_color((0, 0, 0))
            gc.arc(0, 0, R + 0.5 * w - 1, nominal_theta, left_theta)
            gc.stroke_path()

            # Draw the filled arc at the bottom.
            gc.set_line_width(2)
            gc.set_stroke_color((0, 0, 0))
            gc.arc(0, 0, beta * R, math.radians(self.angle),
                   math.radians(180 - self.angle))
            gc.stroke_path()
            gc.set_fill_color((0, 0, 0, 0.25))
            gc.arc(0, 0, beta * R, math.radians(self.angle),
                   math.radians(180 - self.angle))
            gc.fill_path()

            # Draw the needle.
            percent = self.percent
            # If percent exceeds max_percent, the needle is drawn at max_percent.
            if percent > self.max_percent:
                percent = self.max_percent
            needle_theta = self._percent_to_theta(percent)
            gc.rotate_ctm(needle_theta - 0.5 * math.pi)
            self._draw_vertical_needle(gc)

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

    def _draw_vertical_needle(self, gc):
        """ Draw the needle of the meter, pointing straight up. """
        beta = self._beta
        R = self._axis_radius
        end_y = beta * R
        blob_y = R - 0.6 * self._outer_radial_margin
        tip_y = R + 0.2 * self._outer_radial_margin
        lw = 5

        with gc:
            gc.set_alpha(1)
            gc.set_fill_color((0, 0, 0))

            # Draw the needle from the bottom to the blob.
            gc.set_line_width(lw)
            gc.move_to(0, end_y)
            gc.line_to(0, blob_y)
            gc.stroke_path()

            # Draw the thin part of the needle from the blob to the tip.
            gc.move_to(lw, blob_y)
            control_y = blob_y + 0.25 * (tip_y - blob_y)
            gc.quad_curve_to(0.2 * lw, control_y, 0, tip_y)
            gc.quad_curve_to(-0.2 * lw, control_y, -lw, blob_y)
            gc.line_to(lw, blob_y)
            gc.fill_path()

            # Draw the blob on the needle.
            gc.arc(0, blob_y, 6.0, 0, 2 * math.pi)
            gc.fill_path()

    def _draw_rotated_label(self, gc, text, theta, radius):

        tx, ty, tw, th = gc.get_text_extent(text)

        rr = math.sqrt(radius**2 + (0.5 * tw)**2)
        dtheta = math.atan2(0.5 * tw, radius)
        text_theta = theta + dtheta
        x = rr * math.cos(text_theta)
        y = rr * math.sin(text_theta)

        rot_theta = theta - 0.5 * math.pi
        with gc:
            gc.set_text_matrix(affine.affine_from_rotation(rot_theta))
            gc.set_text_position(x, y)
            gc.show_text(text)

    def _percent_to_theta(self, percent):
        """ Convert percent to the angle theta, in radians.

        theta is the angle of the needle measured counterclockwise from
        the horizontal (i.e. the traditional angle of polar coordinates).
        """
        angle = (self.angle + (180.0 - 2 * self.angle) *
                 (self.max_percent - percent) / self.max_percent)
        theta = math.radians(angle)
        return theta

    def _db_to_theta(self, db):
        """ Convert db to the angle theta, in radians. """
        percent = db_to_percent(db)
        theta = self._percent_to_theta(percent)
        return theta
Example #21
0
class MATS1DDamage(MATS1DEval):
    '''
    Scalar Damage Model.
    '''

    E = Float(
        1.,  # 34e+3,
        modified=True,
        label="E",
        desc="Young's Modulus",
        enter_set=True,
        auto_set=False)

    epsilon_0 = Float(
        1.,  # 59e-6,
        modified=True,
        label="eps_0",
        desc="Breaking Strain",
        enter_set=True,
        auto_set=False)

    epsilon_f = Float(
        1.,  # 191e-6,
        modified=True,
        label="eps_f",
        desc="Shape Factor",
        enter_set=True,
        auto_set=False)

    stiffness = Enum("secant", "algorithmic", modified=True)

    # This event can be used by the clients to trigger an action upon
    # the completed reconfiguration of the material model
    #
    changed = Event

    #--------------------------------------------------------------------------
    # View specification
    #--------------------------------------------------------------------------

    traits_view = View(Group(Group(Item('E'),
                                   Item('epsilon_0'),
                                   Item('epsilon_f'),
                                   label='Material parameters',
                                   show_border=True),
                             Group(
                                 Item('stiffness', style='custom'),
                                 Spring(resizable=True),
                                 label='Configuration parameters',
                                 show_border=True,
                             ),
                             layout='tabbed'),
                       resizable=True)

    #-------------------------------------------------------------------------
    # Setup for computation within a supplied spatial context
    #-------------------------------------------------------------------------

    def get_state_array_size(self):
        '''
        Give back the nuber of floats to be saved
        @param sctx:spatial context
        '''
        return 2

    def new_cntl_var(self):
        return np.zeros(1, np.float_)

    def new_resp_var(self):
        return np.zeros(1, np.float_)

    #-------------------------------------------------------------------------
    # Evaluation - get the corrector and predictor
    #-------------------------------------------------------------------------

    def get_corr_pred(self, sctx, eps_app_eng, d_eps, tn, tn1, eps_avg=None):
        '''
        Corrector predictor computation.
        @param eps_app_eng input variable - engineering strain
        '''
        if eps_avg == None:
            eps_avg = eps_app_eng

        E = self.E
        D_el = np.array([E])

        if sctx.update_state_on:

            kappa_n = sctx.mats_state_array[0]
            kappa_k = sctx.mats_state_array[1]
            sctx.mats_state_array[0] = kappa_k

        kappa_k, omega = self._get_state_variables(sctx, eps_avg)
        sctx.mats_state_array[1] = kappa_k

        if self.stiffness == "algorithmic":
            D_e_dam = np.array(
                [self._get_alg_stiffness(sctx, eps_app_eng, kappa_k, omega)])
        else:
            D_e_dam = np.array([(1 - omega) * D_el])

        sigma = np.dot(np.array([(1 - omega) * D_el]), eps_app_eng)

        # print the stress you just computed and the value of the apparent E

        return sigma, D_e_dam

    #--------------------------------------------------------------------------
    # Subsidiary methods realizing configurable features
    #--------------------------------------------------------------------------

    def _get_state_variables(self, sctx, eps):

        kappa_n, kappa_k = sctx.mats_state_array

        kappa_k = max(abs(eps), kappa_n)

        omega = self._get_omega(sctx, kappa_k)

        return kappa_k, omega

    def _get_omega(self, sctx, kappa):
        epsilon_0 = self.epsilon_0
        epsilon_f = self.epsilon_f
        if kappa >= epsilon_0:
            return 1. - epsilon_0 / kappa * exp(
                -1 * (kappa - epsilon_0) / epsilon_f)
        else:
            return 0.

    def _get_alg_stiffness(self, sctx, eps_app_eng, e_max, omega):
        E = self.E
        D_el = np.array([E])
        epsilon_0 = self.epsilon_0
        epsilon_f = self.epsilon_f
        dodk = (epsilon_0 /
                (e_max * e_max) * exp(-(e_max - epsilon_0) / epsilon_f) +
                epsilon_0 / e_max / epsilon_f *
                exp(-(e_max - epsilon_0) / epsilon_f))
        D_alg = (1 - omega) * D_el - D_el * eps_app_eng * dodk
        return D_alg

    #--------------------------------------------------------------------------
    # Response trace evaluators
    #--------------------------------------------------------------------------

    def get_omega(self, sctx, eps_app_eng, eps_avg=None):
        if eps_avg == None:
            eps_avg = eps_app_eng
        return self._get_omega(sctx, eps_avg)

    # Declare and fill-in the rte_dict - it is used by the clients to
    # assemble all the available time-steppers.
    #
    rte_dict = Trait(Dict)

    def _rte_dict_default(self):
        return {
            'sig_app': self.get_sig_app,
            'eps_app': self.get_eps_app,
            'omega': self.get_omega
        }

    #-------------------------------------------------------------------------
    # List of response tracers to be constructed within the mats_explorer
    #-------------------------------------------------------------------------
    def _get_explorer_rtrace_list(self):
        '''Return the list of relevant tracers to be used in mats_explorer.
        '''
        return []

    def _get_explorer_config(self):
        from ibvpy.api import TLine, RTDofGraph, BCDof
        ec = super(MATS1DDamage, self)._get_explorer_config()
        ec['mats_eval'] = MATS1DDamage(E=1.0, epsilon_0=1.0, epsilon_f=5)
        ec['bcond_list'] = [
            BCDof(var='u',
                  dof=0,
                  value=1.7,
                  time_function=lambda t: (1 + 0.1 * t) * sin(t))
        ]
        ec['tline'] = TLine(step=0.1, max=10)
        ec['rtrace_list'] = [
            RTDofGraph(name='strain - stress',
                       var_x='eps_app',
                       idx_x=0,
                       var_y='sig_app',
                       idx_y=0,
                       record_on='update'),
            RTDofGraph(name='time - damage',
                       var_x='time',
                       idx_x=0,
                       var_y='omega',
                       idx_y=0,
                       record_on='update')
        ]
        return ec
Example #22
0
class Spline(DataFit):
    """ Cubic-spline interpolation

        This class works for interpolation and extrapolation.
        !! - extrapolation seems to be broken for this
        !! - only works for 1d y arrays
    """
    # order of polynomial.  Default to cubic spline.
    order = Trait(3, TraitEnum(1, 3, 5))

    # smoothness -- larger values result in more smothing.
    # !! I would prefer the trait to allow 0.0 and any value
    # !! between 1.0-30.0.  Values of 0.0-1.0 are *really*
    # !! CPU intensive.
    smoothness = Float(0.0)

    def __init__(self, x=None, y=None, order=3, smoothness=0):
        self.order = order
        self.smoothness = smoothness
        self._representation = None
        DataFit.__init__(self, x, y)

    def set_xy(self, x, y):
        DataFit.set_xy(self, x, y)
        if not self.using_special_case():
            # in case of exception in code below
            self.initialized = False
            if len(x) <= 3:
                # protect against short lists not having enough points
                # for a cubic-spline.
                order = 1
            else:
                order = self.order

            # catch case when order = 5 and len < 5
            if order > len(x):
                order = len(x) - 2
                # protect against even order.
                if order % 2 == 0:
                    order = order - 1

            self._representation = interpolate.splrep(x, y, k=order,
                                                      s=self.smoothness)
        self.initialized = True

    def interp(self, x):
        # !! fix for bug in splev that seg-faults if handed 1 element
        # !! array http://www.scipy.net/roundup/scipy/issue126
        scalar = False
        if len(x) == 1:
            x = numpy.array((x[0], x[0]))
            scalar = True

        y = interpolate.splev(x, self._representation, der=0)

        # !! fix for bug in splev that seg-faults if handed 1 element
        # !! array http://www.scipy.net/roundup/scipy/issue126
        if scalar == True:
            y = y[0]

        return numpy.atleast_1d(y)
class MATS1DDamageView(ModelView):
    '''
    View into the parametric space constructed over the model.

    The is associated with the PStudySpace instance covering the
    factor ranges using an n-dimensional array.

    The view is responsible for transferring the response values
    into 2D and 3D plots. Depending on the current view specification
    it also initiates the calculation of response values in the 
    currently viewed subspace of the study. 
    '''

    model = Instance(MATS1DDamage)

    max_eps = Float(0.01, enter_set=True, auto_set=False, modified=True)

    n_points = Int(100, enter_set=True, auto_set=False, modified=True)

    data_changed = Event(True)

    @on_trait_change('+modified,model.+modified')
    def _redraw(self):

        get_omega = frompyfunc(self.model._get_omega, 1, 1)
        xdata = linspace(0, self.max_eps, self.n_points)
        ydata = get_omega(xdata)

        axes = self.figure.axes[0]
        axes.clear()

        axes.plot(xdata, ydata, color='blue', linewidth=3)

        self.data_changed = True

    #---------------------------------------------------------------
    # PLOT OBJECT
    #-------------------------------------------------------------------
    figure = Instance(Figure)

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.add_axes([0.12, 0.13, 0.85, 0.74])
        return figure

    traits_view = View(HSplit(
        VGroup(
            Item('model@', show_label=False, resizable=True),
            label='material parameters',
            id='tmodel.viewmodel.model',
            dock='tab',
        ),
        VSplit(
            VGroup(
                Item('figure',
                     editor=MPLFigureEditor(),
                     resizable=True,
                     show_label=False),
                label='plot sheet',
                id='tmodel.viewmodel.figure_window',
                dock='tab',
            ),
            VGroup(
                HGroup(
                    Item('max_eps', label='maximum epsilon [-]', springy=True),
                    Item('n_points', label='plot points', springy=True),
                ),
                label='plot parameters',
                id='tmodel.viewmodel.view_params',
                dock='tab',
            ),
            id='tmodel.viewmodel.right',
        ),
        id='tmodel.viewmodel.splitter',
    ),
                       title='Yarn Size Effect',
                       id='yse.viewmodel',
                       dock='tab',
                       resizable=True,
                       height=0.8,
                       width=0.8,
                       buttons=[OKButton])
Example #24
0
class Slider(Component):
    """ A horizontal or vertical slider bar """

    #------------------------------------------------------------------------
    # Model traits
    #------------------------------------------------------------------------

    min = Float()

    max = Float()

    value = Float()

    # The number of ticks to show on the slider.
    num_ticks = Int(4)

    #------------------------------------------------------------------------
    # Bar and endcap appearance
    #------------------------------------------------------------------------

    # Whether this is a horizontal or vertical slider
    orientation = Enum("h", "v")

    # The thickness, in pixels, of the lines used to render the ticks,
    # endcaps, and main slider bar.
    bar_width = Int(4)

    bar_color = ColorTrait("black")

    # Whether or not to render endcaps on the slider bar
    endcaps = Bool(True)

    # The extent of the endcaps, in pixels.  This is a read-only property,
    # since the endcap size can be set as either a fixed number of pixels or
    # a percentage of the widget's size in the transverse direction.
    endcap_size = Property

    # The extent of the tickmarks, in pixels.  This is a read-only property,
    # since the endcap size can be set as either a fixed number of pixels or
    # a percentage of the widget's size in the transverse direction.
    tick_size = Property

    #------------------------------------------------------------------------
    # Slider appearance
    #------------------------------------------------------------------------

    # The kind of marker to use for the slider.
    slider = SliderMarkerTrait("rect")

    # If the slider marker is "rect", this is the thickness of the slider,
    # i.e. its extent in the dimension parallel to the long axis of the widget.
    # For other slider markers, this has no effect.
    slider_thickness = Int(9)

    # The size of the slider, in pixels.  This is a read-only property, since
    # the slider size can be set as either a fixed number of pixels or a
    # percentage of the widget's size in the transverse direction.
    slider_size = Property

    # For slider markers with a filled area, this is the color of the filled
    # area.  For slider markers that are just lines/strokes (e.g. cross, plus),
    # this is the color of the stroke.
    slider_color = ColorTrait("red")

    # For slider markers with a filled area, this is the color of the outline
    # border drawn around the filled area.  For slider markers that have just
    # lines/strokes, this has no effect.
    slider_border = ColorTrait("none")

    # For slider markers with a filled area, this is the width, in pixels,
    # of the outline around the area.  For slider markers that are just lines/
    # strokes, this is the thickness of the stroke.
    slider_outline_width = Int(1)

    # The kiva.CompiledPath representing the custom path to render for the
    # slider, if the **slider** trait is set to "custom".
    custom_slider = Any()

    #------------------------------------------------------------------------
    # Interaction traits
    #------------------------------------------------------------------------

    # Can this slider be interacted with, or is it just a display
    interactive = Bool(True)

    mouse_button = Enum("left", "right")

    event_state = Enum("normal", "dragging")

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

    # Returns the coordinate index (0 or 1) corresponding to our orientation.
    # Used internally; read-only property.
    axis_ndx = Property()

    _slider_size_mode = Enum("fixed", "percent")
    _slider_percent = Float(0.0)
    _cached_slider_size = Int(10)

    _endcap_size_mode = Enum("fixed", "percent")
    _endcap_percent = Float(0.0)
    _cached_endcap_size = Int(20)

    _tick_size_mode = Enum("fixed", "percent")
    _tick_size_percent = Float(0.0)
    _cached_tick_size = Int(20)

    # A tuple of (dx, dy) of the difference between the mouse position and
    # center of the slider.
    _offset = Any((0, 0))

    def set_range(self, min, max):
        self.min = min
        self.max = max

    def map_screen(self, val):
        """ Returns an (x,y) coordinate corresponding to the location of
        **val** on the slider.
        """
        # Some local variables to handle orientation dependence
        axis_ndx = self.axis_ndx
        other_ndx = 1 - axis_ndx
        screen_low = self.position[axis_ndx]
        screen_high = screen_low + self.bounds[axis_ndx]

        # The return coordinate.  The return value along the non-primary
        # axis will be the same in all cases.
        coord = [0, 0]
        coord[
            other_ndx] = self.position[other_ndx] + self.bounds[other_ndx] / 2

        # Handle exceptional/boundary cases
        if val <= self.min:
            coord[axis_ndx] = screen_low
            return coord
        elif val >= self.max:
            coord[axis_ndx] = screen_high
            return coord
        elif self.min == self.max:
            coord[axis_ndx] = (screen_low + screen_high) / 2
            return coord

        # Handle normal cases
        coord[axis_ndx] = (val - self.min) / (
            self.max - self.min) * self.bounds[axis_ndx] + screen_low
        return coord

    def map_data(self, x, y, clip=True):
        """ Returns a value between min and max that corresponds to the given
        x and y values.

        Parameters
        ==========
        x, y : Float
            The screen coordinates to map
        clip : Bool (default=True)
            Whether points outside the range should be clipped to the max
            or min value of the slider (depending on which it's closer to)

        Returns
        =======
        value : Float
        """
        # Some local variables to handle orientation dependence
        axis_ndx = self.axis_ndx
        other_ndx = 1 - axis_ndx
        screen_low = self.position[axis_ndx]
        screen_high = screen_low + self.bounds[axis_ndx]
        if self.orientation == "h":
            coord = x
        else:
            coord = y

        # Handle exceptional/boundary cases
        if coord >= screen_high:
            return self.max
        elif coord <= screen_low:
            return self.min
        elif screen_high == screen_low:
            return (self.max + self.min) / 2

        # Handle normal cases
        return (coord - screen_low) /self.bounds[axis_ndx] * \
                    (self.max - self.min) + self.min

    def set_slider_pixels(self, pixels):
        """ Sets the width of the slider to be a fixed number of pixels

        Parameters
        ==========
        pixels : int
            The number of pixels wide that the slider should be
        """
        self._slider_size_mode = "fixed"
        self._cached_slider_size = pixels

    def set_slider_percent(self, percent):
        """ Sets the width of the slider to be a percentage of the width
        of the slider widget.

        Parameters
        ==========
        percent : float
            The percentage, between 0.0 and 1.0
        """
        self._slider_size_mode = "percent"
        self._slider_percent = percent
        self._update_sizes()

    def set_endcap_pixels(self, pixels):
        """ Sets the width of the endcap to be a fixed number of pixels

        Parameters
        ==========
        pixels : int
            The number of pixels wide that the endcap should be
        """
        self._endcap_size_mode = "fixed"
        self._cached_endcap_size = pixels

    def set_endcap_percent(self, percent):
        """ Sets the width of the endcap to be a percentage of the width
        of the endcap widget.

        Parameters
        ==========
        percent : float
            The percentage, between 0.0 and 1.0
        """
        self._endcap_size_mode = "percent"
        self._endcap_percent = percent
        self._update_sizes()

    def set_tick_pixels(self, pixels):
        """ Sets the width of the tick marks to be a fixed number of pixels

        Parameters
        ==========
        pixels : int
            The number of pixels wide that the endcap should be
        """
        self._tick_size_mode = "fixed"
        self._cached_tick_size = pixels

    def set_tick_percent(self, percent):
        """ Sets the width of the tick marks to be a percentage of the width
        of the endcap widget.

        Parameters
        ==========
        percent : float
            The percentage, between 0.0 and 1.0
        """
        self._tick_size_mode = "percent"
        self._tick_percent = percent
        self._update_sizes()

    #------------------------------------------------------------------------
    # Rendering methods
    #------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="normal"):
        start = [0, 0]
        end = [0, 0]
        axis_ndx = self.axis_ndx
        other_ndx = 1 - axis_ndx

        bar_x = self.x + self.width / 2
        bar_y = self.y + self.height / 2

        # Draw the bar and endcaps
        gc.set_stroke_color(self.bar_color_)
        gc.set_line_width(self.bar_width)
        if self.orientation == "h":
            gc.move_to(self.x, bar_y)
            gc.line_to(self.x2, bar_y)
            gc.stroke_path()
            if self.endcaps:
                start_y = bar_y - self._cached_endcap_size / 2
                end_y = bar_y + self._cached_endcap_size / 2
                gc.move_to(self.x, start_y)
                gc.line_to(self.x, end_y)
                gc.move_to(self.x2, start_y)
                gc.line_to(self.x2, end_y)
            if self.num_ticks > 0:
                x_pts = linspace(self.x, self.x2,
                                 self.num_ticks + 2).astype(int)
                starts = zeros((len(x_pts), 2), dtype=int)
                starts[:, 0] = x_pts
                starts[:, 1] = bar_y - self._cached_tick_size / 2
                ends = starts.copy()
                ends[:, 1] = bar_y + self._cached_tick_size / 2
                gc.line_set(starts, ends)
        else:
            gc.move_to(bar_x, self.y)
            gc.line_to(bar_x, self.y2)
            if self.endcaps:
                start_x = bar_x - self._cached_endcap_size / 2
                end_x = bar_x + self._cached_endcap_size / 2
                gc.move_to(start_x, self.y)
                gc.line_to(end_x, self.y)
                gc.move_to(start_x, self.y2)
                gc.line_to(end_x, self.y2)
            if self.num_ticks > 0:
                y_pts = linspace(self.y, self.y2,
                                 self.num_ticks + 2).astype(int)
                starts = zeros((len(y_pts), 2), dtype=int)
                starts[:, 1] = y_pts
                starts[:, 0] = bar_x - self._cached_tick_size / 2
                ends = starts.copy()
                ends[:, 0] = bar_x + self._cached_tick_size / 2
                gc.line_set(starts, ends)
        gc.stroke_path()

        # Draw the slider
        pt = self.map_screen(self.value)
        if self.slider == "rect":
            gc.set_fill_color(self.slider_color_)
            gc.set_stroke_color(self.slider_border_)
            gc.set_line_width(self.slider_outline_width)
            rect = self._get_rect_slider_bounds()
            gc.rect(*rect)
            gc.draw_path()
        else:
            self._render_marker(gc, pt, self._cached_slider_size,
                                self.slider_(), self.custom_slider)

    def _get_rect_slider_bounds(self):
        """ Returns the (x, y, w, h) bounds of the rectangle representing the slider.
        Used for rendering and hit detection.
        """
        bar_x = self.x + self.width / 2
        bar_y = self.y + self.height / 2
        pt = self.map_screen(self.value)
        if self.orientation == "h":
            slider_height = self._cached_slider_size
            return (pt[0] - self.slider_thickness, bar_y - slider_height / 2,
                    self.slider_thickness, slider_height)
        else:
            slider_width = self._cached_slider_size
            return (bar_x - slider_width / 2, pt[1] - self.slider_thickness,
                    slider_width, self.slider_thickness)

    def _render_marker(self, gc, point, size, marker, custom_path):
        with gc:
            gc.begin_path()
            if marker.draw_mode == STROKE:
                gc.set_stroke_color(self.slider_color_)
                gc.set_line_width(self.slider_thickness)
            else:
                gc.set_fill_color(self.slider_color_)
                gc.set_stroke_color(self.slider_border_)
                gc.set_line_width(self.slider_outline_width)

            if hasattr(gc, "draw_marker_at_points") and \
                    (marker.__class__ != CustomMarker) and \
                    (gc.draw_marker_at_points([point], size, marker.kiva_marker) != 0):
                pass
            elif hasattr(gc, "draw_path_at_points"):
                if marker.__class__ != CustomMarker:
                    path = gc.get_empty_path()
                    marker.add_to_path(path, size)
                    mode = marker.draw_mode
                else:
                    path = custom_path
                    mode = STROKE
                if not marker.antialias:
                    gc.set_antialias(False)
                gc.draw_path_at_points([point], path, mode)
            else:
                if not marker.antialias:
                    gc.set_antialias(False)
                if marker.__class__ != CustomMarker:
                    gc.translate_ctm(*point)
                    # Kiva GCs have a path-drawing interface
                    marker.add_to_path(gc, size)
                    gc.draw_path(marker.draw_mode)
                else:
                    path = custom_path
                    gc.translate_ctm(*point)
                    gc.add_path(path)
                    gc.draw_path(STROKE)

    #------------------------------------------------------------------------
    # Interaction event handlers
    #------------------------------------------------------------------------

    def normal_left_down(self, event):
        if self.mouse_button == "left":
            return self._mouse_pressed(event)

    def dragging_left_up(self, event):
        if self.mouse_button == "left":
            return self._mouse_released(event)

    def normal_right_down(self, event):
        if self.mouse_button == "right":
            return self._mouse_pressed(event)

    def dragging_right_up(self, event):
        if self.mouse_button == "right":
            return self._mouse_released(event)

    def dragging_mouse_move(self, event):
        dx, dy = self._offset
        self.value = self.map_data(event.x - dx, event.y - dy)
        event.handled = True
        self.request_redraw()

    def dragging_mouse_leave(self, event):
        self.event_state = "normal"

    def _mouse_pressed(self, event):
        # Determine the slider bounds so we can hit test it
        pt = self.map_screen(self.value)
        if self.slider == "rect":
            x, y, w, h = self._get_rect_slider_bounds()
            x2 = x + w
            y2 = y + h
        else:
            x, y = pt
            size = self._cached_slider_size
            x -= size / 2
            y -= size / 2
            x2 = x + size
            y2 = y + size

        # Hit test both the slider and against the bar.  If the user has
        # clicked on the bar but outside of the slider, we set the _offset
        # and call dragging_mouse_move() to teleport the slider to the
        # mouse click position.
        if self.orientation == "v" and (x <= event.x <= x2):
            if not (y <= event.y <= y2):
                self._offset = (event.x - pt[0], 0)
                self.dragging_mouse_move(event)
            else:
                self._offset = (event.x - pt[0], event.y - pt[1])
        elif self.orientation == "h" and (y <= event.y <= y2):
            if not (x <= event.x <= x2):
                self._offset = (0, event.y - pt[1])
                self.dragging_mouse_move(event)
            else:
                self._offset = (event.x - pt[0], event.y - pt[1])
        else:
            # The mouse click missed the bar and the slider.
            return

        event.handled = True
        self.event_state = "dragging"
        return

    def _mouse_released(self, event):
        self.event_state = "normal"
        event.handled = True

    #------------------------------------------------------------------------
    # Private trait event handlers and property getters/setters
    #------------------------------------------------------------------------

    def _get_axis_ndx(self):
        if self.orientation == "h":
            return 0
        else:
            return 1

    def _get_slider_size(self):
        return self._cached_slider_size

    def _get_endcap_size(self):
        return self._cached_endcap_size

    def _get_tick_size(self):
        return self._cached_tick_size

    @on_trait_change("bounds,bounds_items")
    def _update_sizes(self):
        if self._slider_size_mode == "percent":
            if self.orientation == "h":
                self._cached_slider_size = int(self.height *
                                               self._slider_percent)
            else:
                self._cached_slider_size = int(self.width *
                                               self._slider_percent)
        if self._endcap_size_mode == "percent":
            if self.orientation == "h":
                self._cached_endcap_size = int(self.height *
                                               self._endcap_percent)
            else:
                self._cached_endcap_size = int(self.width *
                                               self._endcap_percent)

        return
Example #25
0
class NMGRLFurnaceManager(BaseFurnaceManager):
    funnel = Instance(NMGRLFunnel)
    loader_logic = Instance(LoaderLogic)
    magnets = Instance(NMGRLMagnetDumper)
    setpoint_readback_min = Float(0)
    setpoint_readback_max = Float(1600.0)

    graph = Instance(StreamGraph)
    update_period = Int(2)

    dumper_canvas = Instance(DumperCanvas)
    _alive = False
    _guide_overlay = None
    _dumper_thread = None
    mode = 'normal'

    def activate(self):
        # pref_id = 'pychron.furnace'
        # bind_preference(self, 'update_period', '{}.update_period'.format(pref_id))

        self._start_update()

    def prepare_destroy(self):
        self._stop_update()
        self.loader_logic.manager = None

    def dump_sample(self):
        self.debug('dump sample')

        if self._dumper_thread is None:
            self._dumper_thread = Thread(name='DumpSample',
                                         target=self._dump_sample)
            self._dumper_thread.start()

    def is_dump_complete(self):
        ret = self._dumper_thread is None
        return ret

    def actuate_magnets(self):
        self.debug('actuate magnets')
        if self.loader_logic.check('AM'):
            self.magnet.open()
            # wait for actuate magnets
            pass
        else:
            self.warning('actuate magnets not enabled')

    def lower_funnel(self):
        self.debug('lower funnel')
        if self.loader_logic.check('FD'):
            self.funnel.set_value(self.funnel.down_position)

            # todo: update canvas state

            return True
        else:
            self.warning('lowering funnel not enabled')

    def raise_funnel(self):
        self.debug('raise funnel')
        if self.loader_logic.check('FU'):
            self.funnel.set_value(self.funnel.up_position)

            # todo: update canvas state

            return True
        else:
            self.warning('raising funnel not enabled')

    def set_setpoint(self, v):
        if self.controller:
            # print self.controller, self.controller._cdevice
            self.controller.set_setpoint(v)
            if not self._guide_overlay:
                self._guide_overlay = self.graph.add_horizontal_rule(v)

            self._guide_overlay.visible = bool(v)
            self._guide_overlay.value = v

            # ymi, yma = self.graph.get_y_limits()
            d = self.graph.get_data(axis=1)
            self.graph.set_y_limits(min_=0, max_=max(d.max(), v * 1.1))

            self.graph.redraw()

    def read_setpoint(self, update=False):
        v = 0
        if self.controller:
            force = update and not self.controller.is_scanning()
            v = self.controller.read_setpoint(force=force)

        try:
            self.setpoint_readback = v
            return v
        except TraitError:
            pass

    # canvas
    def set_software_lock(self, name, lock):
        if self.switch_manager is not None:
            if lock:
                self.switch_manager.lock(name)
            else:
                self.switch_manager.unlock(name)

    def open_valve(self, name, **kw):
        if not self._open_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.open_switch(name, **kw)

    def close_valve(self, name, **kw):
        if not self._close_logic(name):
            self.debug('logic failed')
            return False, False

        if self.switch_manager:
            return self.switch_manager.close_switch(name, **kw)

    def set_selected_explanation_item(self, item):
        pass

    # logic
    def get_switch_state(self, name):
        if self.switch_manager:
            return self.switch_manager.get_state_by_name(name, force=True)

    def get_flag_state(self, flag):
        self.debug('get_flag_state {}'.format(flag))

        if flag in ('no_motion', 'no_dump', 'funnel_up', 'funnel_down'):
            return getattr(self, flag)()
        return False

    def funnel_up(self):
        return self.funnel.in_up_position()

    def funnel_down(self):
        return self.funnel.in_down_position()

    def no_motion(self):
        v = not self.stage_manager.in_motion()
        self.debug('no motion {}'.format(v))
        return v

    def no_dump(self):
        v = not self.magnets.dump_in_progress()
        self.debug('no dump {}'.format(v))
        return v

    # private
    def _open_logic(self, name):
        """
        check the logic rules to see if its ok to open "name"

        return True if ok
        """
        return self.loader_logic.open(name)

    def _close_logic(self, name):
        """
        check the logic rules to see if its ok to close "name"

        return True if ok

        """
        return self.loader_logic.close(name)

    def _stop_update(self):
        self._alive = False

    def _start_update(self):
        self._alive = True

        self.graph = g = StreamGraph()
        # g.plotcontainer.padding_top = 5
        # g.plotcontainer.padding_right = 5
        g.new_plot(xtitle='Time (s)',
                   ytitle='Temp. (C)',
                   padding_top=5,
                   padding_right=5)
        g.set_scan_width(600)
        g.new_series()

        self._update_readback()

    def _update_readback(self):
        v = self.read_setpoint(update=True)
        self.graph.record(v, track_y=False)
        if self._alive:
            do_after(self.update_period * 1000, self._update_readback)

    def _dump_sample(self):
        """
        1. open gate valve
        2. open shutters
        3. lower funnel
        4. actuate magnets
        5. raise funnel
        6. close shutters
        7. close gate valve
        :return:
        """
        self.debug('dump sample started')
        for line in self._load_dump_script():
            self.debug(line)
            self._execute_script_line(line)

            self.stage_manager.set_sample_dumped()
            self._dumper_thread = None

    def _load_dump_script(self):
        p = os.path.join(paths.device_dir, 'furnace', 'dump_sequence.txt')
        return pathtolist(p)

    def _execute_script_line(self, line):
        if ' ' in line:
            cmd, args = line.split(' ')
        else:
            cmd, args = line, None

        if cmd == 'sleep':
            time.sleep(float(args))
        elif cmd == 'open':
            self.switch_manager.open_switch(args)
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'close':
            self.switch_manager.close_switch(args)
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'lower_funnel':
            self.lower_funnel()
            self.dumper_canvas.set_item_state(args, True)
        elif cmd == 'raise_funnel':
            self.raise_funnel()
            self.dumper_canvas.set_item_state(args, False)
        elif cmd == 'actuate_magnets':
            self.actuate_magnets()

        self.dumper_canvas.request_redraw()

    # handlers
    def _setpoint_changed(self, new):
        self.set_setpoint(new)

    def _stage_manager_default(self):
        sm = NMGRLFurnaceStageManager(
            stage_manager_id='nmgrl.furnace.stage_map')
        return sm

    def _switch_manager_default(self):
        sm = SwitchManager()
        return sm

    def _dumper_canvas_default(self):
        dc = DumperCanvas(manager=self)

        pathname = os.path.join(paths.canvas2D_dir, 'dumper.xml')
        configpath = os.path.join(paths.canvas2D_dir, 'dumper_config.xml')
        valvepath = os.path.join(paths.extraction_line_dir, 'valves.xml')
        dc.load_canvas_file(pathname, configpath, valvepath, dc)
        return dc

    def _funnel_default(self):
        f = NMGRLFunnel(name='funnel', configuration_dir_name='furnace')
        return f

    def _loader_logic_default(self):
        l = LoaderLogic(manager=self)
        l.load_config()

        return l

    def _magnets_default(self):
        m = NMGRLMagnetDumper(name='magnets', configuration_dir_name='furnace')
        return m
Example #26
0
class ThermoSource(BaseSource):
    trap_voltage = Property(depends_on='_trap_voltage')
    _trap_voltage = Float
    trap_current = Property(depends_on='_trap_current')
    _trap_current = Float

    z_symmetry = Property(depends_on='_z_symmetry')
    y_symmetry = Property(depends_on='_y_symmetry')
    extraction_lens = Property(Range(0, 100.0), depends_on='_extraction_lens')
    emission = Float

    _y_symmetry = Float  # Range(0.0, 100.)
    _z_symmetry = Float  # Range(0.0, 100.)

    y_symmetry_low = Float(-100.0)
    y_symmetry_high = Float(100.0)
    z_symmetry_low = Float(-100.0)
    z_symmetry_high = Float(100.0)

    _extraction_lens = Float  # Range(0.0, 100.)

    def set_hv(self, v):
        return self._set_value('SetHV', v)

    def read_emission(self):
        return self._read_value('GetParameter Source Current Readback',
                                'emission')

    def read_trap_current(self):
        return self._read_value('GetParameter Trap Current Readback',
                                '_trap_current')

    def read_y_symmetry(self):
        return self._read_value('GetYSymmetry', '_y_symmetry')

    def read_z_symmetry(self):
        return self._read_value('GetZSymmetry', '_z_symmetry')

    def read_trap_voltage(self):
        return self._read_value('GetParameter Trap Voltage Readback',
                                '_trap_voltage')

    def read_hv(self):
        return self._read_value('GetHighVoltage', 'current_hv')

    def _set_value(self, name, v):
        r = self.ask('{} {}'.format(name, v))
        if r is not None:
            if r.lower().strip() == 'ok':
                return True

    def _read_value(self, name, value):
        r = self.ask(name)
        try:
            r = float('{:0.3f}'.format(float(r)))
            setattr(self, value, r)
            return getattr(self, value)
        except (ValueError, TypeError):
            pass

    def sync_parameters(self):
        self.read_y_symmetry()
        self.read_z_symmetry()
        self.read_trap_current()
        self.read_hv()

    def traits_view(self):
        v = View(
            Item('nominal_hv', format_str='%0.4f'),
            Item('current_hv', format_str='%0.4f', style='readonly'),
            Item('trap_current'), Item('trap_voltage'),
            Item('y_symmetry',
                 editor=RangeEditor(low_name='y_symmetry_low',
                                    high_name='y_symmetry_high',
                                    mode='slider')),
            Item('z_symmetry',
                 editor=RangeEditor(low_name='z_symmetry_low',
                                    high_name='z_symmetry_high',
                                    mode='slider')), Item('extraction_lens'))
        return v

    # ===============================================================================
    # property get/set
    # ===============================================================================
    def _get_trap_voltage(self):
        return self._trap_voltage

    def _get_trap_current(self):
        return self._trap_current

    def _get_y_symmetry(self):
        return self._y_symmetry

    def _get_z_symmetry(self):
        return self._z_symmetry

    def _get_extraction_lens(self):
        return self._extraction_lens

    def _set_trap_voltage(self, v):
        if self._set_value('SetParameter', 'Trap Voltage Set,{}'.format(v)):
            self._trap_current = v

    def _set_trap_current(self, v):
        if self._set_value('SetParameter', 'Trap Current Set,{}'.format(v)):
            self._trap_current = v

    def _set_y_symmetry(self, v):
        if self._set_value('SetYSymmetry', v):
            self._y_symmetry = v

    def _set_z_symmetry(self, v):
        if self._set_value('SetZSymmetry', v):
            self._z_symmetry = v

    def _set_extraction_lens(self, v):
        if self._set_value('SetExtractionLens', v):
            self._extraction_lens = v
Example #27
0
class BarStrainLocalization(IBVModel):
    '''Model assembling the components for studying the restrained crack localization.
    '''

    shape = Int(20, desc='Number of finite elements',
                   ps_levsls=(10, 40, 4),
                   input=True)

    length = Float(1, desc='Length of the simulated region',
                    input=True)

    n_steps = Int(5, input=True)

    flaw_position = Float(0.5, input=True)

    flaw_radius = Float(0.1, input=True)

    reduction_factor = Float(0.9, input=True)

    avg_radius = Float(0.4, input=True)

    elastic_fraction = Float(0.8, input=True)

    epsilon_0 = Float(0.1, input=True)

    epsilon_f = Float(10, input=True)

    E = Float(1.0, input=True)

    #---------------------------------------------------------------------------
    # Load scaling adapted to the elastic and inelastic regime
    #---------------------------------------------------------------------------
    final_displ = Property(depends_on='+input')
    @cached_property
    def _get_final_displ(self):
        damage_onset_displ = self.mats.epsilon_0 * self.length
        return damage_onset_displ / self.elastic_fraction

    step_size = Property(depends_on='+input')
    @cached_property
    def _get_step_size(self):
        n_steps = self.n_steps
        return 1.0 / float(n_steps)

    time_function = Property(depends_on='+input')
    @cached_property
    def _get_time_function(self):
        '''Get the time function so that the elastic regime
        is skipped in a single step.
        '''
        step_size = self.step_size

        elastic_value = self.elastic_fraction * 0.98 * self.reduction_factor
        inelastic_value = 1.0 - elastic_value

        def ls(t):
            if t <= step_size:
                return (elastic_value / step_size) * t
            else:
                return elastic_value + (t - step_size) * (inelastic_value) / (1 - step_size)

        return ls

    def plot_time_function(self):
        '''Plot the time function.
        '''
        n_steps = self.n_steps
        mats = self.mats
        step_size = self.step_size

        ls_t = linspace(0, step_size * n_steps, n_steps + 1)
        ls_fn = frompyfunc(self.time_function, 1, 1)
        ls_v = ls_fn(ls_t)

        p.subplot(321)
        p.plot(ls_t, ls_v, 'ro-')

        final_epsilon = self.final_displ / self.length

        kappa = linspace(mats.epsilon_0, final_epsilon, 10)
        omega_fn = frompyfunc(lambda kappa: mats._get_omega(None , kappa), 1, 1)
        omega = omega_fn(kappa)
        kappa_scaled = (step_size + (1 - step_size) * (kappa - mats.epsilon_0) /
                   (final_epsilon - mats.epsilon_0))
        xdata = hstack([array([0.0], dtype=float),
                         kappa_scaled ])
        ydata = hstack([array([0.0], dtype=float),
                         omega])
        p.plot(xdata, ydata, 'g')
        p.xlabel('regular time [-]')
        p.ylabel('scaled time [-]')

    #--------------------------------------------------------------------------------------
    # Time integrator
    #--------------------------------------------------------------------------------------
    mats = Property(depends_on='intput')
    @cached_property
    def _get_mats(self):
        mats = MATS1DDamageWithFlaw(E=self.E,
                                     epsilon_0=self.epsilon_0,
                                     epsilon_f=self.epsilon_f,
                                     flaw_position=self.flaw_position,
                                     flaw_radius=self.flaw_radius,
                                     reduction_factor=self.reduction_factor)
        return mats

    #--------------------------------------------------------------------------------------
    # Space integrator
    #--------------------------------------------------------------------------------------
    fets = Property(depends_on='+input')
    @cached_property
    def _get_fets(self):
        fets_eval = FETS1D2L(mats_eval=self.mats)
        # fets_eval = FETS1D2L3U( mats_eval = self.mats )
        return fets_eval

    fe_domain = Property(depends_on='+input')
    @cached_property
    def _get_fe_domain(self):
        return FEDomain()
    #--------------------------------------------------------------------------------------
    # Mesh integrator
    #--------------------------------------------------------------------------------------
    fe_grid_level = Property(depends_on='+input')
    @cached_property
    def _get_fe_grid_level(self):
        '''Container for subgrids at the refinement level.
        '''
        fe_grid_level = FERefinementGrid(domain=self.fe_domain, fets_eval=self.fets)
        return fe_grid_level

    fe_grid = Property(depends_on='+input')
    @cached_property
    def _get_fe_grid(self):

        elem_length = self.length / float(self.shape)

        fe_grid = FEGrid(coord_max=(self.length,),
                          shape=(self.shape,),
                          level=self.fe_grid_level,
                          fets_eval=self.fets)
        return fe_grid

    #--------------------------------------------------------------------------------------
    # Tracers
    #--------------------------------------------------------------------------------------
    rt_list = Property(depends_on='+input')
    @cached_property
    def _get_rt_list(self):
        '''Prepare the list of tracers
        '''
        right_dof = self.fe_grid[-1, -1].dofs[0, 0, 0]

        eps_app = RTraceDomainListField(name='Strain' ,
                                         position='int_pnts',
                                         var='eps_app',
                                         warp=False)

        damage = RTraceDomainListField(name='Damage' ,
                                        position='int_pnts',
                                      var='omega',
                                        warp=False)

        disp = RTraceDomainListField(name='Displacement' ,
                                         position='int_pnts',
                                      var='u',
                                      warp=False)

        sig_app = RTraceDomainListField(name='Stress' ,
                                         position='int_pnts',
                                         var='sig_app')

        rt_fu = RTDofGraph(name='Fi,right over u_right (iteration)' ,
                             var_y='F_int', idx_y=right_dof,
                             var_x='U_k', idx_x=right_dof)

        return [ rt_fu, eps_app, damage, sig_app, disp ]

    def plot_tracers(self):

        rt_fu, eps_app, damage, sig_app, disp = self.rt_list

        p.subplot(323)

        p.xlabel('control displacement [m]')
        p.ylabel('load [N]')

        rt_fu.refresh()
        rt_fu.trace.plot(p, 'o-')

        disp = disp.subfields[0]
        xdata = disp.vtk_X[:, 0]
        ydata = disp.field_arr[:, 0]
        idata = argsort(xdata)

        p.subplot(325)
        p.plot(xdata[idata], ydata[idata], 'o-')
        p.xlabel('bar axis [m]')
        p.ylabel('displacement [m]')

        eps = eps_app.subfields[0]
        xdata = eps.vtk_X[:, 0]
        ydata = eps.field_arr[:, 0, 0]
        idata = argsort(xdata)

        p.subplot(322)
        p.plot(xdata[idata], ydata[idata], 'o-')
        p.ylim(ymin=0)
        p.xlabel('bar axis [m]')
        p.ylabel('strain [-]')

        damage = damage.subfields[0]
        xdata = damage.vtk_X[:, 0]
        ydata = damage.field_arr[:]
        idata = argsort(xdata)

        p.subplot(324)
        p.plot(xdata[idata], ydata[idata], 'o-')
        p.ylim(ymax=1.0, ymin=0)
        p.xlabel('bar axis [m]')
        p.ylabel('damage [-]')

        sig = sig_app.subfields[0]
        xdata = sig.vtk_X[:, 0]
        ydata = sig.field_arr[:, 0, 0]
        idata = argsort(xdata)
        ymax = max(ydata)

        p.subplot(326)
        p.plot(xdata[idata], ydata[idata], 'o-')
        p.ylim(ymin=0, ymax=1.2 * ymax)
        p.xlabel('bar axis [m]')
        p.ylabel('stress [N]')

    bc_list = Property(depends_on='+input')
    @cached_property
    def _get_bc_list(self):
        '''List of boundary concditions
        '''
        right_dof = self.fe_grid[-1, -1].dofs[0, 0, 0]

        bcond_list = [ BCDof(var='u', dof=0, value=0.),
                      BCDof(var='u', dof=right_dof, value=self.final_displ,
                             time_function=self.time_function
                             ) ]
        return bcond_list

    def eval(self):
        '''Run the time loop.
        '''
        #
        avg_processor = None
        if self.avg_radius > 0.0:
            avg_processor = RTNonlocalAvg(sd=self.fe_domain,
                                           avg_fn=QuarticAF(radius=self.avg_radius,
                                                               correction=True))

        ts = TS(u_processor=avg_processor,
                 dof_resultants=True,
                 sdomain=self.fe_domain,
                 bcond_list=self.bc_list,
                 rtrace_list=self.rt_list
                )

        # Add the time-loop control
        tloop = TLoop(tstepper=ts, KMAX=300, tolerance=1e-8,
                       debug=False,
                       verbose_iteration=False,
                       verbose_time=False,
                       tline=TLine(min=0.0, step=self.step_size, max=1.0))

        tloop.eval()

        tloop.accept_time_step()

        self.plot_time_function()
        self.plot_tracers()
Example #28
0
class HeadViewController(HasTraits):
    """Set head views for the given coordinate system.

    Parameters
    ----------
    system : 'RAS' | 'ALS' | 'ARI'
        Coordinate system described as initials for directions associated with
        the x, y, and z axes. Relevant terms are: Anterior, Right, Left,
        Superior, Inferior.
    """

    system = Enum("RAS",
                  "ALS",
                  "ARI",
                  desc="Coordinate system: directions of "
                  "the x, y, and z axis.")

    right = Button()
    front = Button()
    left = Button()
    top = Button()
    interaction = Enum('Trackball', 'Terrain')

    scale = Float(0.16)

    scene = Instance(MlabSceneModel)

    view = View(
        VGrid('0',
              'top',
              '0',
              Item('scale', label='Scale', show_label=True),
              'right',
              'front',
              'left',
              'interaction',
              show_labels=False,
              columns=4))

    @on_trait_change('scene.activated')
    def _init_view(self):
        self.scene.parallel_projection = True
        self._trackball_interactor = None

        # apparently scene,activated happens several times
        if self.scene.renderer:
            self.sync_trait('scale', self.scene.camera, 'parallel_scale')
            # and apparently this does not happen by default:
            self.on_trait_change(self.scene.render, 'scale')

    @on_trait_change('interaction')
    def on_set_interaction(self, _, interaction):
        if self.scene is None:
            return
        if interaction == 'Terrain':
            # Ensure we're in the correct orientatino for the
            # InteractorStyleTerrain to have the correct "up"
            if self._trackball_interactor is None:
                self._trackball_interactor = \
                    self.scene.interactor.interactor_style
            self.on_set_view('front', '')
            self.scene.mlab.draw()
            self.scene.interactor.interactor_style = \
                tvtk.InteractorStyleTerrain()
            self.on_set_view('front', '')
            self.scene.mlab.draw()
        else:  # interaction == 'trackball'
            self.scene.interactor.interactor_style = self._trackball_interactor

    @on_trait_change('top,left,right,front')
    def on_set_view(self, view, _):
        if self.scene is None:
            return

        system = self.system
        kwargs = dict(ALS=dict(front=(0, 90, -90),
                               left=(90, 90, 180),
                               right=(-90, 90, 0),
                               top=(0, 0, -90)),
                      RAS=dict(front=(90., 90., 180),
                               left=(180, 90, 90),
                               right=(0., 90, 270),
                               top=(90, 0, 180)),
                      ARI=dict(front=(0, 90, 90),
                               left=(-90, 90, 180),
                               right=(90, 90, 0),
                               top=(0, 180, 90)))
        if system not in kwargs:
            raise ValueError("Invalid system: %r" % system)
        if view not in kwargs[system]:
            raise ValueError("Invalid view: %r" % view)
        kwargs = dict(
            zip(('azimuth', 'elevation', 'roll'), kwargs[system][view]))
        with SilenceStdout():
            self.scene.mlab.view(distance=None,
                                 reset_roll=True,
                                 figure=self.scene.mayavi_scene,
                                 **kwargs)
Example #29
0
class Degasser(MachineVisionManager, ExecuteMixin):
    _period = 0.05
    crop_width = Int(5)
    crop_height = Int(5)

    _test_lumens = Float(100)
    _test_duration = Int(10)
    _test_graph = Instance(StackedGraph)
    _test_image = Instance(MVImage)
    _testing = False

    pid = Instance(PID, ())
    _detector = Instance(LumenDetector)

    def degas(self, lumens, duration):
        """
            degas for duration trying to maintain
            lumens
        """
        if self.laser_manager:
            self.laser_manager.fiber_light.power_off()

        g = self._make_graph(lumens, duration)
        if self._testing:
            self._test_graph = g
        else:
            self.laser_manager.auxilary_graph = g.plotcontainer

        cw, ch = 2 * self.crop_width * self.pxpermm, 2 * self.crop_height * self.pxpermm

        # if not cw % 5 == 0:
        #     cw += cw % 5
        # if not ch % 5 == 0:
        #     ch += ch % 5
        #
        # cw, ch = 200, 200

        im = MVImage()
        im.setup_images(1, (cw, ch))
        if self._testing:
            self._test_image = im
        else:
            self.view_image(im)

        self._detector = LumenDetector()
        dt = self._period

        pid = self.pid
        st = time.time()
        i = 0
        while 1:
            ct = time.time()
            tt = ct - st
            if not self.isAlive():
                break

            cl = self._get_current_lumens(im, cw, ch)

            err = lumens - cl
            out = pid.get_value(err, dt)
            g.add_data(((tt, out), (tt, err), (tt, cl)))

            self._set_power(out, i)

            if tt > duration:
                break
            et = time.time() - ct
            time.sleep(max(0, dt - et))

            i += 1
            if i > 1e6:
                i = 0

        if self.laser_manager:
            self.laser_manager.fiber_light.power_on()

        self.executing = False

    def _set_power(self, pwr, cnt):
        if self.laser_manager:
            self.laser_manager.set_laser_power(pwr, verbose=cnt == 0)

    def _get_current_lumens(self, im, cw, ch):
        src = self.new_image_frame()
        if src:
            src = self._crop_image(src, cw, ch)
        else:
            src = random.random((ch, cw)) * 255
            src = src.astype('uint8')
        src, v = self._detector.get_value(src)
        im.set_image(src)
        return v

    def _make_graph(self, lumens, duration):
        g = StackedGraph(container_dict=dict(stack_order='top_to_bottom'))
        g.new_plot(ytitle='Output (W)')
        g.new_series()
        g.new_plot(ytitle='Residual')
        g.new_series(plotid=1)
        g.new_plot(ytitle='Lumens', xtitle='time (s)')
        g.new_series(plotid=2)

        g.add_horizontal_rule(lumens, plotid=2)
        g.set_x_limits(0, duration * 1.1)
        return g

    def _do_execute(self):

        self.debug('starting test degas {} {}'.format(self._test_lumens,
                                                      self._test_duration))
        self._testing = True
        self.degas(self._test_lumens, self._test_duration)

    def traits_view(self):
        v = View(UItem('execute',
                       editor=ButtonEditor(label_value='execute_label')),
                 HGroup(Item('_test_lumens'), Item('_test_duration')),
                 UItem('pid', style='custom'),
                 HGroup(UItem('_test_graph', height=400, style='custom'),
                        UItem('_test_image', style='custom')),
                 resizable=True)
        return v
Example #30
0
class BendingTestModel(Simulator, Vis2D):

    #=========================================================================
    # Tree node attributes
    #=========================================================================
    node_name = 'bending test simulation'

    tree_node_list = List([])

    def _tree_node_list_default(self):

        return [
            self.tline, self.mats_eval, self.cross_section, self.geometry,
            self.fixed_left_bc, self.fixed_right_bc, self.control_bc
        ]

    def _update_node_list(self):
        self.tree_node_list = [
            self.tline, self.mats_eval, self.cross_section, self.geometry,
            self.fixed_left_bc, self.fixed_right_bc, self.control_bc
        ]

    #=========================================================================
    # Test setup parameters
    #=========================================================================
    loading_scenario = Instance(LoadingScenario)

    def _loading_scenario_default(self):
        return LoadingScenario()

    cross_section = Instance(CrossSection)

    def _cross_section_default(self):
        return CrossSection()

    geometry = Instance(Geometry)

    def _geometry_default(self):
        return Geometry()

    #=========================================================================
    # Discretization
    #=========================================================================
    n_e_x = Int(20, auto_set=False, enter_set=True)
    n_e_y = Int(8, auto_set=False, enter_set=True)
    n_e_z = Int(1, auto_set=False, enter_set=True)

    w_max = Float(-50, BC=True, auto_set=False, enter_set=True)

    controlled_elem = Property(Int)

    def _get_controlled_elem(self):
        return int(self.n_e_x / 2)

    #=========================================================================
    # Material model
    #=========================================================================
    mats_eval_type = Trait('microplane damage (eeq)', {
        'elastic': MATS3DElastic,
        'microplane damage (eeq)': MATS3DMplDamageEEQ,
        'microplane damage (odf)': MATS3DMplDamageODF,
        'scalar damage': MATS3DScalarDamage,
    },
                           MAT=True)

    @on_trait_change('mats_eval_type')
    def _set_mats_eval(self):
        self.mats_eval = self.mats_eval_type_()

    @on_trait_change('BC,MAT,MESH')
    def reset_node_list(self):
        self._update_node_list()

    mats_eval = Instance(IMATSEval, MAT=True)
    '''Material model'''

    def _mats_eval_default(self):
        return self.mats_eval_type_()

    material = Property

    def _get_material(self):
        return self.mats_eval

    #=========================================================================
    # Finite element type
    #=========================================================================
    fets_eval = Property(Instance(FETS3D8H), depends_on='CS,MAT')
    '''Finite element time stepper implementing the corrector
    predictor operators at the element level'''

    @cached_property
    def _get_fets_eval(self):
        return FETS3D8H()

    bc = Property(Instance(BCondMngr), depends_on='GEO,CS,BC,MAT,MESH')
    '''Boundary condition manager
    '''

    @cached_property
    def _get_bc(self):
        return [
            self.fixed_left_bc, self.fixed_right_bc, self.fixed_middle_bc,
            self.control_bc
        ]

    fixed_left_bc = Property(depends_on='CS, BC,GEO,MESH')
    '''Foxed boundary condition'''

    @cached_property
    def _get_fixed_left_bc(self):
        return BCSlice(slice=self.fe_grid[0, 0, :, 0, 0, :],
                       var='u',
                       dims=[1],
                       value=0)

    fixed_right_bc = Property(depends_on='CS,BC,GEO,MESH')
    '''Foxed boundary condition'''

    @cached_property
    def _get_fixed_right_bc(self):
        return BCSlice(slice=self.fe_grid[-1, 0, :, -1, 0, :],
                       var='u',
                       dims=[1],
                       value=0)

    fixed_middle_bc = Property(depends_on='CS,BC,GEO,MESH')
    '''Foxed boundary condition'''

    @cached_property
    def _get_fixed_middle_bc(self):
        return BCSlice(slice=self.fe_grid[self.controlled_elem, -1, :, 0,
                                          -1, :],
                       var='u',
                       dims=[0],
                       value=0)

    control_bc = Property(depends_on='CS,BC,GEO,MESH')
    '''Control boundary condition - make it accessible directly
    for the visualization adapter as property
    '''

    @cached_property
    def _get_control_bc(self):
        return BCSlice(slice=self.fe_grid[self.controlled_elem, -1, :, :,
                                          -1, :],
                       var='u',
                       dims=[1],
                       value=-self.w_max)

    xdomain = Property(depends_on='CS,MAT,GEO,MESH,FE')
    '''Discretization object.
    '''

    @cached_property
    def _get_xdomain(self):
        cs = self.cross_section
        geo = self.geometry
        dgrid = XDomainFEGrid(dim_u=3,
                              coord_max=(geo.L, cs.h, cs.b),
                              shape=(self.n_e_x, self.n_e_y, self.n_e_z),
                              fets=self.fets_eval)
        return dgrid
        L = self.geometry.L / 2.0
        L_c = self.geometry.L_c
        x_x, _, _ = dgrid.mesh.geo_grid.point_x_grid
        L_1 = x_x[1, 0]
        d_L = L_c - L_1
        x_x[1:, :, :] += d_L * (L - x_x[1:, :]) / (L - L_1)
        return dgrid

    fe_grid = Property

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

    domains = Property(depends_on=itags_str)

    @cached_property
    def _get_domains(self):
        return [(self.xdomain, self.mats_eval)]

    k_max = Int(200, ALG=True)
    acc = Float(1e-4, ALG=True)

    @on_trait_change('ALG')
    def _reset_tloop(self):
        k_max = self.k_max
        acc = self.acc
        self.tloop.trait_set(
            k_max=k_max,
            acc=acc,
        )

    def get_PW(self):
        record_dofs = self.fe_grid[self.controlled_elem, -1, :, :,
                                   -1, :].dofs[:, :, 1].flatten()
        Fd_int_t = np.array(self.tloop.F_int_record)
        Ud_t = np.array(self.tloop.U_record)
        F_int_t = -np.sum(Fd_int_t[:, record_dofs], axis=1)
        U_t = -Ud_t[:, record_dofs[0]]
        return F_int_t, U_t

    viz2d_classes = {
        'F-w': Viz2DForceDeflectionX,
        'load function': Viz2DLoadControlFunction,
    }

    traits_view = View(Item('mats_eval_type'), )

    tree_view = traits_view