使い方
次元順序を確認してから、目的に合うコードを選びます。言語の選択はページ全体に適用され、次回も記憶されます。JavaScript が無効でも全言語を読めます。
配列の次元順序
Fortran の a(nx, ny) と C の dims = {ny, nx} は、同じファイル内の同じバイト列を表します。Fortran は最初の添字、C/C++ は最後の添字が最も速く変わります。次元の表記を逆順に対応させるだけで、転置も、そのためのコピーも不要です。
h5c/docs/FORMAT.md 冒頭の h5dump による確認結果は次のとおりです。Fortran の (2, 3) がファイル上では (3, 2) になり、値の順序は変わりません。
Fortran: real(real64) :: a(2, 3) ! 値 1..6 を reshape で格納
HDF5: shape = (3, 2)
(0,0): 1, 2
(1,0): 3, 4
(2,0): 5, 6
下の実コードも同じ 6 要素の配置です。
Fortran — 2 次元配列を初期化します。
use, intrinsic :: iso_fortran_env, only: real64
implicit none
real(real64) :: values(2, 3)
values = reshape([1.0_real64, 2.0_real64, 3.0_real64, &
4.0_real64, 5.0_real64, 6.0_real64], shape(values))
C — 平坦なバッファとファイル上の shape。
const double values[6] = { 1, 2, 3, 4, 5, 6 };
const size_t dims[2] = { 3, 2 };
C++ — C と同じ次元順序。
const std::vector<double> values = { 1, 2, 3, 4, 5, 6 };
const h5cpp::shape dims = { 3, 2 };
以下は操作ごとのコード例です。配列の宣言・初期化は用途に合わせて用意してください。各コード下のリンクは、関連する実装例を調べるための参考です。
1. ファイルを開いて配列を書く
新規作成モードは同名ファイルを切り詰めます。保存先を確認してください。
Fortran — 主要部分の抜粋です。
call h5open_f(hdferr)
if (hdferr /= 0) error stop "h5open_f failed"
call h5fcreate_f("serial.h5", H5F_ACC_TRUNC_F, file_id, hdferr)
if (hdferr /= 0) error stop "h5fcreate_f failed"
call h5fort_swrite(file_id, "/values", values, hdferr)
if (hdferr /= 0) error stop "h5fort_swrite failed"
call h5fclose_f(file_id, hdferr)
if (hdferr /= 0) error stop "h5fclose_f failed"
call h5close_f(hdferr)
if (hdferr /= 0) error stop "h5close_f failed"
C — 書き込み後は全体コードの h5c_file_status と h5c_close の戻り値を確認します。
if (failed("open", h5c_open(path, H5C_TRUNCATE, &f))) {
return 1;
}
h5c_write_f64(f, "/mesh/coords", values, 2, dims);
C++ — 型と要素数はコンテナから取得します。
h5cpp::file f(path, h5cpp::mode::truncate);
f.write("/mesh/coords", values, dims);
f.close();
参考:h5cpp/example/serial/main.cpp(完全なプログラム)。
RAII によりスコープ終了時に閉じます。デストラクタは例外を投げないため、書き込みの終了エラーを検出するには明示的に close() を呼びます。
2. 配列を読む
保存された shape に合う受け取り先を用意します。
Fortran — fields.h5 の 1 次元の圧力と 2 次元の座標を読みます。
use hdf5
use h5fort, only: h5fort_sread
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer(hid_t) :: file_id
integer :: hdferr
real(real64), allocatable :: pressure(:), coords(:, :)
call h5open_f(hdferr)
if (hdferr /= 0) error stop "HDF5 の初期化に失敗"
call h5fopen_f("fields.h5", H5F_ACC_RDONLY_F, file_id, hdferr)
if (hdferr /= 0) error stop "ファイルを開けません"
call h5fort_sread(file_id, "/pressure", pressure, hdferr)
if (hdferr /= 0) error stop "圧力の読み込みに失敗"
call h5fort_sread(file_id, "/coords", coords, hdferr)
if (hdferr /= 0) error stop "座標の読み込みに失敗"
call h5fclose_f(file_id, hdferr)
if (hdferr /= 0) error stop "ファイルを閉じられません"
call h5close_f(hdferr)
if (hdferr /= 0) error stop "HDF5 の終了に失敗"
参考:h5fortran/test/test-serial.F90。
h5fort_sread は保存された shape に合わせて allocatable 配列を確保します。
C — 全体コードでは先に H5C_READ で開き、読み込み後に free(got) で解放します。
if (failed("info", h5c_dataset_info(f, "/mesh/coords", &info))) {
h5c_close(f);
return 1;
}
got = (double *)malloc(info.count * sizeof *got);
if (got == NULL) {
h5c_close(f);
return 1;
}
if (failed("read", h5c_read_f64(f, "/mesh/coords", got,
info.rank, info.dims))) {
free(got);
h5c_close(f);
return 1;
}
C++ — 保存 shape に合わせて読み込みます。
h5cpp::file f(path, h5cpp::mode::read);
const std::vector<double> got = f.read<double>("/mesh/coords");
参考:h5cpp/example/serial/main.cpp(完全なプログラム)。
read<T>() は必要な長さの std::vector を返します。失敗は h5cpp::error 例外です。全体コードの catch を参照してください。
3. 可視化用に出力する
メッシュ・座標・物理量・時刻を HDF5 に記録します。1 ステップ 1 ファイルで出力し、h5xdmf で XDMF に変換して ParaView で開きます。
Fortran — 四面体メッシュと節点データ。逐次の writer は t_hdf5_writer です(並列は t_phdf5_writer、タスク 6)。
type(t_hdf5_writer) :: fluid
fluid%h5_filepath = trim(filename)
fluid%output_type = "UnstructuredGrid"
fluid%mesh_name = "fluid"
fluid%topology_type = "Tetrahedron"
fluid%nodes_per_element = 4
fluid%num_points = fluid_points
fluid%num_cells = fluid_cells
fluid%time = time
call fluid%init()
call fluid%write_geometry(fluid_nodes, fluid_connectivity)
call fluid%write_point_data(pressure, "Pressure")
call fluid%write_point_data(velocity, "Velocity")
call fluid%close()
C — この抜粋の前に mesh の種類・名前・節点数・要素数を設定します。実行前に result/ を作成してください。
must(h5c_viz_open(path, t, &viz), "viz_open");
must(h5c_viz_begin_mesh(viz, &mesh), "begin_mesh fluid");
must(h5c_viz_write_nodes(viz, nodes, H5C_F64), "write_nodes");
must(h5c_viz_write_connectivity(viz, conn, H5C_I32), "write_conn");
must(h5c_viz_write_point_data(viz, "Pressure", pressure, H5C_F64, 1),
"point Pressure");
参考:h5c/example/serial-viz/main.c(完全なプログラム)。
must はこの例のエラー確認関数です。全体コード末尾で status の確認と close を行います。
C++ — 三角形メッシュの例です。
h5cpp::viz writer(path, time);
writer.begin_mesh({h5cpp::viz_kind::unstructured, "fluid",
"Triangle", 3, x.size(), 2});
writer.write_nodes<double>({x, y, z});
writer.write_connectivity(connectivity);
writer.write_point_data("Pressure", pressure);
writer.write_point_data<double>("Velocity", {x, y, z});
参考:h5cpp/example/serial-viz/main.cpp(完全なプログラム)。
成分別の {x, y, z} は h5cpp::span の非所有ビューとして渡します。ビュー自体は配列をコピーしません。呼び出し中は元の vector を有効に保ちます。
4. 並列で書く
各ランクの局所配列を 1 ファイルに連結します。全ランクが同じ順序・同じパスで並列 API を呼びます。
dims[0]、Fortran では配列の最終次元で、同じ軸を指します。他の次元の長さは全ランクで揃えます。パス P の下に P/data と P/__partition__ が保存されます。後者は長さ nprocs + 1 の int64 境界配列で、ランク r は境界 r から r+1 の直前までを担当します。先頭は 0、末尾は data の第 0 次元の長さです。Fortran — MPI と HDF5 の初期化後、MPI 用のファイルアクセス設定を渡します。
call h5pcreate_f(H5P_FILE_ACCESS_F, fapl_id, hdferr)
call h5pset_fapl_mpio_f(fapl_id, MPI_COMM_WORLD, MPI_INFO_NULL, hdferr)
call h5fcreate_f("parallel.h5", H5F_ACC_TRUNC_F, file_id, hdferr, access_prp=fapl_id)
call h5pclose_f(fapl_id, hdferr)
call h5fort_pwrite(file_id, "/values", local_values, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
C — 全体コードでは h5c_popen(path, H5C_TRUNCATE, &f) で開き、各戻り値を検査します。
nlocal = (size_t)(2 + me);
dims[0] = nlocal; /* the split axis */
dims[1] = 3; /* must be the same on every rank */
st = h5c_pwrite(f, "/coords", local, H5C_F64, 2, dims, H5C_WRITE_DEFAULT);
C++ — ランクごとに異なる行数を渡します。
const std::size_t nlocal = static_cast<std::size_t>(2 + me);
const h5cpp::shape dims = {nlocal, 3}; // dims[0] is the split axis
h5cpp::parallel_file f(path, h5cpp::mode::truncate);
f.write("/coords", local, dims);
参考:h5cpp/example/parallel/main.cpp(完全なプログラム)。
local は std::vector<double> です。RAII の破棄・明示的な close は MPI の終了前に、全ランクで揃えて行います。
5. 並列で読む
ここでは書き込み時と同じランク数で、保存された境界に従って各ランクの担当分を読み戻します。全ランクが同じ順序で呼び出してください。
Fortran — MPI と HDF5 の初期化後、fields-parallel.h5 の /pressure と /coords から局所データを読みます。両パスは h5fort_pwrite で保存したものとします。
use hdf5
use h5fort, only: h5fort_pread, h5fort_pread_fixed
use mpi
use, intrinsic :: iso_fortran_env, only: real64
implicit none
integer(hid_t) :: file_id, fapl_id
integer :: hdferr, ierr
real(real64), allocatable :: pressure(:), coords(:, :)
real(real64), allocatable :: pressure_buffer(:), coords_buffer(:, :)
call h5pcreate_f(H5P_FILE_ACCESS_F, fapl_id, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5pset_fapl_mpio_f(fapl_id, MPI_COMM_WORLD, MPI_INFO_NULL, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fopen_f("fields-parallel.h5", H5F_ACC_RDONLY_F, file_id, hdferr, access_prp=fapl_id)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5pclose_f(fapl_id, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fort_pread(file_id, "/pressure", pressure, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
allocate(pressure_buffer(size(pressure)), stat=ierr)
if (ierr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fort_pread_fixed(file_id, "/pressure", pressure_buffer, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fort_pread(file_id, "/coords", coords, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
allocate(coords_buffer(size(coords, 1), size(coords, 2)), stat=ierr)
if (ierr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fort_pread_fixed(file_id, "/coords", coords_buffer, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
call h5fclose_f(file_id, hdferr)
if (hdferr /= 0) call MPI_Abort(MPI_COMM_WORLD, 1, ierr)
参考:h5fortran/test/test-parallel.F90。
pread は出力の allocatable 配列を確保し、pread_fixed は確保済み配列に読み込みます。ここでは最初の読み込みで局所 shape を取得してバッファを確保しています。局所 shape が分かっていれば、最初から確保済みのバッファを渡せます。ファイルを閉じた後に HDF5 と MPI を終了します。
C — 抜粋はエラー時の分岐までを示します。
if ((st = h5c_popen(path, H5C_READ, &f)) != H5C_OK) {
MPI_Abort(MPI_COMM_WORLD, 1);
}
if ((st = h5c_pread(f, "/coords", got, H5C_F64, 2, dims)) != H5C_OK) {
if (me == 0) {
fprintf(stderr, "pread: %s (%s)\n", h5c_status_string(st),
h5c_last_error()->message);
}
MPI_Abort(MPI_COMM_WORLD, 1);
}
参考:h5c/example/parallel/main.c(完全なプログラム)。
全体コードでは dims = {nlocal, 3} に相当する shape と、その要素数分の got を事前に用意しています。境界と現在のランク数が一致する必要があります。
C++ — 保存された境界から受け取りサイズを決めます。
h5cpp::parallel_file f(path, h5cpp::mode::read);
const std::vector<double> got = f.read<double>("/coords");
参考:h5cpp/example/parallel/main.cpp(完全なプログラム)。
戻り値は局所データの std::vector です。info() や partition() も集団呼び出しなので、ランク 0 だけに限定しないでください。
6. 並列で可視化用に出力する
全ランクが同じ時刻・メッシュの順序で局所データを書きます。connectivity はランク内の 0 始まりの節点番号を渡し、保存時に全体の番号へ補正されます。出力後の XDMF への変換手順は逐次出力と共通です。
Fortran — 並列用の writer に局所の節点数・要素数を設定します。
type(t_phdf5_writer) :: fluid, soil
fluid%h5_filepath = trim(filename)
fluid%output_type = "UnstructuredGrid"
fluid%mesh_name = "fluid"
fluid%topology_type = "Tetrahedron"
fluid%nodes_per_element = 4
fluid%num_points = fluid_points
fluid%num_cells = fluid_cells
fluid%time = time
call fluid%init()
call fluid%write_geometry(fluid_nodes, fluid_connectivity)
call fluid%write_point_data(pressure, "Pressure")
call fluid%close()
C — mesh は局所の節点数・要素数で設定します。出力先ディレクトリは事前に作成してください。
must(h5c_viz_popen(path, t, MPI_COMM_WORLD, MPI_INFO_NULL, &viz),
"viz_popen");
must(h5c_viz_begin_mesh(viz, &mesh), "begin_mesh fluid");
must(h5c_viz_write_nodes(viz, nodes, H5C_F64), "write_nodes");
must(h5c_viz_write_connectivity(viz, conn, H5C_I32), "write_conn");
must(h5c_viz_write_point_data(viz, "Pressure", pressure, H5C_F64, 1),
"point Pressure");
参考:h5c/example/parallel-viz/main.c(完全なプログラム)。
must はこの例の補助関数で、失敗時には全ランクを停止します。
C++ — 局所メッシュと物理量を渡します。
h5cpp::parallel_viz writer(path, time);
writer.begin_mesh({h5cpp::viz_kind::unstructured, "fluid", "Tetrahedron",
4, npoints, kCells});
writer.write_nodes(nodes);
writer.write_connectivity(connectivity);
writer.write_point_data("Pressure", pressure);
writer.write_point_data("Velocity", velocity, 3);
writer.write_cell_data("SubdomainID", subdomain);
参考:h5cpp/example/parallel-viz/main.cpp(完全なプログラム)。
並列 writer でも vector の長さを検査します。全体コードには例外の捕捉とランク間の終了状態の集約が含まれます。