Example program of XLPack for Python and XLPack for Matplotlib: (1) 3D visualization (Laplace equation)
We consider solving the following partial differential equation in the square domain \(x = 0 \sim 1, y = 0 \sim 1\).\[
-d^2u/dx^2 – d^2u/dy^2 = 0
\] The boundary conditions are given as follows.
\[
u(x, 0) = 0, u(0, y) = 0, u(x, 1) = x, u(1, y) = y \space (ディリクレ条件)
\]
Such partial differential equations are applied, for example, to describe the temperature distribution of a plate in thermal equilibrium.
A commonly used method for solving this equation is the finite difference method. For an example of obtaining a numerical solution using the five-point finite difference approximation, see below.
Tutorial for XLPack numerical library: 16. Linear Calculations of Sparse Matrices
Here, we explain an example of obtaining the solution using another method, the finite element method, and visualizing it in 3D.
The square domain is uniformly divided into \(Nx\) and \(Ny\) segments in the \(x\)- and \(y\)-directions, respectively, to form a rectangular grid. Each rectangle is then subdivided into two triangles, thereby generating a simple triangular mesh.

Using this mesh, we obtain the solution by the finite element method. Although the figure above shows an example with 7 divisions (8 x 8 grid), the actual computation was performed with 19 divisions (20 x 20 grid), i.e., (Nx = 19) and (Ny = 19).
1. Overall structure
The main program is as follows.
Sub Main()
Const Nx = 19, Ny = 19
Const Nb2 = 0, LdKnc = 4, LdKs = 3
Const N = (Nx + 1) * (Ny + 1), Ne = 2 * Nx * Ny, Nb = 2 * (Nx + Ny)
Dim X(N - 1) As Double, Y(N - 1) As Double
Dim P(N - 1) As Double, Q(N - 1) As Double, F(N - 1) As Double
Dim Knc(LdKnc - 1, Ne - 1) As Long, Ks(LdKs - 1, Nb - 1) As Long
Dim Lb(Nb - 1) As Long, Ib(Nb - 1) As Long, Bv(Nb - 1) As Double
Dim Ks2() As Long, Alpha() As Double, Beta() As Double
Dim Val(20 * N) As Double, Ptr(N) As Long, Ind(20 * N) As Long
Dim B(N - 1) As Double, U(N - 1) As Double
Dim Ux As Double, Err As Double
Dim Info As Long
Dim I As Long, J As Long
'-- Set mesh data
Call SetData(Nx, Ny, N, Ne, Nb, X(), Y(), Knc(), Ks(), Lb(), P(), Q(), F(), Ib(), Bv())
'-- Assemble FEM matrix
Call Fem2p(N, Ne, X(), Y(), Knc(), P(), Q(), F(), Nb, Ib(), Bv(), Nb2, Ks2(), Alpha(), Beta(), Val(), Ptr(), Ind(), B(), Info)
'-- Solve equation by CG method
Dim Iter As Long, Res As Double
Call Cg1(N, Val(), Ptr(), Ind(), B(), U(), Info, Iter, Res)
Debug.Print "Info =" + Str(Info) + ", Iter =" + Str(Iter) + ", Res =" + Str(Res)
If Info <> 0 Then Exit Sub
'-- Plot solution
Call Plot(Nx, Ny, U())
End Sub
The subroutine Fem2p() in the XLPack library assembles the finite element matrix. This produces a system of equations consisting of a coefficient matrix in sparse (CSR) format and a right-hand-side vector. Since the coefficient matrix is symmetric, the system can be solved by the conjugate gradient method using the subroutine Cg1() in the XLPack library.
The subroutine SetData() sets the data for the grid points, finite elements, and boundary conditions that are used as input to Fem2p().
The obtained solution is displayed by the subroutine Plot(). In Plot(), 3D visualization is performed using XLPack for Python or XLPack for Matplotlib.
The execution result will be as follows.

