Skip to content

Commit 95a8487

Browse files
authored
Merge pull request #1909 from su2code/feature_probes
Point probes
2 parents 12e0ed9 + ab15418 commit 95a8487

File tree

5 files changed

+78
-29
lines changed

5 files changed

+78
-29
lines changed

SU2_CFD/include/output/COutput.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ class COutput {
187187
CustomHistoryOutput customObjFunc; /*!< \brief User-defined expression for a custom objective. */
188188

189189
/*! \brief Type of operation for custom outputs. */
190-
enum class OperationType { MACRO, FUNCTION, AREA_AVG, AREA_INT, MASSFLOW_AVG, MASSFLOW_INT };
190+
enum class OperationType { MACRO, FUNCTION, AREA_AVG, AREA_INT, MASSFLOW_AVG, MASSFLOW_INT, PROBE };
191191

192192
/*! \brief Struct to hold a parsed custom output function. */
193193
struct CustomOutput {
@@ -201,6 +201,9 @@ class COutput {
201201
mel::ExpressionTree<passivedouble> expression;
202202
std::vector<std::string> varSymbols;
203203
std::vector<unsigned short> markerIndices;
204+
static constexpr long PROBE_NOT_SETUP = -2;
205+
static constexpr long PROBE_NOT_OWNED = -1;
206+
long iPoint = PROBE_NOT_SETUP;
204207

205208
/*--- The symbols (strings) are associated with an integer index for efficiency. For evaluation this index
206209
is passed to a functor that returns the value associated with the symbol. This functor is an input to "eval()"

SU2_CFD/src/output/CFlowOutput.cpp

Lines changed: 67 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* License along with SU2. If not, see <http://www.gnu.org/licenses/>.
2626
*/
2727

28+
#include <sstream>
2829
#include <string>
2930

3031
#include "../../include/output/CFlowOutput.hpp"
@@ -765,16 +766,42 @@ void CFlowOutput::SetCustomOutputs(const CSolver* const* solver, const CGeometry
765766
} else {
766767
SU2_MPI::Error("Unknown flow solver type.", CURRENT_FUNCTION);
767768
}
768-
/*--- Convert marker names to their index (if any) in this rank. ---*/
769-
770-
output.markerIndices.clear();
771-
for (const auto& marker : output.markers) {
772-
for (auto iMarker = 0u; iMarker < config->GetnMarker_All(); ++iMarker) {
773-
if (config->GetMarker_All_TagBound(iMarker) == marker) {
774-
output.markerIndices.push_back(iMarker);
775-
continue;
769+
/*--- Convert marker names to their index (if any) in this rank. Or probe locations to nearest points. ---*/
770+
771+
if (output.type != OperationType::PROBE) {
772+
output.markerIndices.clear();
773+
for (const auto& marker : output.markers) {
774+
for (auto iMarker = 0u; iMarker < config->GetnMarker_All(); ++iMarker) {
775+
if (config->GetMarker_All_TagBound(iMarker) == marker) {
776+
output.markerIndices.push_back(iMarker);
777+
continue;
778+
}
779+
}
780+
}
781+
} else {
782+
if (output.markers.size() != nDim) {
783+
SU2_MPI::Error("Wrong number of coordinates to specify probe " + output.name, CURRENT_FUNCTION);
784+
}
785+
su2double coord[3] = {};
786+
for (auto iDim = 0u; iDim < nDim; ++iDim) coord[iDim] = std::stod(output.markers[iDim]);
787+
su2double minDist = std::numeric_limits<su2double>::max();
788+
unsigned long minPoint = 0;
789+
for (auto iPoint = 0ul; iPoint < geometry->GetnPointDomain(); ++iPoint) {
790+
const su2double dist = GeometryToolbox::SquaredDistance(nDim, coord, geometry->nodes->GetCoord(iPoint));
791+
if (dist < minDist) {
792+
minDist = dist;
793+
minPoint = iPoint;
776794
}
777795
}
796+
/*--- Decide which rank owns the probe. ---*/
797+
su2double globMinDist;
798+
SU2_MPI::Allreduce(&minDist, &globMinDist, 1, MPI_DOUBLE, MPI_MIN, SU2_MPI::GetComm());
799+
output.iPoint = fabs(minDist - globMinDist) < EPS ? minPoint : CustomOutput::PROBE_NOT_OWNED;
800+
if (output.iPoint != CustomOutput::PROBE_NOT_OWNED) {
801+
std::cout << "Probe " << output.name << " is using global point "
802+
<< geometry->nodes->GetGlobalIndex(output.iPoint)
803+
<< ", distance from target location is " << sqrt(minDist) << std::endl;
804+
}
778805
}
779806
}
780807

@@ -787,6 +814,37 @@ void CFlowOutput::SetCustomOutputs(const CSolver* const* solver, const CGeometry
787814
continue;
788815
}
789816

817+
/*--- Prepares the functor that maps symbol indices to values at a given point
818+
* (see ConvertVariableSymbolsToIndices). ---*/
819+
820+
auto MakeFunctor = [&](unsigned long iPoint) {
821+
/*--- This returns another lambda that captures iPoint by value. ---*/
822+
return [&, iPoint](unsigned long i) {
823+
if (i < CustomOutput::NOT_A_VARIABLE) {
824+
const auto solIdx = i / CustomOutput::MAX_VARS_PER_SOLVER;
825+
const auto varIdx = i % CustomOutput::MAX_VARS_PER_SOLVER;
826+
if (solIdx == FLOW_SOL) {
827+
return flowNodes->GetPrimitive(iPoint, varIdx);
828+
} else {
829+
return solver[solIdx]->GetNodes()->GetSolution(iPoint, varIdx);
830+
}
831+
} else {
832+
return *output.otherOutputs[i - CustomOutput::NOT_A_VARIABLE];
833+
}
834+
};
835+
};
836+
837+
if (output.type == OperationType::PROBE) {
838+
su2double value = std::numeric_limits<su2double>::max();
839+
if (output.iPoint != CustomOutput::PROBE_NOT_OWNED) {
840+
value = output.eval(MakeFunctor(output.iPoint));
841+
}
842+
su2double tmp = value;
843+
SU2_MPI::Allreduce(&tmp, &value, 1, MPI_DOUBLE, MPI_MIN, SU2_MPI::GetComm());
844+
SetHistoryOutputValue(output.name, value);
845+
continue;
846+
}
847+
790848
/*--- Surface integral of the expression. ---*/
791849

792850
std::array<su2double, 2> integral = {0.0, 0.0};
@@ -812,23 +870,7 @@ void CFlowOutput::SetCustomOutputs(const CSolver* const* solver, const CGeometry
812870
}
813871
weight *= GetAxiFactor(axisymmetric, *geometry->nodes, iPoint, iMarker);
814872
local_integral[1] += weight;
815-
816-
/*--- Prepare the functor that maps symbol indices to values (see ConvertVariableSymbolsToIndices). ---*/
817-
818-
auto Functor = [&](unsigned long i) {
819-
if (i < CustomOutput::NOT_A_VARIABLE) {
820-
const auto solIdx = i / CustomOutput::MAX_VARS_PER_SOLVER;
821-
const auto varIdx = i % CustomOutput::MAX_VARS_PER_SOLVER;
822-
if (solIdx == FLOW_SOL) {
823-
return flowNodes->GetPrimitive(iPoint, varIdx);
824-
} else {
825-
return solver[solIdx]->GetNodes()->GetSolution(iPoint, varIdx);
826-
}
827-
} else {
828-
return *output.otherOutputs[i - CustomOutput::NOT_A_VARIABLE];
829-
}
830-
};
831-
local_integral[0] += weight * output.eval(Functor);
873+
local_integral[0] += weight * output.eval(MakeFunctor(iPoint));
832874
}
833875
END_SU2_OMP_FOR
834876
}

SU2_CFD/src/output/COutput.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2186,6 +2186,7 @@ void COutput::SetCustomOutputs(const CConfig* config) {
21862186
{"AreaInt", OperationType::AREA_INT},
21872187
{"MassFlowAvg", OperationType::MASSFLOW_AVG},
21882188
{"MassFlowInt", OperationType::MASSFLOW_INT},
2189+
{"Probe", OperationType::PROBE},
21892190
};
21902191
std::stringstream knownOps;
21912192
for (const auto& item : opMap) knownOps << item.first << ", ";

TestCases/parallel_regression.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def main():
272272
flatplate_udobj.cfg_dir = "user_defined_functions"
273273
flatplate_udobj.cfg_file = "lam_flatplate.cfg"
274274
flatplate_udobj.test_iter = 20
275-
flatplate_udobj.test_vals = [-6.653802, -1.181430, -0.794887, 0.000611, -0.000369, 0.000736, -0.001104, 596.690000, 299.800000, 296.890000, 21.492000, 0.563990, 2.278700]
275+
flatplate_udobj.test_vals = [-6.653802, -1.181430, -0.794887, 0.000611, -0.000369, 0.000736, -0.001104, 596.690000, 299.800000, 296.890000, 21.492000, 0.563990, 37.148, 2.278700]
276276
test_list.append(flatplate_udobj)
277277

278278
# Laminar cylinder (steady)

TestCases/user_defined_functions/lam_flatplate.cfg

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,22 @@ RESTART_SOL= NO
2424
% - AreaAvg and AreaInt: Computes an area average or integral of a field (the
2525
% expression) over the list of markers.
2626
% - MassFlowAvg and MassFlowInt: Computes a mass flow average or integral.
27+
% - Probe: Evaluates the expression using the values of the mesh point closest
28+
% to the coordinates specified inside "[]", [x, y] or [x, y, z] (2 or 3D).
2729
% NOTE: Each custom output can only use one type, e.g. it is not possible to
2830
% write 'p_drop : AreaAvg{PRESSURE}[inlet] - AreaAvg{PRESSURE}[outlet]'. This
2931
% would need to be separated into two AreaAvg outputs and one Function to
3032
% compute their difference.
3133
CUSTOM_OUTPUTS= 'velocity : Macro{sqrt(pow(VELOCITY_X, 2) + pow(VELOCITY_Y, 2) + pow(VELOCITY_Z, 2))};\
3234
avg_vel : AreaAvg{$velocity}[z_minus, z_plus];\
3335
var_vel : AreaAvg{pow($velocity - avg_vel, 2)}[z_minus, z_plus];\
34-
dev_vel : Function{sqrt(var_vel) / avg_vel}'
36+
dev_vel : Function{sqrt(var_vel) / avg_vel};\
37+
probe1 : Probe{$velocity}[0.005, 0.005, 0.05]'
3538
%
3639
% "COMBO" is the name and group of the output for the objective function
3740
% (regardless of definition). "CUSTOM" is the group for all custom outpus.
3841
SCREEN_OUTPUT= INNER_ITER, RMS_DENSITY, RMS_ENERGY, LINSOL_RESIDUAL, FORCE_Z,\
39-
SURFACE_MASSFLOW, SURFACE_TOTAL_TEMPERATURE, avg_vel, dev_vel, COMBO
42+
SURFACE_MASSFLOW, SURFACE_TOTAL_TEMPERATURE, avg_vel, dev_vel, probe1, COMBO
4043
HISTORY_OUTPUT = ITER, AERO_COEFF, FLOW_COEFF, FLOW_COEFF_SURF, CUSTOM, COMBO
4144
OBJECTIVE_FUNCTION= CUSTOM_OBJFUNC
4245
% Here we define how the custom objective is computed from other outputs. For

0 commit comments

Comments
 (0)