svMultiPhysics
Loading...
Searching...
No Matches
FourierInterpolation.h
1// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the
2// University of California, and others. SPDX-License-Identifier: BSD-3-Clause
3
4#ifndef FOURIER_INTERPOLATION_H
5#define FOURIER_INTERPOLATION_H
6
7#include "Array.h"
8#include "CmMod.h"
9#include "Vector.h"
10
11#include "Core/Exception.h"
13
14#include <string>
15#include <utility>
16#include <vector>
17
18/**
19 * @brief Fourier interpolation of time dependent data.
20 *
21 * This class implements interpolation of time dependent data through either a
22 * Fourier series or a linear clamped ramp. Which of the two is used is
23 * determined by the boolean member variable @ref use_ramp.
24 *
25 * In what follows, let @f$\{t_i, \mathbf{v}_i\}_{i=0}^{N-1}@f$ be the
26 * interpolated time series, and @f$T = t_{N-1} - t_0@f$ be the period of the
27 * time series. The time series is assumed to be ordered, i.e. @f$t_i <
28 * t_{i+1}@f$ for all @f$i@f$.
29 *
30 * ## Using this class
31 *
32 * The FourierInterpolation class is not meant to be constructed directly, but
33 * rather through one of the static methods @ref from_time_series, @ref
34 * from_time_series_file, @ref from_fourier_coefficients, or @ref
35 * from_fourier_coefficients_file.
36 *
37 * If data is only read from the master rank in a parallel setting, the method
38 * @ref distribute can be used to broadcast the data to all ranks.
39 *
40 * Once the object has been constructed, the interpolated value at a given time
41 * can be obtained through the method @ref value, and the interpolated value and
42 * its time derivative can be obtained through the method @ref
43 * value_and_derivative.
44 *
45 * ## Fourier series interpolation
46 *
47 * This gives rise to a periodic interpolation, with period @f$T@f$. Let @f$M@f$
48 * be the user-defined number of Fourier modes. The interpolated value at time
49 * @f$t@f$ is given by
50 * @f[ \begin{aligned}
51 * \tilde{\mathbf{v}}(t) &=
52 * \underbrace{\mathbf{q}_i + \mathbf{q}_s \tau(t)}_{\text{linear trend}} +
53 * \underbrace{
54 * \sum_{k=0}^{M-1} \left(
55 * \mathbf{c}_k^{\text{real}} \cos\left(\frac{2 \pi k \tau(t)}{T}\right) -
56 * \mathbf{c}_k^{\text{imag}} \sin\left(\frac{2 \pi k \tau(t)}{T}\right)
57 * \right)
58 * }_{\text{Fourier series}} \\
59 * \tau(t) &= (t - t_0)\;\text{mod}\;T
60 * \end{aligned} @f]
61 * The coefficients of the linear trend and Fourier series are determined from
62 * the input time series as follows:
63 * @f[ \begin{aligned}
64 * \mathbf{q}_i &= \mathbf{v}_0 \\
65 * \mathbf{q}_s &= \frac{\mathbf{v}_{N-1} - \mathbf{v}_0}{T} \\
66 * \mathbf{c}_0^{\text{real}} &=
67 * \frac{1}{2T} \sum_{i=0}^{N-2} (\hat{t}_{i+1} - \hat{t}_i)
68 * (\hat{\mathbf{v}}_{i+1} + \hat{\mathbf{v}}_i) \\
69 * \mathbf{c}_0^{\text{imag}} &= 0 \\
70 * \mathbf{c}_k^{\text{real}} &= \frac{T}{2 \pi^2 k^2}
71 * \sum_{i=0}^{N-2} \frac{\hat{\mathbf{v}}_{i+1} - \hat{\mathbf{v}}_i}
72 * {\hat{t}_{i+1} - \hat{t}_i} \left(
73 * \cos\left(\frac{2 \pi k \hat{t}_{i+1}}{T}\right) -
74 * \cos\left(\frac{2 \pi k \hat{t}_i}{T}\right)
75 * \right) \\
76 * \mathbf{c}_k^{\text{imag}} &= \frac{T}{2 \pi^2 k^2}
77 * -\sum_{i=0}^{N-2} \frac{\hat{\mathbf{v}}_{i+1} - \hat{\mathbf{v}}_i}
78 * {\hat{t}_{i+1} - \hat{t}_i} \left(
79 * \sin\left(\frac{2 \pi k \hat{t}_{i+1}}{T}\right) -
80 * \sin\left(\frac{2 \pi k \hat{t}_i}{T}\right)
81 * \right)
82 * \end{aligned} @f]
83 * The quantities @f$\hat{t}_i@f$ and @f$\hat{\mathbf{v}}_i@f$ are the time and
84 * value series after subtracting the linear trend:
85 * @f[ \begin{aligned}
86 * \hat{t}_i &= t_i - t_0 \\
87 * \hat{\mathbf{v}}_i &= \mathbf{v}_i - \mathbf{q}_i - \mathbf{q}_s \hat{t}_i
88 * \end{aligned} @f]
89 *
90 * ## Ramp interpolation
91 *
92 * The interpolated value is equal to @f$\mathbf{v}_0@f$ until time @f$t_0@f$,
93 * then it follows a linear ramp from @f$\mathbf{v}_0@f$ to
94 * @f$\mathbf{v}_{N-1}@f$ at @f$t_{N-1}@f$, and then it remains constant at
95 * @f$\mathbf{v}_{N-1}@f$ for all times greater than @f$t_{N-1}@f$. Notice in
96 * particular that this interpolation is not periodic.
97 *
98 * The interpolated value is given by
99 * @f[
100 * \tilde{\mathbf{v}}(t) = \begin{cases}
101 * \mathbf{v}_0 & t < t_0 \\
102 * \mathbf{v}_0 + \frac{\mathbf{v}_{N-1}
103 * - \mathbf{v}_0}{t_{N-1} - t_0} (t - t_0)
104 * & t_0 \leq t < t_{N-1} \\
105 * \mathbf{v}_{N-1} & t \geq t_{N-1}
106 * \end{cases}
107 * @f]
108 *
109 * This is equivalent to only taking the linear trend part of the Fourier series
110 * interpolation, and clamping the time to the interval @f$[t_0, t_{N-1}]@f$.
111 *
112 */
114public:
115 /**
116 * @brief Default constructor.
117 *
118 * This constructor is not meant to be used directly, and is only here to
119 * facilitate storing objects of this type in STL containers. The object
120 * constructed this way is not initialized and will not be usable until it is
121 * assigned to a valid FourierInterpolation instance. Use the static methods
122 * @ref from_time_series, @ref from_time_series_file, @ref
123 * from_fourier_coefficients, or @ref from_fourier_coefficients_file to
124 * construct a valid instance.
125 */
127
128 /**
129 * @brief Construct a FourierInterpolation from a time series.
130 *
131 * @param[in] n_fourier_coefficients The number @f$M@f$ of Fourier modes to
132 * use in the interpolation.
133 * @param[in] times The time points @f$t_i@f$ of the time series. It must be a
134 * vector in strictly ascending order, and an exception will be thrown
135 * otherwise.
136 * @param[in] values The values @f$\mathbf{v}_i@f$ of the time series. It must
137 * be a 2D array with one row for each component and one column for each
138 * time point. The number of columns must match the size of @p times, and an
139 * exception will be thrown otherwise.
140 * @param[in] use_ramp Whether to use a ramp function for the interpolation.
141 * See the general class documentation for the precise meaning of this
142 * choice.
143 */
145 from_time_series(const unsigned int n_fourier_coefficients,
146 const Vector<double> &times, const Array<double> &values,
147 bool use_ramp);
148
149 /**
150 * @brief Read a time series from file and return the corresponding instance
151 * of FourierInterpolation.
152 *
153 * The input file is expected to have the following format:
154 * ```
155 * <number of time points> <number of Fourier coefficients>
156 * <time 0> <value 0, component 0> ... <value 0, component d-1>
157 * <time 1> <value 1, component 0> ... <value 1, component d-1>
158 * ...
159 * <time N-1> <value N-1, component 0> ... <value N-1, component d-1>
160 * ```
161 *
162 * @param[in] file_name The name of the file to read. An exception will be
163 * thrown if the file cannot be opened.
164 * @param[in] n_components The number of components of the data to be
165 * interpolated. If any row in the file does not have this number of entries
166 * (plus one entry for the time), an exception will be thrown.
167 * @param[in] use_ramp Whether to use a ramp function for the interpolation.
168 * See the general class documentation for the precise meaning of this
169 * choice. If this parameter is set to true, then the number of Fourier
170 * coefficients in the file will be ignored, and the time points except the
171 * first and last will have no effect.
172 */
174 from_time_series_file(const std::string &file_name, unsigned int n_components,
175 bool use_ramp);
176
177 /**
178 * @brief Construct a FourierInterpolation from Fourier coefficients.
179 *
180 * This method bypasses the computation of the Fourier coefficients from a
181 * time series, and construct the instance directly from precomputed
182 * coefficients.
183 *
184 * This construction method always sets @ref use_ramp to false, and therefore
185 * always constructs a periodic interpolation.
186 *
187 * @param[in] linear_trend_initial_values The initial values
188 * @f$\mathbf{q}_i@f$ of the linear trend part of the interpolation, with
189 * one entry for each component.
190 * @param[in] linear_trend_slopes The slopes @f$\mathbf{q}_s@f$ of the linear
191 * trend part of the interpolation, with one entry for each component.
192 * @param[in] fourier_coefficients_real The real part
193 * @f$\mathbf{c}_k^\text{real}@f$ of the Fourier interpolation. This is a 2D
194 * array, for which the first index selects the component and the second
195 * index selects the Fourier mode.
196 * @param[in] fourier_coefficients_imaginary The imaginary part
197 * @f$\mathbf{c}_k^\text{imag}@f$ of the Fourier interpolation. This is a 2D
198 * array, for which the first index selects the component and the second
199 * index selects the Fourier mode.
200 * @param[in] initial_time The initial time @f$t_0@f$ of the interpolation.
201 * @param[in] period The period @f$T@f$ of the interpolation.
202 */
204 from_fourier_coefficients(const Vector<double> &linear_trend_initial_values,
205 const Vector<double> &linear_trend_slopes,
206 const Array<double> &fourier_coefficients_real,
207 const Array<double> &fourier_coefficients_imaginary,
208 double initial_time, double period);
209
210 /**
211 * @brief Read Fourier coefficients from file and return the corresponding
212 * FourierInterpolation instance.
213 *
214 * The input file is expected to have the following format:
215 * ```
216 * <initial time> <period>
217 * <q_i, component 0> <q_s, component 0>
218 * ...
219 * <q_i, component d-1> <q_s, component d-1>
220 * <number of Fourier coefficients>
221 * <c_r[0][0]> <c_r[1][0]> ... <c_r[d-1][0]> <c_i[0][0]> <c_i[1][0]> ... <c_i[d-1][0]>
222 * <c_r[0][1]> <c_r[1][1]> ... <c_r[d-1][1]> <c_i[0][1]> <c_i[1][1]> ... <c_i[d-1][1]>
223 * ...
224 * <c_r[0][M-1]> <c_r[1][M-1]> ... <c_r[d-1][M-1]> <c_i[0][M-1]> <c_i[1][M-1]> ... <c_i[d-1][M-1]>
225 * ```
226 * where <kbd>c_r</kbd> and <kbd>c_i</kbd> are the real and imaginary parts of
227 * the Fourier coefficients.
228 *
229 * @param[in] file_name The name of the file to read. An exception will be
230 * thrown if the file cannot be opened.
231 * @param[in] n_components The number of components of the data to be
232 * interpolated. This is used to check the correctness of the file format,
233 * and an exception will be thrown if the check fails.
234 */
236 from_fourier_coefficients_file(const std::string &file_name,
237 unsigned int n_components);
238
239 /**
240 * @brief Distribute the data to all parallel processes.
241 *
242 * Broadcasts the data contained in this object from the master rank to all
243 * other ranks. This is necessary if only the master rank initializes the
244 * object (e.g. by reading its data from a file), but all ranks need to use
245 * it.
246 *
247 * @param[in] cm_mod The communication module to use for the broadcast.
248 * @param[in] cm The communicator to use for the broadcast.
249 */
250 void distribute(const CmMod &cm_mod, const cmType &cm);
251
252 /**
253 * @brief Return the interpolated value at a given time.
254 *
255 * Refer to the general class documentation for details on how the returned
256 * value is computed.
257 *
258 * @param[in] time The time at which to evaluate the interpolation.
259 */
260 Vector<double> value(double time) const;
261
262 /**
263 * @brief Return the interpolated value and its time derivative at a given
264 * time.
265 *
266 * Refer to the general class documentation for details on how the returned
267 * value is computed.
268 *
269 * @param[in] time The time at which to evaluate the interpolation.
270 *
271 * @return A pair in which the first element is the interpolated value and the
272 * second element is its time derivative.
273 */
274 std::pair<Vector<double>, Vector<double>>
275 value_and_derivative(double time) const;
276
277 /// @name Data member access.
278 /// @{
279
280 /// @brief Return whether this object has been initialized.
281 bool defined() const;
282
283 /// @brief Get the dimension of the data interpolated by this object.
284 unsigned int get_n_components() const;
285
286 /// @brief Get the number of Fourier coefficients used by this object.
287 unsigned int get_n_fourier_coefficients() const;
288
289 /// @brief Get the initial value of the linear trend part for one component.
290 double get_linear_trend_initial_value(unsigned int component) const;
291
292 /// @brief Get the slope of the linear trend part for one component.
293 double get_linear_trend_slope(unsigned int component) const;
294
295 /// @brief Get the real part of the Fourier coefficients for one component.
296 double get_coefficient_real(unsigned int component,
297 unsigned int frequency) const;
298
299 /// @brief Get the imaginary part of the Fourier coefficients for one
300 /// component.
301 double get_coefficient_imaginary(unsigned int component,
302 unsigned int frequency) const;
303
304 /// @}
305
306private:
307 /** @brief Internal evaluation function.
308 *
309 * Uses the inverse Fourier transform to evaluate the value, and optionally
310 * the derivative, of the interpolated data. This function is not meant to be
311 * used directly, but only as a backend to @ref value and @ref
312 * value_and_derivative.
313 *
314 * The vectors value and derivative are assumed to be of size @ref d. This is
315 * not checked by this function.
316 *
317 * @throws svmp::FE::NotInitializedException if this FourierInterpolation
318 * instance has not been initialized (i.e. if @ref defined returns false).
319 *
320 * @param[in] time The time at which to evaluate the interpolation.
321 * @param[in] evaluate_derivative Whether to also evaluate the time
322 * derivative of the interpolation.
323 * @param[out] value The interpolated value at the given time.
324 * @param[out] derivative The time derivative of the interpolated value at
325 * the given time. If evaluated_derivative is false, this will not be
326 * modified or accessed.
327 */
328 void evaluate_internal(double time, bool evaluate_derivative,
329 Vector<double> &value,
330 Vector<double> &derivative) const;
331
332 /**
333 * @brief Toggle whether this is a ramp function or not.
334 *
335 * See the general class documentation for details on the difference between
336 * ramp and periodic interpolation.
337 */
338 bool use_ramp = false;
339
340 /**
341 * @brief Number of Fourier coefficients.
342 */
343 unsigned int n_fourier_coefficients = 0;
344
345 /**
346 * @brief Number of components of the interpolated data.
347 */
348 unsigned int n_components = 0;
349
350 /**
351 * @brief Initial value for the linear trend.
352 *
353 * This is a vector with n_components entries, where each entry is the initial
354 * value (i.e. the value for time = ti) of the linear trend part of the
355 * interpolated data for that component.
356 */
357 Vector<double> linear_trend_initial_values;
358
359 /**
360 * @brief Time derivative for the linear trend.
361 *
362 * This is a vector with n_components entries, where each entry is the slope
363 * of the linear trend part of the interpolated data for that component.
364 */
365 Vector<double> linear_trend_slopes;
366
367 /**
368 * @brief Period of the interpolated data.
369 *
370 * This is disregarded if use_ramp is true. See the general class
371 * documentation for details on the difference between ramp and periodic
372 * interpolation.
373 */
374 double period = 0.0;
375
376 /**
377 * @brief Initial time.
378 */
379 double initial_time = 0.0;
380
381 /**
382 * @brief Real part of the Fourier series coefficients.
383 *
384 * This is a 2D array with n_components rows and n_fourier_coefficients
385 * columns.
386 */
387 Array<double> fourier_coefficients_real;
388
389 /**
390 * @brief Imaginary part of the Fourier series coefficients.
391 *
392 * This is a 2D array with n_components rows and n_fourier_coefficients
393 * columns.
394 */
395 Array<double> fourier_coefficients_imaginary;
396};
397
398#endif
Exception hierarchy for error handling in the FE library.
The CmMod class duplicates the data structures in the Fortran CMMOD module defined in COMU....
Definition CmMod.h:36
Fourier interpolation of time dependent data.
Definition FourierInterpolation.h:113
std::pair< Vector< double >, Vector< double > > value_and_derivative(double time) const
Return the interpolated value and its time derivative at a given time.
Definition FourierInterpolation.cpp:446
void distribute(const CmMod &cm_mod, const cmType &cm)
Distribute the data to all parallel processes.
Definition FourierInterpolation.cpp:405
static FourierInterpolation from_time_series_file(const std::string &file_name, unsigned int n_components, bool use_ramp)
Read a time series from file and return the corresponding instance of FourierInterpolation.
Definition FourierInterpolation.cpp:176
double get_coefficient_real(unsigned int component, unsigned int frequency) const
Get the real part of the Fourier coefficients for one component.
Definition FourierInterpolation.cpp:501
FourierInterpolation()=default
Default constructor.
unsigned int get_n_fourier_coefficients() const
Get the number of Fourier coefficients used by this object.
Definition FourierInterpolation.cpp:463
double get_coefficient_imaginary(unsigned int component, unsigned int frequency) const
Get the imaginary part of the Fourier coefficients for one component.
Definition FourierInterpolation.cpp:523
static FourierInterpolation from_time_series(const unsigned int n_fourier_coefficients, const Vector< double > &times, const Array< double > &values, bool use_ramp)
Construct a FourierInterpolation from a time series.
Definition FourierInterpolation.cpp:67
double get_linear_trend_slope(unsigned int component) const
Get the slope of the linear trend part for one component.
Definition FourierInterpolation.cpp:485
static FourierInterpolation from_fourier_coefficients_file(const std::string &file_name, unsigned int n_components)
Read Fourier coefficients from file and return the corresponding FourierInterpolation instance.
Definition FourierInterpolation.cpp:302
bool defined() const
Return whether this object has been initialized.
Definition FourierInterpolation.cpp:455
Vector< double > value(double time) const
Return the interpolated value at a given time.
Definition FourierInterpolation.cpp:436
static FourierInterpolation from_fourier_coefficients(const Vector< double > &linear_trend_initial_values, const Vector< double > &linear_trend_slopes, const Array< double > &fourier_coefficients_real, const Array< double > &fourier_coefficients_imaginary, double initial_time, double period)
Construct a FourierInterpolation from Fourier coefficients.
Definition FourierInterpolation.cpp:245
double get_linear_trend_initial_value(unsigned int component) const
Get the initial value of the linear trend part for one component.
Definition FourierInterpolation.cpp:467
unsigned int get_n_components() const
Get the dimension of the data interpolated by this object.
Definition FourierInterpolation.cpp:459
The Vector template class is used for storing int and double data.
Definition Vector.h:26
The cmType class stores data and defines methods used for mpi communication.
Definition CmMod.h:56