You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

并行化pcolormesh绘制多AMR块至同一轴时出现空轴问题

问题描述

我有AMR网格生成的非结构化数据,每个图表包含2个子图,需要把大量AMR块绘制到同一坐标轴上。因为绘图耗时太长,尝试用并行化让每个进程绘制一个块,但运行脚本后得到的是空坐标轴。我猜需要传递axes对象,但不确定具体方法,以下是返回空轴的代码:

import os,sys
import numpy as np
import function_library as fl
from multiprocessing import Pool
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import tqdm
import time as ttime
import pandas
import dask

# x_corner and y_corner have size [numgrid,17]
# var has size [numgrid,16,16]
# numgrid is the number of blocks

for i in range(indxStart,indxEnd+1):

    fig,axs   = plt.subplots(nrows=1, ncols=len(varPlots),figsize=(10,4),layout='constrained', sharey=True)

    def plotVars(igrid):
        # Rho with electron beams
        pcmRho = axs[0].pcolormesh(x_corner[igrid,:],y_corner[igrid,:], (var[igrid,0,:,:]/HMass).T, norm=colors.LogNorm(vmin=vLims[0][0],vmax=vLims[0][1]), cmap=cmaps[0], antialiased=False)
        # Electron beams
        pcmlQ = axs[0].pcolormesh(x_corner[igrid,:],y_corner[igrid,:], var[igrid,2,:,:].T, norm=colors.Normalize(vmin=vLims[2][0] ,vmax=vLims[2][1]), cmap=cmaps[2], antialiased=False)
        pcmcQ = axs[0].pcolormesh(x_corner[igrid,:],y_corner[igrid,:], var[igrid,3,:,:].T, norm=colors.Normalize(vmin=vLims[3][0],vmax=vLims[3][1]), cmap=cmaps[3], antialiased=False)

        # v_y
        pcmVy = axs[1].pcolormesh(x_corner[igrid,:], y_corner[igrid,:], var[igrid,1,:,:].T, norm=colors.Normalize(vmin=vLims[1][0],vmax=vLims[1][1]), cmap=cmaps[1], antialiased=False)

    pool = Pool(numProc)
    r = list(tqdm.tqdm(pool.imap_unordered(plotVars,range(numgrid)),total=numgrid))

    # axes and cbar rho 
    axs[0].set_xlim(xMin,xMax)
    axs[0].set_ylim(yMin,yMax)
    axs[0].set_xlabel('x (Mm)',fontsize=20,weight='bold')
    axs[0].set_ylabel('y (Mm)',fontsize=20,weight='bold')
    axs[0].xaxis.set_tick_params(labelsize=18)
    axs[0].yaxis.set_tick_params(labelsize=18)
    cbarRho = plt.colorbar(pcmRho,ax=axs[0])
    cbarRho.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    cbarlQ = plt.colorbar(pcmlQ,ax=axs[0])
    #cbarlQ.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    cbarcQ = plt.colorbar(pcmcQ,ax=axs[0])
    #cbarlQ.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    #axes and cbar vy
    axs[1].set_xlim(xMin,xMax)
    axs[1].set_ylim(yMin,yMax)
    axs[1].set_xlabel('x (Mm)',fontsize=20,weight='bold')
    axs[1].set_ylabel('')
    axs[1].xaxis.set_tick_params(labelsize=18)
    cbarVy=plt.colorbar(pcmVy,ax=axs[1])
    #cbarVy.set_label(label=cbTxts[1],weight='bold',fontsize=15)  

    plt.suptitle('t = '+str(round(float(time),2))+'s',fontsize=20,weight='bold')

    figName = fileBase+str(i).rjust(4,'0')+'.png'
    plt.savefig(pathFig+figName,dpi=500)
    print(pathFig+figName)
    plt.close()
问题原因与解决方案

核心问题

Matplotlib的Axes对象不能跨进程共享,子进程里对主进程创建的axs进行绘图操作,不会同步到主进程的Figure对象上,这就是出现空坐标轴的直接原因。

正确思路

并行绘图的合理方式是:让每个子进程独立准备对应AMR块的绘图数据,主进程收集所有数据后,统一将所有块绘制到同一坐标轴上——所有绘图操作必须在同一个进程内完成。

修改后的代码

import os,sys
import numpy as np
import function_library as fl
from multiprocessing import Pool
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import tqdm
import time as ttime
import pandas
import dask

# x_corner and y_corner have size [numgrid,17]
# var has size [numgrid,16,16]
# numgrid is the number of blocks

