Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions SU2_CFD/include/solvers/CSolver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,13 @@ class CSolver {
return base_nodes;
}

/*!
* \brief Find the indices of fields by name.
* \param[in] target_fields - Vector of field names to find (with or without quotes).
* \return Vector of indices (0-based), or -1 for fields not found.
*/
vector<int> FindFieldIndices(const vector<string>& target_fields) const;

/*!
* \brief Helper function to define the type and number of variables per point for each communication type.
* \param[in] config - Definition of the particular problem.
Expand Down
26 changes: 26 additions & 0 deletions SU2_CFD/src/solvers/CSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,32 @@ CSolver::~CSolver() {
delete VerificationSolution;
}

vector<int> CSolver::FindFieldIndices(const vector<string>& target_fields) const {
/*--- Search for field names in the fields vector.
Fields are stored with quotes, e.g., "Temperature", so we need to check
both with and without quotes. ---*/

vector<int> indices;
indices.reserve(target_fields.size());

for (const auto& search : target_fields) {
/*--- Prepare both quoted and unquoted versions ---*/
string fieldNameWithQuotes = "\"" + search + "\"";

/*--- Search for the field (try both with and without quotes) ---*/
auto it = std::find(fields.begin(), fields.end(), fieldNameWithQuotes);
if (it == fields.end()) {
it = std::find(fields.begin(), fields.end(), search);
}

/*--- Store the index or -1 if not found ---*/
indices.push_back(it != fields.end() ? std::distance(fields.begin(), it) : -1);
}

return indices;
}


void CSolver::GetPeriodicCommCountAndType(const CConfig* config,
unsigned short commType,
unsigned short &COUNT_PER_POINT,
Expand Down
78 changes: 61 additions & 17 deletions SU2_CFD/src/solvers/CTransLMSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,21 +515,48 @@ void CTransLMSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfi
Read_SU2_Restart_ASCII(geometry[MESH_0], config, restart_filename);
}

/*--- Skip flow variables ---*/

unsigned short skipVars = nDim + solver[MESH_0][FLOW_SOL]->GetnVar() + solver[MESH_0][TURB_SOL] ->GetnVar();

/*--- Adjust the number of solution variables in the incompressible
restart. We always carry a space in nVar for the energy equation in the
mean flow solver, but we only write it to the restart if it is active.
Therefore, we must reduce skipVars here if energy is inactive so that
the turbulent variables are read correctly. ---*/
/*--- Find indices for LM transition variables ---*/
vector<string> target_fields = {"LM_gamma", "LM_Re_t", "LM_gamma_sep", "LM_gamma_eff"};
vector<int> field_indices = FindFieldIndices(target_fields);

/*--- Fallback: Use legacy offset if name not found ---*/
/*--- nDim + 2 covers Density + Momentum + Energy (Compressible)
and Pressure + Velocity + Temperature (Incompressible) ---*/
const unsigned short flow_offset = geometry[MESH_0]->GetnDim() + 2;

int turb_offset = flow_offset;
switch(config->GetKind_Turb_Model()) {
case TURB_MODEL::SA:
turb_offset += 1;
break;
case TURB_MODEL::SST:
turb_offset += 2;
break;
default: break;
}

const bool incompressible = (config->GetKind_Regime() == ENUM_REGIME::INCOMPRESSIBLE);
const bool energy = config->GetEnergy_Equation();
const bool weakly_coupled_heat = config->GetWeakly_Coupled_Heat();
for (size_t i = 0; i < target_fields.size(); ++i) {
if (field_indices[i] == -1) {
const int fallback_idx = turb_offset + i;
if (Restart_Vars.size() > 1 && fallback_idx < Restart_Vars[1]) {
field_indices[i] = fallback_idx;
if (rank == MASTER_NODE) {
cout << "WARNING: " << target_fields[i] << " field not found in restart file. "
<< "Using fallback index " << fallback_idx << ".\n";
}
}
}
}

if (incompressible && ((!energy) && (!weakly_coupled_heat))) skipVars--;
/*--- Warn if any fields are missing (Master node only) ---*/
if (rank == MASTER_NODE) {
for (size_t i = 0; i < target_fields.size(); ++i) {
if (field_indices[i] == -1) {
cout << "WARNING: " << target_fields[i] << " field not found in restart file. "
<< "Using initialized default.\n";
}
}
}

/*--- Load data from the restart into correct containers. ---*/

Expand All @@ -544,10 +571,27 @@ void CTransLMSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfi
/*--- We need to store this point's data, so jump to the correct
offset in the buffer of data from the restart file and load it. ---*/

