使用Python Sympy绘图时如何为所有零值点绘制红色标识线
需求说明
需要找出目标函数所有取值为0的点位,在生成的3D图像中用醒目的红色线条标注这些零值位置,便于清晰识别。目前未找到Sympy库中可实现类似功能的相关接口,现有实现代码如下:
import inspect from IPython.display import display, Math from sympy import * xi,eta,b,a = symbols("xi,eta,b,a") Nq = Matrix([[1,xi,eta,xi**2,xi*eta,eta**2,xi**2*eta,xi*eta**2]]) AINV = Matrix([[1,1,1,1,-b,-a,-b,-a], [0,0,0,0,0,2*a,0,-2*a], [0,0,0,0,-2*b,0,2*b,0], [0,0,0,0,0,a,0,a], [1,-1,1,-1,0,0,0,0], [0,0,0,0,b,0,b,0], [-1,-1,1,1,2*b,0,-2*b,0], [-1,1,1,-1,0,-2*a,0,2*a]]) N = Nq*AINV N1 = combsimp(N[0]) N2 = combsimp(N[1]) N3 = combsimp(N[2]) N4 = combsimp(N[3]) N5 = combsimp(N[4]) N6 = combsimp(N[5]) N7 = combsimp(N[6]) N8 = combsimp(N[7]) NN = Matrix([[N1,N2,N3,N4,N5,N6,N7,N8]]) NN # Jupyter环境运行请取消下一行注释 # %matplotlib notebook Xi = Matrix([0,12,16,8,4,2,4,7]) Yi = Matrix([4,0,12,7,3,5,7,8]) J1 = diff(NN,xi)*Xi J2 = diff(NN,xi)*Yi J3 = diff(NN,eta)*Xi J4 = diff(NN,eta)*Yi J = Matrix([[J1,J2],[J3,J4]]) JDet = J.det() JDetPLOT = JDet.subs(a,1).subs(b,1) plotting.plot3d(JDetPLOT,(xi,-1,1),(eta,-1,1))
实现方案
Sympy自带的plot3d接口没有直接标注指定等值线的功能,按以下步骤修改即可实现零值线标注:
- 用数值方法提取零值线:通过
lambdify把符号表达式转为支持numpy数组运算的数值函数,在xi、eta的[-1,1]定义域上生成高密度网格,计算所有网格点的函数值后,直接提取z=0的等值线坐标。该方法比符号求解速度更快,不会出现漏根、符号求解失败的问题。 - 图层叠加绘图:先绘制原函数的半透明3D曲面保证底层数据可见,再单独绘制提取到的零值线,将线条设置为3像素粗的正红色保证醒目,叠加后即可清晰识别零值位置。
修改后的可直接运行代码如下:
import numpy as np import matplotlib.pyplot as plt from sympy import * # 原有符号计算逻辑保留 xi,eta,b,a = symbols("xi,eta,b,a") Nq = Matrix([[1,xi,eta,xi**2,xi*eta,eta**2,xi**2*eta,xi*eta**2]]) AINV = Matrix([[1,1,1,1,-b,-a,-b,-a], [0,0,0,0,0,2*a,0,-2*a], [0,0,0,0,-2*b,0,2*b,0], [0,0,0,0,0,a,0,a], [1,-1,1,-1,0,0,0,0], [0,0,0,0,b,0,b,0], [-1,-1,1,1,2*b,0,-2*b,0], [-1,1,1,-1,0,-2*a,0,2*a]]) N = Nq*AINV N1 = combsimp(N[0]) N2 = combsimp(N[1]) N3 = combsimp(N[2]) N4 = combsimp(N[3]) N5 = combsimp(N[4]) N6 = combsimp(N[5]) N7 = combsimp(N[6]) N8 = combsimp(N[7]) NN = Matrix([[N1,N2,N3,N4,N5,N6,N7,N8]]) Xi = Matrix([0,12,16,8,4,2,4,7]) Yi = Matrix([4,0,12,7,3,5,7,8]) J1 = diff(NN,xi)*Xi J2 = diff(NN,xi)*Yi J3 = diff(NN,eta)*Xi J4 = diff(NN,eta)*Yi J = Matrix([[J1,J2],[J3,J4]]) JDet = J.det() JDetPLOT = JDet.subs(a,1).subs(b,1) # 转数值函数,生成网格数据 JDet_np = lambdify((xi, eta), JDetPLOT, 'numpy') xi_arr = np.linspace(-1, 1, 200) eta_arr = np.linspace(-1, 1, 200) XI, ETA = np.meshgrid(xi_arr, eta_arr) Z = JDet_np(XI, ETA) # 绘制3D图 # Jupyter环境运行请取消下一行注释 # %matplotlib notebook fig = plt.figure(figsize=(9, 7)) ax = fig.add_subplot(111, projection='3d') # 绘制半透明原曲面 ax.plot_surface(XI, ETA, Z, cmap='viridis', alpha=0.6) # 绘制粗红色零值线 ax.contour(XI, ETA, Z, levels=[0], colors='#ff0000', linewidths=3) ax.set_xlabel(r'$\xi$') ax.set_ylabel(r'$\eta$') ax.set_zlabel('Jacobian Determinant') plt.tight_layout() plt.show()
如果需要获取零值点的精确坐标,可以对提取到的contour对象的路径数据做解析,即可拿到所有零值线上的离散点坐标。
内容的提问来源于stack exchange,提问作者Lopehert
相关产品推荐
相关产品推荐

