#! /usr/bin/env python3
def plasma_matrix ( n = 101 ):
#*****************************************************************************80
#
## plasma_matrix() computes jacobian and residual for a plasma problem.
#
# Licensing:
#
# This code is distributed under the GnU LGPL license.
#
# Modified:
#
# 10 March 2020
#
# Author:
#
# James Cheung
# Modifications by John Burkardt.
#
# Input:
#
# integer N: the number of nodes in the X and Y directions.
#
# Output:
#
# real sparse A(n*n,n*n): the Jacobian matrix.
#
# real b(n*n,1): the residual vector.
#
# Local:
#
# real XLEFT, XRIGHT: the left and right endpoints of the X and Y intervals.
#
# real H: the spacing between nodes.
#
# real RHO(n,n): the density.
#
# integer NUMNODES: the number of nodes.
#
# real X(n): the X coordinates of the grid.
#
# real Y(n): the Y coordinates of the grid.
#
from scipy.sparse import lil_matrix
import numpy as np
import scipy as sp
xleft = -10.0
xright = +10.0
x = np.linspace ( xleft, xright, n )
y = np.linspace ( xleft, xright, n )
h = ( xright - xleft ) / float ( n - 1 )
hsq = h**2
#
# Set up the plasma density as a step function, which is 1
# in a central square, and 0.25 outside of the square.
#
numnodes = n * n
rho = np.zeros ( numnodes, dtype = np.float )
phi = np.zeros ( numnodes, dtype = np.float )
nr = 0.25
k = 0
for i in range ( 0, n ):
for j in range ( 0, n ):
if ( np.abs ( x[i] ) < 5.0 and np.abs ( y[j] ) < 5.0 ):
rho[k] = 1.0
else:
rho[k] = nr
phi[k] = np.log ( rho[k] )
k = k + 1
#
# Allocate A initially as a row-based "list of lists" sparse matrix.
#
A = lil_matrix ( ( numnodes, numnodes ) )
b = np.zeros ( numnodes )
#
# Bottom
#
b[0] = \
- 4.0 * phi[0] \
+ 2.0 * phi[1] \
+ 2.0 * phi[n] \
- hsq * np.exp ( phi[0] ) \
+ hsq * rho[0]
A[0,0] = - 4.0 - hsq * np.exp ( phi[0] )
A[0,1] = 2.0
A[0,n] = 2.0
for i in range ( 1, n - 1 ):
b[i] = \
phi[i-1] \
- 4.0 * phi[i] \
+ phi[i+1] \
+ 2.0 * phi[i+n] \
- hsq * np.exp ( phi[i] ) \
+ hsq * rho[i]
A[i,i-1] = 1.0
A[i,i] = - 4.0 - hsq * np.exp ( phi[i] )
A[i,i+1] = 1.0
A[i,n+i] = 2.0
b[n-1] = \
2.0 * phi[n-2] \
- 4.0 * phi[n-1] \
+ 2.0 * phi[2*n-1] \
- hsq * np.exp ( phi[n-1] ) \
+ hsq * rho[n-1]
A[n-1,n-2] = 2.0
A[n-1,n-1] = - 4.0 - hsq * np.exp ( phi[n-1] )
A[n-1,n+n-1] = 2.0
#
# Middle
#
for i in range ( 1, n - 1 ):
z = i * n
b[z] = \
phi[z-n] \
- 4.0 * phi[z] \
+ 2.0 * phi[z+1] \
+ phi[z+n] \
- hsq * np.exp ( phi[z] ) \
+ hsq * rho[z]
A[z,z-n] = 1.0
A[z,z] = - 4.0 - hsq * np.exp ( phi[z] )
A[z,z+1] = 2.0
A[z,z+n] = 1.0
for j in range ( 1, n - 1 ):
b[z+j] = \
phi[z+j-1] \
+ phi[z+j-n] \
- 4.0 * phi[z+j] \
+ phi[z+j+1] \
+ phi[z+n+j] \
- hsq * np.exp ( phi[z+j] ) \
+ hsq * rho[z+j]
A[z+j,z-n+j] = 1.0
A[z+j,z+j-1] = 1.0
A[z+j,z+j] = - 4.0 - hsq * np.exp ( phi[z+j] )
A[z+j,z+j+1] = 1.0
A[z+j,z+n+j] = 1.0
b[z+n-1] = \
2.0 * phi[z+n-2] \
+ phi[z-1] \
- 4.0 * phi[z+n-1] \
+ phi[z+2*n-1] \
- hsq * np.exp ( phi[z+n-1] ) \
+ hsq * rho[z+n-1]
A[z+n-1,z-1] = 1.0
A[z+n-1,z+n-2] = 2.0
A[z+n-1,z+n-1] = - 4.0 - hsq * np.exp ( phi[z+n-1] )
A[z+n-1,z+2*n-1] = 1.0
#
# Top
#
z = ( n - 1 ) * n
b[z] = \
2.0 * phi[z-n] \
- 4.0 * phi[z] \
+ 2.0 * phi[z+1] \
- hsq * np.exp ( phi[z] ) \
+ hsq * rho[z]
A[z,z-n] = 2.0
A[z,z] = - 4.0 - hsq * np.exp ( phi[z] )
A[z,z+1] = 2.0
for i in range ( 1, n - 1 ):
b[z+i] = \
phi[z+i-1] \
+ 2.0 * phi[z+i-n] \
- 4.0 * phi[z+i] \
+ phi[z+i+1] \
- hsq * np.exp ( phi[z+i] ) \
+ hsq * rho[z+i]
A[z+i,z-n+i] = 2.0
A[z+i,z+i-1] = 1.0
A[z+i,z+i] = - 4.0 - hsq * np.exp ( phi[z+i] )
A[z+i,z+i+1] = 1.0
b[z+n-1] = \
2.0 * phi[z+n-2] \
+ 2.0 * phi[z-1] \
- 4.0 * phi[z+n-1] \
- hsq * np.exp ( phi[z+n-1] ) \
+ hsq * rho[z+n-1]
A[z+n-1,z-1] = 2.0
A[z+n-1,z+n-2] = 2.0
A[z+n-1,z+n-2] = - 4.0 - hsq * np.exp ( phi[z+n-1] )
#
# In order to use the scipy sparse solver, convert the LIL matrix to CSR form.
#
A = A.tocsr ( )
return A, b
def plasma_matrix_test01 ( n ):
#*****************************************************************************80
#
## plasma_matrix_test01() creates a plasma matrix and solves a linear system.
#
# Discussion:
#
# The use specifies a linear grid dimension of N, corresponding to N^2
# grid points and N^4 matrix entries. However, the actual matrix is
# sparse, to only about 5*N^2 entries are nonzero.
#
# Sparse storage is used for the matrix.
#
# Licensing:
#
# This code is distributed under the MIT license.
#
# Modified:
#
# 20 January 2014
#
# Author:
#
# John Burkardt
#
# Input:
#
# integer N, the linear order of the spatial grid.
#
from scipy.sparse.linalg import spsolve
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
print ( '' )
print ( 'plasma_matrix_test01():' )
print ( ' Linear order of spatial grid = ', n )
print ( ' Number of grid points is ', n * n )
print ( ' Approximate number of nonzero matrix entries is ', 5 * n * n )
print ( ' Full storage matrix would require', n**4, 'entries.' )
A, b = plasma_matrix ( n )
b_norm = np.linalg.norm ( b )
print ( '' )
print ( ' Norm of right hand side b is', b_norm )
plt.spy ( A )
filename = 'plasma_' + str ( n ) + '_sparsity.png'
plt.savefig ( filename )
print ( ' Graphics saved as "' + filename + '"' )
plt.show ( block = False )
plt.close ( )
#
# Solve the system A * x = - b.
#
x = spsolve ( A, -b )
x_norm = np.linalg.norm ( x )
print ( '' )
print ( ' Norm of solution is', x_norm )
return
def plasma_matrix_test02 ( n ):
#*****************************************************************************80
#
## plasma_matrix_test02() solves a plasma matrix linear system using bicg.
#
# Discussion:
#
# The use specifies a linear grid dimension of N, corresponding to N^2
# grid points and N^4 matrix entries. However, the actual matrix is
# sparse, to only about 5*N^2 entries are nonzero.
#
# Licensing:
#
# This code is distributed under the MIT license.
#
# Modified:
#
# 20 January 2014
#
# Author:
#
# John Burkardt
#
# Input:
#
# integer N, the linear order of the spatial grid.
#
from scipy.sparse.linalg import spilu
import numpy as np
import scipy as sp
print ( '' )
print ( 'plasma_matrix_test02():' )
print ( ' Linear order of spatial grid =', n )
print ( ' Number of grid points is', n * n )
print ( ' Approximate number of nonzero matrix entries is ', 5 * n * n )
print ( ' Full storage matrix would require', n**4, 'entries.' )
A, b = plasma_matrix ( n )
b_norm = np.linalg.norm ( b )
print ( '' )
print ( ' Norm of right hand side b is', b_norm )
#
# Use backslash to solve the system A * x = b.
#
x1 = sp.sparse.linalg.spsolve ( A, b )
x1_norm = np.linalg.norm ( x1 )
print ( '' )
print ( ' Norm of backslash solution x is', x1_norm )
#
# Use bicg to solve the system A * x = b.
#
x2, info = sp.sparse.linalg.bicg ( A, b )
x2_norm = np.linalg.norm ( x2 )
print ( '' )
print ( ' Norm of bicg solution x is', x2_norm )
#
# Use ILU approximate inverse to solve the system A * x = b.
#
A = A.tocsc ( )
Ailu = spilu ( A )
x3 = Ailu.solve ( b )
x3_norm = np.linalg.norm ( x3 )
print ( '' )
print ( ' Norm of ilu solution x is', x3_norm )
return
def plasma_matrix_test ( ):
#*****************************************************************************80
#
## plasma_matrix_test() tests plasma_matrix().
#
# Discussion:
#
# This program shows how a MATLAB sparse matrix (and possibly a right
# hand side vector) can be stored into a Harwell-Boeing file,
# and later retrieved.
#
# Harwell-Boeing files are useful as a means of storing sparse matrices,
# especially when data is created with one program and needed by another.
#
# Licensing:
#
# This code is distributed under the MIT license.
#
# Modified:
#
# 10 March 2020
#
# Author:
#
# John Burkardt
#
import platform
print ( '' )
print ( 'plasma_matrix_test():' )
print ( ' Python version: ' + platform.python_version ( ) )
print ( ' Test plasma_matrix()' )
plasma_matrix_test01 ( 5 )
plasma_matrix_test01 ( 101 )
plasma_matrix_test02 ( 101 )
#
# Terminate.
#
print ( '' )
print ( 'plasma_matrix_test():' )
print ( ' Normal end of execution.' )
return
def timestamp ( ):
#*****************************************************************************80
#
## timestamp() prints the date as a timestamp.
#
# Licensing:
#
# This code is distributed under the MIT license.
#
# Modified:
#
# 21 August 2019
#
# Author:
#
# John Burkardt
#
import time
t = time.time ( )
print ( time.ctime ( t ) )
return
if ( __name__ == '__main__' ):
timestamp ( )
plasma_matrix_test ( )
timestamp ( )