const auto index = counter * Restart_Vars[1] + skipVars;
for (auto iVar = 0u; iVar < nVar; iVar++) nodes->SetSolution(iPoint_Local, iVar, Restart_Data[index + iVar]);
nodes->SetIntermittencySep(iPoint_Local, Restart_Data[index + 2]);
nodes->SetIntermittencyEff(iPoint_Local, Restart_Data[index + 3]);
const auto base_idx = counter * Restart_Vars[1];

/*--- Load Gamma (LM_gamma) ---*/
if (field_indices[0] != -1) {
nodes->SetSolution(iPoint_Local, 0, Restart_Data[base_idx + field_indices[0]]);
}

/*--- Load Re_Theta_t (LM_Re_t) ---*/
if (field_indices[1] != -1) {
nodes->SetSolution(iPoint_Local, 1, Restart_Data[base_idx + field_indices[1]]);
}

/*--- Load Intermittency Sep (LM_gamma_sep) ---*/
if (field_indices[2] != -1) {
nodes->SetIntermittencySep(iPoint_Local, Restart_Data[base_idx + field_indices[2]]);
}

/*--- Load Intermittency Eff (LM_gamma_eff) ---*/
if (field_indices[3] != -1) {
nodes->SetIntermittencyEff(iPoint_Local, Restart_Data[base_idx + field_indices[3]]);
}

/*--- Increment the overall counter for how many points have been loaded. ---*/
counter++;
Expand Down
63 changes: 48 additions & 15 deletions SU2_CFD/src/solvers/CTurbSolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,49 @@ void CTurbSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfig*
Read_SU2_Restart_ASCII(geometry[MESH_0], config, restart_filename);
}

/*--- Skip flow variables ---*/
/*--- Identify turbulence variable names based on the model ---*/
vector<string> varNames(nVar);

unsigned short skipVars = nDim + solver[MESH_0][FLOW_SOL]->GetnVar();

/*--- Adjust the number of solution variables in the incompressible
restart. We always carry a space in nVar for the energy equation in the
mean flow solver, but we only write it to the restart if it is active.
Therefore, we must reduce skipVars here if energy is inactive so that
the turbulent variables are read correctly. ---*/

const bool incompressible = (config->GetKind_Regime() == ENUM_REGIME::INCOMPRESSIBLE);
const bool energy = config->GetEnergy_Equation();
const bool weakly_coupled_heat = config->GetWeakly_Coupled_Heat();
const auto kind_turb_model = config->GetKind_Turb_Model();
if (kind_turb_model == TURB_MODEL::SA) {
if (nVar > 0) varNames[0] = "Nu_Tilde";
}
else if (kind_turb_model == TURB_MODEL::SST) {
if (nVar > 0) varNames[0] = "Turb_Kin_Energy";
if (nVar > 1) varNames[1] = "Omega";
}
/*--- Add other turbulence models as needed ---*/

/*--- Find indices for all turbulence variables at once ---*/
vector<int> field_indices = FindFieldIndices(varNames);

/*--- Fallback: Use legacy offset if name not found ---*/
/*--- nDim + 2 covers Density + Momentum + Energy (Compressible)
and Pressure + Velocity + Temperature (Incompressible) ---*/
const unsigned short flow_offset = geometry[MESH_0]->GetnDim() + 2;

for (unsigned short iVar = 0; iVar < nVar; ++iVar) {
if (field_indices[iVar] == -1 && !varNames[iVar].empty()) {
const int fallback_idx = flow_offset + iVar;
if (Restart_Vars.size() > 1 && fallback_idx < Restart_Vars[1]) {
field_indices[iVar] = fallback_idx;
if (rank == MASTER_NODE) {
cout << "WARNING: " << varNames[iVar] << " field not found in restart file. "
<< "Using fallback index " << fallback_idx << ".\n";
}
}
}
}

if (incompressible && ((!energy) && (!weakly_coupled_heat))) skipVars--;
/*--- Warn if any fields are missing (Master node only) ---*/
if (rank == MASTER_NODE) {
for (unsigned short iVar = 0; iVar < nVar; ++iVar) {
if (field_indices[iVar] == -1 && !varNames[iVar].empty()) {
cout << "WARNING: " << varNames[iVar] << " field not found in restart file. "
<< "Using initialized default.\n";
}
}
}

/*--- Load data from the restart into correct containers. ---*/

Expand All @@ -147,8 +175,12 @@ void CTurbSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfig*
/*--- We need to store this point's data, so jump to the correct
offset in the buffer of data from the restart file and load it. ---*/

const auto index = counter * Restart_Vars[1] + skipVars;
for (auto iVar = 0u; iVar < nVar; iVar++) nodes->SetSolution(iPoint_Local, iVar, Restart_Data[index + iVar]);
const auto baseIndex = counter * Restart_Vars[1];
for (auto iVar = 0u; iVar < nVar; iVar++) {
if (field_indices[iVar] != -1) {
nodes->SetSolution(iPoint_Local, iVar, Restart_Data[baseIndex + field_indices[iVar]]);
}
}

