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 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_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_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 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)
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))
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
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)
Berlok, T. & Pfrommer, C. (2019). *On the Kelvin-Helmholtz instability with smooth initial conditions – Linear theory and simulations*, MNRAS, 485, 908 Another reference for the KHI with anisotropic viscosity is Suzuki, K., Ogawa, T., Matsumoto, Y., & Matsumoto, R. (2013). Magnetohydrodynamic simulations of the formation of cold fronts in clusters of galaxies: Effects of anisotropic viscosity. Astrophysical Journal, 768(2). https://doi.org/10.1088/0004-637X/768/2/175 """ directory = './data/' kx_global = np.linspace(3, 4, 5) kx_local = kx_global[comm.rank :: comm.size] grid = FourierGrid(N=64, zmin=0, zmax=2) system = KelvinHelmholtzUniform(grid, beta=1e4, nu=1e-2, kx=0) io = IO(system, directory, __file__, len(kx_global)) solver = Solver(grid, system) for i in range(len(kx_local)): t1 = time.time() system.kx = kx_local[i] omega, v = solver.solve() io.save_system(i) io.log(i, time.time() - t1, 'kx = {:1.4e}'.format(system.kx)) io.finished()
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()
d2vdz_sym = diff(dvdz_sym, z) self.v = lambdify(z, v_sym)(zg) 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
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')
import numpy as np from psecas import Solver, FourierGrid from psecas.systems.kh_uniform import KelvinHelmholtzUniform from psecas import save_system from psecas import plot_solution import matplotlib.pyplot as plt kxmax = 4.54704305 # omega = 1.7087545 grid = FourierGrid(N=64, zmin=0, zmax=2) system = KelvinHelmholtzUniform(grid, beta=1e3, nu=1e-2, kx=kxmax) # Set up a solver solver = Solver(grid, system) # Iteratively solve Ns = np.hstack((np.arange(1, 5) * 16, np.arange(3, 24) * 32)) omega, vec, err = solver.iterate_solver(Ns, verbose=True, tol=1e-8) phi = np.arctan(vec[2].imag / vec[2].real) solver.keep_result(omega, vec * np.exp(-1j * phi), mode=0) # 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
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
485, 908 and the equilibrium has been previously been used for simulations in Lecoanet, D., McCourt, M., Quataert, E., Burns, K. J., Vasil, G. M., Oishi, J. S., O’Leary, R. M. (2016). A validated non-linear kelvin-helmholtz benchmark for numerical hydrodynamics, MNRAS, 455(4), 4274–4288. https://doi.org/10.1093/mnras/stv2564 """ # Plot omega vs kx if True: grid = FourierGrid(N=64, zmin=0.0, zmax=2.0) system = KelvinHelmholtzHydroOnly(grid, u0=1.0, delta=1.0, kx=0) solver = Solver(grid, system) omega_vec = [] kx_vec = np.linspace(0.1, 8, 15) for kx in kx_vec: t1 = time.time() system.kx = kx (omega, v) = solver.solve() omega_vec.append(omega) print(kx, omega) t2 = time.time() print("Solver took {} seconds".format(t2 - t1)) omega_vec = np.array(omega_vec) plt.figure(1) # plt.clf()
by Lloyd Trefethen and this Python script reproduces the figure on page 89. """ # Create grid grid = FourierGrid(64, zmin=0, zmax=2*np.pi, z='x') # Create the system system = System(grid, variables='u', eigenvalue='sigma') system.q = 1 # Add the first (and only) equation system.add_equation("sigma*u = 2*q*np.cos(2*x)*u - dx(dx(u))") # Create a solver object solver = Solver(grid, system) def sorting_strategy(E): """Sorting strategy. E is a list of eigenvalues""" # Sort from smallest to largest eigenvalue index = np.argsort(E) return (E, index) solver.sorting_strategy = sorting_strategy sigmas = [] for q in np.arange(0, 15, 0.2): # Set q in system object system.q = q
import numpy as np from psecas import Solver, FourierGrid from psecas.systems.kh_hydro import KelvinHelmholtzHydroOnly import time import matplotlib.pyplot as plt # Initialize grid, system and solver for hydrodynamic KHI 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) # Define a sorting strategy for the eigenvalues # This function depends on the physical system def sorting_strategy(E): """ The default sorting strategy. "Large" real and imaginary eigenvalues are removed and the eigenvalues are sorted from largest to smallest """ import numpy as np # Set large values to zero, as they are presumably numerical artifact # and unphysical. E[np.abs(E.real) > 20.0] = 0 E[np.abs(E.imag) > 20.0] = 0 # Sort from largest to smallest eigenvalue index = np.argsort(np.real(E))[::-1] return (E, index)