はじめに
化学計算プログラムである GPAW を、マルチCPU(MPI並列)およびGPU(NVIDIA CUDA)の両方を利用できるようにソースからビルドしました。
JupyterHub環境やConda仮想環境の組み合わせでは、ホストシステムとのコンパイラ競合や環境変数の遮断、CUDAのバージョン不整合など、信じられないほどの数の罠(エラー)に直面しました。これらを一つずつ突破した完全な手順を、トラブルシューティングの記録とともに残します。
動作環境
- OS: Ubuntu 22.04
-
GPU: NVIDIA L4 (Ada Lovelaceアーキテクチャ /
sm_89) - Python: 3.14.5 (Conda仮想環境)
- 主要ライブラリ: GPAW 25.7.1b1, CuPy 14.1.1, ASE 3.28.0
1. Conda環境の整備(依存関係の統一)
システムのコンパイラ(/usr/bin/gcc や mpicc)とConda環境内のライブラリが混ざるとリンクエラーの原因になります。まずはコンパイラ、MPI、並列線形計算ライブラリをすべてConda環境内に統一します。
# 1. 基本的なライブラリとConda用コンパイラセット(GCC 13に固定)を導入
# ※ 最新のGCC 14を入れるとCUDA(nvcc)が「サポート外」とエラーを吐くため、バージョン13に固定するのがミソです。
conda install -c conda-forge "gcc_linux-64=13" "gxx_linux-64=13" openmpi scalapack fftw libxc
# 2. Conda環境内にCUDA開発環境を導入
conda install -c nvidia cuda-nvcc cuda-toolkit
さらに、Python用の必須パッケージを導入します。
pip install numpy scipy matplotlib ase cupy
2. siteconfig.py の作成
GPAWのビルド設定を制御する siteconfig.py を、GPAWのソースディレクトリ直下に作成します。
NVIDIA L4で駆動させるため、アーキテクチャに sm_89 を含めること、およびConda環境のパスを明示的に通すことがポイントです。
# siteconfig.py
import os
compiler = 'gcc'
mpicompiler = 'mpicc'
mpilinker = 'mpicc'
# 依存ライブラリの指定
libraries = ['xc', 'fftw3', 'blas', 'lapack', 'scalapack']
scalapack = True
# GPU (CUDA) 設定の有効化
gpu = True
gpu_target = 'cuda'
gpu_compiler = 'nvcc'
# NVIDIA L4 (sm_89) を含む、主要なGPUアーキテクチャを網羅するファットバイナリ設定
gpu_compile_args = [
'-O3', '-g',
'-gencode', 'arch=compute_70,code=sm_70', # V100
'-gencode', 'arch=compute_75,code=sm_75', # T4
'-gencode', 'arch=compute_80,code=sm_80', # A100
'-gencode', 'arch=compute_86,code=sm_86', # RTX 3000系 / A10G
'-gencode', 'arch=compute_89,code=sm_89', # RTX 4000系 / L4
'-gencode', 'arch=compute_90,code=sm_90' # H100
]
libraries += ['cudart', 'cublas']
# Conda環境内の include / lib パスを強制的に読み込ませる
conda_env = os.environ.get('CONDA_PREFIX')
if conda_env:
include_dirs += [os.path.join(conda_env, 'include')]
library_dirs += [os.path.join(conda_env, 'lib')]
runtime_library_dirs += [os.path.join(conda_env, 'lib')]
# CondaのCUDA Toolkit特有のディープな階層もカバー
cuda_target_inc = os.path.join(conda_env, 'targets/x86_64-linux/include')
cuda_target_lib = os.path.join(conda_env, 'targets/x86_64-linux/lib')
if os.path.exists(cuda_target_inc):
include_dirs += [cuda_target_inc]
library_dirs += [cuda_target_lib]
runtime_library_dirs += [cuda_target_lib]
3. GPAWのコンパイルとインストール
設定ファイルを配置したら、一度ソースツリーをクリーンにしてからインストール(Editableモード)を実行します。
git clean -dfx
pip install -e . -v
-v を付けておくことで、nvcc や mpicc が意図したフラグでビルドされているかリアルタイムに監視できます。
ビルド完了後、ポテンシャルデータをインポートします。
gpaw install-data <任意の展開先ディレクトリ>
状態確認
gpaw info
出力の MPI enabled: yes、GPU enabled: yes、scalapack: yes になっていることを確認します。
4. 実行時の罠と解決策
罠①:計算開始時に RuntimeError: Failed to find CUDA headers.
GPAWのC拡張ビルドは通っても、いざPythonで実行するとCuPyがJITコンパイル時に「CUDAのヘッダ(cuda.h)が見つからない」とマヌケなエラーを吐きます。Conda環境の深すぎる階層にヘッダが隠されているのが原因です。
【解決策】
Pythonパッケージとして公式のランタイムヘッダを直接流し込むことで、環境変数の呪縛から解放されます。
pip install nvidia-cuda-runtime-cu12
罠②:mpirun -np 2 で並列実行すると Segmentation fault (Signal 11)
1コア+1GPUだと正常に動くのに、マルチCPU(MPI)にした瞬間、MPI_Allreduce 内の mca_coll_cuda_allreduce でセグフォが起きました。これはConda版OpenMPIの「GPU-aware MPI(GPUメモリ間直接通信)」の不整合によるものです。
【解決策】
実行時に環境変数 GPAW_NO_GPU_MPI=1 を指定し、安全なCPUメモリ経由の通信へとフォールバックさせます。
5. 最終的な動作確認スクリプト
JupyterLabのノートブックや、ターミナルから以下の並列実行スクリプトを走らせます。
# gpaw_parallel.py
import time
from ase.build import bulk
from gpaw import GPAW, PW
from gpaw.mpi import world
# テスト用のシリコンバルク構造
atoms = bulk('Si', 'diamond', a=5.43)
calc = GPAW(
mode=PW(ecut=350),
xc='PBE',
kpts=(6, 6, 6),
parallel={'gpu': True}, # 全MPIプロセスでGPUを利用
txt='gpaw_mpi.txt' if world.rank == 0 else None
)
atoms.calc = calc
if world.rank == 0:
print(f"--- 計算開始 (MPIプロセス数: {world.size}) ---")
start = time.time()
energy = atoms.get_potential_energy()
if world.rank == 0:
print(f"計算終了! 所要時間: {time.time() - start:.2f} 秒")
print(f"シリコンの全エネルギー: {energy:.6f} eV")
実行コマンド(4並列の例)
GPAW_NO_GPU_MPI=1 mpirun -np 4 python gpaw_parallel.py
これで、NVIDIA L4 GPUの圧倒的な恩恵を受けつつ、マルチCPUでタスクを分散した高速な量子化学計算ができるようになります!同じような環境変数の迷宮で困っている方の参考になれば幸いです。