def GetLookupTable(fluid):
    """Retrieve a lookup table for the interpolated velocity generated by a point force.
	
	If table does not already exist it will be created and stored."""

    path = join(dirname(dirname(__file__)), "GreensFunction")
    path = join(path, '2D' if fluid.Dim == 2 else '3D')

    name = join(path, fluid.Params_ToString() + '.npy')

    try:
        # Load the table from file
        Table = numpy.load(name)
    except:
        # File didn't exist, create table from scratch
        print "Greens function at\n	", name, "\ndoesn't exist, creating it...",
        Table = CalculateLookupTable(fluid)

        # Save the new lookup table
        ensure_filedir(name)
        numpy.save(name, Table)

        print "Done."
        print

    return Table
def GetLookupTable (fluid):
	"""Retrieve a lookup table for the interpolated velocity generated by a point force.
	
	If table does not already exist it will be created and stored."""

	path = join(dirname(dirname(__file__)), "GreensFunction")
	path = join(path, '2D' if fluid.Dim == 2 else '3D')
	
	name = join(path, fluid.Params_ToString() + '.npy')

	try:
		# Load the table from file
		Table = numpy.load(name);
	except:
		# File didn't exist, create table from scratch
		print "Greens function at\n	", name, "\ndoesn't exist, creating it...",
		Table = CalculateLookupTable(fluid)
		
		# Save the new lookup table
		ensure_filedir(name)
		numpy.save(name, Table)
		
		print "Done."
		print

	return Table
def CreateDecomposition(u, NumTerms = 50, MinPanelWidth = 1):
	N, dt = u.N[0], u.dt

	# Get the path for where to save the decomposition
	DecompPath = GetDecompPath()

	# Get the lookup table for the Greens function of the Spread/Interpolated Stokes flow
	Greens = GetLookupTable(u)


	# The Greens function is a 3x3 matrix field, hence we must calculate 9 expansions, one for each component.
	# By symmetry only 3 of these expansions are unique.
	for __j in range(1):
		for __k in range(2):
			# Get the (__j,__k) component of the Greens function and call it Phi, and its Fourier transform PhiHat
			Phi = Greens[...,__j,__k]
			PhiHat = real(fft.fftn(Phi))

			# The largest sized panel we care about has width N / 4, because larger panels are never well separated from anything.
			PanelWidth = N / 4
			
			# We loop through all panel sizes, starting with N / 4, then N / 8, then N / 16, and so on, until a preset stopping size is passed.
			while PanelWidth > MinPanelWidth:
				print "__j ==", __j
				print "__k ==", __k
				print "PanelWidth ==", PanelWidth
				
				# Two lists of expansion coefficients, one for the incoming and one for the outgoing.
				U, V = [], []
				
				# Fill the lists with arbitrary initial guesses.
				for l in range(NumTerms):
					U.append(ones((N,N,N)))
					V.append(ones((N,N,N)))
					for j1 in range(N):
						for j2 in range(N):
							for j3 in range(N):
								V[l][j1,j2,j3] = 1

				# Define the incoming and outgoing regions.
								
				# Outgoing Region (Omega_Out)
				# [U_x1,U_x2]x[U_y1,U_y2]x[U_z1,U_z2]
				U_x1 = N / 2 - PanelWidth / 2
				U_x2 = N / 2 + PanelWidth / 2
				U_y1 = N / 2 - PanelWidth / 2
				U_y2 = N / 2 + PanelWidth / 2
				U_z1 = N / 2 - PanelWidth / 2
				U_z2 = N / 2 + PanelWidth / 2
				U_x1 -= 1
				U_x2 += 1
				U_y1 -= 1
				U_y2 += 1
				U_z1 -= 1
				U_z2 += 1

				# Incoming Region (Omega_In)
				# [V_x1,V_x2]x[V_y1,V_y2]x[V_z1,V_z2]
				V_width = 3 * PanelWidth
				V_x1 = N / 2 - V_width / 2
				V_x2 = N / 2 + V_width / 2
				V_y1 = N / 2 - V_width / 2
				V_y2 = N / 2 + V_width / 2
				V_z1 = N / 2 - V_width / 2
				V_z2 = N / 2 + V_width / 2
				V_x1 += 1
				V_x2 -= 1
				V_y1 += 1
				V_y2 -= 1
				V_z1 += 1
				V_z2 -= 1
				
				# Define the indicator functions for the incoming and outgoing regions.
				
				def Enforce_U_Restriction(U):
					"""Ensure that the tensor field U is zero outside the Outgoing Region"""
					hold = U[U_x1:U_x2+1, U_y1:U_y2+1, U_z1:U_z2+1].copy()
					U *= 0
					U[U_x1:U_x2 + 1,U_y1:U_y2 + 1,U_z1:U_z2 + 1] = hold

					return U

				def Enforce_V_Restriction(V):
					"""Ensure that the tensor field V is zero outside the Incoming Region"""
					V[V_x1:V_x2 + 1,V_y1:V_y2 + 1,V_z1:V_z2 + 1] *= 0
					V[1:N/2,:,:] *= 0
					V[:,1:N/2,:] *= 0
					V[:,:,1:N/2] *= 0

					return V

				U[0] = Enforce_U_Restriction(U[0])
				
				# A list to store the singular values
				_s = []
				
				# We wish to calculate the first few singular values, from 1 to NumTerms
				for l in range(NumTerms):
					V[l] = Enforce_V_Restriction(V[l])
					
					# We perform the Power Iteration a fixed number of times to approximate each singular value.
					for i in range(PowerIterations):
						# Calculate the product Phi' * U
						less = 0
						for _l in range(l):
							less += U[_l]*(V[_l]*V[l]).sum()
						U[l] = (Convolve(PhiHat, V[l]) - less) / (V[l]**2).sum()
						U[l] = Enforce_U_Restriction(U[l])

						# Renormalize
						norm1 = (U[l]**2).sum()**.5
						norm2 = (V[l]**2).sum()**.5
						U[l] *= (norm2 / norm1)**.5
						V[l] *= (norm1 / norm2)**.5

						# Calculate the product Phi' * V
						less = 0
						for _l in range(l):
							less += U[_l]*(V[_l]*U[l]).sum()
						V[l] = (Convolve(PhiHat, U[l]) - less) / (U[l]**2).sum()
						V[l] = Enforce_V_Restriction(V[l])

						# Renormalize
						norm1 = (U[l]**2).sum()**.5
						norm2 = (V[l]**2).sum()**.5
						U[l] *= (norm2 / norm1)**.5
						V[l] *= (norm1 / norm2)**.5
						
					print l,
						
					_s.append( ((U[l]**2).sum() * (V[l]**2).sum())**.5 )

				# Save the decomposition
				for l in range(NumTerms):
					name = path.join(DecompPath, u.Params_ToString())
					name = path.join(name, 'Panel_Width_' + str(PanelWidth))
					name = path.join(name, str(l) + '_' + str(__j) + str(__k) + '.npy')
					ensure_filedir(name)

					# We store both components U and V together in the same scalar field.
					Composite = U[l] + V[l]
					Composite = Shift(Composite, -2, -2, -2)
					numpy.save(name, Composite[N/2 - PanelWidth/2 - 2:, N/2 - PanelWidth/2 - 2:, N/2 - PanelWidth/2 - 2:])

				# Sort and print the singular values
				_s.sort()
				_s.reverse()
				print "\n\nSingular Values:\n" + str(_s) + "\n\n\n"

				# Reduce the width of Omega_Out by half
				PanelWidth /= 2