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
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)
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]
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
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
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
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:
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)
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)
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)
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)
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 =============================================
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
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)
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')
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)
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
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
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])
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
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
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
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()
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)
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
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