Skip to content

Commit 5d13417

Browse files
authored
Access solution and primitives via python wrapper (#1938)
* matrix view type to manipulate fields in python without copying data * extra line and update docker images
1 parent 728cb8d commit 5d13417

36 files changed

Lines changed: 363 additions & 178 deletions

.github/workflows/regression.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ jobs:
5252
key: ${{ matrix.config_set }}-${{ github.sha }}
5353
restore-keys: ${{ matrix.config_set }}
5454
- name: Pre Cleanup
55-
uses: docker://ghcr.io/su2code/su2/build-su2:221224-1158
55+
uses: docker://ghcr.io/su2code/su2/build-su2:230225-2136
5656
with:
5757
entrypoint: /bin/rm
5858
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}
5959
- name: Build
60-
uses: docker://ghcr.io/su2code/su2/build-su2:221224-1158
60+
uses: docker://ghcr.io/su2code/su2/build-su2:230225-2136
6161
with:
6262
args: -b ${{github.ref}} -f "${{matrix.flags}}"
6363
- name: Compress binaries
@@ -68,7 +68,7 @@ jobs:
6868
name: ${{ matrix.config_set }}
6969
path: install_bin.tgz
7070
- name: Post Cleanup
71-
uses: docker://ghcr.io/su2code/su2/build-su2:221224-1158
71+
uses: docker://ghcr.io/su2code/su2/build-su2:230225-2136
7272
with:
7373
entrypoint: /bin/rm
7474
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}
@@ -99,7 +99,7 @@ jobs:
9999
tag: OMP
100100
steps:
101101
- name: Pre Cleanup
102-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
102+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
103103
with:
104104
entrypoint: /bin/rm
105105
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}
@@ -125,12 +125,12 @@ jobs:
125125
chmod a+x $BIN_FOLDER/*
126126
ls -lahR $BIN_FOLDER
127127
- name: Run Tests in Container
128-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
128+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
129129
with:
130130
# -t <Tutorials-branch> -c <Testcases-branch>
131131
args: -b ${{github.ref}} -t develop -c develop -s ${{matrix.testscript}}
132132
- name: Cleanup
133-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
133+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
134134
with:
135135
entrypoint: /bin/rm
136136
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}
@@ -151,7 +151,7 @@ jobs:
151151
tag: MPI
152152
steps:
153153
- name: Pre Cleanup
154-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
154+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
155155
with:
156156
entrypoint: /bin/rm
157157
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}
@@ -212,11 +212,11 @@ jobs:
212212
echo $PWD
213213
ls -lahR
214214
- name: Run Unit Tests
215-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
215+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
216216
with:
217217
entrypoint: install/bin/${{matrix.testdriver}}
218218
- name: Post Cleanup
219-
uses: docker://ghcr.io/su2code/su2/test-su2:221224-1158
219+
uses: docker://ghcr.io/su2code/su2/test-su2:230225-2136
220220
with:
221221
entrypoint: /bin/rm
222222
args: -rf install install_bin.tgz src ccache ${{ matrix.config_set }}

.github/workflows/release-management.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ jobs:
3535
key: ${{ matrix.os_bin }}-${{ github.sha }}
3636
restore-keys: ${{ matrix.os_bin }}
3737
- name: Build
38-
uses: docker://ghcr.io/su2code/su2/build-su2-cross:221224-1158
38+
uses: docker://ghcr.io/su2code/su2/build-su2-cross:230225-2136
3939
with:
4040
args: -b ${{ github.sha }} -f "${{matrix.flags}}"
4141
- name: Create Archive
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*!
2+
* \file CPyWrapperMatrixView.hpp
3+
* \brief A simple matrix view to use with the python wrapper.
4+
* \author P. Gomes
5+
* \version 7.5.1 "Blackbird"
6+
*
7+
* SU2 Project Website: https://su2code.github.io
8+
*
9+
* The SU2 Project is maintained by the SU2 Foundation
10+
* (http://su2foundation.org)
11+
*
12+
* Copyright 2012-2023, SU2 Contributors (cf. AUTHORS.md)
13+
*
14+
* SU2 is free software; you can redistribute it and/or
15+
* modify it under the terms of the GNU Lesser General Public
16+
* License as published by the Free Software Foundation; either
17+
* version 2.1 of the License, or (at your option) any later version.
18+
*
19+
* SU2 is distributed in the hope that it will be useful,
20+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22+
* Lesser General Public License for more details.
23+
*
24+
* You should have received a copy of the GNU Lesser General Public
25+
* License along with SU2. If not, see <http://www.gnu.org/licenses/>.
26+
*/
27+
28+
#pragma once
29+
30+
#include <utility>
31+
#include <vector>
32+
#include <string>
33+
34+
#include "C2DContainer.hpp"
35+
#include "../parallelization/mpi_structure.hpp"
36+
37+
/*!
38+
* \class CPyWrapperMatrixView
39+
* \ingroup PySU2
40+
* \brief A simple matrix view to use with the python wrapper. The accessors
41+
* in this class provide a passive double interface to su2activematrix.
42+
* This can be extended to allow access to derivative information of su2double.
43+
*/
44+
class CPyWrapperMatrixView {
45+
private:
46+
static_assert(su2activematrix::IsRowMajor, "");
47+
su2double* data_ = nullptr;
48+
unsigned long rows_ = 0, cols_ = 0;
49+
std::string name_;
50+
bool read_only_ = false;
51+
52+
inline const su2double& Access(unsigned long row, unsigned long col) const {
53+
if (row > rows_ || col > cols_) SU2_MPI::Error(name_ + " out of bounds", "CPyWrapperMatrixView");
54+
return data_[row * cols_ + col];
55+
}
56+
inline su2double& Access(unsigned long row, unsigned long col) {
57+
if (read_only_) SU2_MPI::Error(name_ + " is read-only", "CPyWrapperMatrixView");
58+
const auto& const_me = *this;
59+
return const_cast<su2double&>(const_me.Access(row, col));
60+
}
61+
62+
public:
63+
CPyWrapperMatrixView() = default;
64+
65+
/*!
66+
* \brief Construct the view of the matrix.
67+
* \note "name" should be set to the variable name being returned to give better information to users.
68+
* \note "read_only" can be set to true to prevent the data from being modified.
69+
*/
70+
CPyWrapperMatrixView(su2activematrix& mat, const std::string& name, bool read_only)
71+
: data_(mat.data()), rows_(mat.rows()), cols_(mat.cols()), name_(name), read_only_(read_only) {}
72+
73+
/*!
74+
* \brief Returns the shape of the matrix.
75+
*/
76+
std::pair<unsigned long, unsigned long> Shape() const { return std::make_pair(rows_, cols_); }
77+
78+
/*!
79+
* \brief Returns whether the data is read-only [true] or if it can be modified [false].
80+
*/
81+
bool IsReadOnly() const { return read_only_; }
82+
83+
/*!
84+
* \brief Gets the value for a (row, column) pair.
85+
*/
86+
passivedouble operator() (unsigned long row, unsigned long col) const { return Get(row, col); }
87+
88+
/*!
89+
* \brief Gets the value for a (row, column) pair.
90+
*/
91+
passivedouble Get(unsigned long row, unsigned long col) const { return SU2_TYPE::GetValue(Access(row, col)); }
92+
93+
/*!
94+
* \brief Gets the values for a row of the matrix.
95+
*/
96+
std::vector<passivedouble> Get(unsigned long row) const {
97+
std::vector<passivedouble> vals(cols_);
98+
for (unsigned long j = 0; j < cols_; ++j) vals[j] = Get(row, j);
99+
return vals;
100+
}
101+
102+
/*!
103+
* \brief Sets the value for a (row, column) pair.
104+
* \note This clears derivative information (consistently with C++ operator= with passive rhs).
105+
*/
106+
void Set(unsigned long row, unsigned long col, passivedouble val) { Access(row, col) = val; }
107+
108+
/*!
109+
* \brief Sets the values for a row of the matrix.
110+
*/
111+
void Set(unsigned long row, std::vector<passivedouble> vals) {
112+
unsigned long j = 0;
113+
for (const auto& val : vals) Set(row, j++, val);
114+
}
115+
};

