For conversion three different plotting scripts building each one it's own surface into a multi-threaded (multi-process) version, we separate the code logic for each surface into a separate task. This allows CPU to calculate simultaneously the paraboloid, sine wave, and Möbius strip.
To setup python3.14.3t on Arch Linux/CachyOS along with aqtinstall via UV
$ curl -LsSf https://astral.sh/uv/install.sh | sh
$ uv python install 3.14t
$ uv python list
$ mkdir MULTITHREAD
$ cd MULTITHREAD
$ python3.14t -m venv .env
$ source .env/bin/activate
$ pip install aqtinstall
$ pip install --upgrade pip
$ pip install numpy matplotlib cxroots
❯ cat plotMulti3Dpython12.py
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.tri import Triangulation
from concurrent.futures import ProcessPoolExecutor
# --- Worker Functions for Parallel Execution ---
def calc_paraboloid():
X = np.linspace(-5, 5, 100)
Y = np.linspace(-5, 5, 100)
X_grid, Y_grid = np.meshgrid(X, Y)
Z = X_grid**2 + Y_grid**2
return X_grid, Y_grid, Z
def calc_sine_tri():
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
triang = Triangulation(X.flatten(), Y.flatten())
return triang, Z.flatten()
def calc_mobius():
theta = np.linspace(0, 2*np.pi, 100)
w = np.linspace(-0.5, 0.5, 10)
theta_grid, w_grid = np.meshgrid(theta, w)
r = 1 + w_grid * np.cos(theta_grid / 2)
x = r * np.cos(theta_grid)
y = r * np.sin(theta_grid)
z = w_grid * np.sin(theta_grid / 2)
return x, y, z
if __name__ == "__main__":
# 1. Execute all three calculations in parallel
with ProcessPoolExecutor() as executor:
f1 = executor.submit(calc_paraboloid)
f2 = executor.submit(calc_sine_tri)
f3 = executor.submit(calc_mobius)
# Gather results as they finish
para_data = f1.result()
sine_data = f2.result()
mobi_data = f3.result()
# 2. Plotting (Main Thread)
# Plot 1: Paraboloid
fig1 = plt.figure()
ax1 = fig1.add_subplot(111, projection='3d')
ax1.plot_surface(*para_data, cmap='inferno', edgecolor='none')
ax1.set_title('Surface Plot of a Paraboloid')
# Plot 2: Sine Triangulation
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, projection='3d')
ax2.plot_trisurf(sine_data[0], sine_data[1], cmap='viridis', edgecolor='none')
ax2.set_title('Surface Triangulation Example')
# Plot 3: Möbius Strip
fig3 = plt.figure()
ax3 = fig3.add_subplot(111, projection='3d')
ax3.plot_surface(*mobi_data, cmap='coolwarm')
ax3.set_title('Möbius Strip')
plt.show()
You may also add to the bottom of ~/.bashrc function activatevenv() {
# Names of possible virtualenv directories
VIRTUALENV_DIRS=("venv/" "env/" ".env/" ".venv/" "${PWD##*/}")
for dir in "${VIRTUALENV_DIRS[@]}"; do
if [[ -d "${dir}" ]]; then
# Found a possible venv directory
# Try activating the venv
if [[ -e "./${dir}/bin/activate" ]]; then
source ./$dir/bin/activate
echo "Virtual environment activated automatically"
break
fi
fi
done
}
# Extension for `cd` command in order to automatically activate virtual env when changing directories.
cd() {
builtin cd $1
# Try activating venv
activatevenv
}
=====================================================
GOOGLE'S AI proposed UPDATE as of 04/10/26. Both versions of code involve multiprocessing rather then multi threading.
Dashboard Improvements per Google :-
Single Figure Context: Instead of calling plt.figure() three times, we initialize one large figure (18x6) and use fig.add_subplot(1, 3, i) to place them in a row.
Standardized API: Each worker function now returns a consistent tuple format. This allows the main loop to handle different plotting methods (like plot_surface vs plot_trisurf) dynamically.
Seaborn Consistency: By using sns.set_theme(), the background panes, grid line weights, and font styles are uniform across all three 3D axes. Compare with old style of such kind of plotting [2].
=====================================================
~/MULTITHREADED/PLOTTING
❯ cat plotMulti3DSeaborn12.py
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.tri import Triangulation
from concurrent.futures import ProcessPoolExecutor
# --- Parallel Worker Functions ---
def calc_paraboloid():
X, Y = np.meshgrid(np.linspace(-5, 5, 100), np.linspace(-5, 5, 100))
Z = X**2 + Y**2
# Return (plot_type, data, palette, title)
return ('surface', (X, Y, Z), 'rocket', 'Paraboloid')
def calc_sine_tri():
X, Y = np.meshgrid(np.linspace(-5, 5, 50), np.linspace(-5, 5, 50))
Z = np.sin(np.sqrt(X**2 + Y**2))
triang = Triangulation(X.flatten(), Y.flatten())
return ('trisurf', (triang, Z.flatten()), 'viridis', 'Sine Triangulation')
def calc_mobius():
theta, w = np.meshgrid(np.linspace(0, 2*np.pi, 100), np.linspace(-0.5, 0.5, 10))
r = 1 + w * np.cos(theta / 2)
x, y, z = r * np.cos(theta), r * np.sin(theta), w * np.sin(theta / 2)
return ('surface', (x, y, z), 'mako', 'Möbius Strip')
if __name__ == "__main__":
# Apply Seaborn dashboard styling
sns.set_theme(style="white")
# 1. Execute calculations in parallel
with ProcessPoolExecutor() as executor:
futures = [
executor.submit(calc_paraboloid),
executor.submit(calc_sine_tri),
executor.submit(calc_mobius)
]
results = [f.result() for f in futures]
# 2. Main Thread Rendering: Combined Dashboard
fig = plt.figure(figsize=(18, 6))
for i, (plot_type, data, palette, title) in enumerate(results, 1):
ax = fig.add_subplot(1, 3, i, projection='3d')
# Pull the Seaborn color palette
cmap = sns.color_palette(palette, as_cmap=True)
if plot_type == 'surface':
ax.plot_surface(*data, cmap=cmap, edgecolor='none', alpha=0.9)
else:
ax.plot_trisurf(*data, cmap=cmap, edgecolor='none')
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
ax.xaxis.set_pane_color((1.0, 1.0, 1.0, 0.0)) # Transparency for cleaner look
plt.tight_layout()
plt.show()
REFERENCES
1. https://medium.com/@ebimsv/mastering-matplotlib-part-6-exploring-3d-plotting-0423821e2e10
2. https://www.geeksforgeeks.org/data-visualization/plotting-multiple-figures-in-a-row-using-seaborn/






























