Nektar++
FilterIntegral.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File FilterIntegral.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Outputs integrals of fields during time-stepping.
32//
33///////////////////////////////////////////////////////////////////////////////
34
40
41#include <boost/algorithm/string.hpp>
42
43namespace Nektar::SolverUtils
44{
45std::string FilterIntegral::className =
48
49/**
50 * @brief Initialise the filter and parse the session file parameters.
51 */
54 const std::shared_ptr<EquationSystem> &pEquation, const ParamMap &pParams)
55 : Filter(pSession, pEquation)
56{
57 std::string outName;
58
59 // OutputFile
60 auto it = pParams.find("OutputFile");
61 if (it == pParams.end())
62 {
63 outName = m_session->GetSessionName();
64 }
65 else
66 {
67 ASSERTL0(it->second.length() > 0, "Empty parameter 'OutputFile'.");
68 outName = it->second;
69 }
70 outName += ".int";
71
72 // Composites (to calculate integrals on)
73 it = pParams.find("Composites");
74 ASSERTL0(it != pParams.end(), "Missing parameter 'Composites'.");
75 ASSERTL0(it->second.length() > 0, "Empty parameter 'Composites'.");
76
77 std::vector<std::string> splitComposite;
78 boost::split(m_splitCompString, it->second, boost::is_any_of(","),
79 boost::token_compress_on);
80
81 for (auto &comp : m_splitCompString)
82 {
83 boost::trim(comp);
84 size_t first = comp.find_first_of('[') + 1;
85 size_t last = comp.find_last_of(']') - 1;
86 auto tmpString = comp.substr(first, last - first + 1);
87
88 std::vector<unsigned int> tmpVec;
89 bool parseGood = ParseUtils::GenerateSeqVector(tmpString, tmpVec);
90
91 ASSERTL0(parseGood && !tmpVec.empty(),
92 "Unable to read composite regions index range for "
93 "FilterIntegral: " +
94 comp);
95
96 m_compVector.emplace_back(tmpVec);
97 }
98
99 // OutputPrecision
100 size_t precision;
101 it = pParams.find("OutputPrecision");
102 if (it == pParams.end())
103 {
104 precision = 7;
105 }
106 else
107 {
108 ASSERTL0(it->second.length() > 0, "Empty parameter 'OutputPrecision'.");
109 precision = std::stoi(it->second);
110 }
111
112 // Lock equation system pointer
113 auto equationSys = m_equ.lock();
114 ASSERTL0(equationSys, "Weak pointer expired");
115
116 m_numVariables = equationSys->GetNvariables();
117
118 m_comm = pSession->GetComm();
119 if (m_comm->GetRank() == 0)
120 {
121 m_outFile.open(outName);
122 ASSERTL0(m_outFile.good(), "Unable to open: '" + outName + "'");
123 m_outFile.setf(std::ios::scientific, std::ios::floatfield);
124 m_outFile.precision(precision);
125 m_outFile << "#Time";
126
127 for (auto &compName : m_splitCompString)
128 {
129 for (size_t j = 0; j < m_numVariables; ++j)
130 {
131 std::string varName = equationSys->GetVariable(j);
132 m_outFile << " " + compName + "_" + varName + "_integral";
133 }
134 }
135 m_outFile << std::endl;
136 }
137
138 // OutputFrequency
139 it = pParams.find("OutputFrequency");
140 if (it == pParams.end())
141 {
143 }
144 else
145 {
146 ASSERTL0(it->second.length() > 0, "Empty parameter 'OutputFrequency'.");
147 LibUtilities::Equation equ(m_session->GetInterpreter(), it->second);
148 m_outputFrequency = round(equ.Evaluate());
149 }
150}
151
152/**
153 * @brief Parse composite list and geometric entities to integrate over.
154 */
157 const NekDouble &time)
158{
159
160 // Create map from element ID -> expansion list ID
161 auto expList = pFields[0]->GetExp();
162 auto meshGraph = pFields[0]->GetGraph();
163
164 for (size_t i = 0; i < expList->size(); ++i)
165 {
166 auto exp = (*expList)[i];
167 m_geomElmtIdToExpId[exp->GetGeom()->GetGlobalID()] = i;
168 }
169
170 // Create a map from geom ID -> trace expansion list ID
171 std::map<size_t, size_t> geomIdToTraceId;
172 auto trace = pFields[0]->GetTrace()->GetExp();
173 for (size_t i = 0; i < trace->size(); ++i)
174 {
175 auto exp = (*trace)[i];
176 geomIdToTraceId[exp->GetGeom()->GetGlobalID()] = i;
177 }
178
179 // Get comp list dimension from first composite & element
180 auto composites = pFields[0]->GetGraph()->GetComposites();
181 size_t meshDim = pFields[0]->GetGraph()->GetMeshDimension();
182
183 for (int i = 0; i < m_compVector.size(); ++i)
184 {
185 // Check composite is present in the rank
186 if (composites.find(m_compVector[i][0]) == composites.end())
187 {
188 continue;
189 }
190
191 std::vector<std::shared_ptr<SpatialDomains::Geometry>> geomVec =
192 composites[m_compVector[i][0]]->m_geomVec;
193 size_t dim =
194 composites[m_compVector[i][0]]->m_geomVec[0]->GetShapeDim();
195
196 // Vector of all geometry IDs contained within the composite list
197 std::vector<size_t> compGeomIds;
198 for (auto compNum : m_compVector[i])
199 {
200 ASSERTL0(composites.find(compNum) != composites.end(),
201 "In FilterIntegral defined composite C[" +
202 std::to_string(compNum) +
203 "] does not exist in the mesh.")
204
205 auto compGeom = composites[compNum]->m_geomVec;
206
207 for (auto &geom : compGeom)
208 {
209 compGeomIds.emplace_back(geom->GetGlobalID());
210 }
211
212 // Only check first element in each comp for dimension
213 ASSERTL0(
214 dim == compGeom[0]->GetShapeDim(),
215 "Differing geometry dimensions specified in FilterIntegral '" +
216 m_splitCompString[i] + "'.");
217 }
218
219 std::vector<std::pair<LocalRegions::ExpansionSharedPtr, int>>
220 tmpCompExp(compGeomIds.size());
221
222 // If dimension of composite == dimension of mesh then we only need the
223 // expansion of the element
224 if (dim == pFields[0]->GetShapeDimension())
225 {
226 for (size_t j = 0; j < compGeomIds.size(); ++j)
227 {
228 tmpCompExp[j] = std::make_pair(
229 (*expList)[m_geomElmtIdToExpId[compGeomIds[j]]], -1);
230 }
231 }
232 // however if the dimension is less we need the expansion of the element
233 // containing the global composite geometry and the face/edge local ID
234 // within that. 3D mesh -> 2D, 2D -> 1D.
235 // @TODO: Restructure with the new dimension independent functions
236 // and check all is correct with this filter.
237 else if (meshDim == 3 && dim == 2)
238 {
239 for (size_t j = 0; j < compGeomIds.size(); ++j)
240 {
242 (*trace)[geomIdToTraceId[compGeomIds[j]]];
244 std::dynamic_pointer_cast<LocalRegions::Expansion2D>(exp);
245
246 LocalRegions::ExpansionSharedPtr leftAdjElmtExp =
247 std::dynamic_pointer_cast<LocalRegions::Expansion>(
248 exp2D->GetLeftAdjacentElementExp());
249 int leftAdjElmtFace = exp2D->GetLeftAdjacentElementTrace();
250
251 tmpCompExp[j] = std::make_pair(leftAdjElmtExp, leftAdjElmtFace);
252 }
253 }
254 else if (meshDim == 2 && dim == 1)
255 {
256 for (size_t j = 0; j < compGeomIds.size(); ++j)
257 {
259 (*trace)[geomIdToTraceId[compGeomIds[j]]];
261 std::dynamic_pointer_cast<LocalRegions::Expansion1D>(exp);
262
263 LocalRegions::ExpansionSharedPtr leftAdjElmtExp =
264 std::dynamic_pointer_cast<LocalRegions::Expansion>(
265 exp1D->GetLeftAdjacentElementExp());
266 int leftAdjElmtEdge = exp1D->GetLeftAdjacentElementTrace();
267
268 tmpCompExp[j] = std::make_pair(leftAdjElmtExp, leftAdjElmtEdge);
269 }
270 }
271 else
272 {
273 ASSERTL0(false,
274 "FilterIntegral: Only composite dimensions equal to or "
275 "one lower than the mesh dimension are supported.")
276 }
277
278 m_compExpMap[i] = tmpCompExp;
279 }
280
281 v_Update(pFields, time);
282}
283
284/**
285 * @brief Calculate integral over requested composites.
286 */
289 const NekDouble &time)
290{
291 if (m_index++ % m_outputFrequency > 0)
292 {
293 return;
294 }
295
296 if (m_comm->GetRank() == 0)
297 {
298 m_outFile << time;
299 }
300
301 for (size_t j = 0; j < m_compVector.size(); ++j)
302 {
303 for (size_t i = 0; i < m_numVariables; ++i)
304 {
306 phys = pFields[i]->GetPhys();
307
308 NekDouble sum = 0.0;
309 NekDouble c = 0.0;
310
311 // Check if composite is on the rank
312 if (m_compExpMap.find(j) != m_compExpMap.end())
313 {
314 auto compExp = m_compExpMap[j];
315 size_t dim = compExp[0].first->GetGeom()->GetShapeDim();
316 size_t meshDim = pFields[i]->GetGraph()->GetMeshDimension();
317
318 // Evaluate integral using improved Kahan–Babuška summation
319 // algorithm to reduce numerical error from adding floating
320 // points
321 for (auto &expPair : compExp)
322 {
323 NekDouble input = 0;
324 auto exp = expPair.first;
325
326 if (meshDim == dim)
327 {
328 size_t offset = pFields[i]->GetPhys_Offset(
329 m_geomElmtIdToExpId[exp->GetGeom()->GetGlobalID()]);
330 input = exp->Integral(phys + offset);
331 }
332 else if (meshDim == 3 && dim == 2)
333 {
334 Array<OneD, NekDouble> facePhys;
335 exp->GetTracePhysVals(expPair.second, exp, phys,
336 facePhys);
337 input =
338 pFields[i]
339 ->GetTrace()
340 ->GetExp(exp->GetGeom()->GetTid(expPair.second))
341 ->Integral(facePhys);
342 }
343 else if (meshDim == 2 && dim == 1)
344 {
345 Array<OneD, NekDouble> edgePhys;
346 exp->GetTracePhysVals(expPair.second, exp, phys,
347 edgePhys);
348 input =
349 pFields[i]
350 ->GetTrace()
351 ->GetExp(exp->GetGeom()->GetTid(expPair.second))
352 ->Integral(edgePhys);
353 }
354 else
355 {
356 ASSERTL0(false,
357 "FilterIntegral: Only composite dimensions "
358 "equal to or one lower than the mesh "
359 "dimension are supported.")
360 }
361
362 NekDouble t = sum + input;
363 c += fabs(sum) >= fabs(input) ? (sum - t) + input
364 : (input - t) + sum;
365 sum = t;
366 }
367 }
368
369 // Sum integral values from all ranks
370 Array<OneD, NekDouble> sumArray(1, sum + c);
371 m_comm->AllReduce(sumArray, LibUtilities::ReduceSum);
372 if (m_comm->GetRank() == 0)
373 {
374 m_outFile << " " << sumArray[0];
375 }
376 }
377 }
378
379 if (m_comm->GetRank() == 0)
380 {
381 m_outFile << std::endl;
382 }
383}
384
385/**
386 * @brief This is a time-dependent filter.
387 */
390 &pFields,
391 [[maybe_unused]] const NekDouble &time)
392{
393 if (m_comm->GetRank() == 0)
394 {
395 m_outFile.close();
396 }
397}
398
400{
401 return true;
402}
403
404} // namespace Nektar::SolverUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
static bool GenerateSeqVector(const std::string &str, std::vector< unsigned int > &out)
Takes a comma-separated compressed string and converts it to entries in a vector.
Definition: ParseUtils.cpp:104
LibUtilities::SessionReaderSharedPtr m_session
Definition: Filter.h:83
const std::weak_ptr< EquationSystem > m_equ
Definition: Filter.h:84
std::map< std::string, std::string > ParamMap
Definition: Filter.h:65
std::map< int, std::vector< std::pair< LocalRegions::ExpansionSharedPtr, int > > > m_compExpMap
Map of composite ID to vector of expansions an face/edge local ID.
void v_Update(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) final
Calculate integral over requested composites.
std::map< size_t, size_t > m_geomElmtIdToExpId
Mapping from geometry ID to expansion ID.
static FilterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const std::map< std::string, std::string > &pParams)
Creates an instance of this class.
size_t m_outputFrequency
Frequency to write to output data file in timesteps.
size_t m_numVariables
Number of fields to perform integral on.
std::ofstream m_outFile
Out file.
bool v_IsTimeDependent() final
Returns true as filter depends on time.
LibUtilities::CommSharedPtr m_comm
Global communicator.
static std::string className
Name of the class.
void v_Finalise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) final
This is a time-dependent filter.
std::vector< std::vector< unsigned int > > m_compVector
Vector of vector of composites IDs as integers.
SOLVER_UTILS_EXPORT FilterIntegral(const LibUtilities::SessionReaderSharedPtr &pSession, const std::shared_ptr< EquationSystem > &pEquation, const ParamMap &pParams)
Constructs the integral filter and parses filter options, opens file.
std::vector< std::string > m_splitCompString
Vector of composite IDs as a single string.
void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) final
Parse composite list and geometric entities to integrate over.
std::shared_ptr< SessionReader > SessionReaderSharedPtr
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:66
std::shared_ptr< Expansion2D > Expansion2DSharedPtr
Definition: Expansion1D.h:46
std::shared_ptr< Expansion1D > Expansion1DSharedPtr
Definition: Expansion1D.h:50
FilterFactory & GetFilterFactory()
double NekDouble