はじめに
世の中AI流行りですが、当サイトも遅ればせながらAI関連の初記事です。
内容は業務上必要になったOpenFOAMのコードを作成するに際しての裏話ですが、これを第85回オープンCAE勉強会岐阜(2026/2/28)での発表ネタとして作成したものです。
function / wallHeatFlux on energyTransport
発表資料(近日中公開予定)
AIとのやりとり記録1
AIとのやりとり記録2
作成されたコード
wallHeatFlux_energy
{
type coded;
libs (utilityFunctionObjects);
name energyWallHeatFlux;
executeControl timeStep;
executeInterval 1;
writeControl timeStep;
writeInterval 1;
codeInclude
#{
#include "volFields.H"
#include "fvPatchFields.H"
#};
codeWrite
#{
// 1. 辞書とパラメータの取得 (前回と同様)
IOdictionary energyDict(IOobject("energy", mesh().time().system(), mesh(), IOobject::MUST_READ, IOobject::NO_WRITE));
//const dictionary& dict = energyDict.subDictOrAdd("sTransport");
// 2. 辞書構造への対応(サブディクショナリを考慮)
const dictionary* subDictPtr = &energyDict;
if (!energyDict.found("kappa"))
{
forAllConstIters(energyDict, iter)
{
if (iter().isDict())
{
subDictPtr = &iter().dict();
break;
}
}
}
const dictionary& dict = *subDictPtr;
const scalar kappa = dict.get("kappa").value();
const scalar rhoInf = dict.get("rhoInf").value();
const scalar Cp = dict.get("Cp").value();
const scalar Prt = dict.getOrDefault("Prt", 0.85);
const word TName = dict.getOrDefault("field", "T");
const scalar rhoCp = rhoInf * Cp;
const auto& T = mesh().lookupObject(TName);
const auto& nut = mesh().lookupObject("nut");
// 2. 出力用フィールドの作成 (存在しない場合のみ新規作成)
if (!mesh().foundObject("wallHeatFlux_energy"))
{
volScalarField* qField = new volScalarField
(
IOobject("wallHeatFlux_energy", mesh().time().timeName(), mesh(), IOobject::NO_READ, IOobject::AUTO_WRITE),
mesh(),
dimensionedScalar("zero", dimensionSet(1, 0, -3, 0, 0, 0, 0), 0.0) // [W/m2]
);
qField->store();
}
auto& qField = mesh().lookupObjectRef("wallHeatFlux_energy");
// 2. パッチループ
forAll(T.boundaryField(), patchi)
{
const fvPatch& patch = mesh().boundary()[patchi];
// 【重要】全てのプロセスが全パッチに対してこのループを通る必要があります
if (patch.type() == "wall")
{
const fvPatchScalarField& pT = T.boundaryField()[patchi];
const fvPatchScalarField& pNut = nut.boundaryField()[patchi];
const scalarField& magSf = mesh().magSf().boundaryField()[patchi];
scalarField q = -(kappa + (rhoCp * pNut / Prt)) * pT.snGrad();
qField.boundaryFieldRef()[patchi] = q;
// 【最重要】gSumは全プロセスで必ず実行される場所に配置
scalar totalQ = gSum(q * magSf);
scalar totalArea = gSum(magSf);
// 3. 出力判定 (マスタープロセスのみ実行)
if (Pstream::master() && mag(totalQ) > SMALL)
{
fileName outputDir = mesh().time().globalPath()/"postProcessing"/"wallHeatFlux";
if (!isDir(outputDir)) mkDir(outputDir);
fileName filePath = outputDir/("heatFlux_" + patch.name() + ".dat");
OFstream os(filePath, IOstream::ASCII, IOstream::APPEND);
if (mesh().time().timeIndex() <= 1)
{
os << "# Time\tTotal_W\tAvg_W_m2" << endl;
}
os << mesh().time().value() << "\t" << totalQ << "\t" << (totalQ / (totalArea + SMALL)) << endl;
}
}
}
// 5. 書き出し制御
// データの保存タイミング(writeTime)の時だけ、フィールドファイルをディスクに書き出す
if (mesh().time().writeTime())
{
qField.write();
if (Pstream::master())
{
Info<< "Time = " << mesh().time().value() << ": wallHeatFlux fields written." << endl;
}
}
#};
}
あとがき
昨年までは、あと1年もすると後期高齢者となる自分がAI関連記事を発信することになるとはまずもって無いだろうと思っていたが、必要にせまられてAIさんに助けられることとなった。助けられたからには恩返しの情報発信である。
