1 // -*- C++ -*-
2 /**
3 * @brief Top-level class for all spectral model factories
4 *
5 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6 *
7 * This library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21 #include "openturns/StationaryCovarianceModelFactory.hxx"
22 #include "openturns/PersistentObjectFactory.hxx"
23 #include "openturns/Exception.hxx"
24 #include "openturns/Collection.hxx"
25 #include "openturns/Point.hxx"
26 #include "openturns/PersistentObjectFactory.hxx"
27
28 BEGIN_NAMESPACE_OPENTURNS
29
30 CLASSNAMEINIT(StationaryCovarianceModelFactory)
31 static const Factory<StationaryCovarianceModelFactory> Factory_StationaryCovarianceModelFactory;
32
33 static const Factory<PersistentCollection<Complex> > Factory_PersistentCollection_Complex;
34
35 typedef Collection<SquareMatrix> SquareMatrixCollection;
36
37 /* Default constructor */
StationaryCovarianceModelFactory(const WelchFactory & factory)38 StationaryCovarianceModelFactory::StationaryCovarianceModelFactory(const WelchFactory & factory)
39 : CovarianceModelFactoryImplementation()
40 {
41 setSpectralModelFactory(factory);
42 }
43
44 /* Virtual constructor */
clone() const45 StationaryCovarianceModelFactory * StationaryCovarianceModelFactory::clone() const
46 {
47 return new StationaryCovarianceModelFactory(*this);
48 }
49
50 /* String converter */
__repr__() const51 String StationaryCovarianceModelFactory::__repr__() const
52 {
53 OSS oss;
54 oss << "class=" << StationaryCovarianceModelFactory::GetClassName();
55 return oss;
56 }
57
58 /* SpectralModelFactory accessors */
getSpectralModelFactory() const59 WelchFactory StationaryCovarianceModelFactory::getSpectralModelFactory() const
60 {
61 return spectralFactory_;
62 }
63
setSpectralModelFactory(const WelchFactory & factory)64 void StationaryCovarianceModelFactory::setSpectralModelFactory(const WelchFactory & factory)
65 {
66 spectralFactory_ = factory;
67 }
68
69 /* String converter */
__str__(const String &) const70 String StationaryCovarianceModelFactory::__str__(const String & ) const
71 {
72 return __repr__();
73 }
74
75 /* Build a covariance model based on a user defined spectral model */
buildAsUserDefinedStationaryCovarianceModel(const UserDefinedSpectralModel & mySpectralModel) const76 UserDefinedStationaryCovarianceModel StationaryCovarianceModelFactory::buildAsUserDefinedStationaryCovarianceModel(const UserDefinedSpectralModel & mySpectralModel) const
77 {
78 return buildAsUserDefinedStationaryCovarianceModel(mySpectralModel, mySpectralModel.getFrequencyGrid());
79 }
80
81 /* Build a covariance model based on a spectral model and a frequency grid */
buildAsUserDefinedStationaryCovarianceModel(const SpectralModel & mySpectralModel,const RegularGrid & frequencyGrid) const82 UserDefinedStationaryCovarianceModel StationaryCovarianceModelFactory::buildAsUserDefinedStationaryCovarianceModel(const SpectralModel & mySpectralModel,
83 const RegularGrid & frequencyGrid) const
84 {
85 // We get the dimension of the model
86 const UnsignedInteger dimension = mySpectralModel.getOutputDimension();
87 // From the spectral model, we want to evaluate the autocovariance function
88 const UnsignedInteger N = frequencyGrid.getN();
89 const Scalar df = frequencyGrid.getStep();
90 const Scalar maximalFrequency = frequencyGrid.getValue(N - 1) + 0.5 * df;
91 // We use the integrale of the spectral density through the frequencies, i.e.
92 // \int_{\Omega_c} S(f) exp(i2\pi f h) df ==> the algorithm works on frequencies
93 // The used algorithm imposes both positive and negative ones
94 // Some transformations are needed
95 const UnsignedInteger size = 2 * N;
96 // Care!!! Check the expression of dt - The time grid should corresponds to the frequency grid
97 const Scalar dt = 0.5 / maximalFrequency;
98
99 // As we need Fourier transformations, we need to have a structure which enables to stock elements
100 // For d = dimension, we need dimension * 0.5 * (dimension + 1) transformations
101 const UnsignedInteger numberOfFFT = dimension * (dimension + 1) / 2;
102 ComplexMatrix matrix(size, numberOfFFT);
103 for (UnsignedInteger k = 0; k < size; ++k)
104 {
105 UnsignedInteger columnIndex = 0;
106 // Computation is done for the current frequency value
107 // The frequency is computed thanks to the formula (2k +1 -size) *0.5 * df with k=0,.,..,size-1
108 const Scalar currentFrequency = (2.0 * k + 1 - size) * 0.5 * df;
109 const HermitianMatrix spectralDensity(mySpectralModel(currentFrequency));
110 for (UnsignedInteger i = 0; i < dimension; ++i)
111 {
112 for (UnsignedInteger j = 0; j <= i; ++j)
113 {
114 const Scalar theta = (size - 1.0) * k * M_PI / size;
115 const Complex alpha(cos(theta), -sin(theta));
116 const Complex spectralValue(spectralDensity(i, j));
117 const Complex phi_k(spectralValue * alpha);
118 matrix(k, columnIndex) = phi_k;
119 columnIndex += 1;
120 }
121 }
122 }
123 // At this level, we get elements to launch the fft
124 // We first compute a temporal factor delta(m) = df * N * exp(-\pi * i * (2m + 1 - N) * (N-1) / 2N)
125 // The formula of this last one may be found in the UseCaseGuide
126 Collection<Complex> delta(size);
127 for (UnsignedInteger m = 0; m < size; ++m)
128 {
129 const Scalar theta = (size - 1.0) / size * 0.5 * M_PI * (2.0 * m + 1.0 - size);
130 const Complex alpha(cos(theta), -1.0 * sin(theta));
131 delta[m] = df * size * alpha;
132 }
133
134 // We use the same FFT as the spectral factory
135 FFT fftAlgorithm(spectralFactory_.getFFTAlgorithm());
136 for (UnsignedInteger columnIndex = 0; columnIndex < numberOfFFT; ++columnIndex)
137 {
138 // FFT applications
139 const Collection<Complex> marginal(fftAlgorithm.inverseTransform(*matrix.getImplementation(), columnIndex * size, size));
140 // We save result in the same matrix
141 for (UnsignedInteger rowIndex = 0; rowIndex < size; ++rowIndex)
142 {
143 matrix(rowIndex, columnIndex) = marginal[rowIndex] * delta[rowIndex];
144 }
145 }
146
147 // We rewrite the elements in the adequate structure
148 RegularGrid timeGrid(0.5 * dt, dt, N);
149 SquareMatrixCollection collection(N);
150 for (UnsignedInteger currentIndex = 0; currentIndex < N; ++currentIndex)
151 {
152 const UnsignedInteger index = currentIndex + N;
153 CovarianceMatrix covariance(dimension);
154 UnsignedInteger columnIndex = 0;
155 for (UnsignedInteger i = 0; i < dimension; ++i)
156 {
157 for (UnsignedInteger j = 0; j <= i; ++j)
158 {
159 covariance(i, j) = std::real(matrix(index, columnIndex));
160 ++columnIndex;
161 }
162 }
163 collection[currentIndex] = covariance;
164 }
165 return UserDefinedStationaryCovarianceModel(timeGrid, collection);
166 }
167
168 /* Build a covariance model based on a process sample */
build(const ProcessSample & sample) const169 CovarianceModel StationaryCovarianceModelFactory::build(const ProcessSample & sample) const
170 {
171 return buildAsUserDefinedStationaryCovarianceModel(sample).clone();
172 }
173
174 /* Build a user defined covariance model based on a process sample */
buildAsUserDefinedStationaryCovarianceModel(const ProcessSample & sample) const175 UserDefinedStationaryCovarianceModel StationaryCovarianceModelFactory::buildAsUserDefinedStationaryCovarianceModel(const ProcessSample & sample) const
176 {
177 return buildAsUserDefinedStationaryCovarianceModel(spectralFactory_.buildAsUserDefinedSpectralModel(sample));
178 }
179
180 /* Build a covariance model based on a field */
build(const Field & timeSeries) const181 CovarianceModel StationaryCovarianceModelFactory::build(const Field & timeSeries) const
182 {
183 return buildAsUserDefinedStationaryCovarianceModel(timeSeries).clone();
184 }
185
186 /* Build a user defined covariance model based on a field */
buildAsUserDefinedStationaryCovarianceModel(const Field & timeSeries) const187 UserDefinedStationaryCovarianceModel StationaryCovarianceModelFactory::buildAsUserDefinedStationaryCovarianceModel(const Field & timeSeries) const
188 {
189 return buildAsUserDefinedStationaryCovarianceModel(spectralFactory_.buildAsUserDefinedSpectralModel(timeSeries));
190 }
191
192 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const193 void StationaryCovarianceModelFactory::save(Advocate & adv) const
194 {
195 CovarianceModelFactoryImplementation::save(adv);
196 adv.saveAttribute( "spectralFactory_", spectralFactory_);
197 }
198
199 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)200 void StationaryCovarianceModelFactory::load(Advocate & adv)
201 {
202 CovarianceModelFactoryImplementation::load(adv);
203 adv.loadAttribute( "spectralFactory_", spectralFactory_);
204 }
205
206 END_NAMESPACE_OPENTURNS
207