class ShearCrack(BMCSLeafNode, Vis2D): '''Class representing the geometry of a crack using a piecewise linear representation ''' node_name = 'shear crack geometry' x = tr.Array(np.float_, value=[0.2, 0.15, 0.1], GEO=True) y = tr.Array(np.float_, value=[0.0, 0.15, 0.2], GEO=True) viz2d_classes = { 'shear_crack': ShearCrackViz2D } tree_view = View( HGroup( Include('actions') ) )
class short_rate(trapi.HasTraits): name = trapi.Str rate = trapi.Float time_list = trapi.Array(dtype=np.float, shape=(1, 5)) disc_list = trapi.Array(dtype=np.float, shape=(1, 5)) update = trapi.Button def _update_fired(self): self.disc_list = np.exp(-self.rate * self.time_list) v = trui.View(trui.Group(trui.Item(name='name'), trui.Item(name='rate'), trui.Item(name='time_list', label='Insert Time List Here'), trui.Item('update', show_label=False), trui.Item(name='disc_list', label='Press Update for Factors'), show_border=True, label='Calculate Discount Factors'), buttons=[trui.OKButton, trui.CancelButton], resizable=True)
class TimeFunction(bu.InteractiveModel): name = 'Time function' n_i = bu.Int(0, input=True, desc='number of data points between data points') '''Number of steps between two values ''' values = tr.Array(np.float_, value=[0, 1]) '''Values of the time function. ''' ipw_view = bu.View(bu.Item('n_i', latex=r'n_i', minmax=(0, 100))) t_step = tr.Property(tr.Float, depends_on='+input') '''Step size. ''' @tr.cached_property def _get_t_step(self): n_values = len(self.values) return (self.n_i + 1) * n_values tf = tr.Property(depends_on='data_points') def _get_tf(self): n_values = len(self.values) t_values = np.linspace(0, 1, n_values) return interpolate.interp1d(t_values, self.values) def __call__(self, arg): return self.tf(arg) def update_plot(self, ax): n_values = len(self.values) t = np.linspace(0, 1, (self.n_i) * (n_values - 1) + n_values) v = self.tf(t) ax.plot(t, v, '-o')
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self.update_value, 'index') self.on_trait_change(self.set_index_from_value, 'value') self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isinstance(value, float): return self.value2index(value) else: return value def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isinstance(slice_, float): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isinstance(step, float): step = int(round(step / self.scale)) if isinstance(start, float): try: start = v2i(start) except ValueError: # The value is below the axis limits # we slice from the start. start = None if isinstance(stop, float): try: stop = v2i(stop) except ValueError: # The value is above the axes limits # we slice up to the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") my_slice = slice(start, stop, step) if start is None: if step > 0 or step is None: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): name = (self.name if self.name is not t.Undefined else ("Unnamed " + ordinal(self.index_in_axes_manager))) return name def __repr__(self): text = '<%s axis, size: %i' % ( self._get_name(), self.size, ) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text def __str__(self): return self._get_name() + " axis" def connect(self, f, trait='value'): self.on_trait_change(f, trait) def disconnect(self, f, trait='value'): self.on_trait_change(f, trait, remove=True) def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = (self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'index_in_array': self.index_in_array, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def update_value(self): self.value = self.axis[self.index] def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : float Returns ------- int Raises ------ ValueError if value is out of the axis limits. """ if value is None: return None else: index = int(rounding((value - self.offset) / self.scale)) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): return self.axis[index] def set_index_from_value(self, value): self.index = self.value2index(value) # If the value is above the limits we must correct the value if self.continuous_value is False: self.value = self.index2value(self.index) def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale traits_view = \ tui.View( tui.Group( tui.Group( tui.Item(name='name'), tui.Item(name='size', style='readonly'), tui.Item(name='index_in_array', style='readonly'), tui.Item(name='index'), tui.Item(name='value', style='readonly'), tui.Item(name='units'), tui.Item(name='navigate', label = 'navigate'), show_border = True,), tui.Group( tui.Item(name='scale'), tui.Item(name='offset'), label = 'Calibration', show_border = True,), label = "Data Axis properties", show_border = True,), title = 'Axis configuration', )
class ShmTrackingInterface(T.HasStrictTraits): dwi_images = T.DelegatesTo('all_inputs') all_inputs = T.Instance(InputData, args=()) min_signal = T.DelegatesTo('all_inputs') seed_roi = nifti_file seed_density = T.Array(dtype='int', shape=(3, ), value=[1, 1, 1]) smoothing_kernel_type = T.Enum(None, all_kernels.keys()) smoothing_kernel = T.Instance(T.HasTraits) @T.on_trait_change('smoothing_kernel_type') def set_smoothing_kernel(self): if self.smoothing_kernel_type is not None: kernel_factory = all_kernels[self.smoothing_kernel_type] self.smoothing_kernel = kernel_factory() else: self.smoothing_kernel = None interpolator = T.Enum('NearestNeighbor', all_interpolators.keys()) model_type = T.Enum('SlowAdcOpdf', all_shmodels.keys()) sh_order = T.Int(4) Lambda = T.Float(0, desc="Smoothing on the odf") sphere_coverage = T.Int(5) min_peak_spacing = T.Range(0., 1, np.sqrt(.5), desc="as a dot product") min_relative_peak = T.Range(0., 1, .25) probabilistic = T.Bool(False, label='Probabilistic (Residual Bootstrap)') bootstrap_input = T.Bool(False) bootstrap_vector = T.Array(dtype='int', value=[]) # integrator = Enum('Boundry', all_integrators.keys()) seed_largest_peak = T.Bool(False, desc="Ignore sub-peaks and start follow " "the largest peak at each seed") start_direction = T.Array(dtype='float', shape=(3, ), value=[0, 0, 1], desc="Prefered direction from seeds when " "multiple directions are available. " "(Mostly) doesn't matter when 'seed " "largest peak' and 'track two directions' " "are both True", label="Start direction (RAS)") track_two_directions = T.Bool(False) fa_threshold = T.Float(1.0) max_turn_angle = T.Range(0., 90, 0) stop_on_target = T.Bool(False) targets = T.List(nifti_file, []) # will be set later voxel_size = T.Array(dtype='float', shape=(3, )) affine = T.Array(dtype='float', shape=(4, 4)) shape = T.Tuple((0, 0, 0)) # set for io save_streamlines_to = T.File('') save_counts_to = nifti_file # io methods def save_streamlines(self, streamlines, save_streamlines_to): trk_hdr = empty_header() voxel_order = orientation_to_string(nib.io_orientation(self.affine)) trk_hdr['voxel_order'] = voxel_order trk_hdr['voxel_size'] = self.voxel_size trk_hdr['vox_to_ras'] = self.affine trk_hdr['dim'] = self.shape trk_tracks = ((ii, None, None) for ii in streamlines) write(save_streamlines_to, trk_tracks, trk_hdr) pickle.dump(self, open(save_streamlines_to + '.p', 'wb')) def save_counts(self, streamlines, save_counts_to): counts = density_map(streamlines, self.shape, self.voxel_size) if counts.max() < 2**15: counts = counts.astype('int16') nib.save(nib.Nifti1Image(counts, self.affine), save_counts_to) # tracking methods def track_shm(self, debug=False): if self.sphere_coverage > 7 or self.sphere_coverage < 1: raise ValueError("sphere coverage must be between 1 and 7") verts, edges, faces = create_half_unit_sphere(self.sphere_coverage) verts, pot = disperse_charges(verts, 10, .3) data, voxel_size, affine, fa, bvec, bval = self.all_inputs.read_data() self.voxel_size = voxel_size self.affine = affine self.shape = fa.shape model_type = all_shmodels[self.model_type] model = model_type(self.sh_order, bval, bvec, self.Lambda) model.set_sampling_points(verts, edges) data = np.asarray(data, dtype='float', order='C') if self.smoothing_kernel is not None: kernel = self.smoothing_kernel.get_kernel() convolve(data, kernel, out=data) normalize_data(data, bval, self.min_signal, out=data) dmin = data.min() data = data[..., lazy_index(bval > 0)] if self.bootstrap_input: if self.bootstrap_vector.size == 0: n = data.shape[-1] self.bootstrap_vector = np.random.randint(n, size=n) H = hat(model.B) R = lcr_matrix(H) data = bootstrap_data_array(data, H, R, self.bootstrap_vector) data.clip(dmin, out=data) mask = fa > self.fa_threshold targets = [read_roi(tgt, shape=self.shape) for tgt in self.targets] if self.stop_on_target: for target_mask in targets: mask = mask & ~target_mask seed_mask = read_roi(self.seed_roi, shape=self.shape) seeds = seeds_from_mask(seed_mask, self.seed_density, voxel_size) if ((self.interpolator == 'NearestNeighbor' and not self.probabilistic and not debug)): using_optimze = True peak_finder = NND_ClosestPeakSelector(model, data, mask, voxel_size) else: using_optimze = False interpolator_type = all_interpolators[self.interpolator] interpolator = interpolator_type(data, voxel_size, mask) peak_finder = ClosestPeakSelector(model, interpolator) # Set peak_finder parameters for start steps peak_finder.angle_limit = 90 model.peak_spacing = self.min_peak_spacing if self.seed_largest_peak: model.min_relative_peak = 1 else: model.min_relative_peak = self.min_relative_peak data_ornt = nib.io_orientation(self.affine) best_start = reorient_vectors(self.start_direction, 'ras', data_ornt) start_steps = closest_start(seeds, peak_finder, best_start) if self.probabilistic: interpolator = ResidualBootstrapWrapper(interpolator, model.B, min_signal=dmin) peak_finder = ClosestPeakSelector(model, interpolator) elif using_optimze and self.seed_largest_peak: peak_finder.reset_cache() # Reset peak_finder parameters for tracking peak_finder.angle_limit = self.max_turn_angle model.peak_spacing = self.min_peak_spacing model.min_relative_peak = self.min_relative_peak integrator = BoundryIntegrator(voxel_size, overstep=.1) streamlines = generate_streamlines(peak_finder, integrator, seeds, start_steps) if self.track_two_directions: start_steps = -start_steps streamlinesB = generate_streamlines(peak_finder, integrator, seeds, start_steps) streamlines = merge_streamlines(streamlines, streamlinesB) for target_mask in targets: streamlines = target(streamlines, target_mask, voxel_size) return streamlines
class FETS2DMITC(FETSEval): r'''MITC3 shell finite element: See https://www.sesamx.io/blog/standard_linear_triangular_shell_element/ See http://dx.doi.org/10.1016/j.compstruc.2014.02.005 ''' # vtk_r = tr.Array(np.float_, value=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) # vtk_cells = [[0, 1, 2]] # vtk_cell_types = 'Triangle' # vtk_cell = [0, 1, 2] # vtk_cell_type = 'Triangle' # vtk_expand_operator = tr.Array(np.float_, value=DELTA23_ab) # ========================================================================= # Surface integrals using numerical integration # ========================================================================= # j is gauss point index, p is point coords in natural coords (zeta_1, zeta_2, zeta_3) eta_jp = tr.Array('float_') r'''Integration points within a triangle. ''' def _eta_jp_default(self): # We applay the 7-point Gauss integration to integrate exactly on the plane defined by rr and ss # [r, s, t] at each Gauss point (r, s, t) are natural coords in the shell element r,s in plane # and t along thickness # 2 Gauss points along thickness t # 7 Gauss points on the plane of the element return np.array( [[1. / 3., 1. / 3., 1. / 3.]], dtype='f' ) # should be return np.array([[1. / 3., 1. / 3., 0]], dtype='f') w_m = tr.Array('float_') r'''Weight factors for numerical integration.''' def _w_m_default(self): print('w_m called!!') return np.array([1], dtype='f') # TODO, different node thickness in each node according to the original # implementation can be easily integrated, but here the same thickness # is used for simplicity. a = tr.Float(1.0, label='thickness') n_m = tr.Property(depends_on='w_m') r'''Number of integration points.''' @tr.cached_property def _get_n_m(self): return len(self.w_m) n_nodal_dofs = tr.Int(5) dh_imr = tr.Property(depends_on='eta_jp') r'''Derivatives of the shape functions in the integration points.''' @tr.cached_property def _get_dh_imr(self): # Same for all Gauss points dh_ri = np.array( [ [1, 0, -1], # dh1/d_r, dh2/d_r, dh3/d_r [0, 1, -1], # dh1/d_s, dh2/d_s, dh3/d_s [0, 0, 0] ], # dh1/d_t, dh2/d_t, dh3/d_t dtype=np.float_) dh_mri = np.tile(dh_ri, (self.n_m, 1, 1)) # dh_mri = np.flip(dh_mri, 2) print('dh_imr called!') return np.einsum('mri->imr', dh_mri) dht_imr = tr.Property(depends_on='eta_jp') r'''Derivatives of the (shape functions * t) in the integration points. ''' @tr.cached_property def _get_dht_imr(self): # m: gauss points, r: r, s, t, and i: h1, h2, h3 eta_jp = self.eta_jp dht_mri = np.array( [ [ [t, 0, -t], # (t*dh1)/d_r, (t*dh2)/d_r, (t*dh3)/d_r [0, t, -t], # (t*dh1)/d_s, (t*dh2)/d_s, (t*dh3)/d_s [r, s, 1 - r - s] ] # (t*dh1)/d_t, (t*dh2)/d_t, (t*dh3)/d_t for r, s, t in zip(eta_jp[:, 0], eta_jp[:, 1], eta_jp[:, 2]) ], dtype=np.float_) # dht_mri = np.flip(dht_mri, 2) return np.einsum('mri->imr', dht_mri)
class ModelInteract(tr.HasTraits): models = tr.List([ ]) py_vars = tr.List(tr.Str) map_py2sp = tr.Dict d = tr.Float(0.03, GEO=True) h = tr.Float(0.8, GEO=True) # define the free parameters as traits with default, min and max values w_max = tr.Float(1.0) t = tr.Float(0.0001, min=1e-5, max=1) tau = tr.Float(0.5, interact=True) L_b = tr.Float(200, interact=True) E_f = tr.Float(100000, interact=True) A_f = tr.Float(20, interact=True) p = tr.Float(40, interact=True) E_m = tr.Float(26000, interact=True) A_m = tr.Float(100, interact=True) n_steps = tr.Int(50) sliders = tr.Property @tr.cached_property def _get_sliders(self): traits = self.traits(interact=True) vals = self.trait_get(interact=True) slider_names = self.py_vars[1:] max_vals = {name: getattr(traits, 'max', vals[name] * 2) for name in slider_names} t_slider = {'t': ipw.FloatSlider(1e-5, min=1e-5, max=1, step=0.05, description=r'\(t\)')} param_sliders = {name: ipw.FloatSlider(value=vals[name], min=1e-5, max=max_vals[name], step=max_vals[name] / self.n_steps, description=r'\(%s\)' % self.map_py2sp[name].name) for (name, _) in traits.items() } t_slider.update(param_sliders) return t_slider w_range = tr.Property(tr.Array(np.float_), depends_on='w_max') @tr.cached_property def _get_w_range(self): return np.linspace(0, self.w_max, 50) x_range = tr.Property(tr.Array(np.float_), depends_on='L_b') @tr.cached_property def _get_x_range(self): return np.linspace(-self.L_b, 0, 100) model_plots = tr.Property(tr.List) @tr.cached_property def _get_model_plots(self): return [PlotModel(itr=self, model=m) for m in self.models] def init_fields(self): self.fig, ((self.ax_po, self.ax_u), (self.ax_eps, self.ax_tau)) = plt.subplots( 2, 2, figsize=(9, 5), tight_layout=True ) values = self.trait_get(interact=True) params = list(values[py_var] for py_var in self.py_vars[1:]) for mp in self.model_plots: mp.init_fields(*params) mp.init_Pw(*params) self.ax_po.set_xlim(0, self.w_max * 1.05) def clear_fields(self): clear_plot(self.ax_po, self.ax_u, self.ax_eps, self.ax_tau) def update_fields(self, t, **values): w = t * self.w_max self.trait_set(**values) params = list(values[py_var] for py_var in self.py_vars[1:]) L_b = self.L_b self.clear_fields() for mp in self.model_plots: mp.update_fields(w, *params) mp.update_Pw(w, *params) P_max = np.max(np.array([m.P_max for m in self.model_plots])) self.ax_po.set_ylim(0, P_max * 1.05) self.ax_po.set_xlim(0, self.w_max * 1.05) u_min = np.min(np.array([m.u_min for m in self.model_plots])) u_max = np.max(np.array([m.u_max for m in self.model_plots] + [1])) self.ax_u.set_ylim(u_min, u_max * 1.1) self.ax_u.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b) eps_min = np.min(np.array([m.eps_min for m in self.model_plots])) eps_max = np.max(np.array([m.eps_max for m in self.model_plots])) self.ax_eps.set_ylim(eps_min, eps_max * 1.1) self.ax_eps.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b) self.ax_tau.set_ylim(0, self.tau * 1.1) self.ax_tau.set_xlim(xmin=-1.05 * L_b, xmax=0.05 * L_b) self.fig.canvas.draw_idle() def set_w_max_fields(self, w_max): self.w_max = w_max values = {name: slider.value for name, slider in self.sliders.items()} self.update_fields(**values) def interact_fields(self): self.init_fields() self.on_w_max_change = self.update_fields sliders = self.sliders out = ipw.interactive_output(self.update_fields, sliders) self.widget_layout(out) #========================================================================= # Interaction on the pull-out curve spatial plot #========================================================================= def init_geometry(self): self.fig, (self.ax_po, self.ax_geo) = plt.subplots( 1, 2, figsize=(8, 3.4)) # , tight_layout=True) values = self.trait_get(interact=True) params = list(values[py_var] for py_var in self.py_vars[1:]) h = self.h x_C = np.array([[-1, 0], [0, 0], [0, h], [-1, h]], dtype=np.float_) self.line_C, = self.ax_geo.fill(*x_C.T, color='gray', alpha=0.3) for mp in self.model_plots: mp.line_aw, = self.ax_geo.fill([], [], color='white', alpha=1) mp.line_F, = self.ax_geo.fill([], [], color='black', alpha=0.8) mp.line_F0, = self.ax_geo.fill([], [], color='white', alpha=1) mp.init_Pw(*params) self.ax_po.set_xlim(0, self.w_max * 1.05) def clear_geometry(self): clear_plot(self.ax_po, self.ax_geo) def update_geometry(self, t, **values): w = t * self.w_max self.clear_geometry() self.trait_set(**values) params = list(values[py_var] for py_var in self.py_vars[1:]) h = self.h d = self.d L_b = self.L_b f_top = h / 2 + d / 2 f_bot = h / 2 - d / 2 self.ax_geo.set_xlim( xmin=-1.05 * L_b, xmax=max(0.05 * L_b, 1.1 * self.w_max)) x_C = np.array([[-L_b, 0], [0, 0], [0, h], [-L_b, h]], dtype=np.float_) self.line_C.set_xy(x_C) for mp in self.model_plots: a_val = mp.model.get_aw_pull(w, *params) width = d * 0.5 x_a = np.array([[a_val, f_bot - width], [0, f_bot - width], [0, f_top + width], [a_val, f_top + width]], dtype=np.float_) mp.line_aw.set_xy(x_a) w_L_b = mp.model.get_w_L_b(w, *params) x_F = np.array([[-L_b + w_L_b, f_bot], [w, f_bot], [w, f_top], [-L_b + w_L_b, f_top]], dtype=np.float_) mp.line_F.set_xy(x_F) x_F0 = np.array([[-L_b, f_bot], [-L_b + w_L_b, f_bot], [-L_b + w_L_b, f_top], [-L_b, f_top]], dtype=np.float_) mp.line_F0.set_xy(x_F0) mp.update_Pw(w, *params) P_max = np.max(np.array([mp.P_max for mp in self.model_plots])) self.ax_po.set_ylim(0, P_max * 1.1) self.ax_po.set_xlim(0, self.w_max * 1.05) self.fig.canvas.draw_idle() def set_w_max(self, w_max): self.w_max = w_max values = {name: slider.value for name, slider in self.sliders.items()} self.on_w_max_change(**values) on_w_max_change = tr.Callable def interact_geometry(self): self.init_geometry() self.on_w_max_change = self.update_geometry sliders = self.sliders out = ipw.interactive_output(self.update_geometry, sliders) self.widget_layout(out) def widget_layout(self, out): sliders = self.sliders layout = ipw.Layout(grid_template_columns='1fr 1fr') param_sliders_list = [sliders[py_var] for py_var in self.py_vars[1:]] t_slider = sliders['t'] grid = ipw.GridBox(param_sliders_list, layout=layout) w_max_text = ipw.FloatText( value=self.w_max, description=r'w_max', disabled=False ) out_w_max = ipw.interactive_output(self.set_w_max, {'w_max': w_max_text}) hbox = ipw.HBox([t_slider, w_max_text]) box = ipw.VBox([hbox, grid, out, out_w_max]) display(box)
class TimeLoop(tr.HasStrictTraits): tline = tr.Instance(TLine) '''Time line object specifying the start, end, time step and current time ''' def _tline_default(self): return TLine(min=0.0, max=1.0, step=1.0) ts = tr.Instance(ITStepperEval) '''State object delivering the predictor and corrector ''' bc_mngr = tr.Instance(BCondMngr, ()) '''Boundary condition manager. ''' bc_list = tr.List([]) '''List of boundary conditions. ''' def _bc_list_changed(self): self.bc_mngr.bcond_list = self.bc_list step_tolerance = tr.Float(1e-8) '''Time step tolerance. ''' k_max = tr.Int(300) '''Maximum number of iterations. ''' tolerance = tr.Float(1e-3) '''Tolerance of the residuum norm. ''' t_n1 = tr.Float(0, input=True) '''Target time for the next increment. ''' t_n = tr.Float(0, input=True) '''Time of the last equilibrium state. ''' d_t = tr.Float(0, input=True) '''Current time increment size. ''' paused = tr.Bool(False) restart = tr.Bool(True) stop = tr.Bool(False) def init(self): self.stop = False if self.paused: self.paused = False return if self.restart: self.tline.val = 0 # self.setup() self.restart = False self.bc_mngr.setup(None) for rt in self.response_traces: rt.setup(self) algorithmic = tr.Bool(True) def eval(self): update_state = False K = SysMtxAssembly() self.bc_mngr.apply_essential(K) U_n = np.zeros((self.ts.mesh.n_dofs, ), dtype=np.float_) dU = np.copy(U_n) U_k = np.copy(U_n) F_ext = np.zeros_like(U_n) algorithmic = self.algorithmic pos_def = True while (self.t_n1 - self.tline.max) <= self.step_tolerance: print('current time %f' % self.t_n1, end=' ') self.d_t = self.tline.step k = 0 step_flag = 'predictor' while k < self.k_max: if self.stop: return U_n K.reset_mtx() K_arr, F_int, n_F_int = self.ts.get_corr_pred( U_k, dU, self.t_n, self.t_n1, update_state, algorithmic) if update_state: update_state = False K.sys_mtx_arrays.append(K_arr) F_ext[:] = 0.0 self.bc_mngr.apply(step_flag, None, K, F_ext, self.t_n, self.t_n1) R = F_ext - F_int K.apply_constraints(R) if n_F_int == 0.0: n_F_int = 1.0 norm = np.linalg.norm(R, ord=None) # / n_F_int if norm < self.tolerance: print('converged in %d iterations' % (k + 1)) update_state = True self.record_response(U_k, F_int, self.t_n1) break dU, pos_def = K.solve(check_pos_def=algorithmic) if algorithmic and not pos_def: algorithmic = False print('switched to secant') continue U_k += dU k += 1 step_flag = 'corrector' U_n = np.copy(U_k) self.t_n = self.t_n1 self.t_n1 = self.t_n + self.d_t self.tline.val = min(self.t_n, self.tline.max) return U_n write_dir = tr.Directory F_int_record = tr.List(tr.Array(np.float_)) U_record = tr.List(tr.Array(np.float_)) F_ext_record = tr.List(tr.Array(np.float_)) t_record = tr.List response_traces = tr.List() def record_response(self, U_k, F_int, t): self.F_int_record.append(np.copy(F_int)) self.U_record.append(np.copy(U_k)) self.t_record.append(self.t_n1) for rt in self.response_traces: rt.update(U_k, t) return def get_time_idx_arr(self, vot): '''Get the index corresponding to visual time ''' x = self.t_record idx = np.array(np.arange(len(x)), dtype=np.float_) t_idx = np.interp(vot, x, idx) return np.array(t_idx + 0.5, np.int_) def get_time_idx(self, vot): return int(self.get_time_idx_arr(vot))
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self.update_value, 'index') self.on_trait_change(self.set_index_from_value, 'value') self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isfloat(value): return self.value2index(value) else: return value def _get_array_slices(self, slice_): """Returns a slice to slice the corresponding data axis without changing the offset and scale of the DataAxis. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isfloat(slice_): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isfloat(step): step = int(round(step / self.scale)) if isfloat(start): try: start = v2i(start) except ValueError: # The value is below the axis limits # we slice from the start. start = None if isfloat(stop): try: stop = v2i(stop) except ValueError: # The value is above the axes limits # we slice up to the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") return slice(start, stop, step) def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value my_slice = self._get_array_slices(slice_) start, stop, step = my_slice.start, my_slice.stop, my_slice.step if start is None: if step > 0 or step is None: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): if self.name is t.Undefined: if self.axes_manager is None: name = "Unnamed" else: name = "Unnamed " + ordinal(self.index_in_axes_manager) else: name = self.name return name def __repr__(self): text = '<%s axis, size: %i' % (self._get_name(), self.size,) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text.encode('utf8') def __str__(self): return self._get_name() + " axis" def connect(self, f, trait='value'): self.on_trait_change(f, trait) def disconnect(self, f, trait='value'): self.on_trait_change(f, trait, remove=True) def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = ( self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def __copy__(self): return self.copy() def __deepcopy__(self, memo): cp = self.copy() return cp def update_value(self): self.value = self.axis[self.index] def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : number or numpy array Returns ------- index : integer or numpy array Raises ------ ValueError if any value is out of the axis limits. """ if value is None: return None if isinstance(value, np.ndarray): if rounding is round: rounding = np.round elif rounding is math.ceil: rounding = np.ceil elif rounding is math.floor: rounding = np.floor index = rounding((value - self.offset) / self.scale) if isinstance(value, np.ndarray): index = index.astype(int) if np.all(self.size > index) and np.all(index >= 0): return index else: raise ValueError("A value is out of the axis limits") else: index = int(index) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): if isinstance(index, np.ndarray): return self.axis[index.ravel()].reshape(index.shape) else: return self.axis[index] def set_index_from_value(self, value): self.index = self.value2index(value) # If the value is above the limits we must correct the value if self.continuous_value is False: self.value = self.index2value(self.index) def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale def value_range_to_indices(self, v1, v2): """Convert the given range to index range. When an out of the axis limits, the endpoint is used instead. Parameters ---------- v1, v2 : float The end points of the interval in the axis units. v2 must be greater than v1. """ if v1 > v2: raise ValueError("v2 must be greater than v1.") if v1 is not None and v1 > self.low_value and v1 <= self.high_value: i1 = self.value2index(v1) else: i1 = 0 if v2 is not None and v2 < self.high_value and v2 >= self.low_value: i2 = self.value2index(v2) else: i2 = self.size - 1 return i1, i2
class short_rate(trapi.HasTraits): name = trapi.Str rate = trapi.Float time_list = trapi.Array(dtype = np.float,shape = (5,)) def get_discount_factors(self): return np.exp(- self.rate * self.time_list)
class InputParameter(trapi.HasTraits): """Class the is used to input all of the user parameters through a guy""" tickers_input = trapi.Str time_windows_input = trapi.Array(trapi.Int, (1, nbr_max_time_windows)) data_source = trapi.Enum("Yahoo", "Bloomberg", "Telemaco") date_start = trapi.Date date_end = trapi.Date get_data_button = trapi.Button plot_chart_button = trapi.Button corr_data = pd.DataFrame correlpairs = trapi.List selected_correl_pair_indices = trapi.List corr_pairs_combinations = trapi.List v = trui.View(trui.HGroup(trui.Item(name='tickers_input', style='custom'), trui.VGroup( trui.Item(name='date_start'), trui.Item(name='date_end'), trui.Item(name='time_windows_input'), trui.Item(name='data_source'), trui.Item(name='get_data_button', label='Process Data', show_label=False), trui.Item('correlpairs', show_label=False, editor=correl_pair_editor), trui.Item(name='plot_chart_button', label='Plot Selected Data', show_label=False), ), show_border=True, label='Input Data'), resizable=True, title='Correlation Tool', height=screen_height, width=screen_width, icon='corr.png', image='corr.png') def _plot_chart_button_fired(self): """Method to plot the selected data""" # Read TableEditor to see what the user has chosen to data_to_plot = [] for i in range(0, len(self.correlpairs)): if i == len(self.correlpairs) - 1: pair_name = ['BASKET CORREL', 'BASKET CORREL'] else: pair_name = self.correlpairs[i].correl_pair.split('-') if self.correlpairs[i].time_window_1: data_to_plot.append( (pair_name[0].strip(), pair_name[1].strip(), self.time_windows_input[0][0])) if self.correlpairs[i].time_window_2: data_to_plot.append( (pair_name[0].strip(), pair_name[1].strip(), self.time_windows_input[0][1])) if self.correlpairs[i].time_window_3: data_to_plot.append( (pair_name[0].strip(), pair_name[1].strip(), self.time_windows_input[0][2])) if self.correlpairs[i].time_window_4: data_to_plot.append( (pair_name[0].strip(), pair_name[1].strip(), self.time_windows_input[0][3])) if self.correlpairs[i].time_window_5: data_to_plot.append( (pair_name[0].strip(), pair_name[1].strip(), self.time_windows_input[0][4])) # Plot pl.plot_data(self.corr_data[0], self.corr_data[1], data_to_plot) def check_data_retrieval_error(self, raw_data, tickers_list): """Check whether there was an error retrieving data""" # Check whether data was retrieved successfully: if self.data_source == 'Yahoo': if len(tickers_list) > 1: empty_col = [] for column_name in raw_data.columns: if raw_data[column_name].isna().all(): empty_col.append(column_name) raw_data[column_name].drop if empty_col: message( 'There was a problem loading data for the following underlyings:\n' + '\n'.join(empty_col)) return [x for x in tickers_list if x not in empty_col] else: return tickers_list else: return tickers_list elif self.data_source == 'Bloomberg': if len(tickers_list) > 1: if len(raw_data.columns) < len(tickers_list): und_errors = np.setdiff1d(tickers_list, raw_data.columns) message( 'There was a problem loading data for the following underlyings:\n' + '\n'.join(und_errors)) return [x for x in tickers_list if x not in und_errors] else: return tickers_list else: return tickers_list def _get_data_button_fired(self): """Method to download the relevant data and then compute the relevant correlations""" tickers_list = self.tickers_input.strip().split('\n') time_windows = self.time_windows_input[0, :].astype(int) # Get raw data raw_data = dr.get_relevant_data(tickers_list, self.date_start, self.date_end, self.data_source) # Check whether there was an error retrieving data tickers_list = self.check_data_retrieval_error(raw_data, tickers_list) if len(tickers_list) <= 1: message( 'You need at least two underlyings to compute correlations') return # Process raw data log_returns = cc.process_raw_data(raw_data) # Filter log returns log_returns = cc.filter_log_returns(log_returns) # Compute pairwise correlations self.corr_data = cc.get_correlations(log_returns, time_windows) # Generate TableEditor for i in range(0, len(time_windows)): correl_pair_editor.columns[i + 1].label = str(time_windows[i]) self.corr_pairs_combinations = [ pair[0] + ' - ' + pair[1] for pair in it.combinations(tickers_list, 2) ] self.corr_pairs_combinations.append('BASKET CORREL') self.correlpairs = [ generate_correl_pair(pair) for pair in self.corr_pairs_combinations ]
class TimeLoop(HasStrictTraits): tline = tr.Instance(TLine) '''Time line object specifying the start, end, time step and current time ''' def _tline_default(self): return TLine(min=0.0, max=1.0, step=1.0) ts = tr.Instance(DOTSGrid) '''State object delivering the predictor and corrector ''' bc_mngr = tr.Instance(BCondMngr, ()) '''Boundary condition manager. ''' bc_list = tr.List([]) '''List of boundary conditions. ''' def _bc_list_changed(self): self.bc_mngr.bcond_list = self.bc_list step_tolerance = tr.Float(1e-8) '''Time step tolerance. ''' KMAX = tr.Int(300) '''Maximum number of iterations. ''' tolerance = tr.Float(1e-3) '''Tolerance of the residuum norm. ''' t_n1 = tr.Float(0, input=True) '''Target time for the next increment. ''' t_n = tr.Float(0, input=True) '''Time of the last equilibrium state. ''' d_t = tr.Float(0, input=True) '''Current time increment size. ''' def eval(self): update_state = False tloop.bc_mngr.setup(None) K = SysMtxAssembly() self.bc_mngr.apply_essential(K) U_n = np.zeros((self.ts.mesh.n_dofs, ), dtype=np.float_) dU = np.copy(U_n) U_k = np.copy(U_n) F_ext = np.zeros_like(U_n) while (self.t_n1 - self.tline.max) <= self.step_tolerance: print 'current time %f' % self.t_n1, self.d_t = self.tline.step k = 0 step_flag = 'predictor' while k < self.KMAX: K.reset_mtx() K_arr, F_int, n_F_int = self.ts.get_corr_pred( U_k, dU, self.t_n, self.t_n1, update_state) if update_state: update_state = False K.sys_mtx_arrays.append(K_arr) F_ext[:] = 0.0 self.bc_mngr.apply(step_flag, None, K, F_ext, self.t_n, self.t_n1) R = F_ext - F_int K.apply_constraints(R) if n_F_int == 0.0: n_F_int = 1.0 norm = np.linalg.norm(R, ord=None) # / n_F_int if norm < self.tolerance: # convergence satisfied print 'converged in %d iterations' % (k + 1) update_state = True self.F_int_record.append(F_int) self.U_record.append(np.copy(U_k)) break # update_switch -> on dU = K.solve() U_k += dU k += 1 step_flag = 'corrector' U_n = np.copy(U_k) self.t_n = self.t_n1 self.record_response(U_k, self.t_n) self.t_n1 = self.t_n + self.d_t self.tline.val = min(self.t_n, self.tline.max) return U_n ug = tr.WeakRef write_dir = tr.Directory F_int_record = tr.List(tr.Array(np.float_)) U_record = tr.List(tr.Array(np.float_)) F_ext_record = tr.List(tr.Array(np.float_)) t_record = tr.List(np.float_) record_dofs = tr.Array(np.int_) def record_response(self, U, t): n_c = self.ts.fets.n_nodal_dofs U_Ia = U.reshape(-1, n_c) U_Eia = U_Ia[self.ts.I_Ei] eps_Enab = np.einsum('Einabc,Eic->Enab', self.ts.B_Einabc, U_Eia) sig_Enab = np.einsum('abef,Emef->Emab', self.ts.mats.D_abcd, eps_Enab) U_vector_field = np.einsum('Ia,ab->Ib', U_Eia.reshape(-1, n_c), delta23_ab) self.ug.point_data.vectors = U_vector_field self.ug.point_data.vectors.name = 'displacement' eps_Encd = np.einsum('...ab,ac,bd->...cd', eps_Enab, delta23_ab, delta23_ab) eps_Encd_tensor_field = eps_Encd.reshape(-1, 9) self.ug.point_data.tensors = eps_Encd_tensor_field self.ug.point_data.tensors.name = 'strain' fname = os.path.join(self.write_dir, 'step_%008.4f' % t) write_data(self.ug, fname.replace('.', '_'))
class DOTSGrid(BMCSLeafNode): '''Domain time steppsr on a grid mesh ''' x_0 = tr.Tuple(0., 0., input=True) L_x = tr.Float(200, input=True, MESH=True) L_y = tr.Float(100, input=True, MESH=True) n_x = tr.Int(100, input=True, MESH=True) n_y = tr.Int(30, input=True, MESH=True) integ_factor = tr.Float(1.0, input=True, MESH=True) fets = tr.Instance(IFETSEval, input=True, MESH=True) D1_abcd = tr.Array(np.float_, input=True) '''Symmetric operator distributing the derivatives of the shape functions into the tensor field ''' def _D1_abcd_default(self): delta = np.identity(2) # symmetrization operator D1_abcd = 0.5 * (np.einsum('ac,bd->abcd', delta, delta) + np.einsum('ad,bc->abcd', delta, delta)) return D1_abcd mesh = tr.Property(tr.Instance(FEGrid), depends_on='+input') @tr.cached_property def _get_mesh(self): return FEGrid(coord_min=self.x_0, coord_max=(self.x_0[0] + self.L_x, self.x_0[1] + self.L_y), shape=(self.n_x, self.n_y), fets_eval=self.fets) cached_grid_values = tr.Property(tr.Tuple, depends_on='+input') @tr.cached_property def _get_cached_grid_values(self): x_Ia = self.mesh.X_Id n_I, n_a = x_Ia.shape dof_Ia = np.arange(n_I * n_a, dtype=np.int_).reshape(n_I, -1) I_Ei = self.mesh.I_Ei x_Eia = x_Ia[I_Ei, :] dof_Eia = dof_Ia[I_Ei] x_Ema = np.einsum('im,Eia->Ema', self.fets.N_im, x_Eia) J_Emar = np.einsum('imr,Eia->Emar', self.fets.dN_imr, x_Eia) J_Enar = np.einsum('inr,Eia->Enar', self.fets.dN_inr, x_Eia) det_J_Em = np.linalg.det(J_Emar) inv_J_Emar = np.linalg.inv(J_Emar) inv_J_Enar = np.linalg.inv(J_Enar) B_Eimabc = np.einsum('abcd,imr,Eidr->Eimabc', self.D1_abcd, self.fets.dN_imr, inv_J_Emar) B_Einabc = np.einsum('abcd,inr,Eidr->Einabc', self.D1_abcd, self.fets.dN_inr, inv_J_Enar) BB_Emicjdabef = np.einsum('Eimabc,Ejmefd, Em, m->Emicjdabef', B_Eimabc, B_Eimabc, det_J_Em, self.fets.w_m) return (BB_Emicjdabef, B_Eimabc, dof_Eia, x_Eia, dof_Ia, I_Ei, B_Einabc, det_J_Em) BB_Emicjdabef = tr.Property() '''Quadratic form of the kinematic mapping. ''' def _get_BB_Emicjdabef(self): return self.cached_grid_values[0] B_Eimabc = tr.Property() '''Kinematic mapping between displacements and strains in every integration point. ''' def _get_B_Eimabc(self): return self.cached_grid_values[1] B_Einabc = tr.Property() '''Kinematic mapping between displacement and strain in every visualization point ''' def _get_B_Einabc(self): return self.cached_grid_values[6] dof_Eia = tr.Property() '''Mapping [element, node, direction] -> degree of freedom. ''' def _get_dof_Eia(self): return self.cached_grid_values[2] x_Eia = tr.Property() '''Mapping [element, node, direction] -> value of coordinate. ''' def _get_x_Eia(self): return self.cached_grid_values[3] dof_Ia = tr.Property() '''[global node, direction] -> degree of freedom ''' def _get_dof_Ia(self): return self.cached_grid_values[4] I_Ei = tr.Property() '''[element, node] -> global node ''' def _get_I_Ei(self): return self.cached_grid_values[5] det_J_Em = tr.Property() '''Jacobi determinant in every element and integration point. ''' def _get_det_J_Em(self): return self.cached_grid_values[7] state_arrays = tr.Property(tr.Dict(tr.Str, tr.Array), depends_on='fets, mats') '''Dictionary of state arrays. The entry names and shapes are defined by the material model. ''' @tr.cached_property def _get_state_arrays(self): return { name: np.zeros(( self.mesh.n_active_elems, self.fets.n_m, ) + mats_sa_shape, dtype=np.float_) for name, mats_sa_shape in list( self.mats.state_array_shapes.items()) } def get_corr_pred(self, U, dU, t_n, t_n1, update_state, algorithmic): '''Get the corrector and predictor for the given increment of unknown . ''' n_c = self.fets.n_nodal_dofs U_Ia = U.reshape(-1, n_c) U_Eia = U_Ia[self.I_Ei] eps_Emab = np.einsum('Eimabc,Eic->Emab', self.B_Eimabc, U_Eia) dU_Ia = dU.reshape(-1, n_c) dU_Eia = dU_Ia[self.I_Ei] deps_Emab = np.einsum('Eimabc,Eic->Emab', self.B_Eimabc, dU_Eia) D_Emabef, sig_Emab = self.mats.get_corr_pred(eps_Emab, deps_Emab, t_n, t_n1, update_state, algorithmic, **self.state_arrays) K_Eicjd = self.integ_factor * np.einsum('Emicjdabef,Emabef->Eicjd', self.BB_Emicjdabef, D_Emabef) n_E, n_i, n_c, n_j, n_d = K_Eicjd.shape K_E = K_Eicjd.reshape(-1, n_i * n_c, n_j * n_d) dof_E = self.dof_Eia.reshape(-1, n_i * n_c) K_subdomain = SysMtxArray(mtx_arr=K_E, dof_map_arr=dof_E) f_Eic = self.integ_factor * np.einsum( 'm,Eimabc,Emab,Em->Eic', self.fets.w_m, self.B_Eimabc, sig_Emab, self.det_J_Em) f_Ei = f_Eic.reshape(-1, n_i * n_c) F_dof = np.bincount(dof_E.flatten(), weights=f_Ei.flatten()) F_int = F_dof norm_F_int = np.linalg.norm(F_int) return K_subdomain, F_int, norm_F_int
class EEGSensor(t.HasTraits): preferences = t.Any() connected = t.Bool(False) serial_port = t.Instance(serial.Serial) com_port = t.Int() # If None, we just search for it. def _com_port_default(self): """ Get default com port from preferences. """ if self.preferences: return int( self.preferences.get('sensor.com_port', COM_SEARCH_START)) return t.undefined def _com_port_changed(self, val): """ Save any COM port changes to preferences. """ if self.preferences: return self.preferences.set('sensor.com_port', val) channels = t.Int(CHANNELS) timeseries = t.Array(dtype='float', value=np.zeros([1, CHANNELS + 1])) history = t.List() data_changed = t.Event() # Below, these separate properties and buttons for each channel are a bit # verbose, but it seems to be the most clear way to implement this. channel_1_enabled = t.Bool(False) channel_2_enabled = t.Bool(False) channel_3_enabled = t.Bool(False) channel_4_enabled = t.Bool(False) channel_5_enabled = t.Bool(False) channel_6_enabled = t.Bool(False) channel_7_enabled = t.Bool(False) channel_8_enabled = t.Bool(False) channel_1_on = t.Button() channel_2_on = t.Button() channel_3_on = t.Button() channel_4_on = t.Button() channel_5_on = t.Button() channel_6_on = t.Button() channel_7_on = t.Button() channel_8_on = t.Button() channel_1_off = t.Button() channel_2_off = t.Button() channel_3_off = t.Button() channel_4_off = t.Button() channel_5_off = t.Button() channel_6_off = t.Button() channel_7_off = t.Button() channel_8_off = t.Button() # Properties history_length = t.Property(t.Int, depends_on="data_changed") def _get_history_length(self): return len(self.history) timeseries_length = t.Property(t.Int, depends_on="data_changed") def _get_timeseries_length(self): return self.timeseries.shape[0] @t.on_trait_change(','.join(['channel_%d_on' % i for i in range(1, 9)] + ['channel_%d_off' % i for i in range(1, 9)])) def toggle_channels(self, name, new): if not self.connected: return deactivate_codes = ['1', '2', '3', '4', '5', '6', '7', '8'] activate_codes = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i'] if name.endswith('_off'): cmd = deactivate_codes[int(name[-len('_off') - 1]) - 1] elif name.endswith('_on'): cmd = activate_codes[int(name[-len('_on') - 1]) - 1] else: raise ValueError() self.serial_port.write(cmd + '\n') # self.serial_port.write('b\n') time.sleep(.100) # self.serial_port.flushInput() # time.sleep(.50) def connect(self): if self.connected: self.disconnect() assert self.serial_port is None # If no com port is selected, search for it... this search code could # be drastically sped up by analyzing a listing of actual COM ports. try: if self.com_port is None: for i in range(COM_SEARCH_START, COM_SEARCH_END + 1): try: port = 'COM%d' % i self.serial_port = serial.Serial( port, BAUD, timeout=STARTUP_TIMEOUT) if self.serial_port.read(1) == '': self.serial_port.close() self.serial_port = None continue else: # Assume it's the right one... self.serial_port.write('s\n') # Reset. self.serial_port.write( 'b\n') # Start sending binary. self.serial_port.read( 5) # Make sure we can read something # Okay, we're convinced. self.com_port = i self.connected = True self.serial_port.timeout = RUN_TIMEOUT break except serial.SerialException, e: logging.warn("Couldn't open %s: %s" % (port, str(e))) else: logging.warn("Couldn't find a functioning serial port." % (port, str(e))) else: # A specific COM port is requested.
class FETS3D8H(FETS3D, InjectSymbExpr): symb_class = FETS3D8HSymbExpr dof_r = tr.Array(np.float_, value=[ [-1, -1, -1], [1, -1, -1], [-1, 1, -1], [1, 1, -1], [-1, -1, 1], [1, -1, 1], [-1, 1, 1], [1, 1, 1], ]) geo_r = tr.Array(np.float_, value=[ [-1, -1, -1], [1, -1, -1], [-1, 1, -1], [1, 1, -1], [-1, -1, 1], [1, -1, 1], [-1, 1, 1], [1, 1, 1], ]) vtk_r = tr.Array(np.float_, value=[ [-1, -1, -1], [1, -1, -1], [-1, 1, -1], [1, 1, -1], [-1, -1, 1], [1, -1, 1], [-1, 1, 1], [1, 1, 1], ]) n_nodal_dofs = 3 delta = np.identity(n_nodal_dofs) vtk_cells = [[0, 1, 3, 2, 4, 5, 7, 6]] vtk_cell_types = 'Hexahedron' vtk_cell = [0, 1, 3, 2, 4, 5, 7, 6] vtk_cell_type = 'Hexahedron' vtk_expand_operator = tr.Array(np.float_, value=np.identity(3)) # numerical integration points (IP) and weights xi_m = tr.Array( np.float_, value=[ [-1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [-1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [-1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)], [-1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)], ]) w_m = tr.Array(value=[1, 1, 1, 1, 1, 1, 1, 1], dtype=np.float_) n_m = tr.Property def _get_n_m(self): return len(self.w_m) N_im = tr.Property() '''Shape function values in integration poindots. ''' @tr.cached_property def _get_N_im(self): N_im = self.symb.get_N_xi_i(self.xi_m.T) return N_im dN_imr = tr.Property() '''Shape function derivatives in integration points. ''' @tr.cached_property def _get_dN_imr(self): N_rim = self.symb.get_dN_xi_ai(self.xi_m.T) return np.einsum('rim->imr', N_rim) dN_inr = tr.Property() '''Shape function derivatives in visualization points. ''' @tr.cached_property def _get_dN_inr(self): N_rin = self.symb.get_dN_xi_ai(self.dof_r.T) return np.einsum('rin->inr', N_rin) I_sym_abcd = tr.Array(np.float) def _I_sym_abcd_default(self): return 0.5 * \ (np.einsum('ac,bd->abcd', delta, delta) + np.einsum('ad,bc->abcd', delta, delta)) plot_backend = 'k3d' def update_plot(self, axes): ax = axes import numpy as np v = np.linspace(-1, 1, 10) x, y, z = np.meshgrid(v, v, v) X_aIJK = np.array([x, y, z], dtype=np.float_) xmin, xmax, ymin, ymax, zmin, zmax = -1, 1, -1, 1, -1, 1 N_IJK = fets.symb.get_N_xi_i(X_aIJK)[0, ...] N_IJK.shape plt_iso = k3d.marching_cubes(N_IJK[0, ...], compression_level=9, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, zmin=zmin, zmax=zmax, level=0.5, flat_shading=False) k3d_plot += plt_iso k3d_plot.display()
class MATS3DSlideStrain(MATS3DEval): r''' Isotropic damage model. ''' node_name = 'Slide3D' n_a = tr.Array(np.float_, value=[0, 1, 0]) slide_displ = bu.Instance(Slide34, ()) slide_displ_ = tr.Property(depends_on='+MAT') '''Reconfigure slide with the derived stiffness parameters''' @tr.cached_property def _get_slide_displ_(self): self.slide_displ.trait_set(E_N=self.E, E_T=self.E_T) return self.slide_displ tree = ['slide_displ'] ipw_view = bu.View(bu.Item('n_a'), bu.Item('slide_displ')) state_var_shapes = tr.Property @tr.cached_property def _get_state_var_shapes(self): return self.slide_displ_.state_var_shapes r''' Shapes of the state variables to be stored in the global array at the level of the domain. ''' def get_eps_N(self, eps_ij, n_i): eps_N = np.einsum('...ij,...i,...j->...', eps_ij, n_i, n_i) return eps_N def get_eps_T(self, eps_ij, n_i): delta_ij = np.identity(3) eps_T = 0.5 * ( np.einsum('...i,...jk,...ij->...k', n_i, delta_ij, eps_ij) + np.einsum('...j,...ik,...ij->...k', n_i, delta_ij, eps_ij) - 2 * np.einsum('...i,...j,...k,...ij->...k', n_i, n_i, n_i, eps_ij)) return eps_T def get_eps_T_p(self, eps_T_p, eps_T): director_vector = [0, 0, 1] eps_T_p = np.einsum('...,...i->...i', eps_T_p, director_vector) return eps_T_p E_T = tr.Property(depends_on='MAT') @tr.cached_property def _get_E_T(self): n_i = self.n_a delta_ij = np.identity(3) D_ijkl = self.D_abef operator = 0.5 * (np.einsum('i,jk,l->ijkl', n_i, delta_ij, n_i) + np.einsum('j,ik,l->jikl', n_i, delta_ij, n_i) - 2 * np.einsum('i,j,k,l->ijkl', n_i, n_i, n_i, n_i)) E_T = np.einsum('ijkl,ijkl->', D_ijkl, operator) return E_T def get_corr_pred(self, eps_Emab_n1, tn1, **state): r''' Corrector predictor computation. ''' n_i = self.n_a eps_ij = eps_Emab_n1 eps_N = np.einsum('...ij,...i,...j->...', eps_ij, n_i, n_i) eps_T = self.get_eps_T(eps_ij, n_i) eps_T = np.sqrt(np.einsum('...i,...i->...', eps_T, eps_T)) eps_NT_Ema = np.concatenate( [np.transpose(eps_N), np.transpose(eps_T)], axis=-1) print('eps_NT_Ema', eps_NT_Ema.shape) print(self.state_var_shapes) se = self.slide_displ_ sig_NT_Ema, D_Emab = se.get_corr_pred(eps_NT_Ema, tn1, **state) eps_N_p, eps_T_p_x, eps_T_p_y = state['w_pi'], state['s_pi_x'], state[ 's_pi_y'] eps_T = self.get_eps_T(eps_ij, n_i) eps_T_p_i = self.get_eps_T_p(eps_T_p_x, eps_T) omega_N_Em, omega_T_Em = state['omega_N'], state['omega_T'] print(eps_ij.shape) phi_Emab = np.zeros_like(eps_ij) phi_Emab[:, 1, 1] = 0. phi_Emab[:, 2, 2] = np.sqrt(1 - omega_T_Em) phi_Emab[:, 0, 0] = np.sqrt(1 - omega_N_Em) beta_Emijkl = np.einsum('...ik,...jl->...ijkl', phi_Emab, np.transpose(phi_Emab, (1, 0))) eps_ij_p = (np.einsum('i,...j->...ij', n_i, eps_T_p_i) + np.einsum('...i,j->...ij', eps_T_p_i,n_i)) + \ np.einsum('...,i,j->...ij',eps_N_p, n_i, n_i) D_abef = self.D_abef D_Emabcd = np.einsum('...ijkl,klrs,...rstu->...ijtu', beta_Emijkl, D_abef, beta_Emijkl) sigma_Emab = np.einsum('...ijkl,...kl->...ij', D_Emabcd, (eps_Emab_n1 - eps_ij_p)) return sigma_Emab, D_Emabcd
class FETS1D52ULRH(FETSEval): ''' Fe Bar 2 nodes, deformation ''' debug_on = True A_m = Float(1, desc='matrix area [mm2]') A_f = Float(1, desc='reinforcement area [mm2]') P_b = Float(1, desc='perimeter of the bond interface [mm]') # Dimensional mapping dim_slice = slice(0, 1) n_nodal_dofs = Int(2) dof_r = Array(value=[[-1], [1]]) geo_r = Array(value=[[-1], [1]]) vtk_r = Array(value=[[-1.], [1.]]) vtk_cells = [[0, 1]] vtk_cell_types = 'Line' dots_class = Type n_dof_r = Property '''Number of node positions associated with degrees of freedom. ''' @cached_property def _get_n_dof_r(self): return len(self.dof_r) n_e_dofs = Property '''Number of element degrees ''' @cached_property def _get_n_e_dofs(self): return self.n_nodal_dofs * self.n_dof_r def _get_ip_coords(self): offset = 1e-6 return np.array([[-1 + offset, 0., 0.], [1 - offset, 0., 0.]]) def _get_ip_weights(self): return np.array([1., 1.], dtype=float) # Integration parameters # ngp_r = 2 def get_N_geo_mtx(self, r_pnt): ''' Return geometric shape functions @param r_pnt: ''' r = r_pnt[0] N_mtx = np.array([[0.5 - r / 2., 0.5 + r / 2.]]) return N_mtx def get_dNr_geo_mtx(self, r_pnt): ''' Return the matrix of shape function derivatives. Used for the conrcution of the Jacobi matrix. ''' return np.array([[-1. / 2, 1. / 2]]) def get_N_mtx(self, r_pnt): ''' Return shape functions @param r_pnt:local coordinates ''' 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) xi_m = Array(value=[[-1], [1]], dtype=np.float) r_m = Array(value=[[-1], [1]], dtype=np.float_) w_m = Array(value=[1, 1], dtype=np.float_) Nr_i_geo = List([ (1 - r_) / 2.0, (1 + r_) / 2.0, ]) dNr_i_geo = List([ -1.0 / 2.0, 1.0 / 2.0, ]) Nr_i = Nr_i_geo dNr_i = dNr_i_geo N_mi_geo = Property() @cached_property def _get_N_mi_geo(self): return self.get_N_mi(sp.Matrix(self.Nr_i_geo, dtype=np.float_)) dN_mid_geo = Property() @cached_property def _get_dN_mid_geo(self): return self.get_dN_mid(sp.Matrix(self.dNr_i_geo, dtype=np.float_)) N_mi = Property() @cached_property def _get_N_mi(self): return self.get_N_mi(sp.Matrix(self.Nr_i, dtype=np.float_)) dN_mid = Property() @cached_property def _get_dN_mid(self): return self.get_dN_mid(sp.Matrix(self.dNr_i, dtype=np.float_)) def get_N_mi(self, Nr_i): return np.array([Nr_i.subs(r_, r) for r in self.r_m], dtype=np.float_) def get_dN_mid(self, dNr_i): dN_mdi = np.array([[dNr_i.subs(r_, r)] for r in self.r_m], dtype=np.float_) return np.einsum('mdi->mid', dN_mdi) n_m = Property(depends_on='w_m') @cached_property def _get_n_m(self): return len(self.w_m) A_C = Property(depends_on='A_m,A_f') @cached_property def _get_A_C(self): return np.array((self.A_f, self.P_b, self.A_m), dtype=np.float_) def get_B_EmisC(self, J_inv_Emdd): fets_eval = self n_dof_r = fets_eval.n_dof_r n_nodal_dofs = fets_eval.n_nodal_dofs n_m = fets_eval.n_gp n_E = J_inv_Emdd.shape[0] n_s = 3 #[ d, i] r_ip = fets_eval.ip_coords[:, :-2].T # [ d, n ] geo_r = fets_eval.geo_r.T # [ d, n, i ] dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5 # [ i, n, d ] dNr_geo = np.einsum('dni->ind', dNr_geo) # shape function for the unknowns # [ d, n, i] Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None, :]) dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1]) # [ i, n, d ] Nr = np.einsum('dni->ind', Nr) dNr = np.einsum('dni->ind', dNr) Nx = Nr # [ n_e, n_ip, n_dof_r, n_dim_dof ] dNx = np.einsum('eidf,inf->eind', J_inv_Emdd, dNr) B = np.zeros((n_E, n_m, n_dof_r, n_s, n_nodal_dofs), dtype='f') B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0] B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0] B_factors = np.array([-1, 1], dtype='float_') B[:, :, :, B_N_n_rows, B_N_n_cols] = (B_factors[None, None, :] * Nx[:, :, N_idx]) B[:, :, :, B_dN_n_rows, B_dN_n_cols] = dNx[:, :, :, dN_idx] return B def _get_B_EimsC(self, dN_Eimd, sN_Cim): n_E, _, _, _ = dN_Eimd.shape n_C, n_i, n_m = sN_Cim.shape n_s = 3 B_EimsC = np.zeros((n_E, n_i, n_m, n_s, n_C), dtype='f') B_EimsC[..., [1, 1], [0, 1]] = sN_Cim[[0, 1], :, :] B_EimsC[..., [0, 2], [0, 1]] = dN_Eimd[:, [0, 1], :, :] return B_EimsC shape_function_values = tr.Property(tr.Tuple) '''The values of the shape functions and their derivatives at the IPs ''' @tr.cached_property def _get_shape_function_values(self): N_mi = np.array([ N_xi_i.subs(list(zip([xi_1, xi_2, xi_3], xi))) for xi in self.xi_m ], dtype=np.float_) N_im = np.einsum('mi->im', N_mi) dN_mir_arr = [ np.array(dN_xi_ir.subs(list(zip([xi_1], xi)))).astype(np.float_) for xi in self.xi_m ] dN_mir = np.array(dN_mir_arr, dtype=np.float) dN_nir_arr = [ np.array(dN_xi_ir.subs(list(zip([xi_1], xi)))).astype(np.float_) for xi in self.vtk_r ] dN_nir = np.array(dN_nir_arr, dtype=np.float) dN_imr = np.einsum('mir->imr', dN_mir) dN_inr = np.einsum('nir->inr', dN_nir) return (N_im, dN_imr, dN_inr) N_im = tr.Property() '''Shape function values in integration poindots. ''' def _get_N_im(self): return self.shape_function_values[0] dN_imr = tr.Property() '''Shape function derivatives in integration poindots. ''' def _get_dN_imr(self): return self.shape_function_values[1] dN_inr = tr.Property() '''Shape function derivatives in visualization poindots. ''' def _get_dN_inr(self): return self.shape_function_values[2] I_sym_abcd = tr.Array(np.float) def _I_sym_abcd_default(self): delta = np.identity(3) return 0.5 * \ (np.einsum('ac,bd->abcd', delta, delta) + np.einsum('ad,bc->abcd', delta, delta))
class FETS2D3U1M(FETSEval): r'''Triangular, three-node element. ''' vtk_r = tr.Array(np.float_, value=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) vtk_cells = [[0, 1, 2]] vtk_cell_types = 'Triangle' vtk_cell = [0, 1, 2] vtk_cell_type = 'Triangle' vtk_expand_operator = tr.Array(np.float_, value=DELTA23_ab) # ========================================================================= # Surface integrals using numerical integration # ========================================================================= # i is gauss point index, p is point coords in natural coords (zeta_1, zeta_2, zeta_3) eta_ip = tr.Array('float_') r'''Integration points within a triangle. ''' def _eta_ip_default(self): # here, just one integration point in the middle of the triangle (zeta_1 = 1/3, zeta_2 = 1/3, zeta_3 = 1/3) return np.array([[1. / 3., 1. / 3., 1. / 3.]], dtype='f') w_m = tr.Array('float_') r'''Weight factors for numerical integration. ''' def _w_m_default(self): return np.array([1. / 2.], dtype='f') n_m = tr.Int(1) r'''Number of integration points. ''' @tr.cached_property def _get_w_m(self): return len(self.w_m) n_nodal_dofs = tr.Int(3) # N_im = tr.Property(depends_on='eta_ip') # r'''Shape function values in integration points. # ''' # @tr.cached_property # def _get_N_im(self): # eta = self.eta_ip # return np.array([eta[:, 0], eta[:, 1], 1 - eta[:, 0] - eta[:, 1]], # dtype='f') dN_imr = tr.Property(depends_on='eta_ip') r'''Derivatives of the shape functions in the integration points. ''' @tr.cached_property def _get_dN_imr(self): dN_mri = np.array( [ [ [1, 0, -1], # dN1/d_zeta1, dN2/d_zeta1, dN3/d_zeta1 [0, 1, -1] ], # dN1/d_zeta2, dN2/d_zeta2, dN3/d_zeta2 ], dtype=np.float_) return np.einsum('mri->imr', dN_mri) dN_inr = tr.Property(depends_on='eta_ip') r'''Derivatives of the shape functions in the integration points. ''' @tr.cached_property def _get_dN_inr(self): return self.dN_imr vtk_expand_operator = tr.Array(value=[1, 1, 0]) vtk_node_cell_data = tr.Array vtk_ip_cell_data = tr.Array
class DataAxis(t.HasTraits): name = t.Str() units = t.Str() scale = t.Float() offset = t.Float() size = t.CInt() low_value = t.Float() high_value = t.Float() value = t.Range('low_value', 'high_value') low_index = t.Int(0) high_index = t.Int() slice = t.Instance(slice) navigate = t.Bool(t.Undefined) index = t.Range('low_index', 'high_index') axis = t.Array() continuous_value = t.Bool(False) def __init__(self, size, index_in_array=None, name=t.Undefined, scale=1., offset=0., units=t.Undefined, navigate=t.Undefined): super(DataAxis, self).__init__() self.events = Events() self.events.index_changed = Event(""" Event that triggers when the index of the `DataAxis` changes Triggers after the internal state of the `DataAxis` has been updated. Arguments: --------- obj : The DataAxis that the event belongs to. index : The new index """, arguments=["obj", 'index']) self.events.value_changed = Event(""" Event that triggers when the value of the `DataAxis` changes Triggers after the internal state of the `DataAxis` has been updated. Arguments: --------- obj : The DataAxis that the event belongs to. value : The new value """, arguments=["obj", 'value']) self._suppress_value_changed_trigger = False self._suppress_update_value = False self.name = name self.units = units self.scale = scale self.offset = offset self.size = size self.high_index = self.size - 1 self.low_index = 0 self.index = 0 self.update_axis() self.navigate = navigate self.axes_manager = None self.on_trait_change(self.update_axis, ['scale', 'offset', 'size']) self.on_trait_change(self._update_slice, 'navigate') self.on_trait_change(self.update_index_bounds, 'size') # The slice must be updated even if the default value did not # change to correctly set its value. self._update_slice(self.navigate) def _index_changed(self, name, old, new): self.events.index_changed.trigger(obj=self, index=self.index) if not self._suppress_update_value: new_value = self.axis[self.index] if new_value != self.value: self.value = new_value def _value_changed(self, name, old, new): old_index = self.index new_index = self.value2index(new) if self.continuous_value is False: # Only values in the grid alowed if old_index != new_index: self.index = new_index if new == self.axis[self.index]: self.events.value_changed.trigger(obj=self, value=new) elif old_index == new_index: new_value = self.index2value(new_index) if new_value == old: self._suppress_value_changed_trigger = True try: self.value = new_value finally: self._suppress_value_changed_trigger = False elif new_value == new and not\ self._suppress_value_changed_trigger: self.events.value_changed.trigger(obj=self, value=new) else: # Intergrid values are alowed. This feature is deprecated self.events.value_changed.trigger(obj=self, value=new) if old_index != new_index: self._suppress_update_value = True self.index = new_index self._suppress_update_value = False @property def index_in_array(self): if self.axes_manager is not None: return self.axes_manager._axes.index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") @property def index_in_axes_manager(self): if self.axes_manager is not None: return self.axes_manager._get_axes_in_natural_order().\ index(self) else: raise AttributeError( "This DataAxis does not belong to an AxesManager" " and therefore its index_in_array attribute " " is not defined") def _get_positive_index(self, index): if index < 0: index = self.size + index if index < 0: raise IndexError("index out of bounds") return index def _get_index(self, value): if isfloat(value): return self.value2index(value) else: return value def _get_array_slices(self, slice_): """Returns a slice to slice the corresponding data axis without changing the offset and scale of the DataAxis. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ v2i = self.value2index if isinstance(slice_, slice): start = slice_.start stop = slice_.stop step = slice_.step else: if isfloat(slice_): start = v2i(slice_) else: start = self._get_positive_index(slice_) stop = start + 1 step = None if isfloat(step): step = int(round(step / self.scale)) if isfloat(start): try: start = v2i(start) except ValueError: if start > self.high_value: # The start value is above the axis limit raise IndexError( "Start value above axis high bound for axis %s." "value: %f high_bound: %f" % (repr(self), start, self.high_value)) else: # The start value is below the axis limit, # we slice from the start. start = None if isfloat(stop): try: stop = v2i(stop) except ValueError: if stop < self.low_value: # The stop value is below the axis limits raise IndexError( "Stop value below axis low bound for axis %s." "value: %f low_bound: %f" % (repr(self), stop, self.low_value)) else: # The stop value is below the axis limit, # we slice until the end. stop = None if step == 0: raise ValueError("slice step cannot be zero") return slice(start, stop, step) def _slice_me(self, slice_): """Returns a slice to slice the corresponding data axis and change the offset and scale of the DataAxis acordingly. Parameters ---------- slice_ : {float, int, slice} Returns ------- my_slice : slice """ i2v = self.index2value my_slice = self._get_array_slices(slice_) start, stop, step = my_slice.start, my_slice.stop, my_slice.step if start is None: if step is None or step > 0: start = 0 else: start = self.size - 1 self.offset = i2v(start) if step is not None: self.scale *= step return my_slice def _get_name(self): if self.name is t.Undefined: if self.axes_manager is None: name = "Unnamed" else: name = "Unnamed " + ordinal(self.index_in_axes_manager) else: name = self.name return name def __repr__(self): text = '<%s axis, size: %i' % ( self._get_name(), self.size, ) if self.navigate is True: text += ", index: %i" % self.index text += ">" return text def __str__(self): return self._get_name() + " axis" def update_index_bounds(self): self.high_index = self.size - 1 def update_axis(self): self.axis = generate_axis(self.offset, self.scale, self.size) if len(self.axis) != 0: self.low_value, self.high_value = (self.axis.min(), self.axis.max()) def _update_slice(self, value): if value is False: self.slice = slice(None) else: self.slice = None def get_axis_dictionary(self): adict = { 'name': self.name, 'scale': self.scale, 'offset': self.offset, 'size': self.size, 'units': self.units, 'navigate': self.navigate } return adict def copy(self): return DataAxis(**self.get_axis_dictionary()) def __copy__(self): return self.copy() def __deepcopy__(self, memo): cp = self.copy() return cp def value2index(self, value, rounding=round): """Return the closest index to the given value if between the limit. Parameters ---------- value : number or numpy array Returns ------- index : integer or numpy array Raises ------ ValueError if any value is out of the axis limits. """ if value is None: return None if isinstance(value, np.ndarray): if rounding is round: rounding = np.round elif rounding is math.ceil: rounding = np.ceil elif rounding is math.floor: rounding = np.floor index = rounding((value - self.offset) / self.scale) if isinstance(value, np.ndarray): index = index.astype(int) if np.all(self.size > index) and np.all(index >= 0): return index else: raise ValueError("A value is out of the axis limits") else: index = int(index) if self.size > index >= 0: return index else: raise ValueError("The value is out of the axis limits") def index2value(self, index): if isinstance(index, np.ndarray): return self.axis[index.ravel()].reshape(index.shape) else: return self.axis[index] def calibrate(self, value_tuple, index_tuple, modify_calibration=True): scale = (value_tuple[1] - value_tuple[0]) /\ (index_tuple[1] - index_tuple[0]) offset = value_tuple[0] - scale * index_tuple[0] if modify_calibration is True: self.offset = offset self.scale = scale else: return offset, scale def value_range_to_indices(self, v1, v2): """Convert the given range to index range. When an out of the axis limits, the endpoint is used instead. Parameters ---------- v1, v2 : float The end points of the interval in the axis units. v2 must be greater than v1. """ if v1 is not None and v2 is not None and v1 > v2: raise ValueError("v2 must be greater than v1.") if v1 is not None and self.low_value < v1 <= self.high_value: i1 = self.value2index(v1) else: i1 = 0 if v2 is not None and self.high_value > v2 >= self.low_value: i2 = self.value2index(v2) else: i2 = self.size - 1 return i1, i2 def update_from(self, axis, attributes=["scale", "offset", "units"]): """Copy values of specified axes fields from the passed AxesManager. Parameters ---------- axis : DataAxis The DataAxis instance to use as a source for values. attributes : iterable container of strings. The name of the attribute to update. If the attribute does not exist in either of the AxesManagers, an AttributeError will be raised. Returns ------- A boolean indicating whether any changes were made. """ any_changes = False changed = {} for f in attributes: if getattr(self, f) != getattr(axis, f): changed[f] = getattr(axis, f) if len(changed) > 0: self.trait_set(**changed) any_changes = True return any_changes
class MATS2DMplDamageEEQ(MATSXDMicroplaneDamageJir, MATS2DEval): implements(IMATSEval) #----------------------------------------------- # number of microplanes - currently fixed for 3D #----------------------------------------------- n_mp = Constant(28) _alpha_list = Property(depends_on='n_mp') @cached_property def _get__alpha_list(self): return array( [np.pi / self.n_mp * (i - 0.5) for i in range(1, self.n_mp + 1)]) #----------------------------------------------- # get the normal vectors of the microplanes #----------------------------------------------- _MPN = Property(depends_on='n_mp') @cached_property def _get__MPN(self): return array([[np.cos(alpha), np.sin(alpha)] for alpha in self._alpha_list]) #------------------------------------- # get the weights of the microplanes #------------------------------------- _MPW = Property(depends_on='n_mp') @cached_property def _get__MPW(self): return np.ones(self.n_mp) / self.n_mp * 2.0 #------------------------------------------------------------------------- # Cached elasticity tensors #------------------------------------------------------------------------- stress_state = tr.Enum("plane_stress", "plane_strain", input=True) #------------------------------------------------------------------------- # Material parameters #------------------------------------------------------------------------- E = tr.Float(34e+3, label="E", desc="Young's Modulus", auto_set=False, input=True) nu = tr.Float(0.2, label='nu', desc="Poison ratio", auto_set=False, input=True) def _get_lame_params(self): la = self.E * self.nu / ((1. + self.nu) * (1. - 2. * self.nu)) # second Lame parameter (shear modulus) mu = self.E / (2. + 2. * self.nu) return la, mu D_ab = tr.Property(tr.Array, depends_on='+input') '''Elasticity matrix (shape: (3,3)) ''' @tr.cached_property def _get_D_ab(self): if self.stress_state == 'plane_stress': return self._get_D_ab_plane_stress() elif self.stress_state == 'plane_strain': return self._get_D_ab_plane_strain() def _get_D_ab_plane_stress(self): ''' Elastic Matrix - Plane Stress ''' E = self.E nu = self.nu D_stress = np.zeros([3, 3]) D_stress[0, 0] = E / (1.0 - nu * nu) D_stress[0, 1] = E / (1.0 - nu * nu) * nu D_stress[1, 0] = E / (1.0 - nu * nu) * nu D_stress[1, 1] = E / (1.0 - nu * nu) D_stress[2, 2] = E / (1.0 - nu * nu) * (1.0 / 2.0 - nu / 2.0) return D_stress def _get_D_ab_plane_strain(self): ''' Elastic Matrix - Plane Strain ''' E = self.E nu = self.nu D_strain = np.zeros([3, 3]) D_strain[0, 0] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[0, 1] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1, 0] = E / (1.0 + nu) / (1.0 - 2.0 * nu) * nu D_strain[1, 1] = E * (1.0 - nu) / (1.0 + nu) / (1.0 - 2.0 * nu) D_strain[2, 2] = E * (1.0 - nu) / (1.0 + nu) / (2.0 - 2.0 * nu) return D_strain map2d_ijkl2a = tr.Array(np.int_, value=[[[[0, 0], [0, 0]], [[2, 2], [2, 2]]], [[[2, 2], [2, 2]], [[1, 1], [1, 1]]]]) map2d_ijkl2b = tr.Array(np.int_, value=[[[[0, 2], [2, 1]], [[0, 2], [2, 1]]], [[[0, 2], [2, 1]], [[0, 2], [2, 1]]]]) D_abef = tr.Property(tr.Array, depends_on='+input') @tr.cached_property def _get_D_abef(self): return self.D_ab[self.map2d_ijkl2a, self.map2d_ijkl2b]
class GCS(t.HasTraits): """ This is the ground control station GUI class. For usage, for example if telemetry radio is on COM8 and the GCS Pixhawk is on COM7: >>> gcs = GCS() >>> gcs.setup_uav_link("COM8") >>> gcs.poll_uav() >>> gcs.setup_gcs_link("COM7") >>> gcs.poll_gcs() >>> gcs.configure_traits() >>> gcs.close() """ # Connections dialect = t.Str("gcs_pixhawk") show_errors = t.Bool(True) # ON GCS: Outgoing mission_message = t.Enum(set_mission_mode.keys(), label="Mission Type") sweep_angle = t.Float(label="Angle (degrees)") sweep_alt_start = t.Float(label="Start Altitude (m)") sweep_alt_end = t.Float(label="End Altitude (m)") sweep_alt_step = t.Float(label="Number of Altitude Steps") mavlink_message = t.Enum(mavlink_msgs, label="Mavlink Message") mavlink_message_filt = t.Enum(mavlink_msgs_filt, label="Mavlink Message") mavlink_message_params = t.Str(label="params") mavlink_message_args = t.Str(', '.join( mavlink_msgs_attr[mavlink_msgs_filt[0]]['args'][1:]), label="Arguments") # ON GCS: Incoming # Tether tether_length = t.Float(t.Undefined, label='Length (m)') tether_tension = t.Float(t.Undefined, label='Tension (N)') tether_velocity = t.Float(t.Undefined, label="Velocity") # ON GCS: Incoming # GCS Pixhawk gcs_eph = t.Float(t.Undefined) gcs_epv = t.Float(t.Undefined) gcs_satellites_visible = t.Int(t.Undefined) gcs_fix_type = t.Int(t.Undefined) gcs_airspeed = t.Float(t.Undefined) gcs_groundspeed = t.Float(t.Undefined) gcs_heading = t.Float(t.Undefined) gcs_velocity = t.Array(shape=(3, )) # Location inputs gcs_alt = t.Float(t.Undefined) gcs_lat = t.Float(t.Undefined) gcs_lon = t.Float(t.Undefined) # Attitude inputs gcs_pitch = t.Float(t.Undefined) gcs_roll = t.Float(t.Undefined) gcs_yaw = t.Float(t.Undefined) gcs_pitchspeed = t.Float(t.Undefined) gcs_yawspeed = t.Float(t.Undefined) gcs_rollspeed = t.Float(t.Undefined) # Battery Inputs gcs_current = t.Float(t.Undefined) gcs_level = t.Float(t.Undefined) gcs_voltage = t.Float(t.Undefined) # GCS connectinos gcs = t.Any(t.Undefined) gcs_polling = t.Bool(False) gcs_msg_thread = t.Instance(threading.Thread) gcs_error = t.Int(0) gcs_port = t.Str(t.Undefined) gcs_baud = t.Int(t.Undefined) # ON DRONE: Incoming # Mission Status mission_status = t.Enum(mission_status.keys()) # Probe probe_u = t.Float(t.Undefined, label="u (m/s)") probe_v = t.Float(t.Undefined, label="v (m/s)") probe_w = t.Float(t.Undefined, label="w (m/s)") # Vehicle inputs uav_modename = t.Str(t.Undefined) uav_armed = t.Bool(t.Undefined) uav_eph = t.Float(t.Undefined) uav_epv = t.Float(t.Undefined) uav_satellites_visible = t.Int(t.Undefined) uav_fix_type = t.Int(t.Undefined) uav_airspeed = t.Float(t.Undefined) uav_groundspeed = t.Float(t.Undefined) uav_heading = t.Float(t.Undefined) uav_velocity = t.Array(shape=(3, )) # Location inputs uav_alt = t.Float(t.Undefined) uav_lat = t.Float(t.Undefined) uav_lon = t.Float(t.Undefined) # Attitude inputs uav_pitch = t.Float(t.Undefined) uav_roll = t.Float(t.Undefined) uav_yaw = t.Float(t.Undefined) uav_pitchspeed = t.Float(t.Undefined) uav_yawspeed = t.Float(t.Undefined) uav_rollspeed = t.Float(t.Undefined) # Battery Inputs uav_current = t.Float(t.Undefined) uav_level = t.Float(t.Undefined) uav_voltage = t.Float(t.Undefined) # Vehicle Connections uav = t.Any(t.Undefined) uav_polling = t.Bool(False) uav_msg_thread = t.Instance(threading.Thread) uav_error = t.Int(0) uav_port = t.Str(t.Undefined) uav_baud = t.Int(t.Undefined) # GCS connectinos gcs = t.Any(t.Undefined) gcs_polling = t.Bool(False) gcs_msg_thread = t.Instance(threading.Thread) gcs_error = t.Int(0) gcs_port = t.Str(t.Undefined) gcs_baud = t.Int(t.Undefined) # ui Buttons and display groups update_mission = t.Button("Update") send_mavlink_message = t.Button("Send") filtered = t.Bool(True) group_input = tui.Group( tui.Item(name="mission_status", enabled_when='False'), tui.Item(name="mission_message"), tui.Item(name="sweep_angle", visible_when='mission_message=="SCHEDULE_SWEEP"'), tui.Item(name="sweep_alt_start", visible_when='mission_message=="SCHEDULE_SWEEP"'), tui.Item(name="sweep_alt_end", visible_when='mission_message=="SCHEDULE_SWEEP"'), tui.Item(name="sweep_alt_step", visible_when='mission_message=="SCHEDULE_SWEEP"'), tui.Item(name="update_mission"), tui.Item("_"), tui.Item("filtered"), tui.Item("mavlink_message", visible_when='filtered==False'), tui.Item("mavlink_message_filt", visible_when='filtered'), tui.Item("mavlink_message_args", enabled_when='False', editor=tui.TextEditor(), height=-40), tui.Item("mavlink_message_params"), tui.Item("send_mavlink_message"), tui.Item("_"), tui.Item(name="tether_tension", enabled_when='False'), tui.Item(name="tether_length", enabled_when='False'), tui.Item(name="tether_velocity", enabled_when='False'), tui.Item("_"), orientation="vertical", show_border=True, label="On GCS") group_uav = tui.Group(tui.Item(name="uav_modename", enabled_when='False'), tui.Item(name="uav_airspeed", enabled_when='False'), tui.Item(name="uav_groundspeed", enabled_when='False'), tui.Item(name='uav_armed', enabled_when='False'), tui.Item(name='uav_alt', enabled_when='False'), tui.Item(name='uav_lat', enabled_when='False'), tui.Item(name='uav_lon', enabled_when='False'), tui.Item(name='uav_velocity', enabled_when='False'), tui.Item(name='uav_pitch', enabled_when='False'), tui.Item(name='uav_roll', enabled_when='False'), tui.Item(name='uav_yaw', enabled_when='False'), tui.Item(name='uav_current', enabled_when='False'), tui.Item(name='uav_level', enabled_when='False'), tui.Item(name='uav_voltage', enabled_when='False'), tui.Item("_"), tui.Item(name='probe_u', enabled_when='False'), tui.Item(name='probe_v', enabled_when='False'), tui.Item(name='probe_w', enabled_when='False'), orientation='vertical', show_border=True, label="Incoming") group_gcs = tui.Group(tui.Item(name="gcs_airspeed", enabled_when='False'), tui.Item(name="gcs_groundspeed", enabled_when='False'), tui.Item(name='gcs_alt', enabled_when='False'), tui.Item(name='gcs_lat', enabled_when='False'), tui.Item(name='gcs_lon', enabled_when='False'), tui.Item(name='gcs_velocity', enabled_when='False'), tui.Item(name='gcs_pitch', enabled_when='False'), tui.Item(name='gcs_roll', enabled_when='False'), tui.Item(name='gcs_yaw', enabled_when='False'), tui.Item(name='gcs_current', enabled_when='False'), tui.Item(name='gcs_level', enabled_when='False'), tui.Item(name='gcs_voltage', enabled_when='False'), orientation='vertical', show_border=True, label="GCS") traits_view = tui.View(tui.Group(group_input, group_uav, group_gcs, orientation='horizontal'), resizable=True) def _update_mission_fired(self): """ This will fire when the update_mission button is clicked In that case we send one of our custom MAVLINK messages, either set_mission_mode or schedule_sweep """ mode = set_mission_mode[self.mission_message] if mode >= 0: self.uav.mav.set_mission_mode_send(mode) else: self.uav.mav.schedule_sweep_send(self.sweep_angle, self.sweep_alt_start, self.sweep_alt_end, self.sweep_alt_step) def _mavlink_message_changed(self): """ This will fire when the dropdown is changed """ self.mavlink_message_args = ', '.join( mavlink_msgs_attr[self.mavlink_message]['args'][1:]) def _mavlink_message_filt_changed(self): """ This will fire when the filtered dropdown is changed """ self.mavlink_message_args = ', '.join( mavlink_msgs_attr[self.mavlink_message_filt]['args'][1:]) def _send_mavlink_message_fired(self): """ This will fire when the send_mavlink_message button is clicked In that case we pass on the mavlink message that the user is trying to send. """ func = mavlink_msgs_attr[self.mavlink_message]['name'] args = [float(m) for m in self.mavlink_message_params.split(',')] getattr(self.uav.mav, func)(*args) def setup_uav_link(self, uav_port, uav_baud=56700): """ This sets up the connection to the UAV. Parameters ----------- uav_port : str Serial port where UAV is connected (via telemetry radio) uav_baud: int, optional The baud rate. Default is 56700 """ mavutil.set_dialect(self.dialect) self.uav = mavutil.mavlink_connection(uav_port, uav_baud) self.uav_port = uav_port self.uav_baud = uav_baud def setup_gcs_link(self, gcs_port, gcs_baud=115200): """ This sets up the connection to the GCS Pixhawk. Parameters ----------- uav_port : str Serial port where GCS Pixhawk is connected (via usb cable) uav_baud: int, optional The baud rate. Default is 115200 """ mavutil.set_dialect(self.dialect) self.gcs = mavutil.mavlink_connection(gcs_port, gcs_baud) self.gcs_port = gcs_port self.gcs_baud = gcs_baud def poll_uav(self): """ This runs a new thread that listens for messages from the UAV and parses them for the GCS """ self.uav_polling = True def worker(): # Make sure we are connected m = self.uav m.mav.heartbeat_send(mavutil.mavlink.MAV_TYPE_GCS, mavutil.mavlink.MAV_AUTOPILOT_INVALID, 0, 0, 0) print("Waiting for heartbeat from %s" % m.address) self.uav.wait_heartbeat() print "Found Heardbeat, continuing" i = 0 while self.uav_polling: # print "uav_polling round", i i += 1 try: s = m.recv(16 * 1024) except Exception: time.sleep(0.1) # prevent a dead serial port from causing the CPU to spin. The user hitting enter will # cause it to try and reconnect if len(s) == 0: time.sleep(0.1) if 'windows' in platform.architecture()[-1].lower(): # strip nsh ansi codes s = s.replace("\033[K", "") if m.first_byte: m.auto_mavlink_version(s) msgs = m.mav.parse_buffer(s) if msgs: for msg in msgs: if getattr(m, '_timestamp', None) is None: m.post_message(msg) if msg.get_type() == "BAD_DATA": if self.show_errors: print "MAV error: %s" % msg self.uav_error += 1 else: self.parse_uav_msg(msg) print "uav_polling Stopped" self.uav_polling = False self.uav_msg_thread = threading.Thread(target=worker) self.uav_msg_thread.start() def poll_gcs(self): """ This runs a new thread that listens for messages from the GCS Pixhawk and parses them for the GCS, it also forwards relevant messages to the UAV """ self.gcs_polling = True def worker(): # Make sure we are connected m = self.gcs m.mav.heartbeat_send(mavutil.mavlink.MAV_TYPE_GCS, mavutil.mavlink.MAV_AUTOPILOT_INVALID, 0, 0, 0) print("Waiting for heartbeat from %s" % m.address) self.gcs.wait_heartbeat() print "Found Heardbeat, continuing" i = 0 while self.gcs_polling: # print "gcs_polling round", i i += 1 try: s = m.recv(16 * 1024) except Exception: time.sleep(0.1) # prevent a dead serial port from causing the CPU to spin. The user hitting enter will # cause it to try and reconnect if len(s) == 0: time.sleep(0.1) if 'windows' in platform.architecture()[-1].lower(): # strip nsh ansi codes s = s.replace("\033[K", "") if m.first_byte: m.auto_mavlink_version(s) msgs = m.mav.parse_buffer(s) if msgs: for msg in msgs: if getattr(m, '_timestamp', None) is None: m.post_message(msg) if msg.get_type() == "BAD_DATA": if self.show_errors: print "MAV error: %s" % msg self.gcs_error += 1 else: self.parsefwd_gcs_msg(msg) print "gcs_polling Stopped" self.gcs_polling = False self.gcs_msg_thread = threading.Thread(target=worker) self.gcs_msg_thread.start() def parse_uav_msg(self, m): """ This parses a message received from the UAV and stores the values in the class attributes so that the GUI will update """ # print "Parsing Message" typ = m.get_type() if typ == 'GLOBAL_POSITION_INT': (self.uav_lat, self.uav_lon) = (m.lat / 1.0e7, m.lon / 1.0e7) self.uav_velocity = (m.vx / 100.0, m.vy / 100.0, m.vz / 100.0) elif typ == 'GPS_RAW': pass # better to just use global position int # (self.lat, self.lon) = (m.lat, m.lon) # self.__on_change('location') elif typ == 'GPS_RAW_INT': # (self.lat, self.lon) = (m.lat / 1.0e7, m.lon / 1.0e7) self.uav_eph = m.eph self.uav_epv = m.epv self.uav_satellites_visible = m.satellites_visible self.uav_fix_type = m.fix_type elif typ == "VFR_HUD": self.uav_heading = m.heading self.uav_alt = m.alt self.uav_airspeed = m.airspeed self.uav_groundspeed = m.groundspeed elif typ == "ATTITUDE": self.uav_pitch = m.pitch self.uav_yaw = m.yaw self.uav_roll = m.roll self.uav_pitchspeed = m.pitchspeed self.uav_yawspeed = m.yawspeed self.uav_rollspeed = m.rollspeed elif typ == "SYS_STATUS": self.uav_voltage = m.voltage_battery self.uav_current = m.current_battery self.uav_level = m.battery_remaining elif typ == "HEARTBEAT": pass # print "Parsing Message DONE" def fwd_msg_to_uav(self, m): """This forwards messages from the GCS Pixhawk to the UAV if there is a UAV connected""" if self.uav is not t.Undefined: self.uav.write(m.get_msgbuf()) def parsefwd_gcs_msg(self, m): """ This parses a message received from the GCS Pixhawk, stores the values in the class attributes so that the GUI will update, and forwards relevant messages to the UAV """ # print "Parsing Message" typ = m.get_type() if typ == 'GLOBAL_POSITION_INT': (self.gcs_lat, self.gcs_lon) = (m.lat / 1.0e7, m.lon / 1.0e7) self.gcs_velocity = (m.vx / 100.0, m.vy / 100.0, m.vz / 100.0) # Forward message self.fwd_msg_to_uav(m) elif typ == 'GPS_RAW': # better to just use global position int # (self.lat, self.lon) = (m.lat, m.lon) # self.__on_change('location') # Forward message self.fwd_msg_to_uav(m) elif typ == 'GPS_RAW_INT': # (self.lat, self.lon) = (m.lat / 1.0e7, m.lon / 1.0e7) self.gcs_eph = m.eph self.gcs_epv = m.epv self.gcs_satellites_visible = m.satellites_visible self.gcs_fix_type = m.fix_type # Forward message self.fwd_msg_to_uav(m) elif typ == "VFR_HUD": self.gcs_heading = m.heading self.gcs_alt = m.alt self.gcs_airspeed = m.airspeed self.gcs_groundspeed = m.groundspeed # Forward message self.fwd_msg_to_uav(m) elif typ == "ATTITUDE": self.gcs_pitch = m.pitch self.gcs_yaw = m.yaw self.gcs_roll = m.roll self.gcs_pitchspeed = m.pitchspeed self.gcs_yawspeed = m.yawspeed self.gcs_rollspeed = m.rollspeed # Forward message self.fwd_msg_to_uav(m) elif typ == "SYS_STATUS": self.gcs_voltage = m.voltage_battery self.gcs_current = m.current_battery self.gcs_level = m.battery_remaining elif typ == "HEARTBEAT": # Forward message self.fwd_msg_to_uav(m) # print "Parsing Message DONE" def close(self, *args, **kwargs): """ This closes down the serial connections and stop the GUI polling """ print 'Closing down connection' try: self.uav_polling = False self.uav.close() except: pass try: self.gcs_polling = False self.gcs.close() except: pass
class LinAlgSolve(tr.HasStrictTraits): '''Example of a linear equation system Solve the system of equations of the form :math A u = b_0 - b_t To define the boundary conditions prescribing the values on the left-hand-side a sequential inclusion of the condition is managed by this class. Thus, the registration of the condition is performed first to register the variables affected by the problem. ''' o_I = tr.Array(np.int_) n_dofs = tr.Property(depends_on='dof_map') @tr.cached_property def _get_n_dofs(self): return np.max(self.o_I) + 1 A = tr.Array(np.float_) R_t = tr.Property(tr.Array(np.float), depends_on='dof_map') @tr.cached_property def _get_R_t(self): return np.zeros((self.n_dofs,), np.float_) R_0 = tr.Property(tr.Array(np.float), depends_on='dof_map') @tr.cached_property def _get_R_0(self): return np.zeros((self.n_dofs,), np.float_) # The dependency graph constraints = tr.List '''Constraints are stored as a list of arrays. ''' def register_constraint(self, a_U, U_a=0, tf=lambda t: t, b_alpha=[], alpha_b=[]): '''Register the prescribed values of u as linear transformations - mappings ''' self.constraints.append((a_U, U_a, tf, b_alpha, alpha_b)) t_n1 = tr.Float(1.0) U_arr = tr.Property(depends_on='t_n1') @tr.cached_property def _get_U_arr(self): '''Get the value of the constraint for the current step. ''' carr = np.array(self.constraints) U_arr = carr[:, 1] tfarr = carr[:, 2] val_arr = np.array( [tf(u) for tf, u in zip(tfarr, U_arr)], dtype=np.float_) return val_arr def apply_constraints(self): carr = np.array(self.constraints) a_arr = np.array(carr[:, 0], dtype=np.int_) A_aa = self.A[a_arr, a_arr] self.A[a_arr, :] = 0 self.A[:, a_arr] = 0 self.A[a_arr, a_arr] = A_aa self.R_0[:] = np.einsum('i,ij->j', self.U_arr, self.A[a_arr, :]) def solve(self): '''Solve the system ''' return np.linalg.solve(self.A, self.R_0 - self.R_t)
class TStepBC(TStep): tloop_type = tr.Type(TLoopImplicit) primary_var_changed = tr.Event def __init__(self, *args, **kw): super().__init__(*args, **kw) self.fe_domain #========================================================================= # Time and state #========================================================================= t_n = tr.Float(0.0) '''Fundamental state time used for time dependent essential BC' ''' U_n = tr.Property(tr.Array(np.float_), depends_on='model_structure_changed') '''Fundamental value of the primary (control variable) ''' @tr.cached_property def _get_U_n(self): return np.zeros_like(self.U_k) '''Current fundamental value of the primary variable. ''' U_k = tr.Property(tr.Array(np.float_), depends_on='model_structure_changed') '''Fundamental value of the primary (control variable) ''' @tr.cached_property def _get_U_k(self): U_var_shape = self.fe_domain.U_var_shape return np.zeros(U_var_shape, dtype=np.float_).flatten() model_structure_changed = tr.Event bc = tr.List r'''Boundary conditions ''' record = tr.Dict r'''Recorded variables ''' # Boundary condition manager # bcond_mngr = tr.Property(tr.Instance(BCondMngr), depends_on='bc,bc_items,model_structure_changed') @tr.cached_property def _get_bcond_mngr(self): return BCondMngr(bcond_list=self.bc) def init_state(self): '''Initialize state. ''' self.t_n = 0.0 self.t_n1 = 0.0 self.model_structure_changed = True step_flag = tr.Enum('predictor', 'corrector') '''Step flag to control the inclusion of essential BC' ''' @tr.on_trait_change('model_structure_changed') def _reset(self): self.step_flag = 'predictor' K = tr.Property( tr.Instance(SysMtxAssembly), depends_on='model_structure_changed' ) '''System matrix with registered essencial boundary conditions. ''' @tr.cached_property def _get_K(self): K = SysMtxAssembly() self.bcond_mngr.setup(None) self.bcond_mngr.register(K) return K #========================================================================= # Spatial domain #========================================================================= domains = tr.List([]) r'''Spatial domain represented by a finite element discretization. providing the kinematic mapping between the linear algebra (vector and matrix) and field representation of the primary variables. ''' fe_domain = tr.Property(depends_on='model_structure_changed') @tr.cached_property def _get_fe_domain(self): domains = [ DomainState(tstep=self, xdomain=xdomain, tmodel=tmodel) for xdomain, tmodel in self.domains ] return XDomain(domains) corr_pred = tr.Property(depends_on='primary_var_changed,t_n1') @tr.cached_property def _get_corr_pred(self): self.K.reset_mtx() f_Eis, K_ks, dof_Es = np.array( [s.get_corr_pred(self.U_k, self.t_n, self.t_n1) for s in self.fe_domain] ).T self.K.sys_mtx_arrays = list(K_ks) # improve F_ext = np.zeros_like(self.U_k) self.bcond_mngr.apply( self.step_flag, None, self.K, F_ext, self.t_n, self.t_n1 ) F_int = np.bincount( np.hstack(np.hstack(dof_Es)), weights=np.hstack(np.hstack(f_Eis)) ) R = F_ext - F_int self.K.apply_constraints(R) if self.debug: print('t_n1', self.t_n1) print('U_k\n', self.U_k) print('F_int\n', F_int) print('F_ext\n', F_ext) return R, self.K, F_int def make_iter(self): '''Perform a single iteration ''' d_U_k, pos_def = self.K.solve(check_pos_def=True) if self.debug: print('positive definite', pos_def) self.U_k[:] += d_U_k self.primary_var_changed = True self.step_flag = 'corrector' def make_incr(self): '''Update the control, primary and state variables.. ''' self.U_n[:] = self.U_k[:] states = [d.record_state() for d in self.fe_domain] self.hist.record_timestep(self.t_n1, self.U_k, self.F_k, states) self.t_n = self.t_n1 self.step_flag = 'predictor' def __str__(self): s = '\nt_n: %g, t_n1: %g' % (self.t_n, self.t_n1) s += '\nU_n' + str(self.U_n) s += '\nU_k' + str(self.U_k) return s R_norm = tr.Property def _get_R_norm(self): R = self.R return np.sqrt(np.einsum('...i,...i', R, R)) R = tr.Property def _get_R(self): R, _, _ = self.corr_pred return R.flatten() dR = tr.Property def _get_dR(self): _, dR, _ = self.corr_pred return dR F_k = tr.Property def _get_F_k(self): _, _, F_k = self.corr_pred return F_k
class CXDViz(tr.HasTraits): coords = tr.Array() arr = tr.Array() cropx = tr.Int() cropy = tr.Int() cropz = tr.Int() def __init__(self): self.imd = tvtk.ImageData() self.sg = tvtk.StructuredGrid() pass def set_geometry(self, params, shape): lam = params.lamda tth = params.delta gam = params.gamma dpx = params.dpx dpy = params.dpy dth = params.dth dx = 1.0 / shape[0] dy = 1.0 / shape[1] dz = 1.0 / shape[2] dQdpx = np.zeros(3) dQdpy = np.zeros(3) dQdth = np.zeros(3) Astar = np.zeros(3) Bstar = np.zeros(3) Cstar = np.zeros(3) dQdpx[0] = -m.cos(tth) * m.cos(gam) dQdpx[1] = 0.0 dQdpx[2] = +m.sin(tth) * m.cos(gam) dQdpy[0] = m.sin(tth) * m.sin(gam) dQdpy[1] = -m.cos(gam) dQdpy[2] = m.cos(tth) * m.sin(gam) dQdth[0] = -m.cos(tth) * m.cos(gam) + 1.0 dQdth[1] = 0.0 dQdth[2] = m.sin(tth) * m.cos(gam) Astar[0] = 2 * m.pi / lam * dpx * dQdpx[0] Astar[1] = 2 * m.pi / lam * dpx * dQdpx[1] Astar[2] = 2 * m.pi / lam * dpx * dQdpx[2] Bstar[0] = (2 * m.pi / lam) * dpy * dQdpy[0] Bstar[1] = (2 * m.pi / lam) * dpy * dQdpy[1] Bstar[2] = (2 * m.pi / lam) * dpy * dQdpy[2] Cstar[0] = (2 * m.pi / lam) * dth * dQdth[0] Cstar[1] = (2 * m.pi / lam) * dth * dQdth[1] Cstar[2] = (2 * m.pi / lam) * dth * dQdth[2] denom = np.dot(Astar, np.cross(Bstar, Cstar)) A = 2 * m.pi * np.cross(Bstar, Cstar) / denom B = 2 * m.pi * np.cross(Cstar, Astar) / denom C = 2 * m.pi * np.cross(Astar, Bstar) / denom self.T = np.zeros(9) self.T.shape = (3, 3) space = 'direct' if space == 'recip': self.T[:, 0] = Astar self.T[:, 1] = Bstar self.T[:, 2] = Cstar self.dx = 1.0 self.dy = 1.0 self.dz = 1.0 elif space == 'direct': self.T = np.array((A, B, C)) self.dx = dx self.dy = dy self.dz = dz else: pass def update_coords(self): dims = list(self.arr[self.cropobj].shape) r = np.mgrid[(dims[0] - 1) * self.dx:-self.dx:-self.dx, \ 0:dims[1] * self.dy:self.dy, 0:dims[2] * self.dz:self.dz] r.shape = 3, dims[0] * dims[1] * dims[2] r = r.transpose() print r.shape print self.T.shape self.coords = np.dot(r, self.T) def set_array(self, array, logentry=None): self.arr = array if len(self.arr.shape) < 3: newdims = list(self.arr.shape) for i in range(3 - len(newdims)): newdims.append(1) self.arr.shape = tuple(newdims) def set_crop(self, cropx, cropy, cropz): dims = list(self.arr.shape) if len(dims) == 2: dims.append(1) if dims[0] > cropx and cropx > 0: self.cropx = cropx else: self.cropx = dims[0] if dims[1] > cropy and cropy > 0: self.cropy = cropy else: self.cropy = dims[1] if dims[2] > cropz and cropz > 0: self.cropz = cropz else: self.cropz = dims[2] start1 = dims[0] / 2 - self.cropx / 2 end1 = dims[0] / 2 + self.cropx / 2 if start1 == end1: end1 = end1 + 1 start2 = dims[1] / 2 - self.cropy / 2 end2 = dims[1] / 2 + self.cropy / 2 if start2 == end2: end2 = end2 + 1 start3 = dims[2] / 2 - self.cropz / 2 end3 = dims[2] / 2 + self.cropz / 2 if start3 == end3: end3 = end3 + 1 self.cropobj = (slice(start1, end1, None), slice(start2, end2, None), slice(start3, end3, None)) def get_structured_grid(self, **args): self.update_coords() dims = list(self.arr[self.cropobj].shape) self.sg.points = self.coords if args.has_key("mode"): if args["mode"] == "Phase": arr1 = self.arr[self.cropobj].ravel() arr = (np.arctan2(arr1.imag, arr1.real)) else: arr = np.abs(self.arr[self.cropobj].ravel()) else: arr = self.arr[self.cropobj].ravel() if (arr.dtype == np.complex128 or arr.dtype == np.complex64): self.sg.point_data.scalars = np.abs(arr) self.sg.point_data.scalars.name = "Amp" ph = tvtk.DoubleArray() ph.from_array(np.arctan2(arr.imag, arr.real)) ph.name = "Phase" self.sg.point_data.add_array(ph) else: self.sg.point_data.scalars = arr self.sg.dimensions = (dims[2], dims[1], dims[0]) self.sg.extent = 0, dims[2] - 1, 0, dims[1] - 1, 0, dims[0] - 1 return self.sg def get_image_data(self, **args): self.set_crop(self.cropx, self.cropy, self.cropz) dims = list(self.arr[self.cropobj].shape) if len(dims) == 2: dims.append(1) self.imd.dimensions = tuple(dims) self.imd.extent = 0, dims[2] - 1, 0, dims[1] - 1, 0, dims[0] - 1 self.imd.point_data.scalars = self.arr[self.cropobj].ravel() return self.imd def write_structured_grid(self, filename, **args): print 'in WriteStructuredGrid' sgwriter = tvtk.StructuredGridWriter() sgwriter.file_type = 'binary' if filename.endswith(".vtk"): sgwriter.file_name = filename else: sgwriter.file_name = filename + '.vtk' sgwriter.set_input_data(self.get_structured_grid()) print sgwriter.file_name sgwriter.write()
class FETS2D4Q(FETS2D): dof_r = tr.Array(np.float_, value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) geo_r = tr.Array(np.float_, value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) vtk_r = tr.Array(np.float_, value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) n_nodal_dofs = 2 vtk_r = tr.Array(np.float_, value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) vtk_cells = [[0, 1, 2, 3]] vtk_cell_types = 'Quad' vtk_cell = [0, 1, 2, 3] vtk_cell_type = 'Quad' vtk_expand_operator = tr.Array(np.float_, value=DELTA23_ab) # numerical integration points (IP) and weights xi_m = tr.Array(np.float_, value=[[-1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), -1.0 / np.sqrt(3.0)], [1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)], [-1.0 / np.sqrt(3.0), 1.0 / np.sqrt(3.0)] ]) w_m = tr.Array(value=[1, 1, 1, 1], dtype=np.float_) n_m = tr.Property def _get_n_m(self): return len(self.w_m) shape_function_values = tr.Property(tr.Tuple) '''The values of the shape functions and their derivatives at the IPs ''' @tr.cached_property def _get_shape_function_values(self): N_mi = np.array([N_xi_i.subs(list(zip([xi_1, xi_2], xi))) for xi in self.xi_m], dtype=np.float_)[...,0] N_im = np.einsum('mi->im', N_mi) dN_mir = np.array([dN_xi_ir.subs(list(zip([xi_1, xi_2], xi))) for xi in self.xi_m], dtype=np.float_).reshape(4, 4, 2) dN_nir = np.array([dN_xi_ir.subs(list(zip([xi_1, xi_2], xi))) for xi in self.vtk_r], dtype=np.float_).reshape(4, 4, 2) dN_imr = np.einsum('mir->imr', dN_mir) dN_inr = np.einsum('nir->inr', dN_nir) return (N_im, dN_imr, dN_inr) # shape_function_values = tr.Property(tr.Tuple) # '''The values of the shape functions and their derivatives at the IPs # ''' # @tr.cached_property # def _get_shape_function_values(self): # N_mi = get_N_xi_i(*self.xi_m.T) # N_im = np.einsum('mi->im', N_mi) # dN_mir = get_dN_xi_ir(*self.xi_m.T) # dN_nir = get_dN_xi_ir(*self.xi_n.T) # dN_imr = np.einsum('mir->imr', dN_mir) # dN_inr = np.einsum('nir->inr', dN_nir) # return (N_im, dN_imr, dN_inr) N_im = tr.Property() '''Shape function values in integration poindots. ''' def _get_N_im(self): return self.shape_function_values[0] dN_imr = tr.Property() '''Shape function derivatives in integration poindots. ''' def _get_dN_imr(self): return self.shape_function_values[1] dN_inr = tr.Property() '''Shape function derivatives in visualization poindots. ''' def _get_dN_inr(self): return self.shape_function_values[2]
class MomentCurvature(tr.HasStrictTraits): r'''Class returning the moment curvature relationship. ''' b_z = tr.Any get_b_z = tr.Property @tr.cached_property def _get_get_b_z(self): return sp.lambdify(z, self.b_z, 'numpy') h = tr.Float model_params = tr.Dict({ E_ct: 24000, E_cc: 25000, eps_cr: 0.001, eps_cy: -0.003, eps_cu: -0.01, mu: 0.33, eps_tu: 0.003 }) # Number of material points along the height of the cross section n_m = tr.Int(100) # Reinforcement z_j = tr.Array(np.float_, value=[10]) A_j = tr.Array(np.float_, value=[[np.pi * (16 / 2.)**2]]) E_j = tr.Array(np.float_, value=[[210000]]) eps_sy_j = tr.Array(np.float_, value=[[500. / 210000.]]) z_m = tr.Property(depends_on='n_m, h') @tr.cached_property def _get_z_m(self): return np.linspace(0, self.h, self.n_m) kappa_range = tr.Tuple(-0.001, 0.001, 101) kappa_t = tr.Property(tr.Array(np.float_), depends_on='kappa_range') @tr.cached_property def _get_kappa_t(self): return np.linspace(*self.kappa_range) get_eps_z = tr.Property(depends_on='model_params_items') @tr.cached_property def _get_get_eps_z(self): return sp.lambdify( (kappa, eps_bot, z), eps_z.subs(subs_eps), 'numpy' ) get_sig_c_z = tr.Property(depends_on='model_params_items') @tr.cached_property def _get_get_sig_c_z(self): return sp.lambdify( (kappa, eps_bot, z), sig_c_z_lin.subs(self.model_params), 'numpy' ) get_sig_s_eps = tr.Property(depends_on='model_params_items') @tr.cached_property def _get_get_sig_s_eps(self): return sp.lambdify((eps, E_s, eps_sy), sig_s_eps, 'numpy') # Normal force def get_N_s_tj(self, kappa_t, eps_bot_t): eps_z_tj = self.get_eps_z( kappa_t[:, np.newaxis], eps_bot_t[:, np.newaxis], self.z_j[np.newaxis, :] ) sig_s_tj = self.get_sig_s_eps(eps_z_tj, self.E_j, self.eps_sy_j) return np.einsum('j,tj->tj', self.A_j, sig_s_tj) def get_N_c_t(self, kappa_t, eps_bot_t): z_tm = self.z_m[np.newaxis, :] b_z_m = self.get_b_z(z_tm) # self.get_b_z(self.z_m) also OK N_z_tm = b_z_m * self.get_sig_c_z( kappa_t[:, np.newaxis], eps_bot_t[:, np.newaxis], z_tm ) return np.trapz(N_z_tm, x=z_tm, axis=-1) def get_N_t(self, kappa_t, eps_bot_t): N_s_t = np.sum(self.get_N_s_tj(kappa_t, eps_bot_t), axis=-1) return self.get_N_c_t(kappa_t, eps_bot_t) + N_s_t # SOLVER: Get eps_bot to render zero force eps_bot_t = tr.Property() r'''Resolve the tensile strain to get zero normal force for the prescribed curvature ''' def _get_eps_bot_t(self): res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t), 0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6) return res.x # POSTPROCESSING eps_cr = tr.Property() def _get_eps_cr(self): return np.array([self.model_params[eps_cr]], dtype=np.float_) kappa_cr = tr.Property() def _get_kappa_cr(self): res = root(lambda kappa: self.get_N_t(kappa, self.eps_cr), 0.0000001 + np.zeros_like(self.eps_cr), tol=1e-10) return res.x # Bending moment M_s_t = tr.Property() def _get_M_s_t(self): eps_z_tj = self.get_eps_z( self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], self.z_j[np.newaxis, :] ) sig_z_tj = self.get_sig_s_eps( eps_z_tj, self.E_j, self.eps_sy_j) return -np.einsum('j,tj,j->t', self.A_j, sig_z_tj, self.z_j) M_c_t = tr.Property() def _get_M_c_t(self): z_tm = self.z_m[np.newaxis, :] b_z_m = self.get_b_z(z_tm) N_z_tm = b_z_m * self.get_sig_c_z( self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], z_tm ) return -np.trapz(N_z_tm * z_tm, x=z_tm, axis=-1) M_t = tr.Property() def _get_M_t(self): return self.M_c_t + self.M_s_t N_s_tj = tr.Property() def _get_N_s_tj(self): return self.get_N_s_tj(self.kappa_t, self.eps_bot_t) eps_tm = tr.Property() def _get_eps_tm(self): return self.get_eps_z( self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], self.z_m[np.newaxis, :], ) sig_tm = tr.Property() def _get_sig_tm(self): return self.get_sig_c_z( self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], self.z_m[np.newaxis, :], ) idx = tr.Int(0) M_norm = tr.Property() def _get_M_norm(self): # Section modulus @TODO optimize W for var b W = (self.b * self.h**2) / 6 sig_cr = self.model_params[E_ct] * self.model_params[eps_cr] return W * sig_cr kappa_norm = tr.Property() def _get_kappa_norm(self): return self.kappa_cr def plot_norm(self, ax1, ax2): idx = self.idx ax1.plot(self.kappa_t / self.kappa_norm, self.M_t / self.M_norm) ax1.plot(self.kappa_t[idx] / self.kappa_norm, self.M_t[idx] / self.M_norm, marker='o') ax2.barh(self.z_j, self.N_s_tj[idx, :], height=2, color='red', align='center') #ax2.fill_between(eps_z_arr[idx,:], z_arr, 0, alpha=0.1); ax3 = ax2.twiny() # ax3.plot(self.eps_tm[idx, :], self.z_m, color='k', linewidth=0.8) ax3.plot(self.sig_tm[idx, :], self.z_m) ax3.axvline(0, linewidth=0.8, color='k') ax3.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1) self._align_xaxis(ax2, ax3) def plot(self, ax1, ax2): idx = self.idx ax1.plot(self.kappa_t, self.M_t / (1e6)) ax1.set_ylabel('Moment [kN.m]') ax1.set_xlabel('Curvature [$m^{-1}$]') ax1.plot(self.kappa_t[idx], self.M_t[idx] / (1e6), marker='o') ax2.barh(self.z_j, self.N_s_tj[idx, :], height=6, color='red', align='center') #ax2.plot(self.N_s_tj[idx, :], self.z_j, color='red') #print('Z', self.z_j) #print(self.N_s_tj[idx, :]) #ax2.fill_between(eps_z_arr[idx,:], z_arr, 0, alpha=0.1); ax3 = ax2.twiny() # ax3.plot(self.eps_tm[idx, :], self.z_m, color='k', linewidth=0.8) ax3.plot(self.sig_tm[idx, :], self.z_m) ax3.axvline(0, linewidth=0.8, color='k') ax3.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1) self._align_xaxis(ax2, ax3) def _align_xaxis(self, ax1, ax2): """Align zeros of the two axes, zooming them out by same ratio""" axes = (ax1, ax2) extrema = [ax.get_xlim() for ax in axes] tops = [extr[1] / (extr[1] - extr[0]) for extr in extrema] # Ensure that plots (intervals) are ordered bottom to top: if tops[0] > tops[1]: axes, extrema, tops = [list(reversed(l)) for l in (axes, extrema, tops)] # How much would the plot overflow if we kept current zoom levels? tot_span = tops[1] + 1 - tops[0] b_new_t = extrema[0][0] + tot_span * (extrema[0][1] - extrema[0][0]) t_new_b = extrema[1][1] - tot_span * (extrema[1][1] - extrema[1][0]) axes[0].set_xlim(extrema[0][0], b_new_t) axes[1].set_xlim(t_new_b, extrema[1][1])
class FETriangularMesh(bu.Model): name = 'FETriangularMesh' X_Id = tr.Array(np.float_, value=[[0, 0, 0], [2, 0, 0], [2, 2, 0], [1, 1, 0]]) I_Fi = tr.Array(np.int_, value=[ [0, 1, 3], [1, 2, 3], ]) fets = tr.Instance(FETSEval) def _fets_default(self): return FETS2D3U1M() show_node_labels = bu.Bool(False) n_nodal_dofs = tr.DelegatesTo('fets') dof_offset = tr.Int(0) n_active_elems = tr.Property def _get_n_active_elems(self): return len(self.I_Fi) ipw_view = bu.View(bu.Item('show_node_labels'), ) #========================================================================= # 3d Visualization #========================================================================= plot_backend = 'k3d' show_wireframe = bu.Bool(True) def setup_plot(self, pb): X_Id = self.X_Id.astype(np.float32) I_Fi = self.I_Fi.astype(np.uint32) fe_mesh = k3d.mesh(X_Id, I_Fi, color=0x999999, opacity=1.0, side='double') pb.plot_fig += fe_mesh pb.objects['mesh'] = fe_mesh if self.show_wireframe: k3d_mesh_wireframe = k3d.mesh(X_Id, I_Fi, color=0x000000, wireframe=True) pb.plot_fig += k3d_mesh_wireframe pb.objects['mesh_wireframe'] = k3d_mesh_wireframe if self.show_node_labels: self._add_nodes_labels_to_fig(pb, X_Id) NODES_LABELS = 'nodes_labels' def update_plot(self, pb): X_Id = self.X_Id.astype(np.float32) I_Fi = self.I_Fi.astype(np.uint32) mesh = pb.objects['mesh'] mesh.vertices = X_Id mesh.indices = I_Fi if self.show_wireframe: wireframe = pb.objects['mesh_wireframe'] wireframe.vertices = X_Id wireframe.indices = I_Fi if self.show_node_labels: if self.NODES_LABELS in pb.objects: pb.clear_object(self.NODES_LABELS) self._add_nodes_labels_to_fig(pb, X_Id) else: if self.NODES_LABELS in pb.objects: pb.clear_object(self.NODES_LABELS) def _add_nodes_labels_to_fig(self, pb, X_Id): text_list = [] for I, X_d in enumerate(X_Id): k3d_text = k3d.text('%g' % I, tuple(X_d), label_box=False, size=0.8, color=0x00FF00) pb.plot_fig += k3d_text text_list.append(k3d_text) pb.objects[self.NODES_LABELS] = text_list
class FETS2DMITC(FETSEval): r'''MITC3 shell finite element: See https://www.sesamx.io/blog/standard_linear_triangular_shell_element/ See http://dx.doi.org/10.1016/j.compstruc.2014.02.005 ''' vtk_r = tr.Array(np.float_, value=[[1, 0, 0], [0, 1, 0], [0, 0, 1]]) vtk_cells = [[0, 1, 2]] vtk_cell_types = 'Triangle' vtk_cell = [0, 1, 2] vtk_cell_type = 'Triangle' vtk_expand_operator = tr.Array(np.float_, value=DELTA23_ab) # ========================================================================= # Surface integrals using numerical integration # ========================================================================= # i is point indices, p is point coords in natural coords (zeta_1, zeta_2, zeta_3) eta_ip = tr.Array('float_') r'''Integration points within a triangle. ''' def _eta_ip_default(self): # We applay the 7-point Gauss integration to integrate exactly on the plane defined by rr and ss # [r, s, t] at each Gauss point (r, s, t) are natural coords in the shell element r,s in plane # and t along thickness # 2 Gauss points along thickness t # 7 Gauss points on the plane of the element return np.array([[0.1012865073235, 0.1012865073235, -0.5773502691896], [0.7974269853531, 0.1012865073235, -0.5773502691896], [0.1012865073235, 0.7974269853531, -0.5773502691896], [0.4701420641051, 0.0597158717898, -0.5773502691896], [0.4701420641051, 0.4701420641051, -0.5773502691896], [0.0597158717898, 0.4701420641051, -0.5773502691896], [0.3333333333333, 0.3333333333333, -0.5773502691896], [0.1012865073235, 0.1012865073235, 0.5773502691896], [0.7974269853531, 0.1012865073235, 0.5773502691896], [0.1012865073235, 0.7974269853531, 0.5773502691896], [0.4701420641051, 0.0597158717898, 0.5773502691896], [0.4701420641051, 0.4701420641051, 0.5773502691896], [0.0597158717898, 0.4701420641051, 0.5773502691896], [0.3333333333333, 0.3333333333333, 0.5773502691896]], dtype='f') w_m = tr.Array('float_') r'''Weight factors for numerical integration. ''' def _w_m_default(self): return np.array([ 0.0629695902724, 0.0629695902724, 0.0629695902724, 0.0661970763943, 0.0661970763943, 0.0661970763943, 0.1125, 0.0629695902724, 0.0629695902724, 0.0629695902724, 0.0661970763943, 0.0661970763943, 0.0661970763943, 0.1125 ], dtype='f') # TODO, different node thickness in each node according to the original # implementation can be easily integrated, but here the same thickness # is used for simplicity. a = tr.Float(1.0, label='thickness') n_m = tr.Property(depends_on='w_m') r'''Number of integration points. ''' @tr.cached_property def _get_n_m(self): return len(self.w_m) n_nodal_dofs = tr.Int(5) N_im = tr.Property(depends_on='eta_ip') r'''Shape function values in integration points. ''' @tr.cached_property def _get_N_im(self): # Shouldn't be mi instead of im? eta = self.eta_ip return np.array([1 - eta[:, 0] - eta[:, 1], eta[:, 0], eta[:, 1]], dtype='f') dh_imr = tr.Property(depends_on='eta_ip') r'''Derivatives of the shape functions in the integration points. ''' @tr.cached_property def _get_dh_imr(self): # Same for all Gauss points dh_ri = np.array( [ [-1, 1, 0], # dh1/d_r, dh2/d_r, dh3/d_r [-1, 0, 1], # dh1/d_s, dh2/d_s, dh3/d_s [0, 0, 0] ], # dh1/d_t, dh2/d_t, dh3/d_t dtype=np.float_) dh_mri = np.tile(dh_ri, (self.n_m, 1, 1)) return np.einsum('mri->imr', dh_mri) dht_imr = tr.Property(depends_on='eta_ip') r'''Derivatives of the (shape functions * t) in the integration points. ''' @tr.cached_property def _get_dht_imr(self): # m: gauss points, r: r, s, t, and i: h1, h2, h3 eta_ip = self.eta_ip dh_mri = np.array( [ [ [-t, t, 0], # (t*dh1)/d_r, (t*dh2)/d_r, (t*dh3)/d_r [-t, 0, t], # (t*dh1)/d_s, (t*dh2)/d_s, (t*dh3)/d_s [1 - r - s, r, s] ] # (t*dh1)/d_t, (t*dh2)/d_t, (t*dh3)/d_t for r, s, t in zip(eta_ip[:, 0], eta_ip[:, 1], eta_ip[:, 2]) ], dtype=np.float_) return np.einsum('mri->imr', dh_mri) dh_inr = tr.Property(depends_on='eta_ip') r'''Derivatives of the shape functions in the integration points. ''' @tr.cached_property def _get_dh_inr(self): return self.dh_imr def get_B(self): pass vtk_expand_operator = tr.Array(value=[1, 1, 0]) vtk_node_cell_data = tr.Array vtk_ip_cell_data = tr.Array
class FETS2D4u4x(FETSEval): dof_r = tr.Array(value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) geo_r = tr.Array(value=[[-1, -1], [1, -1], [1, 1], [-1, 1]]) vtk_r = [[-1, -1], [1, -1], [1, 1], [-1, 1]] n_nodal_dofs = 2
class MKappa(InteractiveModel, InjectSymbExpr): """Class returning the moment curvature relationship.""" name = 'Moment-Curvature' symb_class = MKappaSymbolic cs_design = Instance(CrossSectionDesign, ()) tree = ['cs_design'] # Use PrototypedFrom only when the prototyped object is a class # (The prototyped attribute behaves similarly # to a delegated attribute, until it is explicitly # changed; from that point forward, the prototyped attribute # changes independently from its prototype.) # (it's kind of like tr.DelegatesTo('cs_design.cross_section_shape')) cross_section_shape = tr.DelegatesTo('cs_design') cross_section_shape_ = tr.DelegatesTo('cs_design') cross_section_layout = tr.DelegatesTo('cs_design') matrix_ = tr.DelegatesTo('cs_design') # Geometry H = tr.DelegatesTo('cross_section_shape_') DEPSTR = 'state_changed' n_m = Int( 100, DSC=True, desc= 'Number of discretization points along the height of the cross-section' ) # @todo: fix the dependency - `H` should be replaced by _GEO z_m = tr.Property(depends_on=DEPSTR) @tr.cached_property def _get_z_m(self): return np.linspace(0, self.H, self.n_m) low_kappa = Float(0.0, BC=True, GEO=True) high_kappa = Float(0.00002, BC=True, GEO=True) n_kappa = Int(100, BC=True) step_kappa = tr.Property(Float, depends_on='low_kappa, high_kappa') @tr.cached_property def _get_step_kappa(self): return float((self.high_kappa - self.low_kappa) / self.n_kappa) kappa_slider = Float(0.0000001) ipw_view = View( Item( 'low_kappa', latex=r'\text{Low}~\kappa'), #, editor=FloatEditor(step=0.00001)), Item('high_kappa', latex=r'\text{High}~\kappa' ), # , editor=FloatEditor(step=0.00001)), Item('n_kappa', latex='n_{\kappa}'), Item('plot_strain'), Item('solve_for_eps_bot_pointwise'), Item('n_m', latex='n_m'), Item('kappa_slider', latex='\kappa', readonly=True), # editor=FloatRangeEditor(low_name='low_kappa', # high_name='high_kappa', # n_steps_name='n_kappa') # ), time_editor=HistoryEditor( var='kappa_slider', min_var='low_kappa', max_var='high_kappa', ), ) idx = tr.Property(depends_on='kappa_slider') apply_material_safety_factors = tr.Bool(False) @tr.cached_property def _get_idx(self): ks = self.kappa_slider idx = np.argmax(ks <= self.kappa_t) return idx kappa_t = tr.Property(tr.Array(np.float_), depends_on=DEPSTR) '''Curvature values for which the bending moment must be found ''' @tr.cached_property def _get_kappa_t(self): return np.linspace(self.low_kappa, self.high_kappa, self.n_kappa) z_j = tr.Property def _get_z_j(self): return self.cross_section_layout.z_j A_j = tr.Property def _get_A_j(self): return self.cross_section_layout.A_j # Normal force in steel (tension and compression) def get_N_s_tj(self, kappa_t, eps_bot_t): # get the strain at the height of the reinforcement eps_z_tj = self.symb.get_eps_z(kappa_t[:, np.newaxis], eps_bot_t[:, np.newaxis], self.z_j[np.newaxis, :]) # Get the crack bridging force in each reinforcement layer # given the corresponding crack-bridge law. N_s_tj = self.cross_section_layout.get_N_tj(eps_z_tj) return N_s_tj # TODO - [RC] avoid repeated evaluations of stress profile in # N and M calculations for the same inputs as it # is the case now. def get_sig_c_z(self, kappa_t, eps_bot_t, z_tm): """Get the stress profile over the height""" eps_z = self.symb.get_eps_z(kappa_t[:, np.newaxis], eps_bot_t[:, np.newaxis], z_tm) sig_c_z = self.matrix_.get_sig(eps_z) return sig_c_z # Normal force in concrete (tension and compression) def get_N_c_t(self, kappa_t, eps_bot_t): z_tm = self.z_m[np.newaxis, :] b_z_m = self.cross_section_shape_.get_b(z_tm) N_z_tm2 = b_z_m * self.get_sig_c_z(kappa_t, eps_bot_t, z_tm) return np.trapz(N_z_tm2, x=z_tm, axis=-1) def get_N_t(self, kappa_t, eps_bot_t): N_s_t = np.sum(self.get_N_s_tj(kappa_t, eps_bot_t), axis=-1) N_c_t = self.get_N_c_t(kappa_t, eps_bot_t) return N_c_t + N_s_t # SOLVER: Get eps_bot to render zero force # num_of_trials = tr.Int(30) eps_bot_t = tr.Property(depends_on=DEPSTR) r'''Resolve the tensile strain to get zero normal force for the prescribed curvature''' # @tr.cached_property # def _get_eps_bot_t(self): # initial_step = (self.high_kappa - self.low_kappa) / self.num_of_trials # for i in range(self.num_of_trials): # print('Solution started...') # res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t), # 0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6) # if res.success: # print('success high_kappa: ', self.high_kappa) # if i == 0: # print('Note: high_kappa success from 1st try! selecting a higher value for high_kappa may produce ' # 'a more reliable result!') # return res.x # else: # print('failed high_kappa: ', self.high_kappa) # self.high_kappa -= initial_step # self.kappa_t = np.linspace(self.low_kappa, self.high_kappa, self.n_kappa) # # print('No solution', res.message) # return res.x # @tr.cached_property # def _get_eps_bot_t(self): # res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t), # 0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6) # if not res.success: # raise SolutionNotFoundError('No solution', res.message) # return res.x solve_for_eps_bot_pointwise = Bool(True, BC=True, GEO=True) @tr.cached_property def _get_eps_bot_t(self): if self.solve_for_eps_bot_pointwise: """ INFO: Instability in eps_bot solutions was caused by unsuitable init_guess value causing a convergence to non-desired solutions. Solving the whole kappa_t array improved the init_guess after each calculated value, however, instability still there. The best results were obtained by taking the last solution as the init_guess for the next solution like in the following.. """ # One by one solution for kappa values eps_bot_sol_for_pos_kappa = self._get_eps_bot_piecewise_sol( kappa_pos=True) eps_bot_sol_for_neg_kappa = self._get_eps_bot_piecewise_sol( kappa_pos=False) res = np.concatenate( [eps_bot_sol_for_neg_kappa, eps_bot_sol_for_pos_kappa]) return res else: # Array solution for the whole kappa_t res = root(lambda eps_bot_t: self.get_N_t(self.kappa_t, eps_bot_t), 0.0000001 + np.zeros_like(self.kappa_t), tol=1e-6) if not res.success: print('No solution', res.message) return res.x def _get_eps_bot_piecewise_sol(self, kappa_pos=True): if kappa_pos: kappas = self.kappa_t[np.where(self.kappa_t >= 0)] else: kappas = self.kappa_t[np.where(self.kappa_t < 0)] res = [] if kappa_pos: init_guess = 0.00001 kappa_loop_list = kappas else: init_guess = -0.00001 kappa_loop_list = reversed(kappas) for kappa in kappa_loop_list: sol = root( lambda eps_bot: self.get_N_t(np.array([kappa]), eps_bot), np.array([init_guess]), tol=1e-6).x[0] # This condition is to avoid having init_guess~0 which causes non-convergence if abs(sol) > 1e-5: init_guess = sol res.append(sol) if kappa_pos: return res else: return list(reversed(res)) # POSTPROCESSING kappa_cr = tr.Property(depends_on=DEPSTR) '''Curvature at which a critical strain is attained at the eps_bot''' @tr.cached_property def _get_kappa_cr(self): res = root(lambda kappa: self.get_N_t(kappa, self.eps_cr), 0.0000001 + np.zeros_like(self.eps_cr), tol=1e-10) if not res.success: print('No kappa_cr solution (for plot_norm() function)', res.message) return res.x M_s_t = tr.Property(depends_on=DEPSTR) '''Bending moment (steel) ''' @tr.cached_property def _get_M_s_t(self): if len(self.z_j) == 0: return np.zeros_like(self.kappa_t) eps_z_tj = self.symb.get_eps_z(self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], self.z_j[np.newaxis, :]) # Get the crack bridging force in each reinforcement layer # given the corresponding crack-bridge law. N_tj = self.cross_section_layout.get_N_tj(eps_z_tj) return -np.einsum('tj,j->t', N_tj, self.z_j) M_c_t = tr.Property(depends_on=DEPSTR) '''Bending moment (concrete) ''' @tr.cached_property def _get_M_c_t(self): z_tm = self.z_m[np.newaxis, :] b_z_m = self.cross_section_shape_.get_b(z_tm) N_z_tm2 = b_z_m * self.get_sig_c_z(self.kappa_t, self.eps_bot_t, z_tm) return -np.trapz(N_z_tm2 * z_tm, x=z_tm, axis=-1) M_t = tr.Property(depends_on=DEPSTR) '''Bending moment ''' @tr.cached_property def _get_M_t(self): # print('M - k recalculated') eta_factor = 1. return eta_factor * (self.M_c_t + self.M_s_t) # @tr.cached_property # def _get_M_t(self): # initial_step = (self.high_kappa - self.low_kappa) / self.num_of_trials # for i in range(self.num_of_trials): # try: # M_t = self.M_c_t + self.M_s_t # except SolutionNotFoundError: # print('failed high_kappa: ', self.high_kappa) # self.high_kappa -= initial_step # else: # # This will run when no exception has been received # print('success high_kappa: ', self.high_kappa) # if i == 0: # print('Note: high_kappa success from 1st try! selecting a higher value for high_kappa may produce ' # 'a more reliable result!') # return M_t # print('No solution has been found!') # return M_t N_s_tj = tr.Property(depends_on=DEPSTR) '''Normal forces (steel) ''' @tr.cached_property def _get_N_s_tj(self): return self.get_N_s_tj(self.kappa_t, self.eps_bot_t) eps_tm = tr.Property(depends_on=DEPSTR) '''strain profiles ''' @tr.cached_property def _get_eps_tm(self): return self.symb.get_eps_z(self.kappa_t[:, np.newaxis], self.eps_bot_t[:, np.newaxis], self.z_m[np.newaxis, :]) sig_tm = tr.Property(depends_on=DEPSTR) '''strain profiles ''' @tr.cached_property def _get_sig_tm(self): return self.get_sig_c_z(self.kappa_t, self.eps_bot_t, self.z_m[np.newaxis, :]) M_norm = tr.Property(depends_on=DEPSTR) ''' ''' @tr.cached_property def _get_M_norm(self): # Section modulus @TODO optimize W for var b W = (self.b * self.H**2) / 6 sig_cr = self.E_ct * self.eps_cr return W * sig_cr kappa_norm = tr.Property() def _get_kappa_norm(self): return self.kappa_cr inv_M_kappa = tr.Property(depends_on=DEPSTR) '''Return the inverted data points ''' @tr.cached_property def _get_inv_M_kappa(self): try: """cut off the descending tails""" M_t = self.M_t I_max = np.argmax(M_t) I_min = np.argmin(M_t) M_I = np.copy(M_t[I_min:I_max + 1]) kappa_I = np.copy(self.kappa_t[I_min:I_max + 1]) # find the index corresponding to zero kappa idx = np.argmax(0 <= kappa_I) # and modify the values such that the # Values of moment are non-descending M_plus = M_I[idx:] M_diff = M_plus[:, np.newaxis] - M_plus[np.newaxis, :] n_ij = len(M_plus) ij = np.mgrid[0:n_ij:1, 0:n_ij:1] M_diff[np.where(ij[1] >= ij[0])] = 0 i_x = np.argmin(M_diff, axis=1) M_I[idx:] = M_plus[i_x] return M_I, kappa_I except ValueError: print( 'M inverse has not succeeded, the M-Kappa solution may have failed due to ' 'a wrong kappa range or not suitable material law!') return np.array([0]), np.array([0]) def get_kappa_M(self, M): M_I, kappa_I = self.inv_M_kappa return np.interp(M, M_I, kappa_I) def plot_norm(self, ax1, ax2): idx = self.idx ax1.plot(self.kappa_t / self.kappa_norm, self.M_t / self.M_norm) ax1.plot(self.kappa_t[idx] / self.kappa_norm, self.M_t[idx] / self.M_norm, marker='o') ax2.barh(self.z_j, self.N_s_tj[idx, :], height=2, color='red', align='center') # ax2.fill_between(eps_z_arr[idx,:], z_arr, 0, alpha=0.1); ax3 = ax2.twiny() # ax3.plot(self.eps_tm[idx, :], self.z_m, color='k', linewidth=0.8) ax3.plot(self.sig_tm[idx, :], self.z_m) ax3.axvline(0, linewidth=0.8, color='k') ax3.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1) mpl_align_xaxis(ax2, ax3) M_scale = Float(1e+6) plot_strain = Bool(False) def plot(self, ax1, ax2, ax3): self.plot_mk_and_stress_profile(ax1, ax2) if self.plot_strain: self.plot_strain_profile(ax3) else: self.plot_mk_inv(ax3) @staticmethod def subplots(fig): ax1, ax2, ax3 = fig.subplots(1, 3) return ax1, ax2, ax3 def update_plot(self, axes): self.plot(*axes) def plot_mk_inv(self, ax3): try: M, kappa = self.inv_M_kappa ax3.plot(M / self.M_scale, kappa) except ValueError: print( 'M inverse has not succeeded, the M-Kappa solution may have failed due to a wrong kappa range!' ) ax3.set_xlabel('Moment [kNm]') ax3.set_ylabel('Curvature[mm$^{-1}$]') def plot_mk_and_stress_profile(self, ax1, ax2): self.plot_mk(ax1) idx = self.idx ax1.plot(self.kappa_t[idx], self.M_t[idx] / self.M_scale, color='orange', marker='o') if len(self.z_j): ax2.barh(self.z_j, self.N_s_tj[idx, :] / self.A_j, height=4, color='red', align='center') ax2.set_ylabel('z [mm]') ax2.set_xlabel('$\sigma_r$ [MPa]') ax22 = ax2.twiny() ax22.set_xlabel('$\sigma_c$ [MPa]') ax22.plot(self.sig_tm[idx, :], self.z_m) ax22.axvline(0, linewidth=0.8, color='k') ax22.fill_betweenx(self.z_m, self.sig_tm[idx, :], 0, alpha=0.1) mpl_align_xaxis(ax2, ax22) def plot_mk(self, ax1): ax1.plot(self.kappa_t, self.M_t / self.M_scale, label='M-K') ax1.set_ylabel('Moment [kNm]') ax1.set_xlabel('Curvature [mm$^{-1}$]') ax1.legend() def plot_strain_profile(self, ax): ax.set_ylabel('z [mm]') ax.set_xlabel(r'$\varepsilon$ [-]') ax.plot(self.eps_tm[self.idx, :], self.z_m) ax.axvline(0, linewidth=0.8, color='k') ax.fill_betweenx(self.z_m, self.eps_tm[self.idx, :], 0, alpha=0.1) def get_mk(self): return self.M_t / self.M_scale, self.kappa_t