2. Input data settings
A simple triangular mesh can be generated using the subroutine Mesh23() in the XLPack library.
Next, we set the coefficients of the Laplace equation as p(x, y) = 1, q(x, y) = 0, and f(x, y) = 0. We also set the Dirichlet boundary conditions.
The following subroutine performs these tasks.
Sub SetData(Nx As Long, Ny As Long, N As Long, Ne As Long, Nb As Long, X() As Double, Y() As Double, Knc() As Long, Ks() As Long, Lb() As Long, P() As Double, Q() As Double, F() As Double, Ib() As Long, Bv() As Double)
Dim Id() As Long
Dim I As Long, J As Long, K As Long
'-- Generates simple rectangular mesh for 2D triangular element
Call Mesh23(Nx, Ny, X(), Y(), Knc(), Ks(), Lb())
'-- Set p(x, y) = 1, q(x, y) = 0 and f(x, y) = 0
For I = 0 To N - 1
P(I) = 1
Q(I) = 0
F(I) = 0
Next
'-- Set boundary condition 1
ReDim Id(N - 1)
For I = 0 To Nb - 1
For J = 1 To 2
Id(Ks(J, I) - 1) = Lb(I)
Next
Next
K = 0
For I = 0 To N - 1
If Id(I) = 1 Or Id(I) = 4 Then
Bv(K) = 0
ElseIf Id(I) = 2 Then
Bv(K) = Y(I)
ElseIf Id(I) = 3 Then
Bv(K) = X(I)
Else
GoTo Continue
End If
Ib(K) = I + 1
K = K + 1
Continue:
Next
End Sub
3. 3D visualization of solution
The obtained solution can be visualized using Python code directly written with using XLPack for Python.
Sub Plot(Nx As Long, Ny As Long, U() As Double)
Dim I As Long
For I = 0 To (Nx + 1) * (Ny + 1) - 1
Cells(6 + I, 2) = U(I)
Next
'-- Python codes
PY "import matplotlib.pyplot as plt"
PY "from matplotlib import cm"
PY "import numpy as np"
PY "from XLPackPy import *"
PY "nx =" + Str(Nx) + "+ 1"
PY "ny =" + Str(Ny) + "+ 1"
PY "U = xlc_get(5, 1, 4 + nx*ny)"
PY "X = np.empty((ny, nx))"
PY "Y = np.empty((ny, nx))"
PY "Z = np.empty((ny, nx))"
PY _
"for i in range(ny):" + vbNewLine + _
" for j in range(nx):" + vbNewLine + _
" X[i][j] = (1.0/(nx - 1))*j" + vbNewLine + _
" Y[i][j] = (1.0/(ny - 1))*i" + vbNewLine + _
" Z[i][j] = U[nx*i + j]"
PY "fig = plt.figure()"
PY "ax3 = fig.add_subplot(projection='3d')"
PY "surf = ax3.plot_surface(X, Y, Z, cmap=cm.coolwarm)"
PY "fig.colorbar(surf, shrink=0.5, aspect=5)"
PY "plt.show()"
End Sub
Using XLPack for Matplotlib, everything can be written in VBA. Fig.Add_subplot_3d() corresponds to Python’s fig.add_subplot(projection=’3d’). Except that, the code is almost the same as in Python.
Dim Plt As New Pyplot
Sub Plot(Nx As Long, Ny As Long, U() As Double)
Dim X() As Double, Y() As Double, Z() As Double
ReDim X(Ny, Nx), Y(Ny, Nx), Z(Ny, Nx)
Dim I As Long, J As Long
Dim Fig As Figure, Ax3 As Axs3d, Surf As PyObject
'-- Prepare data
For I = 0 To Ny
For J = 0 To Nx
X(I, J) = (1# / Nx) * I
Y(I, J) = (1# / Ny) * J
Z(I, J) = U((Nx + 1) * I + J)
Next
Next
'-- Plot by Matplotlib
Set Fig = Plt.Figure()
Set Ax3 = Fig.Add_subplot_3d()
Set Surf = Ax3.Plot_surface(Ny + 1, Nx + 1, X(), Y(), Z(), "coolwarm")
Call Fig.Colorbar(Surf, KwArgs:="shrink=0.5, aspect=5")
Call Plt.Show
End Sub


