def test_a(self): ''' Poisson equation on a sine curve. In 3D? It wasn't originally, but the restriciton algorithm was having trouble with 1D. This might bear investigation. ''' problemscale = 12 size = problemscale ** 3 gridLevels = 4 u_zeros = np.zeros((size,)) u_actual = np.sin(np.array(range(int(size))) * 3.0 / size).T A = operators.poisson((size,)) b = tools.flexibleMmult(A, u_actual) uSmoothed = smooth(A, b, u_zeros, iterations=1) parameters = {'coarsestLevel': gridLevels - 1, 'problemShape': (problemscale, problemscale, problemscale), 'gridLevels': gridLevels, 'threshold': 8e-3, } u_mmg = mgSolve(A, b, parameters) if self.verbose: print 'norm is', (np.linalg.norm(tools.getresidual(b, A, uSmoothed.reshape((size, 1)), size))) residual_norm = np.linalg.norm(tools.flexibleMmult(A, u_mmg) - b) assert parameters['threshold'] > residual_norm
def mgCycle(A, b, level, R, parameters, initial=None): """ Internally used function that shows the actual multi-level solution method, through a recursive call within the "level < coarsestLevel" block, below. Parameters ---------- A : list of ndarrays A list of square arrays; one for each level of resolution. As returned by operators.coeffecientList(). b : ndarray top-level RHS vector level : int The current multigrid level. This value is 0 at the entry point for standard, recursive multigrid. R : list of ndarrays A list of (generally nonsquare) arrays; one for each transition between levels of resolution. As returned by operators.restrictionList(). parameters : dict A dictionary of parameters. See documentation for mgSolve() for details. Optional Parameters ------------------- initial=np.zeros((b.size, )) : ndarray Initial iterate. Defaults to the zero vector, but the last supplied solution should be used for chained v-cycles. Returns ------- uOut : ndarray solution infoDict Dictionary of information about the solution process. Fields: norm The final norm of the residual """ verbose = parameters["verbose"] if initial is None: initial = np.zeros((b.size,)) N = b.size # The general case is a recursive call. It comprises (1) pre-smoothing, # (2) finding the coarse residual, (3) solving for the coarse correction # to account for that residual via recursive call, (4) adding that # correction to the pre-smoothed solution, (5) then possibly post-smoothing. if level < parameters["coarsestLevel"]: # (1) pre-smoothing uApx = smooth(A[level], b, initial, parameters["preIterations"], verbose=verbose) # (2) coarse residual bCoarse = tools.flexibleMmult(R[level], b.reshape((N, 1))) NH = len(bCoarse) if verbose: print level * " " + "calling mgCycle at level %i" % level residual = tools.getresidual(b, A[level], uApx, N) coarseResidual = tools.flexibleMmult(R[level], residual.reshape((N, 1))).reshape((NH,)) # (3) correction for residual via recursive call coarseCorrection = mgCycle(A, coarseResidual, level + 1, R, parameters)[0] correction = (tools.flexibleMmult(R[level].transpose(), coarseCorrection.reshape((NH, 1)))).reshape((N,)) if parameters["postIterations"] > 0: # (5) post-smoothing uOut = smooth(A[level], b, uApx + correction, parameters["postIterations"], verbose=verbose) # (4) else: uOut = uApx + correction # (4) # Save norm, to monitor for convergence at topmost level. norm = scipy.sparse.base.np.linalg.norm(tools.getresidual(b, A[level], uOut, N)) # The recursive calls only end when we're at the coarsest level, where # we simply apply the chosen coarse solver. else: norm = 0 if verbose: print level * " " + "direct solving at level %i" % level uOut = coarseSolve(A[level], b.reshape((N, 1))) return uOut, {"norm": norm}
def multifreq_1d(self, N, finaliterations, spectralxscale='linear', solutionname='whitenoise', save=False): '''Generates 1-dimensional uniform noise with N unknowns, then applies Gauss-Siedel smoothing iterations to an initial zero-vector guess until finaliterations is reached, graphing the Fourier transform of the error each time. Does so pretty inefficiently, by doing 1 iteration, 2 iterations, 3, iterations, etc. until finaliterations, instead of just doing finaliterations iterations, and graphing after each sweep. Uses ffmpeg and mplayer.''' if solutionname == 'summedsine': # Summed sine waves of several frequencies. data = np.zeros((N,)) # initialize domainwidthsexponents = range(10) domainwidths = range(10) # initialize for i in range(10): domainwidths[i] = float(N) / (2.0 ** domainwidthsexponents[i]) for domainwidth in domainwidths: sininput = np.arange(0.0, domainwidth, domainwidth / N) xdata = (np.sin(sininput) / domainwidth * 10.0)[0:N:] data = data + xdata else: solutionname = 'whitenoise' data = np.random.random((N,)) # 1D white noise gif_output_name = 'spectral_solution_rate-%i_unknowns-%s_xscale-%s_solution.gif' % (N, spectralxscale, solutionname) A = operators.poisson((N,)) b = tools.flexibleMmult(A, data) if self.saveFig: from scipy import fftpack import matplotlib.pyplot as plt # Prepare for Graphing fig = plt.figure(figsize=(7.5, 7)) fig.suptitle(gif_output_name) fig.subplots_adjust(wspace=.2) fig.subplots_adjust(hspace=.2) solnax = fig.add_subplot(211) # subplot to hold the solution solnaxylim = [0, 1] specax = fig.add_subplot(212) # subplot to hold the spectrum specaxylim = [10.0 ** (-18), 10.0 ** 3] solutions = [] spectra = [] filenames = [] loop = 1 # for monitoring progress iterationslist = range(finaliterations) # from random import shuffle # shuffle(iterationslist) if save: iterations_to_save = range(1, finaliterations + 1) str_tosave = [] for x in iterations_to_save: str_tosave.append(str(x)) from os import system; system("mkdir -p output") csvfilelabel = 'iterative_spectra-%i_N' % N csvfile = open('output/%s.csv' % csvfilelabel, 'a') csvfile.write('frequency,' + ','.join(str_tosave) + '\n') csvfile.flush() for iterations in iterationslist: solutions.append(smooth(A, b, np.zeros((N,)), iterations) ) if self.saveFig: spectra.append(fftpack.fft(b - tools.flexibleMmult(A, solutions[iterations] ) ) ) if self.saveFig: filebasename = '%i_unknowns-%i_iterations' % (N, iterations) filename = filebasename + '-solution.png' solnax.set_ylim(solnaxylim) solnax.set_autoscaley_on(False) solnax.set_title(filebasename + ': Solution') solnax.plot(solutions[iterations]) specax.set_yscale('log') specax.set_xscale(spectralxscale) specax.set_ylim(specaxylim) specax.set_autoscaley_on(False) specax.set_title(filebasename + ': Solution Error (frequency domain)') specax.plot(spectra[iterations]) filename = 'temp/' + filebasename + '-combined.png' filenames.append(filename) if self.saveFig: print "%i of %i: " % (loop, finaliterations) + "saving " + filename fig.savefig(filename, dpi=80) solnax.cla() specax.cla() loop += 1 frequencies = range(len(solutions[0])) for i in frequencies: towrite = '%i,' % frequencies[i] for spectrum in spectra: towrite += '%.8f,' % abs(spectrum[i]) if save: csvfile.write(towrite + '\n') csvfile.flush() if save: csvfile.close() # make .gif and .mp4 files: if self.saveFig: # TODO (Tom) Might use subprocess to suppress super verbose ffmpeg # import subprocess # s = subprocess.Popen(['cowsay', 'hello'] # stderr=subprocess.STDOUT # stdout=subprocess.PIPE).communicate()[0] # print s torun = [] torun.extend([ 'convert ' + ' '.join(filenames) + ' output/%s' % gif_output_name, 'rm ' + ' '.join(filenames), 'cp %s simple.gif' % ('output/' + gif_output_name), 'mplayer -vo jpeg simple.gif > /dev/null 2> /dev/null', 'ffmpeg -r 12 -i %%08d.jpg -y -an output/%s.avi > /dev/null' % gif_output_name, 'rm simple.gif 0*.jpg', 'ffmpeg -y -i output/%s.avi output/%s.mp4 > /dev/null && rm output/%s.avi' % (gif_output_name, gif_output_name, gif_output_name), ]) for command in torun: system(command)
def multifreq_2d(self, problemscale, finaliterations): '''Attempts to generate a pretty 2D solution, by summing several 2D sine waves of different frequencies. Then, proceeds to apply a GS smoother to the generated problem, saving pretty pictures after each iteration. This might work OK if problemscale is big enough. ''' if self.saveFig: from scipy import fftpack import matplotlib.pyplot as plt from matplotlib import cm from mpl_toolkits.mplot3d import Axes3D # Generate test problem. Summed sine waves of several resolutions: NX = problemscale floatNX = float(NX) data = np.zeros((NX, NX)) domainwidths = [floatNX, floatNX / 2.0, 10., 3.1415] for domainwidth in domainwidths: sininput = np.arange(0.0, domainwidth, domainwidth / NX) xdata = (np.sin(sininput) / domainwidth * 10.0)[0:NX:] data = data + xdata[:, np.newaxis] + xdata[:, np.newaxis].T X = np.tile(np.array(range(NX)), (NX, 1)) Y = X.T if self.saveFig: # Prepare for Graphing: fig = plt.figure() ax = Axes3D(fig) ax.set_zlim3d([-1, 6]) specfig = plt.figure() specax = specfig.add_subplot(111) specax.set_ylim([.000001, 10000]) specax.set_autoscaley_on(False) specax.set_xscale('log') # Problem Setup: A = operators.poisson((NX, NX)) solnforb = data.ravel().reshape((NX ** 2, 1)) b = tools.flexibleMmult(A, solnforb) # Initial "Solution" placeholder: solutions = range(finaliterations) spectra = range(finaliterations) iterations = 0 solutions[iterations] = np.zeros((NX, NX)).ravel() spectra[iterations] = b - tools.flexibleMmult(A, solutions[iterations]) if self.saveFig: # Initial graphs: filebasename = 'output/N_%i-iterations_%i' % (NX ** 2, iterations) filename = filebasename + '.png' ax.set_title(filebasename) # surf = ax.plot_wireframe(X, Y, solutions[iterations].reshape(NX,NX),rstride=stridelength,cstride=stridelength) surf = ax.plot_surface(X, Y, data, cmap=cm.jet) # print "saving", filename if self.saveFig: fig.savefig(filename) surf.remove() ax.cla() # Error Spectrum specax.plot(spectra[iterations]) specax.set_title(filebasename + ': Error Spectrum') filename = filebasename + '-error.png' if self.saveFig: specfig.savefig(filename) del specax.lines[0] specax.cla() verbose = False for iterations in range(1, finaliterations): solutions[iterations] = smooth(A, b, np.zeros(NX ** 2), iterations, verbose=verbose ).reshape(NX, NX) if self.saveFig: spectra[iterations] = fftpack.fft2(solutions[iterations]) if self.saveFig: filebasename = 'output/N_%i-iterations_%i' % (NX ** 2, iterations) filename = filebasename + '-solution.png' ax.set_zlim3d([-1, 6]) surf = ax.plot_surface(X, Y, solutions[iterations], cmap=cm.jet) ax.set_title(filebasename) surf = ax.plot_wireframe(X, Y, solutions[iterations]) # print "saving", filename if self.saveFig: fig.savefig(filename) surf.remove() ax.cla() specax.set_yscale('log') specax.set_ylim([.000001, 10000]) specax.set_autoscaley_on(False) specax.set_xscale('log') specax.plot(spectra[iterations]) specax.set_title(filebasename + ': Error Spectrum') filename = filebasename + '-error.png' # print "saving", filename if self.saveFig: specfig.savefig(filename) del specax.lines[0] specax.cla()
def mgCycle(A, b, level, R, parameters, initial=None): """ Internally used function that shows the actual multi-level solution method, through a recursive call within the "level < coarsestLevel" block, below. Parameters ---------- A : list of ndarrays A list of square arrays; one for each level of resolution. As returned by operators.coeffecientList(). b : ndarray top-level RHS vector level : int The current multigrid level. This value is 0 at the entry point for standard, recursive multigrid. R : list of ndarrays A list of (generally nonsquare) arrays; one for each transition between levels of resolution. As returned by operators.restrictionList(). parameters : dict A dictionary of parameters. See documentation for mgSolve() for details. Optional Parameters ------------------- initial=np.zeros((b.size, )) : ndarray Initial iterate. Defaults to the zero vector, but the last supplied solution should be used for chained v-cycles. Returns ------- uOut : ndarray solution infoDict Dictionary of information about the solution process. Fields: norm The final norm of the residual """ verbose = parameters['verbose'] if initial is None: initial = np.zeros((b.size, )) N = b.size # The general case is a recursive call. It comprises (1) pre-smoothing, # (2) finding the coarse residual, (3) solving for the coarse correction # to account for that residual via recursive call, (4) adding that # correction to the pre-smoothed solution, (5) then possibly post-smoothing. if level < parameters['coarsestLevel']: # (1) pre-smoothing uApx = smooth(A[level], b, initial, parameters['preIterations'], verbose=verbose) # (2) coarse residual bCoarse = tools.flexibleMmult(R[level], b.reshape((N, 1))) NH = len(bCoarse) if verbose: print level * " " + "calling mgCycle at level %i" % level residual = tools.getresidual(b, A[level], uApx, N) coarseResidual = tools.flexibleMmult(R[level], residual.reshape((N, 1))).reshape((NH,)) # (3) correction for residual via recursive call coarseCorrection = mgCycle(A, coarseResidual, level+1, R, parameters)[0] correction = (tools.flexibleMmult(R[level].transpose(), coarseCorrection.reshape((NH, 1)))).reshape((N, )) if parameters['postIterations'] > 0: # (5) post-smoothing uOut = smooth(A[level], b, uApx + correction, # (4) parameters['postIterations'], verbose=verbose) else: uOut = uApx + correction # (4) # Save norm, to monitor for convergence at topmost level. norm = scipy.sparse.base.np.linalg.norm(tools.getresidual(b, A[level], uOut, N)) # The recursive calls only end when we're at the coarsest level, where # we simply apply the chosen coarse solver. else: norm = 0 if verbose: print level * " " + "direct solving at level %i" % level uOut = coarseSolve(A[level], b.reshape((N, 1))) return uOut, {'norm': norm}