Common/src/containers/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
common_src += files(['CTrapezoidalMap.cpp',
2-
'CFileReaderLUT.cpp',
2+
'CFileReaderLUT.cpp',
33
'CLookUpTable.cpp'])

SU2_CFD/include/drivers/CDriverBase.hpp

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#pragma once
2929

3030
#include "../../../Common/include/CConfig.hpp"
31+
#include "../../../Common/include/containers/CPyWrapperMatrixView.hpp"
3132
#include "../numerics/CNumerics.hpp"
3233
#include "../output/COutput.hpp"
3334
#include "../solvers/CSolver.hpp"
@@ -38,7 +39,6 @@
3839
* \brief Base class for all drivers.
3940
* \author H. Patel, A. Gastaldi
4041
*/
41-
4242
class CDriverBase {
4343
protected:
4444
int rank, /*!< \brief MPI Rank. */
@@ -189,25 +189,24 @@ class CDriverBase {
189189
bool GetNodeDomain(unsigned long iPoint) const;
190190

191191
/*!
192-
* \brief Get the initial (un-deformed) coordinates of a mesh node.
193-
* \param[in] iPoint - Mesh node index.
194-
* \return Initial node coordinates (nDim).
195-
*/
196-
vector<passivedouble> GetInitialCoordinates(unsigned long iPoint) const;
197-
198-
/*!
199-
* \brief Get the current coordinates of a mesh node.
200-
* \param[in] iPoint - Mesh node index.
201-
* \return Node coordinates (nDim).
192+
* \brief Get a read-only view of the initial (undeformed) coordinates of all mesh nodes.
202193
*/
203-
vector<passivedouble> GetCoordinates(unsigned long iPoint) const;
194+
inline CPyWrapperMatrixView InitialCoordinates() const {
195+
if (!main_config->GetDeform_Mesh()) {
196+
SU2_MPI::Error("Initial coordinates are only available with DEFORM_MESH= YES", CURRENT_FUNCTION);
197+
}
198+
auto* coords =
199+
const_cast<su2activematrix*>(solver_container[ZONE_0][INST_0][MESH_0][MESH_SOL]->GetNodes()->GetMesh_Coord());
200+
return CPyWrapperMatrixView(*coords, "InitialCoordinates", true);
201+
}
204202

205203
/*!
206-
* \brief Set the coordinates of a mesh node.
207-
* \param[in] iPoint - Mesh node index.
208-
* \param[in] values - Node coordinates (nDim).
204+
* \brief Get a read/write view of the current coordinates of all mesh nodes.
209205
*/
210-
void SetCoordinates(unsigned long iPoint, vector<passivedouble> values);
206+
inline CPyWrapperMatrixView Coordinates() {
207+
auto& coords = const_cast<su2activematrix&>(main_geometry->nodes->GetCoord());
208+
return CPyWrapperMatrixView(coords, "Coordinates", false);
209+
}
211210

212211
/*!
213212
* \brief Get the number of markers in the mesh.
@@ -323,6 +322,39 @@ class CDriverBase {
323322
* \brief Communicate the boundary mesh displacements.
324323
*/
325324
void CommunicateMeshDisplacements(void);
325+
326+
/*!
327+
* \brief Get all the active solver names with their associated indices (which can be used to access their data).
328+
*/
329+
map<string, unsigned short> GetSolverIndices() const;
330+
331+
/*!
332+
* \brief Get a read/write view of the current solution on all mesh nodes of a solver.
333+
*/
334+
inline CPyWrapperMatrixView Solution(unsigned short iSolver) {
335+
auto* solver = solver_container[ZONE_0][INST_0][MESH_0][iSolver];
336+
if (solver == nullptr) SU2_MPI::Error("The selected solver does not exist.", CURRENT_FUNCTION);
337+
auto& solution = solver->GetNodes()->GetSolution();
338+
return CPyWrapperMatrixView(solution, "Solution of " + solver->GetSolverName(), false);
339+
}
340+
341+
/*!
342+
* \brief Get the flow solver primitive variable names with their associated indices.
343+
* These correspond to the column indices in the matrix returned by Primitives.
344+
*/
345+
map<string, unsigned short> GetPrimitiveIndices() const;
346+
347+
/*!
348+
* \brief Get a read/write view of the current primitive variables on all mesh nodes of the flow solver.
349+
* \warning Primitive variables are only available for flow solvers.
350+
*/
351+
inline CPyWrapperMatrixView Primitives() {
352+
auto* solver = solver_container[ZONE_0][INST_0][MESH_0][FLOW_SOL];
353+
if (solver == nullptr) SU2_MPI::Error("The flow solver does not exist.", CURRENT_FUNCTION);
354+
auto& primitives = const_cast<su2activematrix&>(solver->GetNodes()->GetPrimitive());
355+
return CPyWrapperMatrixView(primitives, "Primitives", false);
356+
}
357+
326358
/// \}
327359

328360
protected:

SU2_CFD/include/output/CFlowOutput.hpp

Lines changed: 5 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
#include "CFVMOutput.hpp"
3131
#include "../variables/CVariable.hpp"
3232

33+
/*--- Forward declare to avoid including here. ---*/
34+
template <class>
35+
struct CPrimitiveIndices;
36+
3337
class CFlowOutput : public CFVMOutput{
3438
protected:
3539
unsigned long lastInnerIter;
@@ -211,71 +215,7 @@ class CFlowOutput : public CFVMOutput{
211215
* \brief Helper for custom outputs, converts variable names to indices and pointers which are then used
212216
* to evaluate the custom expressions.
213217
*/
214-
template <class FlowIndices>
215-
void ConvertVariableSymbolsToIndices(const FlowIndices& idx, CustomOutput& output) const {
216-
217-
static const auto knownVariables =
218-
"TEMPERATURE, TEMPERATURE_VE, VELOCITY_X, VELOCITY_Y, VELOCITY_Z, PRESSURE,\n"
219-
"DENSITY, ENTHALPY, SOUND_SPEED, LAMINAR_VISCOSITY, EDDY_VISCOSITY, THERMAL_CONDUCTIVITY\n"
220-
"TURB[0,1,...], RAD[0,1,...], SPECIES[0,1,...]";
221-
222-
auto IndexOfVariable = [&](const FlowIndices& idx, const std::string& var) {
223-
/*--- Primitives of the flow solver. ---*/
224-
const auto flow_offset = FLOW_SOL * CustomOutput::MAX_VARS_PER_SOLVER;
225-
226-
if ("TEMPERATURE" == var) return flow_offset + idx.Temperature();
227-
if ("TEMPERATURE_VE" == var) return flow_offset + idx.Temperature_ve();
228-
if ("VELOCITY_X" == var) return flow_offset + idx.Velocity();
229-
if ("VELOCITY_Y" == var) return flow_offset + idx.Velocity() + 1;
230-
if ("VELOCITY_Z" == var) return flow_offset + idx.Velocity() + 2;
231-
if ("PRESSURE" == var) return flow_offset + idx.Pressure();
232-
if ("DENSITY" == var) return flow_offset + idx.Density();
233-
if ("ENTHALPY" == var) return flow_offset + idx.Enthalpy();
234-
if ("SOUND_SPEED" == var) return flow_offset + idx.SoundSpeed();
235-
if ("LAMINAR_VISCOSITY" == var) return flow_offset + idx.LaminarViscosity();
236-
if ("EDDY_VISCOSITY" == var) return flow_offset + idx.EddyViscosity();
237-
if ("THERMAL_CONDUCTIVITY" == var) return flow_offset + idx.ThermalConductivity();
238-
239-
/*--- Index-based (no name) access to variables of other solvers. ---*/
240-
auto GetIndex = [](const std::string& s, int nameLen) {
241-
/*--- Extract an int from "name[int]", nameLen is the length of "name". ---*/
242-
return std::stoi(std::string(s.begin() + nameLen + 1, s.end() - 1));
243-
};
244-
if (var.rfind("SPECIES", 0) == 0) return SPECIES_SOL * CustomOutput::MAX_VARS_PER_SOLVER + GetIndex(var, 7);
245-
if (var.rfind("TURB", 0) == 0) return TURB_SOL * CustomOutput::MAX_VARS_PER_SOLVER + GetIndex(var, 4);
246-
if (var.rfind("RAD", 0) == 0) return RAD_SOL * CustomOutput::MAX_VARS_PER_SOLVER + GetIndex(var, 3);
247-
248-
return CustomOutput::NOT_A_VARIABLE;
249-
};
250-
251-
output.otherOutputs.clear();
252-
output.varIndices.clear();
253-
output.varIndices.reserve(output.varSymbols.size());
254-
255-
for (const auto& var : output.varSymbols) {
256-
output.varIndices.push_back(IndexOfVariable(idx, var));
257-
258-
if (output.type == OperationType::FUNCTION && output.varIndices.back() != CustomOutput::NOT_A_VARIABLE) {
259-
SU2_MPI::Error("Custom outputs of type 'Function' cannot reference solver variables.", CURRENT_FUNCTION);
260-
}
261-
/*--- Symbol is a valid solver variable. ---*/
262-
if (output.varIndices.back() < CustomOutput::NOT_A_VARIABLE) continue;
263-
264-
/*--- An index above NOT_A_VARIABLE is not valid with current solver settings. ---*/
265-
if (output.varIndices.back() > CustomOutput::NOT_A_VARIABLE) {
266-
SU2_MPI::Error("Inactive solver variable (" + var + ") used in function " + output.name + "\n"
267-
"E.g. this may only be a variable of the compressible solver.", CURRENT_FUNCTION);
268-
}
269-
270-
/*--- An index equal to NOT_A_VARIABLE may refer to a history output. ---*/
271-
output.varIndices.back() += output.otherOutputs.size();
272-
output.otherOutputs.push_back(GetPtrToHistoryOutput(var));
273-
if (output.otherOutputs.back() == nullptr) {
274-
SU2_MPI::Error("Invalid history output or solver variable (" + var + ") used in function " + output.name +
275-
"\nValid solvers variables: " + knownVariables, CURRENT_FUNCTION);
276-
}
277-
}
278-
}
218+
void ConvertVariableSymbolsToIndices(const CPrimitiveIndices<unsigned long>& idx, CustomOutput& output) const;
279219

280220
/*!
281221
* \brief Compute value of the Q criteration for vortex idenfitication

SU2_CFD/include/solvers/CSolver.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4246,9 +4246,9 @@ class CSolver {
42464246

42474247
/*!
42484248
* \brief Retrieve the solver name for output purposes.
4249-
* \param[out] val_solvername - Name of the solver.
4249+
* \returns Name of the solver.
42504250
*/
4251-
inline string GetSolverName(void) {return SolverName;}
4251+
inline const string& GetSolverName() const { return SolverName; }
42524252

42534253
/*!
42544254
* \brief Get the solution fields.

0 commit comments

Comments
 (0)