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_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
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 # Save system pickle object save_system(system, "./khi_hydro_delta.p")
# Overwrite the default sorting strategy in the Solver class ch.sorting_strategy = sorting_strategy plt.figure(1) plt.clf() modes = 11 fig, axes = plt.subplots(num=1, nrows=2, ncols=modes, sharex=True, sharey='row') for mode in range(modes): Ns = np.hstack((np.arange(3, 6) * 32, np.arange(4, 12) * 64)) K2, vec, err = ch.iterate_solver(Ns, mode=mode, tol=1e-4, verbose=True, guess_tol=5e-3) K = np.sqrt(K2.real) phi = np.arctan(vec[2].imag / vec[2].real) A = np.max(np.abs(vec)) ch.keep_result(K2, vec * np.exp(-1j * phi) / A, mode=mode) axes[0, mode].set_title(r"$\sigma = ${:1.4f}".format(K), fontsize=10) axes[0, mode].plot(grid.zg, system.result["F"].real) axes[0, mode].plot(grid.zg, system.result["F"].imag) axes[0, mode].set_xlim(-4, 4) axes[1, mode].set_xlabel(r'$z$') G = grid.der(system.result['F']) / K axes[1, mode].plot(grid.zg, G.real) axes[1, mode].plot(grid.zg, G.imag)
from psecas.systems.kh_uniform import KelvinHelmholtzUniform """ This example shows how the eigenmodes can be stored in text format which can be loaded into the MHD code Athena (Stone, J. et al, 2008). It also shows how to save a system using Python's pickle package. """ # Set up a grid grid = FourierGrid(N=64, zmin=0, zmax=2) # Set up the system of equations kx = 4.627762711864407 # kx = 2*np.pi system = KelvinHelmholtzUniform(grid, beta=1e3, nu=1e-2, kx=kx) # Set up a solver solver = Solver(grid, system) # Iteratively solve Ns = np.hstack((np.arange(1, 5) * 32, np.arange(3, 12) * 64)) solver.iterate_solver(Ns, verbose=True, tol=1e-10) # Write files for loading into Athena write_athena(system, Nz=256, Lz=2.0) # Write directly to the Athena directory write_athena(system, Nz=256, Lz=2.0, path='/Users/berlok/codes/athena/bin/') save_system(system, '/Users/berlok/codes/athena/bin/kh-visc.p')
The solver class in Psecas contains a different method, iterate_solver, which increases N until one eigenvalue agrees with the value found at the previous value of N (to within relative and absolute tolerances, tol & atol). This method can only find one eigenmode at a time (unlike the solve method which finds all eigenvalues but is unable to check for convergence and can be very expensive to call at high N). Most often, one is only interested in the fastest growing mode (or the first few). This can be controlled with the input 'mode' in the call to iterate_solver. """ # Let us find converged solutions # Define a range of grid resolutions to try Ns = np.hstack((np.arange(1, 5) * 32, np.arange(3, 20) * 64)) # Solve for the fastest growing mode (mode=0) omega, v, err = solver.iterate_solver(Ns, verbose=True, tol=1e-6, mode=0) # Solve for the second fastest growing mode (mode=1) omega, v, err = solver.iterate_solver(Ns, verbose=True, tol=1e-6, mode=1) """ The sorting_strategy function is necessary to weed out the worst of the numerical eigenvalues. For some physical systems, infinite or nan solutions are returned. These are discarded by the sorting_strategy function. The solver class is initialized with a standard sorting_strategy that has worked well on the problems I have considered so far. One should however be careful not to remove physical eigenvalues and for some problems it is probably necessary to define a new sorting_strategy. """
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') 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.arange(1, 32) * 32 - 1 omega, vec, err = solver.iterate_solver(Ns, mode=mode, verbose=True, tol=1e-4) phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode=mode) if isinstance(grid, ChebyshevTLnGrid): plot_solution(system, limits=[0, 1.5]) plt.xlim(0, 1.5) else: plot_solution(system)
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
self.dvdz = lambdify(z, dvdz_sym)(zg) self.d2vdz = lambdify(z, d2vdz_sym)(zg) if __name__ == '__main__': import numpy as np from psecas import Solver, LegendreExtremaGrid import matplotlib.pyplot as plt from psecas import plot_solution grid = LegendreExtremaGrid(N=200, zmin=-0.5, zmax=0.5) system = KelvinHelmholtzMiura(grid, kx=2*np.pi, B=0.2, z1=0, a=1/25.0) solver = Solver(grid, system) Ns = np.arange(1, 8) * 64 omega, v, err = solver.iterate_solver(Ns, verbose=True, tol=1e-8) print('\nB={:1.2f} has gammma*2a = {:1.6f}'.format(system.B, omega.real*system.a*2)) plot_solution(system, num=1, filename='Ryu_and_Frank1996.pdf') # Make figure 4 in Miura & Pritchett (1982) # This calculation takes some time. if True: # The iterative solver is more precise but slow at high kx # Set fast=True to instead run with a fixed grid size and get a figure # much faster. The result looks okay but is imprecise at high k! fast = False
return -omega.real (a, b) = golden_section(f, 3.512295, 3.513135, tol=1e-5) # Create initial conditions for Athena simulation if False: from psecas import write_athena, save_system kxmax = 3.5128286141291243 grid = FourierGrid(N=64, 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=False, tol=1e-6) # Write files for loading into Athena # write_athena(system, Nz=256, Lz=2.0) # Write directly to the Athena directory write_athena( system, Nz=256, Lz=2.0, path='/Users/berlok/codes/athena/bin/' ) save_system(system, '/Users/berlok/codes/athena/bin/kh-with-delta.p') plt.figure(1) plt.plot(kxmax, omega.real, '+') Lx = 2 * np.pi / system.kx
""" grid = ChebyshevRationalGrid(N=32, C=0.4) u0 = 1.0 delta = 1.0 kx = 5.1540899 kx = 3.5128310 system = KelvinHelmholtzHydroOnlySlab(grid, u0, delta, kx) system.boundaries = [True, True, True, True] solver = Solver(grid, system) Ns = np.arange(1, 32) * 32 - 1 omega, vec, err = solver.iterate_solver(Ns, mode=0, verbose=True) xmin = 0 xmax = 2 * np.pi / kx zmin = -4 zmax = 4 Nx = 256 Nz = 1024 plt.rc("image", origin="lower", cmap="RdBu", interpolation="None") extent = [xmin, xmax, zmin, zmax] phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode=0)