@@ -172,7 +172,7 @@ class COutput {
172172 // ! Structure to store the value initial residuals for relative residual computation
173173 std::map<string, su2double> initialResiduals;
174174
175- /* * \brief Struct to hold a parsed user-defined expression. */
175+ /* ! \brief Struct to hold a parsed user-defined expression. */
176176 struct CustomHistoryOutput {
177177 mel::ExpressionTree<passivedouble> expression;
178178 /* --- Pointers to values in the history output maps, to avoid key lookup every time. ---*/
@@ -186,17 +186,62 @@ class COutput {
186186
187187 CustomHistoryOutput customObjFunc; /* !< \brief User-defined expression for a custom objective. */
188188
189- /* ----------------------------- Volume output ----------------------------*/
189+ /* ! \brief Type of operation for custom outputs. */
190+ enum class OperationType { MACRO, FUNCTION, AREA_AVG, AREA_INT, MASSFLOW_AVG, MASSFLOW_INT };
190191
191- CParallelDataSorter* volumeDataSorter; // !< Volume data sorter
192- CParallelDataSorter* surfaceDataSorter; // !< Surface data sorter
192+ /* ! \brief Struct to hold a parsed custom output function. */
193+ struct CustomOutput {
194+ /* --- First level of parsing the syntax "name : type{func}[markers];". ---*/
195+ std::string name;
196+ OperationType type;
197+ std::string func;
198+ std::vector<std::string> markers;
193199
194- vector<string> volumeFieldNames; // !< Vector containing the volume field names
195- unsigned short nVolumeFields; /* !< \brief Number of fields in the volume output */
200+ /* --- Second level, func into expression, and acceleration structures. ---*/
201+ mel::ExpressionTree<passivedouble> expression;
202+ std::vector<std::string> varSymbols;
203+ std::vector<unsigned short > markerIndices;
204+
205+ /* --- The symbols (strings) are associated with an integer index for efficiency. For evaluation this index
206+ is passed to a functor that returns the value associated with the symbol. This functor is an input to "eval()"
207+ and needs to be generated on-the-fly for each point. The functor approach is more generic than a pointer, for
208+ example it allows wrapping the access to multiple solvers. The interpretation of these indices is dictated by
209+ the functor used in eval, for example indices may be established as 32 * solver_idx + variable_idx.
210+ The parts of the code that assign and interpret indices need to be in sync. ---*/
211+ std::vector<unsigned long > varIndices;
212+
213+ /* --- Offset between varIndices of different solvers (see above). Power of 2 to make decoding faster. ---*/
214+ static constexpr unsigned long MAX_VARS_PER_SOLVER = 32 ;
215+
216+ /* --- Arbitrary number to indicate that a string did not match a variable. ---*/
217+ static constexpr unsigned long NOT_A_VARIABLE = MAX_SOLS * MAX_VARS_PER_SOLVER;
218+
219+ /* --- Other outputs can be referenced in expressions, e.g. to compute variance.
220+ We store pointers to the required outputs to speed-up access. ---*/
221+ std::vector<const su2double*> otherOutputs;
222+
223+ /* --- For evaluation, "vars" is a functor (i.e. has operator()) that returns the value of a variable at a given
224+ point. For example, it can be a wrapper to the primitives pointer, in which case varIndices needs to be setup
225+ with primitive indices. ---*/
226+ template <class Variables >
227+ su2double eval (const Variables& vars) const {
228+ return mel::Eval<su2double>(expression, [&](int iSymbol) {return vars (varIndices[iSymbol]);});
229+ }
230+ };
231+
232+ std::vector<CustomOutput> customOutputs; /* !< \brief User-defined outputs. */
233+
234+ /* ----------------------------- Volume output ----------------------------*/
196235
197- string volumeFilename, // !< Volume output filename
198- surfaceFilename, // !< Surface output filename
199- restartFilename; // !< Restart output filename
236+ CParallelDataSorter* volumeDataSorter; // !< Volume data sorter
237+ CParallelDataSorter* surfaceDataSorter; // !< Surface data sorter
238+
239+ vector<string> volumeFieldNames; // !< Vector containing the volume field names
240+ unsigned short nVolumeFields; // !< Number of fields in the volume output
241+
242+ string volumeFilename, // !< Volume output filename
243+ surfaceFilename, // !< Surface output filename
244+ restartFilename; // !< Restart output filename
200245
201246 /* * \brief Structure to store information for a volume output field.
202247 *
@@ -628,6 +673,29 @@ class COutput {
628673 }
629674 }
630675
676+ /* !
677+ * \brief Returns a pointer to the value of an history output.
678+ * \note For per-surface outputs the marker index is specified as "name[index]".
679+ */
680+ inline const su2double* GetPtrToHistoryOutput (const string& name) const {
681+ /* --- Decide if it should be per surface. ---*/
682+ const auto pos = name.find (' [' );
683+ const su2double* ptr = nullptr ;
684+ if (pos == std::string::npos) {
685+ const auto it = historyOutput_Map.find (name);
686+ if (it != historyOutput_Map.end ()) {
687+ ptr = &(it->second .value );
688+ }
689+ } else {
690+ const auto idx = std::stoi (std::string (name.begin ()+pos+1 , name.end ()-1 ));
691+ const auto it = historyOutputPerSurface_Map.find (std::string (name, 0 , pos));
692+ if (it != historyOutputPerSurface_Map.end ()) {
693+ ptr = &(it->second [idx].value );
694+ }
695+ }
696+ return ptr;
697+ }
698+
631699 /* !
632700 * \brief Setup a custom history output object for a given expression.
633701 * \param[in] expression - Some user-defined math with the history field names as variables.
@@ -726,6 +794,11 @@ class COutput {
726794 */
727795 void SetCommonHistoryFields ();
728796
797+ /* !
798+ * \brief Parses user-defined outputs.
799+ */
800+ void SetCustomOutputs (const CConfig *config);
801+
729802 /* !
730803 * \brief Load values of the history fields common for all solvers.
731804 * \param[in] config - Definition of the particular problem.
0 commit comments