diff --git a/SU2_CFD/include/solvers/CSolver.hpp b/SU2_CFD/include/solvers/CSolver.hpp index 67a8bb8112c..deff11d372a 100644 --- a/SU2_CFD/include/solvers/CSolver.hpp +++ b/SU2_CFD/include/solvers/CSolver.hpp @@ -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 FindFieldIndices(const vector& 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. diff --git a/SU2_CFD/src/solvers/CSolver.cpp b/SU2_CFD/src/solvers/CSolver.cpp index f544b694ba8..860e83369e9 100644 --- a/SU2_CFD/src/solvers/CSolver.cpp +++ b/SU2_CFD/src/solvers/CSolver.cpp @@ -188,6 +188,32 @@ CSolver::~CSolver() { delete VerificationSolution; } +vector CSolver::FindFieldIndices(const vector& 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 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, diff --git a/SU2_CFD/src/solvers/CTransLMSolver.cpp b/SU2_CFD/src/solvers/CTransLMSolver.cpp index 38356c86a59..a23e14e5552 100644 --- a/SU2_CFD/src/solvers/CTransLMSolver.cpp +++ b/SU2_CFD/src/solvers/CTransLMSolver.cpp @@ -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 target_fields = {"LM_gamma", "LM_Re_t", "LM_gamma_sep", "LM_gamma_eff"}; + vector 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. ---*/ @@ -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++; diff --git a/SU2_CFD/src/solvers/CTurbSolver.cpp b/SU2_CFD/src/solvers/CTurbSolver.cpp index f6aed2de852..d017b6a5f01 100644 --- a/SU2_CFD/src/solvers/CTurbSolver.cpp +++ b/SU2_CFD/src/solvers/CTurbSolver.cpp @@ -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 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 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. ---*/ @@ -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++; @@ -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); diff --git a/TestCases/rans/naca0012/euler_NACA0012_precursor.cfg b/TestCases/rans/naca0012/euler_NACA0012_precursor.cfg new file mode 100644 index 00000000000..c9cb76e29bd --- /dev/null +++ b/TestCases/rans/naca0012/euler_NACA0012_precursor.cfg @@ -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) diff --git a/TestCases/rans/naca0012/turb_SA_restart_test.cfg b/TestCases/rans/naca0012/turb_SA_restart_test.cfg new file mode 100644 index 00000000000..4f4b42bcf1f --- /dev/null +++ b/TestCases/rans/naca0012/turb_SA_restart_test.cfg @@ -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)