def test_kh_uniform_solution(show=False, verbose=False): """Test eigenvalue solver using FourierGrid""" import numpy as np from psecas import Solver, FourierGrid from psecas.systems.kh_uniform import KelvinHelmholtzUniform grid = FourierGrid(N=64, zmin=0, zmax=2) system = KelvinHelmholtzUniform(grid, beta=1e4, nu=1e-2, kx=3.52615254237) system.u0 = 1. solver = Solver(grid, system) Ns = np.hstack((np.arange(2, 16) * 32, np.arange(2, 12) * 64)) for useOPinv in [True, False]: omega, v, err = solver.iterate_solver(Ns, tol=1e-5, useOPinv=useOPinv, verbose=verbose) np.testing.assert_allclose(1.66548246011, omega, atol=1e-5) if show: from psecas import plot_solution plot_solution(system, smooth=True, num=2) # Check that information converged is set to False when solver does not converge solver.iterate_solver(np.arange(1, 3) * 16, tol=1e-16, useOPinv=useOPinv, verbose=verbose) assert solver.system.result['converged'] is False return err
def test_channel(show=False, verbose=False): """Test eigenvalue solver using ChebyshevRationalGrid""" import numpy as np from psecas import Solver, ChebyshevRationalGrid, System grid = ChebyshevRationalGrid(N=199, z='z') class Channel(System): def make_background(self): import numpy as np zg = self.grid.zg self.h = np.exp(-zg**2 / 2) # Create the Channel system system = Channel(grid, variables='G', eigenvalue='K2') # Add the first (and only) equation system.add_equation("-h*K2*G = dz(dz(G)) +z*dz(G)", boundary=True) solver = Solver(grid, system, do_gen_evp=True) # Number of modes to test modes = 3 results = np.zeros(modes, dtype=np.complex128) checks = np.array([85.08037778, 69.4741069099, 55.4410282999]) def sorting_strategy(E): """Sorting strategy for channel modes""" E[E.real > 100.0] = 0 E[E.real < -10.0] = 0 index = np.argsort(np.real(E))[::-1] return (E, index) solver.sorting_strategy = sorting_strategy if show: import matplotlib.pyplot as plt plt.figure(3) plt.clf() fig, axes = plt.subplots(num=3, ncols=modes, sharey=True) for mode in range(modes): Ns = np.arange(1, 6) * 32 omega, vec, err = solver.iterate_solver(Ns, mode=mode, verbose=True) results[mode] = omega if show: phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode) axes[mode].set_title(r"$\sigma = ${:1.4f}".format(omega.real), fontsize=10) axes[mode].plot(grid.zg, system.result['G'].real) axes[mode].plot(grid.zg, system.result['G'].imag) axes[mode].set_xlim(-4, 4) if show: plt.show() np.testing.assert_allclose(results, checks, rtol=1e-6)
def f(kx): grid = FourierGrid(N=64, zmin=0.0, zmax=2.0) system = KelvinHelmholtzHydroOnly(grid, u0=1.0, delta=1.0, kx=kx) solver = Solver(grid, system) Ns = np.hstack((np.arange(1, 5) * 16, np.arange(3, 12) * 32)) omega, v, err = solver.iterate_solver(Ns, verbose=False, tol=1e-8) return -omega.real
def f(kx): grid = FourierGrid(N=64, zmin=0.0, zmax=2.0) system = KelvinHelmholtzUniform(grid, beta=5, nu=0, kx=kx) solver = Solver(grid, system) Ns = np.hstack((np.arange(4, 5) * 16, np.arange(3, 16) * 32)) omega, v, err = solver.iterate_solver(Ns, verbose=True, tol=1e-8) return -omega.real
def f(kx, **kwargs): # Set up a grid grid = FourierGrid(N=64, zmin=0, zmax=2) system = KelvinHelmholtzUniform(grid, beta=1e3, nu=0, kx=kx) if 'nu' in kwargs.keys(): system.nu = kwargs['nu'] # Set up a solver solver = Solver(grid, system) # Iteratively solve Ns = np.hstack((np.arange(1, 5) * 16, np.arange(3, 12) * 32)) omega, v, err = solver.iterate_solver(Ns, verbose=False, tol=1e-4) return -omega.real
def test_parser_findmatrices(verbose=False): """ Here we add the value A to the equation without setting it in the system. This should return a NameError """ # Create system system = System(grid, variables='f', eigenvalue='sigma') # Add the first (and only) equation system.add_equation("sigma*z*f = dz(dz(f)) + 2*A*f") with pytest.raises(NameError) as e_info: solver = Solver(grid, system) if verbose: print(str(e_info.value))
def test_parser_boundaries(verbose=False): """ Here we add the value B to the boundary without setting it in the system. This should return a NameError """ # Create system system = System(grid, variables='f', eigenvalue='sigma') # Add the first (and only) equation system.add_equation("sigma*z*f = dz(dz(f))") system.add_boundary('f', 'Dirichlet', 'B*f = 0') solver = Solver(grid, system) with pytest.raises(NameError) as e_info: # The error is found when the solve method is called solver.solve() if verbose: print(str(e_info.value))
def test_mti_solution(show=False, verbose=False): """Test eigenvalue solver using ChebyshevExtremaGrid""" import numpy as np from psecas import Solver, ChebyshevExtremaGrid from psecas.systems.mti import MagnetoThermalInstability grid = ChebyshevExtremaGrid(N=64, zmin=0, zmax=1) system = MagnetoThermalInstability(grid, beta=1e5, Kn0=200, kx=4 * np.pi) system.beta = 1e5 system.Kn0 = 200 assert system.Kn0 == 200 assert system.beta == 1e5 solver = Solver(grid, system) Ns = np.hstack(np.arange(1, 10) * 16) mode = 0 omega, vec, err = solver.iterate_solver(Ns, mode=mode, tol=1e-8, verbose=verbose) system.get_bx_and_by() if show: from psecas import plot_solution phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode=mode) plot_solution(system, smooth=True, num=1) np.testing.assert_allclose(1.7814514515967603, omega, atol=1e-8) return err
def test_solver_methods(verbose=False): """Show how the solver class methods can be called directly, using the MTI as an example. This can be useful when setting up a new problem. """ import numpy as np from psecas import Solver, ChebyshevExtremaGrid from psecas.systems.mti import MagnetoThermalInstability from scipy.linalg import eig grid = ChebyshevExtremaGrid(N=64, zmin=0, zmax=1) system = MagnetoThermalInstability(grid, beta=1e5, Kn0=200, kx=4 * np.pi) solver = Solver(grid, system) solver.get_matrix1(verbose=verbose) solver.get_matrix2(verbose=verbose) E, V = eig(solver.mat1.toarray(), solver.mat2.toarray()) # Sort the eigenvalues E, index = solver.sorting_strategy(E) mode = 0 # Choose the eigenvalue mode value only sigma = E[index[mode]] v = V[:, index[mode]] np.testing.assert_allclose(1.7814514515967603, sigma, atol=1e-8) # Compare with solve without using OPinv solver.solve(useOPinv=False, saveall=True) np.testing.assert_allclose(solver.E[mode], sigma, atol=1e-8)
omega, v, err = solver.iterate_solver(Ns, verbose=False, tol=1e-8) return -omega.real a = 3.512831867406509 b = 3.512831875508205 (a, b) = golden_section(f, a, b, tol=1e-8) # Create initial conditions for Athena simulation if True: from psecas import save_system kxmax = 3.5128319 grid = FourierGrid(N=256, zmin=0.0, zmax=2.0) system = KelvinHelmholtzHydroOnly(grid, u0=1.0, delta=1.0, kx=kxmax) solver = Solver(grid, system) Ns = np.hstack((np.arange(1, 5) * 16, np.arange(3, 12) * 32)) omega, v, err = solver.iterate_solver(Ns, verbose=True, tol=1e-8) # Normalize eigenmodes y = np.vstack([ system.result["dvx"].real, system.result["dvx"].imag, system.result["dvz"].real, system.result["dvz"].imag, ]) val = np.max(np.abs(y)) for key in system.variables: system.result[key] /= val
and is solved by employing a Neumann boundary condition on F. One of the modes obtained here, with KH=3.1979, is not found when solving -h K² G = z G' + G'' Visual inspection reveals that the extra mode is numerical garbage. Automated detection of such modes would be a good feature to implement. """ grid = ChebyshevRationalGrid(N=199, z='z') system = Channel(grid) ch = Solver(grid, system) def sorting_strategy(E): """Sorting strategy for channel modes""" E[E.real > 100.0] = 0 E[E.real < -10.0] = 0 index = np.argsort(np.real(E))[::-1] return (E, index) # Overwrite the default sorting strategy in the Solver class ch.sorting_strategy = sorting_strategy plt.figure(1) plt.clf()
def test_mri_solution(show=False, verbose=False): """Test eigenvalue solver using ChebyshevExtremaGrid""" import numpy as np from psecas import Solver, ChebyshevExtremaGrid, System class HallMRI(System): def __init__(self, grid, kz, variables, eigenvalue): # Set parameters self.q = 1.5 self.eta = 0.003 self.lh = 1 self.h = 0.25 self.va = 0.002 self.kz = kz super().__init__(grid, variables, eigenvalue) # Create a grid grid = ChebyshevExtremaGrid(N=128, zmin=1, zmax=2, z='r') variables = ['rho', 'vr', 'vphi', 'vz', 'Aphi', 'bphi'] kz = 2 * np.pi # Create the system system = HallMRI(grid, kz, variables=variables, eigenvalue='sigma') # The linearized equations system.add_equation("-r*sigma*rho = r*dr(vr) + vr + 1j*kz*r*vz") system.add_equation( "-r*r*sigma*vr = - 2*r**(2-q)*vphi + h**2*r*r*dr(rho) + va**2*(DrAphi)" ) system.add_equation("-sigma*vphi = + (2-q)*r**(-q)*vr - va**2*1j*kz*bphi") system.add_equation("-sigma*vz = h**2*1j*kz*rho") system.add_equation( "-r*r*sigma*Aphi = + r*r*vr - eta*(DrAphi) + lh*va*1j*kz*r*r*bphi") system.add_equation( "-r*r*sigma*bphi = - 1j*kz*r*r*vphi - 1j*kz*q*r**(2-q)*Aphi - eta*(Drbphi) - lh*va*1j*kz*(DrAphi)" ) # The boundary conditions Aphi_bound = 'r**2*dr(dr(Aphi)) + r*dr(Aphi) - Aphi = 0' system.add_boundary('vr', 'Dirichlet', 'Dirichlet') system.add_boundary('vphi', 'Dirichlet', 'Dirichlet') system.add_boundary('vz', 'Neumann', 'Neumann') system.add_boundary('Aphi', Aphi_bound, Aphi_bound) system.add_boundary('bphi', 'Dirichlet', 'Dirichlet') # Short hands for long expressions for derivatives system.add_substitution( 'DrAphi = r*r*dr(dr(Aphi)) + r*dr(Aphi) - Aphi - kz**2*r*r*Aphi') system.add_substitution( 'Drbphi = r*r*dr(dr(bphi)) + r*dr(bphi) - bphi - kz**2*r*r*bphi') solver = Solver(grid, system) mode = 0 Ns = np.hstack(np.arange(1, 10) * 32) omega, vec, err = solver.iterate_solver(Ns, mode=mode, tol=1e-8, verbose=verbose) if show: from psecas import plot_solution phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode=mode) plot_solution(system, smooth=True, num=1) np.testing.assert_allclose(0.09892641, omega, atol=1e-8) return err
drhodz_sym = sym.diff(rho_sym, z) domgdz_sym = sym.diff(Omg_sym, z) zg = self.grid.zg self.rho = np.ones_like(zg) * lambdify(z, rho_sym)(zg) self.Omg = np.ones_like(zg) * lambdify(z, Omg_sym)(zg) self.shr = np.ones_like(zg) * lambdify(z, shr_sym)(zg) self.drhodz = np.ones_like(zg) * lambdify(z, drhodz_sym)(zg) self.domgdz = np.ones_like(zg) * lambdify(z, domgdz_sym)(zg) # Create a grid grid = ChebyshevRationalGrid(N=200, C=0.2) # grid = ChebyshevExtremaGrid(N=199, zmin=-5, zmax=5) # Create the system system = VerticalShearInstability(grid, variables=['rh', 'wx', 'wy', 'wz'], eigenvalue='sigma') # The linearized equations system.add_equation("-sigma*rh = - 1j*kx*wx - 1/h*dz(wz) - 1/h*drhodz/rho*wz") system.add_equation("-sigma*wx = + 2*Omg*wy - 1j*kx*(h/O0)**2*rh") system.add_equation("-sigma*wy = - (2*Omg + shr)*wx - 1/h*domgdz*wz") system.add_equation("-sigma*wz = - h/O0**2*dz(rh)", boundary=True) solver = Solver(grid, system, do_gen_evp=True) omega, vec = solver.solve(mode=0, verbose=True) plot_solution(system)