Spaces:
Sleeping
Sleeping
Commit ·
36c6a32
1
Parent(s): da57d25
- rank_analysis_proof.py → rank_analysis_proof.nb +0 -0
- vec_temp.py +191 -150
rank_analysis_proof.py → rank_analysis_proof.nb
RENAMED
|
File without changes
|
vec_temp.py
CHANGED
|
@@ -1,168 +1,209 @@
|
|
| 1 |
import sympy as sp
|
| 2 |
import numpy as np
|
| 3 |
import matplotlib.pyplot as plt
|
| 4 |
-
from
|
| 5 |
-
from matplotlib.widgets import Slider
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
# 1. Define symbolic variables
|
| 12 |
u, v, theta, a, b, gamma = sp.symbols('u v theta a b gamma')
|
| 13 |
pi = sp.pi
|
| 14 |
|
| 15 |
-
#
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
# Position vector
|
| 23 |
-
r = sp.Matrix([X, Y, Z])
|
| 24 |
-
|
| 25 |
-
# 3. Define the Vector Field
|
| 26 |
-
# Partial derivatives of the position vector
|
| 27 |
-
r_v = sp.diff(r, v)
|
| 28 |
-
r_theta = sp.diff(r, theta)
|
| 29 |
-
# Sum of component-wise derivatives
|
| 30 |
-
B_vec = sp.Matrix([
|
| 31 |
-
sp.diff(X, v) + sp.diff(X, theta),
|
| 32 |
-
sp.diff(Y, v) + sp.diff(Y, theta),
|
| 33 |
-
sp.diff(Z, v) + sp.diff(Z, theta)
|
| 34 |
])
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
u_plot_range = np.linspace(0, 2, 40) # Range of u for the 2D plot
|
| 63 |
-
|
| 64 |
-
# Create high-resolution grids for calculation and plotting
|
| 65 |
-
v_res, theta_res = 100, 100
|
| 66 |
-
v_range = np.linspace(0, 2 * np.pi, v_res)
|
| 67 |
-
theta_range = np.linspace(0, 2 * np.pi, theta_res)
|
| 68 |
-
v_grid, theta_grid = np.meshgrid(v_range, theta_range)
|
| 69 |
-
dv = v_range[1] - v_range[0]
|
| 70 |
-
dtheta = theta_range[1] - theta_range[0]
|
| 71 |
-
dA = dv * dtheta # Area of one grid cell for integration
|
| 72 |
-
|
| 73 |
-
# 3. Set up the plotting figure and axes
|
| 74 |
-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8), width_ratios=[1.2, 1])
|
| 75 |
-
ax1 = plt.subplot(1, 2, 1, projection='3d')
|
| 76 |
-
ax2 = plt.subplot(1, 2, 2)
|
| 77 |
-
fig.subplots_adjust(bottom=0.25) # Make space for sliders
|
| 78 |
-
|
| 79 |
-
# 4. Helper function for numerical integration
|
| 80 |
-
def calculate_integral(u_val, R_val):
|
| 81 |
-
"""Calculates the integral for a specific u and R."""
|
| 82 |
-
# Calculate manifold coordinates
|
| 83 |
-
X_surf = X_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 84 |
-
Y_surf = Y_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 85 |
-
Z_surf = Z_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 86 |
-
|
| 87 |
-
# Condition: find which points are inside the sphere
|
| 88 |
-
is_inside = (X_surf**2 + Y_surf**2 + Z_surf**2) < R_val**2
|
| 89 |
-
|
| 90 |
-
# Calculate integrand values across the whole surface
|
| 91 |
-
integrand_values = integrand_func(u_val, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 92 |
-
|
| 93 |
-
# Apply the condition: only consider values inside the sphere
|
| 94 |
-
integrand_masked = np.where(is_inside, integrand_values, 0)
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
|
| 109 |
-
#
|
| 110 |
-
|
| 111 |
-
Y_surf = Y_func(u_current, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 112 |
-
Z_surf = Z_func(u_current, v_grid, theta_grid, A_val, B_val, GAMMA_val)
|
| 113 |
|
| 114 |
-
#
|
| 115 |
-
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
X_inside = np.where(is_inside, X_surf, np.nan) # Set points outside to NaN so they are not plotted
|
| 120 |
-
Y_inside = np.where(is_inside, Y_surf, np.nan)
|
| 121 |
-
Z_inside = np.where(is_inside, Z_surf, np.nan)
|
| 122 |
-
ax1.plot_surface(X_inside, Y_inside, Z_inside, alpha=0.8, rstride=5, cstride=5)
|
| 123 |
-
|
| 124 |
-
# Draw the sphere wireframe
|
| 125 |
-
phi, psi = np.mgrid[0:2*np.pi:10j, 0:np.pi:10j]
|
| 126 |
-
x_sphere = R_current * np.sin(psi) * np.cos(phi)
|
| 127 |
-
y_sphere = R_current * np.sin(psi) * np.sin(phi)
|
| 128 |
-
z_sphere = R_current * np.cos(psi)
|
| 129 |
-
ax1.plot_wireframe(x_sphere, y_sphere, z_sphere, color="r", alpha=0.3)
|
| 130 |
|
| 131 |
-
#
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
# --- Update 2D Plot (ax2) ---
|
| 137 |
-
# This only needs to re-calculate if the radius changes, but it's simpler to update always.
|
| 138 |
-
print(f"Recalculating integral curve for R = {R_current:.2f}...")
|
| 139 |
-
integral_values = [calculate_integral(u_val, R_current) for u_val in u_plot_range]
|
| 140 |
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
ax2.axvline(u_current, color='r', linestyle='--', label=f'Current u={u_current:.2f}')
|
| 144 |
-
ax2.set_title(f"Integral vs. u (for Sphere Radius R={R_current:.2f})")
|
| 145 |
-
ax2.set_xlabel("u")
|
| 146 |
-
ax2.set_ylabel("Numerical Integral of ||B|| over surface inside sphere")
|
| 147 |
-
ax2.grid(True)
|
| 148 |
-
ax2.legend()
|
| 149 |
-
ax2.set_xlim(0, 2)
|
| 150 |
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
#
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
#
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
#
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
plt.show()
|
|
|
|
| 1 |
import sympy as sp
|
| 2 |
import numpy as np
|
| 3 |
import matplotlib.pyplot as plt
|
| 4 |
+
from scipy.integrate import dblquad
|
|
|
|
| 5 |
|
| 6 |
+
# --- RICH IMPORTS ---
|
| 7 |
+
from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn, TimeElapsedColumn, track
|
| 8 |
+
|
| 9 |
+
# --- NEW: Import joblib for parallel processing ---
|
| 10 |
+
from joblib import Parallel, delayed, cpu_count
|
| 11 |
+
|
| 12 |
+
import warnings
|
| 13 |
+
|
| 14 |
+
# Use this in a Jupyter/IPython environment for nicely formatted math output
|
| 15 |
+
sp.init_printing(use_unicode=True)
|
| 16 |
+
|
| 17 |
+
# Reduced for faster testing, feel free to increase it back to 100_000
|
| 18 |
+
n_samples = 20_000
|
| 19 |
+
|
| 20 |
+
# --- Part 1: Symbolic Definitions (Unchanged) ---
|
| 21 |
|
|
|
|
| 22 |
u, v, theta, a, b, gamma = sp.symbols('u v theta a b gamma')
|
| 23 |
pi = sp.pi
|
| 24 |
|
| 25 |
+
# Position Vector R
|
| 26 |
+
R_vec = sp.Matrix([
|
| 27 |
+
(a + gamma * sp.sin(u/4)) * sp.cos(v/2) + (b * sp.cos(theta) * sp.cos(u/4)),
|
| 28 |
+
b * sp.cos(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 + sp.cos(v/2))/2) * \
|
| 29 |
+
((v/(2*pi))*(sp.cos(u/4) - 1) - u/(2*pi) + (u*v)/(4*pi**2) + 1),
|
| 30 |
+
b * sp.sin(theta) * sp.sin(u/4) + b * sp.sin(theta) * ((1 - sp.cos(v/2))/2) * \
|
| 31 |
+
((v/(2*pi))*(1 - sp.cos(u/4)) + sp.cos(u/4) - (u*v)/(4*pi**2))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
])
|
| 33 |
+
|
| 34 |
+
# Vector Field B
|
| 35 |
+
B_vec = R_vec.diff(v) + R_vec.diff(theta)
|
| 36 |
+
|
| 37 |
+
print("Successfully defined symbolic Manifold and Vector Field B.")
|
| 38 |
+
|
| 39 |
+
# --- Part 2: Lambdify (Unchanged) ---
|
| 40 |
+
|
| 41 |
+
print("Converting to numerical functions...")
|
| 42 |
+
|
| 43 |
+
params = [u, v, theta, a, b, gamma]
|
| 44 |
+
|
| 45 |
+
f_R = sp.lambdify(params, R_vec, modules=[{'ImmutableDenseMatrix': np.array}, 'numpy'])
|
| 46 |
+
f_B = sp.lambdify(params, B_vec, modules=[{'ImmutableDenseMatrix': np.array}, 'numpy'])
|
| 47 |
+
|
| 48 |
+
print("Lambdification complete.")
|
| 49 |
+
|
| 50 |
+
# --- Part 3: Optimized Integrand (Unchanged) ---
|
| 51 |
+
|
| 52 |
+
a_val, b_val, gamma_val = 1.0, 1.5, 6
|
| 53 |
+
JACOBIAN_TOL = 1e-8
|
| 54 |
+
EPSILON = 1e-5
|
| 55 |
+
|
| 56 |
+
def integrand_vectorized(v_vals, theta_vals, u_val):
|
| 57 |
+
v_vals = np.atleast_1d(v_vals)
|
| 58 |
+
theta_vals = np.atleast_1d(theta_vals)
|
| 59 |
+
results = np.zeros_like(v_vals)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
+
for i, (v_val, theta_val) in enumerate(zip(v_vals, theta_vals)):
|
| 62 |
+
args = (u_val, v_val, theta_val, a_val, b_val, gamma_val)
|
| 63 |
+
try:
|
| 64 |
+
B_center = np.array(f_B(*args)).flatten()
|
| 65 |
+
R_center = np.array(f_R(*args)).flatten()
|
| 66 |
+
args_u = (u_val + EPSILON, v_val, theta_val, a_val, b_val, gamma_val)
|
| 67 |
+
args_v = (u_val, v_val + EPSILON, theta_val, a_val, b_val, gamma_val)
|
| 68 |
+
args_t = (u_val, v_val, theta_val + EPSILON, a_val, b_val, gamma_val)
|
| 69 |
+
B_u = np.array(f_B(*args_u)).flatten()
|
| 70 |
+
B_v = np.array(f_B(*args_v)).flatten()
|
| 71 |
+
B_t = np.array(f_B(*args_t)).flatten()
|
| 72 |
+
R_u = np.array(f_R(*args_u)).flatten()
|
| 73 |
+
R_v = np.array(f_R(*args_v)).flatten()
|
| 74 |
+
R_t = np.array(f_R(*args_t)).flatten()
|
| 75 |
+
grad_p_B = np.column_stack([(B_u - B_center) / EPSILON, (B_v - B_center) / EPSILON, (B_t - B_center) / EPSILON])
|
| 76 |
+
J_val = np.column_stack([(R_u - R_center) / EPSILON, (R_v - R_center) / EPSILON, (R_t - R_center) / EPSILON])
|
| 77 |
+
det = np.linalg.det(J_val)
|
| 78 |
+
if abs(det) < JACOBIAN_TOL:
|
| 79 |
+
results[i] = 0.0
|
| 80 |
+
continue
|
| 81 |
+
J_inv = np.linalg.inv(J_val)
|
| 82 |
+
grad_xyz_B = J_inv.T @ grad_p_B
|
| 83 |
+
div_B = np.trace(grad_xyz_B)
|
| 84 |
+
cross_prod = np.cross(J_val[:, 1], J_val[:, 2])
|
| 85 |
+
norm_val = np.linalg.norm(cross_prod)
|
| 86 |
+
if norm_val < JACOBIAN_TOL:
|
| 87 |
+
results[i] = 0.0
|
| 88 |
+
continue
|
| 89 |
+
result = div_B * norm_val
|
| 90 |
+
results[i] = result if np.isfinite(result) else 0.0
|
| 91 |
+
except Exception:
|
| 92 |
+
results[i] = 0.0
|
| 93 |
+
return results[0] if len(results) == 1 else results
|
| 94 |
+
|
| 95 |
+
def integrand(v_val, theta_val, u_val):
|
| 96 |
+
return integrand_vectorized(v_val, theta_val, u_val)
|
| 97 |
+
|
| 98 |
+
# --- Part 4: Integration with Parallel Monte Carlo ---
|
| 99 |
+
|
| 100 |
+
print("\nStarting integration...")
|
| 101 |
+
|
| 102 |
+
u_values = np.linspace(0, 2, 100)
|
| 103 |
+
|
| 104 |
+
# --- NEW: Define a worker function for parallel execution ---
|
| 105 |
+
# This function encapsulates the work for a *single* u_val.
|
| 106 |
+
# It takes all necessary parameters and returns the result tuple.
|
| 107 |
+
def worker_integrate(u_val, n_samples):
|
| 108 |
+
"""
|
| 109 |
+
Performs Monte Carlo integration for a single u_val.
|
| 110 |
+
This function is designed to be called in parallel.
|
| 111 |
+
"""
|
| 112 |
+
# Seed the random number generator differently for each process
|
| 113 |
+
# to ensure they don't all produce the same random numbers.
|
| 114 |
+
np.random.seed(int(u_val * 1000) + np.random.randint(0, 1000))
|
| 115 |
|
| 116 |
+
v_samples = np.random.uniform(0, 2 * np.pi, n_samples)
|
| 117 |
+
theta_samples = np.random.uniform(0, 2 * np.pi, n_samples)
|
| 118 |
|
| 119 |
+
# Using a list comprehension is slightly cleaner here
|
| 120 |
+
values = [integrand(v, t, u_val) for v, t in zip(v_samples, theta_samples)]
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
# Filter out any non-finite results (inf, NaN)
|
| 123 |
+
finite_values = [v for v in values if np.isfinite(v)]
|
| 124 |
|
| 125 |
+
if len(finite_values) == 0:
|
| 126 |
+
return 0.0, 0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
+
# Monte Carlo estimate: (volume) * (average value)
|
| 129 |
+
volume = (2 * np.pi) * (2 * np.pi)
|
| 130 |
+
mean_val = np.mean(finite_values)
|
| 131 |
+
std_val = np.std(finite_values)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
+
integral = volume * mean_val
|
| 134 |
+
error = volume * std_val / np.sqrt(len(finite_values))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
+
return integral, error
|
| 137 |
+
|
| 138 |
+
# --- MODIFIED: The main integration loop is now parallel ---
|
| 139 |
+
# Use n_jobs=-1 to use all available CPU cores.
|
| 140 |
+
n_jobs = -1
|
| 141 |
+
num_cores = cpu_count()
|
| 142 |
+
print(f"Using parallel Monte Carlo integration on {num_cores} cores ({n_samples} samples per u)...")
|
| 143 |
+
|
| 144 |
+
# Create a list of "delayed" tasks, one for each u_value.
|
| 145 |
+
# 'delayed' is a wrapper that captures the function and its arguments.
|
| 146 |
+
tasks = [delayed(worker_integrate)(u, n_samples) for u in u_values]
|
| 147 |
+
|
| 148 |
+
# Execute the tasks in parallel. rich.progress.track provides the progress bar.
|
| 149 |
+
# Parallel(...) returns a list of the results from each worker call.
|
| 150 |
+
parallel_results = Parallel(n_jobs=n_jobs)(
|
| 151 |
+
track(tasks, description="[bold cyan]Integrating over u values")
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
# Unpack the list of (result, error) tuples into two separate lists.
|
| 155 |
+
integral_results, integral_errors = zip(*parallel_results)
|
| 156 |
+
|
| 157 |
+
print("\nIntegration finished.")
|
| 158 |
+
print(f"Average integration error: {np.mean(integral_errors):.2e}")
|
| 159 |
+
print(f"Max integration error: {np.max(integral_errors):.2e}")
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# --- Part 5: Plotting (Unchanged) ---
|
| 163 |
+
|
| 164 |
+
plt.figure(figsize=(12, 6))
|
| 165 |
+
|
| 166 |
+
plt.subplot(1, 2, 1)
|
| 167 |
+
plt.plot(u_values, integral_results, marker='o', linestyle='-',
|
| 168 |
+
label='∫ (div B) dA (Monte Carlo)', linewidth=2, markersize=4)
|
| 169 |
+
plt.title('Integral of div(B) over Surface vs. u', fontsize=14)
|
| 170 |
+
plt.xlabel('u', fontsize=12)
|
| 171 |
+
plt.ylabel('Integral Value', fontsize=12)
|
| 172 |
+
plt.grid(True, alpha=0.3)
|
| 173 |
+
plt.legend()
|
| 174 |
+
|
| 175 |
+
plt.subplot(1, 2, 2)
|
| 176 |
+
plt.semilogy(u_values, integral_errors, marker='s', linestyle='-',
|
| 177 |
+
color='red', label='MC Error Estimate', markersize=4)
|
| 178 |
+
plt.title('Integration Error vs. u', fontsize=14)
|
| 179 |
+
plt.xlabel('u', fontsize=12)
|
| 180 |
+
plt.ylabel('Error (log scale)', fontsize=12)
|
| 181 |
+
plt.grid(True, alpha=0.3, which='both')
|
| 182 |
+
plt.legend()
|
| 183 |
+
|
| 184 |
+
plt.tight_layout()
|
| 185 |
+
plt.show()
|
| 186 |
+
|
| 187 |
+
# --- Part 6: 3D Visualization (Unchanged) ---
|
| 188 |
+
|
| 189 |
+
print("\nGenerating 3D visualization...")
|
| 190 |
+
|
| 191 |
+
u_vis = 2 * np.pi
|
| 192 |
+
v_grid, theta_grid = np.meshgrid(np.linspace(0, 2*np.pi, 30), np.linspace(0, 2*np.pi, 30))
|
| 193 |
+
X, Y, Z = f_R(u_vis, v_grid, theta_grid, a_val, b_val, gamma_val)
|
| 194 |
+
|
| 195 |
+
v_quiver, theta_quiver = np.meshgrid(np.linspace(0, 2*np.pi, 6), np.linspace(0, 2*np.pi, 6))
|
| 196 |
+
Xq, Yq, Zq = f_R(u_vis, v_quiver, theta_quiver, a_val, b_val, gamma_val)
|
| 197 |
+
Bxq, Byq, Bzq = f_B(u_vis, v_quiver, theta_quiver, a_val, b_val, gamma_val)
|
| 198 |
+
|
| 199 |
+
fig = plt.figure(figsize=(14, 10))
|
| 200 |
+
ax = fig.add_subplot(111, projection='3d')
|
| 201 |
+
surf = ax.plot_surface(X, Y, Z, alpha=0.7, cmap='viridis', rstride=1, cstride=1, edgecolor='none')
|
| 202 |
+
ax.quiver(Xq, Yq, Zq, Bxq, Byq, Bzq, length=0.3, normalize=True, color='red', arrow_length_ratio=0.3, linewidth=1.5)
|
| 203 |
+
ax.set_title(f'Manifold and Vector Field B at u = {u_vis/np.pi:.2f}π', fontsize=16)
|
| 204 |
+
ax.set_xlabel('X', fontsize=12)
|
| 205 |
+
ax.set_ylabel('Y', fontsize=12)
|
| 206 |
+
ax.set_zlabel('Z', fontsize=12)
|
| 207 |
+
ax.view_init(elev=20., azim=-65)
|
| 208 |
+
fig.colorbar(surf, ax=ax, shrink=0.5, aspect=5)
|
| 209 |
plt.show()
|