Overview of python functions from helmholtz.selfinteractions#
Overview of python functions from biosspheres.helmholtz.selfinteractions.
[1]:
import biosspheres.helmholtz.selfinteractions as selfinteractions
[2]:
import numpy as np
import scipy.sparse as sparse
import matplotlib.pyplot as plt
from matplotlib import cm, colors
Preliminaries#
Geometry#
\(N\) disjoint spheres in \(\mathbb{R}^3\):
Position vectors of the sphere centers: \(\mathbf{p_j} \in \mathbb{R}^3\), with \(j \in \{1,...,N\}\).
Radii: \(\tilde{r}_j\in \mathbb{R}^+\), with \(j \in \{1,...,N\}\).
Interior of sphere \(j\): \(\Omega_j:=\{ \mathbf{x} \in \mathbb{R}^3:||\mathbf{x}-\mathbf{p_j}||_2<\tilde{r}_j \}\).
Boundary of sphere \(j\): \(\Gamma_j:=\partial \Omega_j\).
Exterior medium, \(\Omega_0\): defined as \(\mathbb{R}^3\) without the spheres and their boundaries.
Free space fundamental solution for the Helmholtz equation#
The free space fundamental solution of the Helmholtz equation with \(k>0\), which satisfies the radiation condition is:
Layer operators#
Single and double layer operators defined for smooth densities:
with the gradient being taken with respect to \(\mathbf{r}'\), \(\widehat{\mathbf{n}}_j\) being the exterior normal vector of \(\Omega_j\), and \(\widehat{\mathbf{n}}_j=-\widehat{\mathbf{n}}_{0j}\).
These operators are linear and continuous in the following Sobolev spaces:
Boundary integral operators#
For \(u\in C^\infty(\overline\Omega)\), Dirichlet and Neumann traces operators are defined as
where \(\widehat{n}\) is the exterior unit normal.
By density arguments, the definition of Dirichlet and Neumann traces operator can be extended to \(u_j \in H^1_{loc}(\Omega_j)\), with \(j \{0, ..., N \}\). We extend the notation as follows
where \(\widehat{n}_{j}\) is the exterior normal of \(\Omega_j\), with \(j\in \{ 1, ..., N\}\) and \(\widehat{n}_{0j}=-\widehat{n}_{j}\).
Now, we recall the definition of the boundary integral operators:
These operators are linear and continuous in the following Sobolev spaces:
Since the domains are smooth, the jump relations for the potentials across a closed boundary yield
and
Calderón operator#
Spherical coordinate system#
A vector is written as \(\mathbf{r}=\left(r,\varphi,\theta\right)^t\), with \(r \in [0,\infty)\), \(\varphi \in [0,2\pi)\) and \(\theta \in [0,\pi]\), which in Cartesian coordinates is equivalent to \(\mathbf{r}=r\left(\sin \theta \cos \varphi,\sin \theta \sin \varphi,\cos \theta\right)^t\). The unitary vectors of the spherical coordinate system can be written in Cartesian coordinates as
Also, the gradient operator in spherical coordinates:
Legendre functions#
\(P_l^m\) are the associated Legendre functions of degree \(l\) and order \(m\) defined as:
Here, the term \((-1)^m\) is the Condon-Shortley phase factor.
Complex spherical harmonics#
Complex spherical harmonics of degree \(l\) and order \(m\) are defined using spherical coordinates:
with \(l\in \mathbb{N}_0\), \(m\in \mathbb{Z}\) such that \(0\leq m\leq l\). If \(m=0\), then \(\delta_{m,0}=1\), and it is zero otherwise.
Orthonormality of spherical harmonics#
Spherical harmonics are dense in \(C(\mathbb{S}^2)\), with \(\mathbb{S}^2\) the surface of the unit sphere, and form a complete orthonormal system in \(L^2(\mathbb{S}^2)\) with respect to the internal product defined by:
with \(\overline{\xi\left(\theta,\varphi\right)}\) the conjugate of \(\xi\left(\theta,\varphi\right)\).
They also are orthogonal in \(H^1(\mathbb{S}^2)\).
Reference systems#
Let be \(j\in \{1,...,N\}\). We define the reference system \(j\) as the one centered at \(\mathbf{p_j}\) with the same orientation that the reference system centered in the origin. Furthermore, we denote by \(Y_{l,m,j}\) the spherical harmonic \(Y_{l,m}\) centered in the origin of the reference system \(j\). Thus, if \(\left( r_j, \varphi_j, \theta_j \right)\) are the vector spherical coordinates of \(\mathbf{r_j}\) in the reference system \(j\), we have that \(Y_{l,m,j}\left(\mathbf{r}_j\right)=Y_{l,m}\left(\theta_j, \varphi_j\right)\).
Complex spherical harmonics and Helmholtz free space fundamental solution#
Let \(\mathbf{r}\), \(\mathbf{r}'\) be vectors, whose spherical coordinates in the reference system \(j\) are \(\left(r_j,\theta_j, \varphi_j \right)\) and \(\left(r_j',\theta_j', \varphi_j' \right)\), respectively. For \(r_j>r_j'\) we have
with \(j_l\) and \(h_l^{(1)}\) the spherical Bessel function and the spherical Hankel function of first kind. This series and its term by term first derivatives with respect to \(r_j\) or \(r_j'\) are absolutely and uniformly convergent on compact subsets with \(r_j>r_j'\).
Let be \(j\in \{1,\ldots,N\}\), and \(\tilde{r}_j\) the radius of \(\Omega_j\).
Let \(\mathbf{r} \in \Omega_0\) and \(\mathbf{r}=(r,\theta,\varphi )\) be expressed in the \(j\)th spherical coordinate system, then
\[\begin{split}\begin{aligned} DL_{0j} ( Y_{l,m,j}) (\mathbf{r}) &= -i \left( k_0 \tilde{r}_j \right)^2 j_l' (k_0 \tilde{r}_j) h_l^{(1)}(k_0 r) Y_{l,m}(\theta, \varphi ),\\ SL_{0j} ( Y_{l,m,j}) (\mathbf{r}) &= i k_0 \tilde{r}_j^2 j_l (k_0 \tilde{r}_j) h_l^{(1)}(k_0 r) Y_{l,m}(\theta, \varphi ). \end{aligned}\end{split}\]Let \(\mathbf{r} \in \Omega_j\) and \(\mathbf{r}=(r,\theta,\varphi )\) expressed in the \(j\)th spherical coordinate system, then
\[\begin{split}\begin{aligned} DL_{j} ( Y_{l,m,j}) (\mathbf{r}) &= i \left( k_j \tilde{r}_j \right)^2 h_l^{(1)'}(k_j \tilde{r}_j) j_l (k_j r) Y_{l,m}(\theta, \varphi ),\\ SL_{j} ( Y_{l,m,j}) (\mathbf{r}) &= i k_j \tilde{r}_j^2 h_l^{(1)}(k_j \tilde{r}_j) j_l (k_j r) Y_{l,m}(\theta, \varphi ). \end{aligned}\end{split}\]
The diagonal forms of the BIOs are:
v_jj_azimuthal_symmetry#
This function returns a numpy array, which we will call v. Its length is big_l + 1. Each entry contains
Docstring#
[3]:
print(selfinteractions.v_jj_azimuthal_symmetry.__doc__)
Returns a numpy array with the boundary integral operator V_{j,j}
with Helmholtz kernel evaluated and tested with spherical harmonics
of order 0 in a sphere of radius r.
Notes
-----
v[l] = ( V_{j,j} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r).
= i r**4 k j_l(kr) h_l(kr)
for each l such that 0 <= l <= big_l, and with
( ., .)_L^2(surface sphere radius r): inner product where indicated.
Y_l,0: spherical harmonic degree l, order 0.
j_l: spherical Bessel function.
h_l: spherical Hanckel function of first kind.
Notice that the expression could also be computed as:
0.5 i r pi J_{l+0.5}(k r) H_{l+0.5}(k r)
with J_{nu} the Bessel function and
H_{nu} the Hanckel function of first kind.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
Returns
-------
v : np.ndarray
with complex values. Length (big_l+1). See the section notes for
the ordering of the array.
Example#
Definition of parameters:
[4]:
r = 2.5
big_l = 50
k = 7.
Calling of the function:
[5]:
v = selfinteractions.v_jj_azimuthal_symmetry(big_l, r, k)
Plot of the values:
[6]:
plt.figure()
plt.plot(
np.real(v),
marker='x',
label='Real part of V'
)
plt.plot(
np.imag(v),
marker='x',
label='Imaginary part of V'
)
plt.title('V')
plt.xlabel('l')
plt.legend()
plt.show()
k_0_jj_azimuthal_symmetry#
This function returns a numpy array, which we will call data_k. Its length is big_l + 1. Each entry contains
where \(\mathbb{S_j}\) is the surface of the sphere \(j\) and \(r_j\) its radius.
Docstring#
[7]:
print(selfinteractions.k_0_jj_azimuthal_symmetry.__doc__)
Returns a numpy array with the boundary integral operator K_{j,j}^0,
with normals from out to in of the sphere, with Helmholtz kernel
evaluated and tested with spherical harmonics of order 0.
Notes
-----
k_0_jj[l] = (K_{j,j}^0 Y_l,0 ; Y_l,0)_L^2(surface sphere radius r).
= -1j * k**2 * r**4 * 0.5
* (j_l(r k) * h_l_d(r k) + j_l_d(r k) * h_l(r k))
for each l such that 0 <= l <= big_l, and with
( ., .)_L^2(surface sphere radius r): inner product where indicated.
Y_l,0: spherical harmonic degree l, order 0.
j_l: spherical Bessel function, and j_l_d its derivative.
h_l: spherical Hanckel function of first kind, and j_l_d its
derivative.
Notice that in this specific case
( K_{j,j}^0 Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r)
= ( K_{j,j}^{*0} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r)
where K_{j,j}^{*0} has normals from out to in of the sphere, and
with Helmholtz kernel.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
Returns
-------
k_0 : np.ndarray
with complex values. Length (big_l+1). See the section notes for
the ordering of the array.
Example#
Definition of parameters:
[8]:
r = 2.5
big_l = 50
k = 7.
Calling of the function:
[9]:
k_0_jj = selfinteractions.k_0_jj_azimuthal_symmetry(big_l, r, k)
Plotting the values:
[10]:
plt.figure()
plt.plot(
np.real(k_0_jj),
marker='x',
label='Real part of K'
)
plt.plot(
np.imag(k_0_jj),
marker='x',
label='Imaginary part of K'
)
plt.title('K')
plt.xlabel('l')
plt.legend()
plt.show()
k_1_jj_azimuthal_symmetry#
This function returns a numpy array, which we will call k_j. Its length is big_l + 1. Each entry contains
where \(\mathbb{S_j}\) is the surface of the sphere \(j\) and \(r_j\) its radius.
Docstring#
[11]:
print(selfinteractions.k_1_jj_azimuthal_symmetry.__doc__)
Returns a numpy array with the boundary integral operator K_{j,j},
with normals from in to out of the sphere, with Helmholtz kernel
evaluated and tested with spherical harmonics of order 0.
Notes
-----
k_jj[l] = ( K_{j,j} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r).
= 1j * k**2 * r**4 * 0.5
* (j_l(r k) * h_l_d(r k) + j_l_d(r k) * h_l(r k))
for each l such that 0 <= l <= big_l, and with
( ., .)_L^2(surface sphere radius r): inner product where indicated.
Y_l,0: spherical harmonic degree l, order 0.
j_l: spherical Bessel function, and j_l_d its derivative.
h_l: spherical Hanckel function of first kind, and j_l_d its
derivative.
Notice that in this specific case
( K_{j,j} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r)
= ( K_{j,j}^{*} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r)
where K_{j,j}^{*} has normals from in to out of the sphere, and with
Helmholtz kernel.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
Returns
-------
k_jj : np.ndarray
with complex values, length (big_l+1). See the section notes for
the ordering of the array.
Example#
Definition of parameters:
[12]:
r = 2.5
big_l = 50
k = 7.
Calling of the function:
[13]:
k_1_jj = selfinteractions.k_1_jj_azimuthal_symmetry(big_l, r, k)
Plotting the values:
[14]:
plt.figure()
plt.plot(
np.real(k_1_jj),
marker='x',
label='Real part of K'
)
plt.plot(
np.imag(k_1_jj),
marker='x',
label='Imaginary part of K'
)
plt.title('K')
plt.xlabel('l')
plt.legend()
plt.show()
w_jj_azimuthal_symmetry#
This function returns a numpy array, which we will call w. Its length is big_l + 1. Each entry contains
Docstring#
[15]:
print(selfinteractions.w_jj_azimuthal_symmetry.__doc__)
Returns a numpy array with the boundary integral operator W_{j,j}
with Helmholtz kernel evaluated and tested with spherical harmonics
of order 0.
Notes
-----
w[l] = ( W_{j,j} Y_l,0 ; Y_l,0 )_L^2(surface sphere radius r).
= -1j k**3 * r**4 * j_l_d(k r) h_l_d(k r)
for each l such that 0 <= l <= big_l, and with
( ., .)_L^2(surface sphere radius r): inner product where indicated.
Y_l,0: spherical harmonic degree l, order 0.
j_l_d: derivative of the spherical Bessel function.
h_l_d: derivative of the spherical Hanckel function of first kind.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
Returns
-------
w: np.ndarray
with complex values, length (big_l+1). See the section notes for
the ordering of the array.
Example#
Definition of parameters:
[16]:
r = 2.5
big_l = 50
k = 7.
Calling of the function:
[17]:
w = selfinteractions.w_jj_azimuthal_symmetry(big_l, r, k)
Plotting of the values:
[18]:
plt.figure()
plt.plot(
np.real(w),
marker='x',
label='Real part of W'
)
plt.plot(
np.imag(w),
marker='x',
label='Imaginary part of W'
)
plt.title('W')
plt.xlabel('l')
plt.legend()
plt.show()
bio_jj#
Docstring#
[19]:
print(selfinteractions.bio_jj.__doc__)
Returns a numpy array with the corresponding boundary integral
operator from the function bio_azimuthal with Helmholtz kernel
evaluated and tested with spherical harmonics of all orders.
Notes
-----
D[l*(2l+1) + m] = (D_{j,j}Y_l,m;Y_l,m)_L^2(surface sphere radius r).
which in this case is equal to
= D[l*(2l+1)]
for each l such that 0 <= l <= big_l, and with
( ., .)_L^2(surface sphere radius r): inner product where indicated.
D: boundary integral operator corresponding to the function
bio_azimuthal
Y_l,m: spherical harmonic degree l, order m.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
bio_azimuthal : Callable[[int, float, float]
python function for computing the operator for m = 0. Must be
one of the functions written before this one.
Returns
-------
np.ndarray
with complex values, length (big_l+1)**2. See the section notes
for the ordering of the array.
Example#
Definition of the parameters:
[20]:
r = 2.5
big_l = 3
k = 7.
operator = selfinteractions.w_jj_azimuthal_symmetry
Calling of the function:
[21]:
full = selfinteractions.bio_jj(big_l, r, k, operator)
[22]:
print(full)
[-11.59514193 -3.3131677j 13.61117226-39.00171561j
13.61117226-39.00171561j 13.61117226-39.00171561j
-17.04878169 -8.25398472j -17.04878169 -8.25398472j
-17.04878169 -8.25398472j -17.04878169 -8.25398472j
-17.04878169 -8.25398472j 20.40932274-28.34521185j
20.40932274-28.34521185j 20.40932274-28.34521185j
20.40932274-28.34521185j 20.40932274-28.34521185j
20.40932274-28.34521185j 20.40932274-28.34521185j]
a_0j_matrix#
Docstring#
[23]:
print(selfinteractions.a_0j_matrix.__doc__)
Returns a numpy array that represents the following matrix boundary
integral operator
A_{j,j}^0 = [ -K_{j,j}^0 , V_{j,j}^0 ]
[ W_{j,j}^0 , K*_{j,j}^0 ]
with Helmholtz kernel evaluated and tested with spherical harmonics
of order 0 if azimuthal = True, or all orders if azimuthal = False.
Each block is a diagonal matrix.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
azimuthal : bool
Default True.
Returns
-------
matrix_0j : np.ndarray
with complex values.
If azimuthal = True
Shape (2*(big_l+1), 2*(big_l+1))
Else
Shape (2*(big_l+1)**2, 2*(big_l+1)**2)
See Also
--------
a_0j_linear_operator
v_jj_azimuthal_symmetry
k_0_jj_azimuthal_symmetry
w_jj_azimuthal_symmetry
bio_jj
Example#
Definition of the parameters:
[24]:
r = 2.5
big_l = 2
k = 7.
Calling of the function with azimuthal = True:
[25]:
a_0j = selfinteractions.a_0j_matrix(big_l, r, k) # azimuthal = True
Plotting of the values:
[26]:
fig = plt.figure(figsize=plt.figaspect(.5), dpi=68., layout='constrained')
ax_1 = fig.add_subplot(121)
ax_1.set_title("Real part of a_0j")
ax_1.imshow(np.real(a_0j), vmin=np.min(np.real(a_0j)), vmax=np.max(np.real(a_0j)))
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.real(a_0j)), vmax=np.max(np.real(a_0j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
ax_1 = fig.add_subplot(122)
ax_1.imshow(np.imag(a_0j), vmin=np.min(np.imag(a_0j)), vmax=np.max(np.imag(a_0j)))
ax_1.set_title("Imaginary part of a_0j")
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.imag(a_0j)), vmax=np.max(np.imag(a_0j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
[26]:
<matplotlib.colorbar.Colorbar at 0x7dd95f9848f0>
Calling of the function with azimuthal = False:
[27]:
a_0j = selfinteractions.a_0j_matrix(big_l, r, k, azimuthal=False)
Plotting of the values:
[28]:
fig = plt.figure(figsize=plt.figaspect(.5), dpi=68., layout='constrained')
ax_1 = fig.add_subplot(121)
ax_1.set_title("Real part of a_0j")
ax_1.imshow(np.real(a_0j), vmin=np.min(np.real(a_0j)), vmax=np.max(np.real(a_0j)))
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.real(a_0j)), vmax=np.max(np.real(a_0j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
ax_1 = fig.add_subplot(122)
ax_1.imshow(np.imag(a_0j), vmin=np.min(np.imag(a_0j)), vmax=np.max(np.imag(a_0j)))
ax_1.set_title("Imaginary part of a_0j")
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.imag(a_0j)), vmax=np.max(np.imag(a_0j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
[28]:
<matplotlib.colorbar.Colorbar at 0x7dd95f84c950>
a_j_matrix#
Docstring#
[29]:
print(selfinteractions.a_j_matrix.__doc__)
Returns a numpy array that represents the following matrix boundary
integral operator
A_{j,j}^0 = [ -K_{j,j} , V_{j,j} ]
[ W_{j,j} , K*_{j,j} ]
with Helmholtz kernel evaluated and tested with spherical harmonics
of order 0 if azimuthal = True, or all orders if azimuthal = False.
Each block is a diagonal matrix.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
azimuthal : bool
Default True.
Returns
-------
matrix_j : np.ndarray
with complex values.
If azimuthal = True
Shape (2*(big_l+1), 2*(big_l+1))
Else
Shape (2*(big_l+1)**2, 2*(big_l+1)**2)
See Also
--------
a_j_linear_operator
v_jj_azimuthal_symmetry
k_1_jj_azimuthal_symmetry
w_jj_azimuthal_symmetry
bio_jj
Example#
Definition of the parameters:
[30]:
r = 2.5
big_l = 2
k = 7.
Calling of the function with azimuthal = True:
[31]:
a_j = selfinteractions.a_j_matrix(big_l, r, k) # azimuthal = True
Plotting of the values:
[32]:
fig = plt.figure(figsize=plt.figaspect(.5), dpi=68., layout='constrained')
ax_1 = fig.add_subplot(121)
ax_1.set_title("Real part of a_j")
ax_1.imshow(np.real(a_j), vmin=np.min(np.real(a_j)), vmax=np.max(np.real(a_j)))
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.real(a_j)), vmax=np.max(np.real(a_j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
ax_1 = fig.add_subplot(122)
ax_1.imshow(np.imag(a_j), vmin=np.min(np.imag(a_j)), vmax=np.max(np.imag(a_j)))
ax_1.set_title("Imaginary part of a_j")
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.imag(a_j)), vmax=np.max(np.imag(a_j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
[32]:
<matplotlib.colorbar.Colorbar at 0x7dd95f9afef0>
Calling of the function with azimuthal = False:
[33]:
a_j = selfinteractions.a_j_matrix(big_l, r, k, azimuthal=False)
Plotting of the values:
[34]:
fig = plt.figure(figsize=plt.figaspect(.5), dpi=68., layout='constrained')
ax_1 = fig.add_subplot(121)
ax_1.set_title("Real part of a_j")
ax_1.imshow(np.real(a_j), vmin=np.min(np.real(a_j)), vmax=np.max(np.real(a_j)))
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.real(a_j)), vmax=np.max(np.real(a_j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
ax_1 = fig.add_subplot(122)
ax_1.imshow(np.imag(a_j), vmin=np.min(np.imag(a_j)), vmax=np.max(np.imag(a_j)))
ax_1.set_title("Imaginary part of a_j")
fig.colorbar(cm.ScalarMappable(norm=colors.Normalize(vmin=np.min(np.imag(a_j)), vmax=np.max(np.imag(a_j)))), ax=ax_1, orientation="horizontal", shrink=0.75)
[34]:
<matplotlib.colorbar.Colorbar at 0x7dd95f7d1490>
a_0j_linear_operator#
Docstring#
[35]:
print(selfinteractions.a_0j_linear_operator.__doc__)
Returns a scipy linear operator equivalent to the given by
a_0j_matrix.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
azimuthal : bool
Default True
Returns
-------
linear_operator : sparse.linalg.LinearOperator
a scipy linear operator.
See Also
--------
a_0j_matrix
Examples#
Simpler example#
Definition of parameters:
[36]:
r = 2.5
big_l = 2
k = 7.
Calling of the function with azimuthal = True:
[37]:
a_0j = selfinteractions.a_0j_linear_operator(big_l, r, k) # azimuthal = True
Calling of the function with azimuthal = False:
[38]:
a_0j = selfinteractions.a_0j_matrix(big_l, r, k, azimuthal = False)
Comparing with the matrix version#
Definition of parameters:
[39]:
big_l = 10
r = 0.3
k = 1.3
tole = 10.**-10
Initialization of a random vector as right hand side:
[40]:
num = big_l + 1
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
Calling of the functions to be compared
[41]:
linear_operator = selfinteractions.a_0j_linear_operator(big_l, r, k) # azimuthal = True
matrix = selfinteractions.a_0j_matrix(big_l, r, k) # azimuthal = True
Solving of the iterative system:
[42]:
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**3,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Solving of the direct system:
[43]:
solution2 = np.linalg.solve(matrix, b)
Difference between solutions:
[44]:
print(np.linalg.norm(solution2 - solution))
4.5723754933069785e-12
Which means that solving the numpy array from a_0j_matrix and the linear operator from a_0j_linear_operator can be used to solve the same linear system.
Let us see what happens with azimuthal=False.
Definition of parameters:
[45]:
big_l = 10
r = 0.3
k = 1.3
tole = 10.**-5
Initialization of a random vector as right hand side:
[46]:
num = (big_l + 1)**2
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
Calling of the functions to be compared
[47]:
linear_operator = selfinteractions.a_0j_linear_operator(big_l, r, k, False)
matrix = selfinteractions.a_0j_matrix(big_l, r, k, False)
Solving of the iterative system:
[48]:
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**3,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Solving of the direct system:
[49]:
solution2 = np.linalg.solve(matrix, b)
Difference between solutions:
[50]:
print(np.linalg.norm(solution2 - solution))
2.5550504989755213e-10
Comparing the azimuthal version against the no azimuthal#
For a right hand with azimuthal symmetry when extended by 0 to explicitly compute the result without considering the symmetry, the result should be the same with the azimuthal and the not azimuth versions.
Definition of parameters:
[51]:
big_l = 10
r = 1.3
k = 2
tole = 10.**-7
Initialization of a random vector as right hand side, and its extension by zero:
[52]:
import biosspheres.miscella.extensions as righthandsextensions
num = big_l + 1
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
b_2_1 = righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, b[0:num])
b_2_2 = righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, b[num:2 * num])
b2 = np.concatenate((b_2_1, b_2_2))
Calling of the function with azimuthal=False:
[53]:
linear_operator = selfinteractions.a_0j_linear_operator(big_l, r, k, False)
Solving of the iterative system:
[54]:
num = (big_l + 1)**2
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b2,
rtol=tole,
restart=(2 * num)**2,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Calling of the function with azimuthal=True:
[55]:
linear_operator = selfinteractions.a_0j_linear_operator(big_l, r, k) # azimuthal = True
Solving of the iterative system:
[56]:
num = big_l + 1
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution2, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**2,
callback=callback_function,
callback_type='pr_norm',
)
print(info)
0
Comparison of the solutions:
[57]:
solution2_1 = \
righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, solution2[0:num])
solution2_2 = \
righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, solution2[num:2 * num])
solution2 = np.concatenate((solution2_1, solution2_2))
[58]:
print(np.linalg.norm(solution2 - solution))
7.940632618156166e-15
a_j_linear_operator#
Docstring#
[59]:
print(selfinteractions.a_j_linear_operator.__doc__)
Returns a scipy linear operator equivalent to the given by
a_j_matrix.
Parameters
----------
big_l : int
>= 0, max degree.
r : float
> 0, radius.
k : float
wave number.
azimuthal : bool
Default True.
Returns
-------
linear_operator : sparse.linalg.LinearOperator
a scipy linear operator.
See Also
--------
a_j_matrix
Examples#
Simple example#
Definition of parameters:
[60]:
r = 2.5
big_l = 2
k = 7.
Calling of the function with azimuthal=True:
[61]:
a_0j = selfinteractions.a_0j_linear_operator(big_l, r, k) # azimuthal = True
Calling of the function with azimuthal=False:
[62]:
a_0j = selfinteractions.a_0j_matrix(big_l, r, k, azimuthal = False)
Comparing with the matrix version#
Definition of parameters:
[63]:
big_l = 10
r = 0.3
k = 1.3
tole = 10.**-10
Initialization of a random vector as right hand side:
[64]:
num = big_l + 1
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
Calling of the functions to be compared
[65]:
linear_operator = selfinteractions.a_j_linear_operator(big_l, r, k) # azimuthal = True
matrix = selfinteractions.a_j_matrix(big_l, r, k) # azimuthal = True
Solving of the iterative system:
[66]:
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**3,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Solving of the direct system:
[67]:
solution2 = np.linalg.solve(matrix, b)
Difference between solutions:
[68]:
print(np.linalg.norm(solution2 - solution))
1.3355528626430198e-11
Which means that solving the numpy array from a_0j_matrix and the linear operator from a_0j_linear_operator can be used to solve the same linear system.
Let us see what happens with azimuthal=False.
Definition of parameters:
[69]:
big_l = 10
r = 0.3
k = 1.3
tole = 10.**-5
Initialization of a random vector as right hand side:
[70]:
num = (big_l + 1)**2
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
Calling of the functions to be compared
[71]:
linear_operator = selfinteractions.a_j_linear_operator(big_l, r, k, False)
matrix = selfinteractions.a_j_matrix(big_l, r, k, False)
Solving of the iterative system:
[72]:
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**3,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Solving of the direct system:
[73]:
solution2 = np.linalg.solve(matrix, b)
Difference between solutions:
[74]:
print(np.linalg.norm(solution2 - solution))
1.7724454839951905e-10
Comparing the azimuthal version against the no azimuthal#
For a right hand with azimuthal symmetry when extended by 0 to explicitly compute the result without considering the symmetry, the result should be the same with the azimuthal and the not azimuth versions.
Definition of parameters:
[75]:
big_l = 10
r = 1.3
k = 2
tole = 10.**-7
Initialization of a random vector as right hand side, and its extension by zero:
[76]:
import biosspheres.miscella.extensions as righthandsextensions
num = big_l + 1
b = np.random.random((2 * num)) + 1j * np.random.random((2 * num))
b_2_1 = righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, b[0:num])
b_2_2 = righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, b[num:2 * num])
b2 = np.concatenate((b_2_1, b_2_2))
Calling of the function with azimuthal=False:
[77]:
linear_operator = selfinteractions.a_j_linear_operator(big_l, r, k, False)
Solving of the iterative system:
[78]:
num = (big_l + 1)**2
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution, info = sparse.linalg.gmres(
linear_operator, b2,
rtol=tole,
restart=(2 * num)**2,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Calling of the function with azimuthal=True:
[79]:
linear_operator = selfinteractions.a_j_linear_operator(big_l, r, k) # azimuthal = True
Solving of the iterative system:
[80]:
num = big_l + 1
norms = []
def callback_function(pr_norm):
norms.append(pr_norm)
return 0.
solution2, info = sparse.linalg.gmres(
linear_operator, b,
rtol=tole,
restart=(2 * num)**2,
callback=callback_function,
callback_type='pr_norm')
print(info)
0
Comparison of the solutions:
[81]:
solution2_1 = \
righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, solution2[0:num])
solution2_2 = \
righthandsextensions.azimuthal_trace_to_general_with_zeros(
big_l, solution2[num:2 * num])
solution2 = np.concatenate((solution2_1, solution2_2))
[82]:
print(np.linalg.norm(solution2 - solution))
9.130324111516091e-15
a_0_a_n_sparse_matrices#
Example#
Definition of parameters:
[83]:
n = 2
big_l = 5
r = 1.3
radii = np.ones(n) * r
kii = np.ones(n+1)
k = 7.
kii[0] = k
Calling of the function with azimuthal = True:
[84]:
sparse_big_a_0_self, sparse_big_a_n = selfinteractions.a_0_a_n_sparse_matrices(n, big_l, radii, kii) # azimuthal = False
Calling of the function with azimuthal = False:
[85]:
sparse_big_a_0_self, sparse_big_a_n = selfinteractions.a_0_a_n_sparse_matrices(n, big_l, radii, kii, azimuthal = True)
reduced_a_sparse_matrix#
Example#
Definition of parameters:
[86]:
n = 2
big_l = 5
r = 1.3
radii = np.ones(n) * r
kii = np.ones(n+1)
k = 7.
kii[0] = k
pi0 = 0.5
pii = np.ones(n) * pi0
Calling of the function with azimuthal = True:
[87]:
reduced_a = selfinteractions.reduced_a_sparse_matrix(n, big_l, radii, kii, pii) # azimuthal = False
Calling of the function with azimuthal = False:
[88]:
reduced_a = selfinteractions.reduced_a_sparse_matrix(n, big_l, radii, kii, pii, azimuthal=True)
[ ]: