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