# 子进程任务:仅准备当前块的绘图所需数据,不操作坐标轴
def prepare_plot_data(igrid):
    rho_data = (var[igrid,0,:,:]/HMass).T
    lQ_data = var[igrid,2,:,:].T
    cQ_data = var[igrid,3,:,:].T
    vy_data = var[igrid,1,:,:].T
    return (x_corner[igrid,:], y_corner[igrid,:], rho_data, lQ_data, cQ_data, vy_data)

for i in range(indxStart,indxEnd+1):

    fig,axs   = plt.subplots(nrows=1, ncols=len(varPlots),figsize=(10,4),layout='constrained', sharey=True)
    
    # 并行生成所有块的绘图数据
    pool = Pool(numProc)
    all_plot_data = list(tqdm.tqdm(pool.imap_unordered(prepare_plot_data, range(numgrid)), total=numgrid))
    pool.close()
    pool.join()

    # 主进程统一绘制所有AMR块到坐标轴
    pcmRho_list = []
    pcmlQ_list = []
    pcmcQ_list = []
    pcmVy_list = []

    for x_c, y_c, rho_d, lQ_d, cQ_d, vy_d in all_plot_data:
        # 绘制第一个子图的三层数据
        pcmRho = axs[0].pcolormesh(x_c, y_c, rho_d, norm=colors.LogNorm(vmin=vLims[0][0],vmax=vLims[0][1]), cmap=cmaps[0], antialiased=False)
        pcmlQ = axs[0].pcolormesh(x_c, y_c, lQ_d, norm=colors.Normalize(vmin=vLims[2][0] ,vmax=vLims[2][1]), cmap=cmaps[2], antialiased=False)
        pcmcQ = axs[0].pcolormesh(x_c, y_c, cQ_d, norm=colors.Normalize(vmin=vLims[3][0],vmax=vLims[3][1]), cmap=cmaps[3], antialiased=False)
        # 绘制第二个子图
        pcmVy = axs[1].pcolormesh(x_c, y_c, vy_d, norm=colors.Normalize(vmin=vLims[1][0],vmax=vLims[1][1]), cmap=cmaps[1], antialiased=False)
        # 保存绘图对象用于后续生成色条
        pcmRho_list.append(pcmRho)
        pcmlQ_list.append(pcmlQ)
        pcmcQ_list.append(pcmcQ)
        pcmVy_list.append(pcmVy)

    # 配置第一个子图的坐标轴与色条
    axs[0].set_xlim(xMin,xMax)
    axs[0].set_ylim(yMin,yMax)
    axs[0].set_xlabel('x (Mm)',fontsize=20,weight='bold')
    axs[0].set_ylabel('y (Mm)',fontsize=20,weight='bold')
    axs[0].xaxis.set_tick_params(labelsize=18)
    axs[0].yaxis.set_tick_params(labelsize=18)
    # 所有块用同一刻度范围,取最后一个块的绘图对象生成色条即可
    cbarRho = plt.colorbar(pcmRho_list[-1],ax=axs[0])
    cbarRho.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    cbarlQ = plt.colorbar(pcmlQ_list[-1],ax=axs[0])
    #cbarlQ.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    cbarcQ = plt.colorbar(pcmcQ_list[-1],ax=axs[0])
    #cbarlQ.set_label(label=cbTxts[0],weight='bold',fontsize=15)

    # 配置第二个子图的坐标轴与色条
    axs[1].set_xlim(xMin,xMax)
    axs[1].set_ylim(yMin,yMax)
    axs[1].set_xlabel('x (Mm)',fontsize=20,weight='bold')
    axs[1].set_ylabel('')
    axs[1].xaxis.set_tick_params(labelsize=18)
    cbarVy=plt.colorbar(pcmVy_list[-1],ax=axs[1])
    #cbarVy.set_label(label=cbTxts[1],weight='bold',fontsize=15)  

    plt.suptitle('t = '+str(round(float(time),2))+'s',fontsize=20,weight='bold')

    figName = fileBase+str(i).rjust(4,'0')+'.png'
    plt.savefig(pathFig+figName,dpi=500)
    print(pathFig+figName)
    plt.close()

关键说明

  • 禁止跨进程操作Axes:子进程仅负责数据预处理,把绘图操作全部放在主进程完成,避免进程间的对象同步问题。
  • 色条处理逻辑:因为所有AMR块使用同一套刻度范围,用最后一个块的绘图对象生成色条不会影响色条的准确性。
  • 进程池管理:添加pool.close()和pool.join()确保所有子进程完成任务后再执行主进程的绘图逻辑。

内容的提问来源于stack exchange,提问作者Forged

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:17:47