Nektar++
FilterHilbertFFTPhase.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File FilterHilbertFFTPhase.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// License for the specific language governing rights and limitations under
14// Permission is hereby granted, free of charge, to any person obtaining a
15// copy of this software and associated documentation files (the "Software"),
16// to deal in the Software without restriction, including without limitation
17// the rights to use, copy, modify, merge, publish, distribute, sublicense,
18// and/or sell copies of the Software, and to permit persons to whom the
19// Software is furnished to do so, subject to the following conditions:
20//
21// The above copyright notice and this permission notice shall be included
22// in all copies or substantial portions of the Software.
23//
24// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30// DEALINGS IN THE SOFTWARE.
31//
32// Description: Outputs phase fields during time-stepping.
33//
34///////////////////////////////////////////////////////////////////////////////
35
37
38namespace Nektar
39{
42 "HilbertPhase", FilterHilbertFFTPhase::create);
43
46 const std::weak_ptr<SolverUtils::EquationSystem> &pEquation,
47 const ParamMap &pParams)
48 : Filter(pSession, pEquation)
49{
50 if (pParams.find("OutputFile") == pParams.end())
51 {
52 m_outputFile = m_session->GetSessionName();
53 }
54 else
55 {
56 ASSERTL0(!(pParams.find("OutputFile")->second.empty()),
57 "Missing parameter 'OutputFile'.");
58 m_outputFile = pParams.find("OutputFile")->second;
59 }
60
61 ASSERTL0(pParams.find("OutputFrequency") != pParams.end(),
62 "Missing parameter 'OutputFrequency'."); // Sampling frequency =
63 // per number of timesteps
64 LibUtilities::Equation equ(m_session->GetInterpreter(),
65 pParams.find("OutputFrequency")->second);
66 m_outputFrequency = floor(equ.Evaluate());
67
68 ASSERTL0(pParams.find("WindowSize") != pParams.end(),
69 "Missing parameter 'WindowSize'.");
70 LibUtilities::Equation equ1(m_session->GetInterpreter(),
71 pParams.find("WindowSize")->second);
72 m_window = floor(equ1.Evaluate()); // No. of output steps taken in each FFT
73 ASSERTL0(m_window % 2 == 0, "'WindowSize' must be even.");
74
75 ASSERTL0(pParams.find("OverlapSize") != pParams.end(),
76 "Missing parameter 'OverlapSize'.");
77 LibUtilities::Equation equ2(m_session->GetInterpreter(),
78 pParams.find("OverlapSize")->second);
79 m_overlap = floor(equ2.Evaluate());
81 "'OverlapSize' must be smaller than 'WindowSize'.");
82
83 if (pParams.find("MeanV") != pParams.end())
84 {
85 provided_mean = true;
86 LibUtilities::Equation equ3(m_session->GetInterpreter(),
87 pParams.find("MeanV")->second);
88 m_mean = equ3.Evaluate();
89 }
90
91 if (pParams.find("LinearTransitionOverlap") != pParams.end())
92 {
94 (pParams.find("LinearTransitionOverlap")->second == "true");
95 }
96
97 m_outputIndex = 0;
98 m_index = 0;
99
101}
102
104{
105}
106
109 [[maybe_unused]] const NekDouble &time)
110{
111 m_index = 0;
112 m_outputIndex = 0;
113 vCounter = 0;
114
115 m_TimeStep = pFields[0]->GetSession()->GetParameter("TimeStep");
116
117 npoints = pFields[0]->GetPhys().size();
123 std::fill_n(&overlap_phase[0], npoints * m_overlap, 0.0);
124
125 // Initialise FFTW
126 fftw_r2r_kind *fwd_kind = new fftw_r2r_kind[npoints];
127 std::fill_n(fwd_kind, npoints, FFTW_R2HC);
128 plan_forward = fftw_plan_many_r2r(
130 &fcoef[0], &m_window, npoints, 1, &fwd_kind[0], FFTW_ESTIMATE);
131
132 fftw_r2r_kind *bwd_kind = new fftw_r2r_kind[npoints];
133 std::fill_n(bwd_kind, npoints, FFTW_HC2R);
134 plan_backward = fftw_plan_many_r2r(1, &m_window, npoints, &fcoef[0],
135 &m_window, npoints, 1, &h[0], &m_window,
136 npoints, 1, &bwd_kind[0], FFTW_ESTIMATE);
137
138 delete[] fwd_kind;
139 delete[] bwd_kind;
140 v_Update(pFields, 0.0);
141}
142
145 [[maybe_unused]] const NekDouble &time)
146{
147 if (m_index++ % m_outputFrequency > 0)
148 {
149 return;
150 }
151
152 // Store old data
153 Array<OneD, NekDouble> currentV = pFields[0]->GetPhys();
154 memcpy(&oldV[vCounter * npoints], &currentV[0],
155 npoints * sizeof(NekDouble));
156 vCounter++;
157
158 // Do Hilbert Transform and output phase
159 if (vCounter == m_window)
160 {
161 // Zero mean
162 if (!provided_mean)
163 {
164 for (int j = 0; j < npoints; ++j)
165 {
166 m_mean = 0.0;
167 for (int i = 0; i < m_window; ++i)
168 {
169 m_mean += oldV[i * npoints + j];
170 }
171 m_mean = m_mean / (double)m_window; // time-average
172
173 for (int i = 0; i < m_window; ++i)
174 {
175 oldV_zero_mean[i * npoints + j] =
176 oldV[i * npoints + j] - m_mean;
177 }
178 }
179 }
180 else // minus mean directly
181 {
182 for (int i = 0; i < npoints * m_window; ++i)
183 {
184 oldV_zero_mean[i] = oldV[i] - m_mean;
185 }
186 }
187
188 // Do Hilbert Transform:
189 fftw_execute(plan_forward);
190 // Rearrange out: imag->real, -real->imag
191 double tmp_fcoef;
192 for (int i = 1; i < m_window / 2; ++i)
193 {
194 for (int j = 0; j < npoints; ++j)
195 {
196 tmp_fcoef = -fcoef[i * npoints + j] / m_window;
197 fcoef[i * npoints + j] =
198 fcoef[(m_window - i) * npoints + j] / m_window;
199 fcoef[(m_window - i) * npoints + j] = tmp_fcoef;
200 }
201 }
202 std::fill_n(&fcoef[0], npoints, 0.0);
203 std::fill_n(&fcoef[(m_window / 2) * npoints], npoints, 0.0);
204 fftw_execute(plan_backward);
205
206 // Output batch
207 Array<OneD, NekDouble> phase_coeff(pFields[0]->GetNcoeffs());
209
210 for (int t = 0; t < m_window - m_overlap; ++t)
211 {
212 std::stringstream vOutputFilename;
213 vOutputFilename << m_outputFile << "_" << m_outputIndex << ".chk";
214
215 // Get FieldDefinitions and allocate Fielddata
216 std::vector<LibUtilities::FieldDefinitionsSharedPtr> FieldDef =
217 pFields[0]->GetFieldDefinitions();
218 std::vector<std::vector<NekDouble>> FieldData(FieldDef.size());
219
220 // Calculate phase
221 if (t < m_overlap)
222 {
223 if (linear_trans)
224 {
225 for (int i = 0; i < npoints; ++i)
226 {
227 phase[i] = ((double)(m_overlap - t) / m_overlap) *
228 overlap_phase[t * npoints + i] +
229 ((double)t / m_overlap) *
230 atan2(h[t * npoints + i],
231 oldV_zero_mean[t * npoints + i]);
232 }
233 }
234 else // simple average
235 {
236 for (int i = 0; i < npoints; ++i)
237 {
238 phase[i] = 0.5 * overlap_phase[t * npoints + i] +
239 0.5 * atan2(h[t * npoints + i],
240 oldV_zero_mean[t * npoints + i]);
241 }
242 }
243 }
244 else
245 {
246 for (int i = 0; i < npoints; ++i)
247 {
248 phase[i] = atan2(h[t * npoints + i],
249 oldV_zero_mean[t * npoints + i]);
250 }
251 }
252
253 // Convert to coefficient space
254 pFields[0]->FwdTransLocalElmt(phase, phase_coeff);
255
256 // for each item in FieldDef
257 // push "phase" to FieldDef[i]->m_fields
258 // call AppendFieldData(FieldDef[i], FieldData[i], data)
259 for (int i = 0; i < FieldDef.size(); ++i)
260 {
261 FieldDef[i]->m_fields.push_back("phase");
262 pFields[0]->AppendFieldData(FieldDef[i], FieldData[i],
263 phase_coeff);
264 }
265
266 // Update time in field info if required
267 LibUtilities::FieldMetaDataMap fieldMetaDataMap;
268 fieldMetaDataMap["Time"] = boost::lexical_cast<std::string>(
270
271 m_fld->Write(vOutputFilename.str(), FieldDef, FieldData,
272 fieldMetaDataMap);
274 }
275
276 // Save phase for next iter
277 for (int t = m_window - m_overlap; t < m_window; ++t)
278 {
279 for (int i = 0; i < npoints; ++i)
280 {
281 overlap_phase[(t - m_window + m_overlap) * npoints + i] =
282 atan2(h[t * npoints + i], oldV_zero_mean[t * npoints + i]);
283 }
284 }
285
286 // Move overlap part to front
287 memcpy(&oldV[0], &oldV[(m_window - m_overlap) * npoints],
288 npoints * m_overlap * sizeof(NekDouble));
290 }
291}
292
295 &pFields,
296 [[maybe_unused]] const NekDouble &time)
297{
298}
299
301{
302 return true;
303}
304} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
Array< OneD, NekDouble > overlap_phase
FilterHilbertFFTPhase(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< SolverUtils::EquationSystem > &pEquation, const ParamMap &pParams)
void v_Update(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
void v_Finalise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
Array< OneD, NekDouble > oldV_zero_mean
void v_Initialise(const Array< OneD, const MultiRegions::ExpListSharedPtr > &pFields, const NekDouble &time) override
Array< OneD, NekDouble > fcoef
LibUtilities::FieldIOSharedPtr m_fld
static SolverUtils::FilterSharedPtr create(const LibUtilities::SessionReaderSharedPtr &pSession, const std::weak_ptr< SolverUtils::EquationSystem > &pEquation, const ParamMap &pParams)
Creates an instance of this class.
static std::string className
Name of the class.
static std::shared_ptr< FieldIO > CreateDefault(const LibUtilities::SessionReaderSharedPtr session)
Returns an object for the default FieldIO method.
Definition: FieldIO.cpp:195
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
LibUtilities::SessionReaderSharedPtr m_session
Definition: Filter.h:83
std::map< std::string, std::string > ParamMap
Definition: Filter.h:65
std::map< std::string, std::string > FieldMetaDataMap
Definition: FieldIO.h:50
std::shared_ptr< SessionReader > SessionReaderSharedPtr
FilterFactory & GetFilterFactory()
Definition: Filter.cpp:39
double NekDouble