def set_height(self, height): """(Deprecated) Set the height of the rectangle.""" warning_message('set_width(...) is deprecated. Use property ' \ 'myrect.width=... instead.', 'emopt.grid') libGrid.Rectangle_set_height(self._object, height) self._yspan = height
def set_width(self, width): """(Deprecated) Set the width of the rectangle. """ warning_message('set_width(...) is deprecated. Use property ' \ 'myrect.width=... instead.', 'emopt.grid') libGrid.Rectangle_set_width(self._object, width) self._xspan = width
def set_layer(self, layer): """Set the layer of the primitive. Parameters ---------- layer : int The new layer. """ warning_message('set_layer(...) is deprecated. Use property ' \ 'myprim.layer=... instead.', 'emopt.grid') libGrid.MaterialPrimitive_set_layer(self._object, c_int(layer))
def get_layer(self): """Get the layer of the primitive. Returns ------- int The layer. """ warning_message('get_layer() is deprecated. Use property ' \ 'myprim.layer instead.', 'emopt.grid') return libGrid.MaterialPrimitive_get_layer(self._object)
def set_for_version(self, value): self.for_version = self.for_version_prop.get_string_value() if self.for_version.startswith('3.'): ## disable lisp for wx > 2.8 if self.codewriters_prop.get_string_value() == 'lisp': misc.warning_message( _('Generating Lisp code for wxWidgets version %s is not supported.\n' 'Set version to "2.8" instead.') % self.for_version ) self.for_version_prop.set_str_value('2.8') self.set_for_version('2.8') return self.codewriters_prop.enable_item('lisp', False) else: # enable lisp again self.codewriters_prop.enable_item('lisp', True)
def _set_language(self): "Set code generator language and adapt corresponding settings like file dialog wild cards (value: str or int)" language = self.language # update wildcards and default extension in the dialog self._update_output_path(language) # check that the new language supports all the widgets in the tree self.check_codegen() # disable lisp for wx > 2.8 if language == 'lisp': for_version = self.for_version if for_version == '3.0': misc.warning_message( _('Generating Lisp code for wxWidgets version %s is not supported.\n' 'Set version to "2.8" instead.') % self.for_version) self.properties["for_version"].set('2.8') self.properties["for_version"].set_blocked(True) else: self.properties["for_version"].set_blocked(False) # don't change the extension in multiple files mode if self.multiple_files == 1: return # update file extensions current_name = self.output_path if not current_name: return base, ext = os.path.splitext(current_name) # is already a valid extension? ext has a leading . but default_extensions hasn't if ext and ext[1:] in common.code_writers[language].default_extensions: return new_name = "%s.%s" % ( base, common.code_writers[language].default_extensions[0]) self.properties["output_path"].set(new_name) blocked = self.language != "C++" self.properties["source_extension"].set_blocked(blocked) self.properties["header_extension"].set_blocked(blocked)
def check_gradient(self, params, indices=[], plot=True): """Verify that the gradient is accurate. It is highly recommended that the accuracy of the gradients be checked prior to being used. If the accuracy is above ~1%, it is likely that there is an inconsitency between how the figure of merit and adjoint sources (dFdx) are being computed. The adjoint method gradient error is evaluated by comparing the gradient computed using the adjoint method to a gradient computed by direct finite differences. In other words, the "correct" derivatives to which the adjoint method gradient is compared are given by .. math:: \\frac{\partial F}{\partial p_i} \\approx \\frac{F(p_i + \Delta p) - F(p_i)}{\Delta p} Note that this method for calculating the gradient is not used in a practical setting because it requires performing N+1 simulations in order to compute the gradient with respect to N design variables (compared to only 2 simulations in the case of the adjoint method). Parameters ---------- params : numpy.ndarray design parameters indices : list or numpy.ndarray list of gradient indices to check. An empty list indicates that the whole gradient should be verified. A subset of indices may be desirable for large problems. (default = []) Returns ------- float Relative error in gradient. """ if (indices == []): indices = np.arange(0, len(params), 1) # make sure everything is up to date self.update_system(params) self.sim.update() grad_am = self.gradient(params) grad_fd = np.zeros(len(indices)) fom0 = self.fom(params) # calculate the "true" derivatives using finite differences if (NOT_PARALLEL): info_message('Checking gradient...') for i in range(len(indices)): if (NOT_PARALLEL): print('\tDerivative %d of %d' % (i + 1, len(indices))) j = indices[i] p0 = params[j] params[j] += self._step fom1 = self.fom(params) if (NOT_PARALLEL): grad_fd[i] = (fom1 - fom0) / self._step params[j] = p0 if (NOT_PARALLEL): errors = np.abs(grad_fd - grad_am[indices]) / np.abs(grad_fd) error_tot = np.linalg.norm( grad_fd - grad_am[indices]) / np.linalg.norm(grad_fd) if (error_tot < 0.01): info_message('The total error in the gradient is %0.4E' % \ (error_tot)) else: warning_message('The total error in the gradient is %0.4E ' 'which is over 1%%' % (error_tot), \ 'emopt.adjoint_method') if (plot): import matplotlib.pyplot as plt f = plt.figure() ax1 = f.add_subplot(311) ax2 = f.add_subplot(312) ax3 = f.add_subplot(313) ax1.bar(indices, grad_fd) ax1.set_title('Finite Differences') ax2.bar(indices, grad_am[indices]) ax2.set_title('Adjoint Method') ax3.bar(indices, errors) ax3.set_title('Error in Adjoint Method') ax3.set_yscale('log', nonposy='clip') plt.show() return error_tot else: return None
def step(self, val): if (np.abs(val) > self.sim.dx / 1e3): if (NOT_PARALLEL): warning_message('Step size used for adjoint method may be too ' 'large. Consider reducing it to ~1e-3*dx') self._step = val
def plot_iteration(field, structure, W, H, foms, fname='', layout='auto', show_now=False, dark=True, Nmats=2): """Plot the current iteration of an optimization. Plots the following: 1) A 2D field 2) A 2D representation of the geometry 3) A line plot of the figure of merit history When plotting 3D structures, multiple 2D slices of the geometry can be passed in by passing a 3D array to structure. The different "pages" of this array will be flattened into a 2D representation of the sliced geometry. Furthermore, multiple figures of merit may be plotted. This is done by putting more than one key:value pair in the foms dictionary. The key names will be used as the legend label. Parameters ---------- field : numpy.ndarray 2D array containing current iteration's field slice structure : numpy.ndarray Either a 2D array containing a representation of the current structure or a 3D array containing a FEW 2D slices. W : float The width of the field/structure H : float The height of the field/structure foms : dict A dictionary containing the fom history. The key strings should be names which describe each supplied figure of merit fname : str Filename for saving layout : str Layout method. 'auto' = automatically choose layout based on aspect ratio. 'horizontal' = single row layour. 'vertical' = single column layout. 'balanced' = field+structure on left, foms on right. show_now : bool If True, show plot now (warning: this is a blocking operation) dark : bool If True, use a dark color scheme for plot. (default = True) Returns ------- matplotlib.pyplot.figure The current matplotlib figure """ import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec from matplotlib.colors import LinearSegmentedColormap dpi = 300 aspect = W / H # determine and setup the plot layout unknown_layout = layout not in [ 'auto', 'horizontal', 'vertical', 'balanced' ] if (unknown_layout): warning_message("Unknown layout '%s'. Using 'auto'" % (layout), 'emopt.io') if (layout == 'auto' or unknown_layout): if (aspect < 1.0): layout = 'horizontal' elif (aspect >= 1.0 and aspect <= 2.5): layout = 'balanced' else: layout = 'vertical' gs = None Wplot = 10.0 if (layout == 'horizontal'): f = plt.figure(figsize=(Wplot * aspect * 3, Wplot)) gs = gridspec.GridSpec(1, 3, height_ratios=[1]) ax_field = f.add_subplot(gs[:, 0]) ax_struct = f.add_subplot(gs[:, 1]) ax_foms = f.add_subplot(gs[:, 2]) elif (layout == 'vertical'): f = plt.figure(figsize=(Wplot, Wplot / aspect * 3)) gs = gridspec.GridSpec(3, 1, height_ratios=[1, 1, 1]) ax_field = f.add_subplot(gs[0, :]) ax_struct = f.add_subplot(gs[1, :]) ax_foms = f.add_subplot(gs[2, :]) elif (layout == 'balanced'): f = plt.figure(figsize=(Wplot, Wplot / aspect * 2 / 1.5)) gs = gridspec.GridSpec(2, 3, height_ratios=[1, 1]) ax_field = f.add_subplot(gs[0, 0:2]) ax_struct = f.add_subplot(gs[1, 0:2]) ax_foms = f.add_subplot(gs[:, 2]) # define dark colormaps if (dark): field_cols = ['#3d9aff', '#111111', '#ff3d63'] field_cmap = LinearSegmentedColormap.from_list('field_cmap', field_cols) struct_cols = ['#212730', '#bcccdb'] struct_cmap = LinearSegmentedColormap.from_list( 'struct_cmap', struct_cols) else: field_cmap = 'seismic' struct_cmap = 'Blues' extent = [0, W, 0, H] fmin = -1 * np.max(np.abs(field)) fmax = -1 * fmin ax_field.imshow(field, extent=extent, vmin=fmin, vmax=fmax, cmap=field_cmap) # "flatten" multi-layer structure if (len(structure.shape) == 3): Nlevels = structure.shape[0] + 1 structure = np.sum(structure, axis=0) / structure.shape[0] else: Nlevels = Nmats smin = np.min(structure) smax = np.max(structure) ax_struct.imshow(structure, extent=extent, vmin=smin, vmax=smax, cmap=struct_cmap) # outline structure in field plot ax_field.contour(np.flipud(structure), extent=extent, levels=Nlevels, colors='#666666', linewidths=0.1) # set dark colors # plot title with important iteration number, etc # sum along z-axis for structure # define fom plot colors Nplot = len(foms.keys()) red = np.linspace(0.2, 1.0, Nplot) blue = np.linspace(1.0, 0.2, Nplot) green = np.zeros(Nplot) red_base = 0.0 blue_base = 0.0 green_base = 0.55 i = 0 Niter = 0 current_foms = [] for desc in foms.keys(): fom = foms[desc] Niter = len(fom) iters = np.arange(Niter) current_foms.append(fom[-1]) pcolor = (red_base + red[i], green_base + green[i], blue_base + blue[i]) i += 1 pline = ax_foms.plot(iters, fom, '.-', color=pcolor, markersize=10) ax_foms.set_xlabel('Iteration', fontsize=12) ax_foms.set_ylabel('Figure of Merit', fontsize=12) ax_foms.legend(foms.keys(), loc=4) ax_foms.grid(True, linewidth=0.5) # general tick properties for ax in [ax_field, ax_struct, ax_foms]: ax.get_yaxis().set_tick_params(which='both', direction='in', top=True, right=True) ax.get_xaxis().set_tick_params(which='both', direction='in', top=True, right=True) # Dark theme easier on eyes if (dark): c_text_main = '#BBBBBB' c_bg_main = '#101010' c_plot_bg = '#353535' c_lines = '#666666' c_plot_tick = '#CCCCCC' c_plot_grid = '#555555' f.patch.set_facecolor(c_bg_main) for ax in [ax_field, ax_struct, ax_foms]: for tl in ax.get_xticklabels(): tl.set_color(c_text_main) for tl in ax.get_yticklabels(): tl.set_color(c_text_main) ax.xaxis.get_label().set_color(c_text_main) ax.yaxis.get_label().set_color(c_text_main) for spine in ax.spines: ax.spines[spine].set_color(c_lines) ax.get_yaxis().set_tick_params(color=c_plot_tick) ax.get_xaxis().set_tick_params(color=c_plot_tick) ax_foms.set_facecolor(c_plot_bg) ax_foms.grid(True, color=c_plot_grid, linewidth=0.5) else: c_text_main = '#000000' # title contains info of current iteration f.suptitle(''.join(['Iteration %d, ' % (Niter), 'FOMs = '] + ['%0.4f ' % (fom) for fom in current_foms]), fontsize=12, color=c_text_main) if (fname != ''): if (dark): plt.savefig(fname, dpi=300, facecolor=c_bg_main, bbox_inches='tight') else: plt.savefig(fname, dpi=300, bbox_inches='tight') if (show_now): plt.tight_layout() plt.show()
def zmaxs(self): warning_message( 'The list of maximum z coordinates cannot be changed in this way.', 'emopt.grid')
def primitive(self): warning_message('The primitive list cannot be modified in this way.', 'emopt.grid')
def set_material(self, mat): warning_message('set_material(...) is deprecated. Use property ' \ 'mypoly.material_value=... instead.', 'emopt.grid') libGrid.Polygon_set_material(self._object, mat.real, mat.imag) self._value = mat
def Np(self, N): warning_message('Polygon.Np cannot be modified in this way.', \ 'emopt.grid.Polygon')
def ys(self, vals): warning_message('Polygon.ys cannot be set on its own. Use ' \ 'set_points(x,y) instead', 'emopt.grid.Polygon')
def set_material(self, mat): """(Deprecated) Set the material value of the Rectangle's interior. """ warning_message('set_mateiral(...) is deprecated. Use property ' \ 'myrect.material_value=... instead.', 'emopt.grid') libGrid.Rectangle_set_material(self._object, mat.real, mat.imag)
def fillet(x, y, R, make_round=None, points_per_90=10, equal_thresh=1e-8, ignore_roc_lim=False): """Round corners of a polygon. This function replaces sharp corners with circular arcs. The radius of these arcs will be equal to the specified radius as long as the line segments which make up the corner are sufficiently long. If they are too short, the arc will be made with a smaller radius. If a non-closed polygon is supplied, the end points will be ignored. A polygon is considered closed if the first and last point in the provided x,y coordinate lists are the same. Parameters ---------- x : list The x coordinates of the chain of line segments to round. y : list The y coordinates of the chain of line segments to round. R : float The desired fillet radius make_round : list or None (optional) A list of boolean values which specifies which points should be rounded. If None, then all roundable points are rounded. If supplying a list, the list must have the same length as x and y. (default = None) points_per_90 : int (optional) The number of points to generate per 90 degrees of the arc. (default = 10) equal_thresh : float The threshold used for comparing values. If the |difference| between two values is less than this value, they are considered equal. Returns ------- list, list The x and y coordinates of the new set of lines segments or polygon. """ xn = [] yn = [] i = 0 N = len(x) N0 = N # we always ignore the last point N -= 1 closed = True if(x[0] != x[-1] or y[0] != y[-1]): closed = False i += 1 xn.append(x[0]) yn.append(y[0]) i = 0 while(i < N): if(make_round is None or make_round[i]): # get current and adjacent points x1 = x[i] y1 = y[i] if(i == 0): x0 = x[-2] y0 = y[-2] else: x0 = x[i-1] y0 = y[i-1] if(i == N0-1): x2 = x[1] y2 = y[1] else: x2 = x[i+1] y2 = y[i+1] # calc angle dx10 = x0-x1 dy10 = y0-y1 dx12 = x2-x1 dy12 = y2-y1 d10 = np.sqrt(dx10**2 + dy10**2) d12 = np.sqrt(dx12**2 + dy12**2) theta = np.arccos((dx10*dx12 + dy10*dy12)/(d10*d12)) if(theta != 0 and theta != pi): dxc = (dx10/d10 + dx12/d12) dyc = (dy10/d10 + dy12/d12) dc = np.sqrt(dxc**2 + dyc**2) nxc = dxc/dc nyc = dyc/dc nx10 = dx10/d10 ny10 = dy10/d10 nx12 = dx12/d12 ny12 = dy12/d12 # reduce fillet radius if necessary Ri = R iprev = i-1 inext = i+1 if(iprev < 0): iprev = N0-1 if(inext > N0-1): inext = 0 if( (d10 < 2*R or d12 < 2*R) and not ignore_roc_lim): Ri = np.min([d12, d10])/2.0 if(NOT_PARALLEL): warning_message('Warning: Desired radius of curvature too large at ' \ 'point %d. Reducing to maximum allowed.' % \ (i), 'emopt.geometry') # figure out where the circle "fits" in the corner S10 = Ri / np.tan(theta/2.0) S12 = S10 Sc = np.sqrt(S10**2 + Ri**2) # generate the fillet theta1 = np.arctan2((S10*ny10 - Sc*nyc), (S10*nx10 - Sc*nxc)) theta2 = np.arctan2((S12*ny12 - Sc*nyc), (S12*nx12 - Sc*nxc)) if(theta1 < 0): theta1 += 2*pi if(theta2 < 0): theta2 += 2*pi theta1 = np.mod(theta1, 2*pi) theta2 = np.mod(theta2, 2*pi) if(theta2 - theta1 > pi): theta2 -= 2*pi elif(theta1 - theta2 > pi): theta2 += 2*pi Np = int(np.abs(theta2-theta1) / (pi/2) * points_per_90) if(Np < 1): Np = 1 thetas = np.linspace(theta1, theta2, Np) for t in thetas: xfil = x[i] + Sc*nxc + Ri*np.cos(t) yfil = y[i] + Sc*nyc + Ri*np.sin(t) # only add point if not duplicate (this can happen if # the desired radis of curvature equals or exceeds the # maximum allowed) if(np.abs(xfil - xn[-1]) > equal_thresh or np.abs(yfil - yn[-1]) > equal_thresh): xn.append(xfil) yn.append(yfil) else: xn.append(x[i]) yn.append(y[i]) else: xn.append(x[i]) yn.append(y[i]) i += 1 if(not closed): xn.append(x[-1]) yn.append(y[-1]) else: xn.append(xn[0]) yn.append(yn[0]) return np.array(xn), np.array(yn)