/*--- Increment the overall counter for how many points have been loaded. ---*/
counter++;
Expand All @@ -166,6 +198,7 @@ void CTurbSolver::LoadRestart(CGeometry** geometry, CSolver*** solver, CConfig*
} // end safe global access, pre and postprocessing are thread-safe.
END_SU2_OMP_SAFE_GLOBAL_ACCESS


/*--- MPI solution and compute the eddy viscosity ---*/

solver[MESH_0][TURB_SOL]->InitiateComms(geometry[MESH_0], config, MPI_QUANTITIES::SOLUTION);
Expand Down
48 changes: 48 additions & 0 deletions TestCases/rans/naca0012/euler_NACA0012_precursor.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% SU2 configuration file %
% Case description: Euler precursor for restart test (NACA0012) %
% Author: SU2 Development Team %
% File Version 8.4.0 "Harrier" %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SOLVER= EULER
MATH_PROBLEM= DIRECT
RESTART_SOL= NO

MACH_NUMBER= 0.8
AOA= 1.25
FREESTREAM_PRESSURE= 101325.0
FREESTREAM_TEMPERATURE= 288.15

REF_ORIGIN_MOMENT_X = 0.25
REF_ORIGIN_MOMENT_Y = 0.00
REF_ORIGIN_MOMENT_Z = 0.00
REF_LENGTH= 1.0
REF_AREA= 1.0

MARKER_EULER= ( airfoil )
MARKER_FAR= ( farfield )

NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES
CFL_NUMBER= 10.0
ITER= 10

LINEAR_SOLVER= FGMRES
LINEAR_SOLVER_PREC= ILU
LINEAR_SOLVER_ERROR= 1E-10
LINEAR_SOLVER_ITER= 10

CONV_NUM_METHOD_FLOW= ROE
TIME_DISCRE_FLOW= EULER_IMPLICIT

MESH_FILENAME= ../../../QuickStart/mesh_NACA0012_inv.su2
MESH_FORMAT= SU2

TABULAR_FORMAT= CSV
CONV_FILENAME= history
RESTART_FILENAME= restart_flow_euler
OUTPUT_WRT_FREQ= 10

SCREEN_OUTPUT=(INNER_ITER, WALL_TIME, RMS_DENSITY, RMS_ENERGY, LIFT, DRAG)
56 changes: 56 additions & 0 deletions TestCases/rans/naca0012/turb_SA_restart_test.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% SU2 configuration file %
% Case description: SA restart from Euler (tests missing field initialization)%
% Author: SU2 Development Team %
% File Version 8.4.0 "Harrier" %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

SOLVER= RANS
KIND_TURB_MODEL= SA
MATH_PROBLEM= DIRECT
RESTART_SOL= YES

MACH_NUMBER= 0.8
AOA= 1.25
FREESTREAM_PRESSURE= 101325.0
FREESTREAM_TEMPERATURE= 288.15
REYNOLDS_NUMBER= 6.5e6
REYNOLDS_LENGTH= 1.0

REF_ORIGIN_MOMENT_X = 0.25
REF_ORIGIN_MOMENT_Y = 0.00
REF_ORIGIN_MOMENT_Z = 0.00
REF_LENGTH= 1.0
REF_AREA= 1.0

MARKER_HEATFLUX= ( airfoil, 0.0 )
MARKER_FAR= ( farfield )

NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES
CFL_NUMBER= 10.0
ITER= 10

LINEAR_SOLVER= FGMRES
LINEAR_SOLVER_PREC= ILU
LINEAR_SOLVER_ERROR= 1E-10
LINEAR_SOLVER_ITER= 10

CONV_NUM_METHOD_FLOW= ROE
TIME_DISCRE_FLOW= EULER_IMPLICIT
CONV_NUM_METHOD_TURB= SCALAR_UPWIND
MUSCL_TURB= NO
SLOPE_LIMITER_TURB= VENKATAKRISHNAN
TIME_DISCRE_TURB= EULER_IMPLICIT

MESH_FILENAME= ../../../QuickStart/mesh_NACA0012_inv.su2
MESH_FORMAT= SU2

TABULAR_FORMAT= CSV
CONV_FILENAME= history
RESTART_FILENAME= restart_flow_sa
SOLUTION_FILENAME= restart_flow_euler.dat
OUTPUT_WRT_FREQ= 10

SCREEN_OUTPUT=(INNER_ITER, WALL_TIME, RMS_DENSITY, RMS_ENERGY, RMS_NU_TILDE, LIFT, DRAG)