Wednesday, April 8, 2026

Simultaneously build several python 3D plots via Multi-threaded Python 3.14t on Arch Linux

 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/