Skip to content
2 changes: 2 additions & 0 deletions Common/include/geometry/CGeometry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ class CGeometry {
std::vector<unsigned long> colorOffsets;

std::vector<uint8_t> lineletColor; /*!< \brief Coloring transfered to points, for visualization. */

bool computed = false; /*!< \brief Set once the (collective) computation is done, even if a rank has no linelets. */
};

protected:
Expand Down
10 changes: 7 additions & 3 deletions Common/src/geometry/CGeometry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4339,7 +4339,10 @@ void CGeometry::ColorMGLevels(unsigned short nMGLevels, const CGeometry* const*

const CGeometry::CLineletInfo& CGeometry::GetLineletInfo(const CConfig* config) const {
auto& li = lineletInfo;
if (!li.linelets.empty() || nPoint == 0) return li;
/*--- Do not use "linelets.empty()" as the check, some ranks may have no linelets, and this
* function contains collective communication that all ranks must call the same number of times. ---*/
if (li.computed) return li;
Comment thread
bigfooted marked this conversation as resolved.
li.computed = true;

li.lineletIdx.resize(nPoint, CLineletInfo::NO_LINELET);

Expand Down Expand Up @@ -4444,7 +4447,7 @@ const CGeometry::CLineletInfo& CGeometry::GetLineletInfo(const CConfig* config)
SU2_MPI::Allreduce(&sumNPoints, &globalNPoints, 1, MPI_UNSIGNED_LONG, MPI_SUM, SU2_MPI::GetComm());
SU2_MPI::Allreduce(&nLinelet, &globalNLineLets, 1, MPI_UNSIGNED_LONG, MPI_SUM, SU2_MPI::GetComm());

if (rank == MASTER_NODE) {
if (rank == MASTER_NODE && globalNLineLets > 0) {
std::cout << "Computed linelet structure, "
<< static_cast<unsigned long>(passivedouble(globalNPoints) / globalNLineLets)
<< " points in each line (average)." << std::endl;
Expand All @@ -4470,7 +4473,8 @@ const CGeometry::CLineletInfo& CGeometry::GetLineletInfo(const CConfig* config)

const auto coloring = colorSparsePattern<uint8_t, std::numeric_limits<uint8_t>::max()>(
CCompressedSparsePatternUL(adjacency), 1, false, true);
const auto nColors = coloring.getOuterSize();
/*--- unsigned long because it is communicated as MPI_UNSIGNED_LONG below. ---*/
const unsigned long nColors = coloring.getOuterSize();

/*--- Sort linelets by color. ---*/
std::vector<std::vector<unsigned long>> sortedLinelets;
Expand Down
12 changes: 6 additions & 6 deletions Common/src/linear_algebra/CSysMatrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1460,13 +1460,13 @@ void CSysMatrix<ScalarType>::BuildLineletPreconditioner(const CGeometry* geometr

const auto nThreads = omp_get_max_threads();

/*--- The linelet info must be computed on all ranks (it is collective), and the working vectors
* must be allocated even if this rank has no linelets (e.g. partitions away from walls). ---*/
BEGIN_SU2_OMP_SAFE_GLOBAL_ACCESS {
const auto& li = geometry->GetLineletInfo(config);
if (!li.linelets.empty()) {
LineletUpper.resize(nThreads);
LineletVector.resize(nThreads);
LineletInvDiag.resize(nThreads);
}
geometry->GetLineletInfo(config);
LineletUpper.resize(nThreads);
LineletVector.resize(nThreads);
LineletInvDiag.resize(nThreads);
}
END_SU2_OMP_SAFE_GLOBAL_ACCESS

Expand Down
97 changes: 97 additions & 0 deletions TestCases/navierstokes/naca0012/lam_NACA0012_LINELET.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% SU2 configuration file %
% Case description: NACA0012 Laminar (Re 5000), LINELET preconditioner %
% Author: Francisco Palacios (base case), Syphonicc (LINELET variant) %
% Institution: Stanford University (base case) %
% Date: Oct 2, 2026 %
% File Version 8.5.0 "Harrier" %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% ------------- DIRECT, ADJOINT, AND LINEARIZED PROBLEM DEFINITION ------------%
%
SOLVER= NAVIER_STOKES
KIND_TURB_MODEL= NONE
MATH_PROBLEM= DIRECT
RESTART_SOL= NO

% -------------------- COMPRESSIBLE FREE-STREAM DEFINITION --------------------%
%
MACH_NUMBER= 0.5
AOA= 1.0
FREESTREAM_TEMPERATURE= 288.15
REYNOLDS_NUMBER= 5000.0
REYNOLDS_LENGTH= 1.0

% ---------------------- REFERENCE VALUE DEFINITION ---------------------------%
%
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

% -------------------- BOUNDARY CONDITION DEFINITION --------------------------%
%
MARKER_HEATFLUX= ( airfoil, 0.0 )
MARKER_FAR= ( farfield )
MARKER_PLOTTING= ( airfoil )
MARKER_MONITORING= ( airfoil )

% ------------- COMMON PARAMETERS DEFINING THE NUMERICAL METHOD ---------------%
%
NUM_METHOD_GRAD= WEIGHTED_LEAST_SQUARES
CFL_NUMBER= 30.0
CFL_ADAPT= NO
CFL_ADAPT_PARAM= ( 0.5, 1.5, 1.0, 100.0 )
RK_ALPHA_COEFF= ( 0.66667, 0.66667, 1.000000 )
ITER= 10000
LINEAR_SOLVER= FGMRES
LINEAR_SOLVER_PREC= LINELET
LINEAR_SOLVER_ERROR= 1E-1
LINEAR_SOLVER_ITER= 3

% -------------------------- MULTIGRID PARAMETERS -----------------------------%
%
MGLEVEL= 3
MGCYCLE= V_CYCLE
MG_PRE_SMOOTH= ( 4, 4, 4, 4 )
MG_POST_SMOOTH= ( 4, 4, 4, 4 )
MG_CORRECTION_SMOOTH= ( 1, 1, 1, 1 )
MG_DAMP_RESTRICTION= 0.5
MG_DAMP_PROLONGATION= 0.5

% -------------------- FLOW NUMERICAL METHOD DEFINITION -----------------------%
%
CONV_NUM_METHOD_FLOW= ROE
MUSCL_FLOW= YES
SLOPE_LIMITER_FLOW= VENKATAKRISHNAN
VENKAT_LIMITER_COEFF= 0.0001
JST_SENSOR_COEFF= ( 0.5, 0.02 )
TIME_DISCRE_FLOW= EULER_IMPLICIT

% --------------------------- CONVERGENCE PARAMETERS --------------------------%
%
CONV_RESIDUAL_MINVAL= -12
CONV_STARTITER= 10
CONV_CAUCHY_ELEMS= 100
CONV_CAUCHY_EPS= 1E-10

% ------------------------- INPUT/OUTPUT INFORMATION --------------------------%
%
MESH_FILENAME= mesh_NACA0012_lam_hybrid_v1.su2
MESH_FORMAT= SU2
MESH_OUT_FILENAME= mesh_out
SOLUTION_FILENAME= solution_flow
SOLUTION_ADJ_FILENAME= solution_adj
TABULAR_FORMAT= CSV
CONV_FILENAME= history
RESTART_FILENAME= restart_flow
RESTART_ADJ_FILENAME= restart_adj
VOLUME_FILENAME= flow
VOLUME_ADJ_FILENAME= adjoint
GRAD_OBJFUNC_FILENAME= of_grad
SURFACE_FILENAME= surface_flow
SURFACE_ADJ_FILENAME= surface_adjoint
OUTPUT_WRT_FREQ= 250
9 changes: 9 additions & 0 deletions TestCases/parallel_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,15 @@ def main():

test_list.append(cylinder_lowmach)

# Laminar NACA0012 with linelet preconditioner (see #2877), 4 ranks so that some partitions have no linelets
lam_naca0012_linelet = TestCase('lam_naca0012_linelet')
lam_naca0012_linelet.cfg_dir = "navierstokes/naca0012"
lam_naca0012_linelet.cfg_file = "lam_NACA0012_LINELET.cfg"
lam_naca0012_linelet.test_iter = 20
lam_naca0012_linelet.test_vals = [-5.335771, -2.936187, -2.943926, 0.102449]
lam_naca0012_linelet.command = TestCase.Command("mpirun --oversubscribe -n 4", "SU2_CFD")
test_list.append(lam_naca0012_linelet)

# 2D Poiseuille flow (body force driven with periodic inlet / outlet)
poiseuille = TestCase('poiseuille')
poiseuille.cfg_dir = "navierstokes/poiseuille"
Expand Down
8 changes: 8 additions & 0 deletions TestCases/serial_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ def main():
cylinder_lowmach.test_vals = [-8.698687, -3.236566, 0.401347, 1.754864, 0.000000]
test_list.append(cylinder_lowmach)

# Laminar NACA0012 with linelet preconditioner (see #2877)
lam_naca0012_linelet = TestCase('lam_naca0012_linelet')
lam_naca0012_linelet.cfg_dir = "navierstokes/naca0012"
lam_naca0012_linelet.cfg_file = "lam_NACA0012_LINELET.cfg"
lam_naca0012_linelet.test_iter = 20
lam_naca0012_linelet.test_vals = [-5.345015, -2.936895, -2.943468, 0.096348]
test_list.append(lam_naca0012_linelet)

# 2D Poiseuille flow (body force driven with periodic inlet / outlet)
poiseuille = TestCase('poiseuille')
poiseuille.cfg_dir = "navierstokes/poiseuille"
Expand Down
Loading