1 //                                               -*- C++ -*-
2 /**
3  *  @brief P1 Lagrange piecewise linear function.
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/P1LagrangeEvaluation.hxx"
22 #include "openturns/OSS.hxx"
23 #include "openturns/PersistentObjectFactory.hxx"
24 #include "openturns/Description.hxx"
25 #include "openturns/Exception.hxx"
26 #include "openturns/Os.hxx"
27 
28 BEGIN_NAMESPACE_OPENTURNS
29 
30 CLASSNAMEINIT(P1LagrangeEvaluation)
31 
32 static const Factory<P1LagrangeEvaluation> Factory_P1LagrangeEvaluation;
33 
34 
35 /* Default constructor */
P1LagrangeEvaluation()36 P1LagrangeEvaluation::P1LagrangeEvaluation()
37   : EvaluationImplementation()
38   , nearestNeighbour_()
39   , enclosingSimplex_()
40 {
41   // Nothing to do
42 }
43 
44 /* Parameters constructor */
P1LagrangeEvaluation(const Field & field)45 P1LagrangeEvaluation::P1LagrangeEvaluation(const Field & field)
46   : EvaluationImplementation()
47   , nearestNeighbour_(field.getMesh().getVertices())
48   , enclosingSimplex_(field.getMesh().getVertices(), field.getMesh().getSimplices())
49 {
50   setField(field);
51 }
52 
53 
54 /* Parameters constructor */
P1LagrangeEvaluation(const ProcessSample & sample)55 P1LagrangeEvaluation::P1LagrangeEvaluation(const ProcessSample & sample)
56   : EvaluationImplementation()
57   , nearestNeighbour_(sample.getMesh().getVertices())
58   , enclosingSimplex_(sample.getMesh().getVertices(), sample.getMesh().getSimplices())
59 {
60   const Mesh mesh(sample.getMesh());
61   const UnsignedInteger length = mesh.getVerticesNumber();
62   if (!(length > 0)) throw InvalidArgumentException(HERE) << "Error: expected a non-empty ProcessSample";
63   const UnsignedInteger size = sample.getSize();
64   const UnsignedInteger dimension = sample.getDimension();
65   // Copy values in expected order
66   values_ = Sample(length, size * dimension);
67   for(UnsignedInteger i = 0; i < size; ++i)
68   {
69     const Sample dataI(sample[i]);
70     for(UnsignedInteger l = 0; l < length; ++l)
71       std::copy(&dataI(l, 0), &dataI(l, 0) + dimension, &values_(l, i * dimension));
72   }
73   // To check for pending vertices
74   setMesh(mesh);
75 }
76 
77 
78 /* Virtual constructor */
clone() const79 P1LagrangeEvaluation * P1LagrangeEvaluation::clone() const
80 {
81   return new P1LagrangeEvaluation(*this);
82 }
83 
84 
85 /* Comparison operator */
operator ==(const P1LagrangeEvaluation & other) const86 Bool P1LagrangeEvaluation::operator ==(const P1LagrangeEvaluation & other) const
87 {
88   if (this == &other) return true;
89   return (mesh_ == other.mesh_) && (values_ == other.values_);
90 }
91 
92 
93 /* String converter */
__repr__() const94 String P1LagrangeEvaluation::__repr__() const
95 {
96   OSS oss(true);
97   oss << "class=" << P1LagrangeEvaluation::GetClassName()
98       << " name=" << getName()
99       << " mesh=" << mesh_
100       << " values=" << values_;
101   return oss;
102 }
103 
__str__(const String & offset) const104 String P1LagrangeEvaluation::__str__( const String & offset ) const
105 {
106   OSS oss(false);
107   oss << P1LagrangeEvaluation::GetClassName() << Os::GetEndOfLine()
108       << offset << "field :" << Os::GetEndOfLine()
109       << offset << getField().__str__(offset);
110   return oss;
111 }
112 
113 /* Field accessor */
setField(const Field & field)114 void P1LagrangeEvaluation::setField(const Field & field)
115 {
116   values_ = field.getValues();
117   // To check for pending vertices
118   setMesh(field.getMesh());
119 }
120 
getField() const121 Field P1LagrangeEvaluation::getField() const
122 {
123   return Field(mesh_, values_);
124 }
125 
126 /* Mesh accessor */
setMesh(const Mesh & mesh)127 void P1LagrangeEvaluation::setMesh(const Mesh & mesh)
128 {
129   const UnsignedInteger nrVertices = mesh.getVerticesNumber();
130   if (nrVertices != values_.getSize()) throw InvalidArgumentException(HERE) << "Error: expected a mesh with =" << values_.getSize() << " vertices, got " << nrVertices << " vertices";
131   mesh_ = mesh;
132   nearestNeighbour_.setSample(mesh_.getVertices());
133   enclosingSimplex_.setVerticesAndSimplices(mesh_.getVertices(), mesh_.getSimplices());
134 
135   // Check for pending vertices
136   Interval::BoolCollection seenVertices(nrVertices);
137   const UnsignedInteger nrSimplices = mesh_.getSimplicesNumber();
138   const IndicesCollection simplices(mesh_.getSimplices());
139   // Iterate over simplices
140   for(UnsignedInteger i = 0; i < nrSimplices; ++i)
141   {
142     // Iterate over vertices
143     for(IndicesCollection::const_iterator vertexIt = simplices.cbegin_at(i), vertexGuard = simplices.cend_at(i); vertexIt != vertexGuard; ++vertexIt)
144     {
145       const UnsignedInteger vertexIndex = (*vertexIt);
146       if (!(vertexIndex < nrVertices)) throw InvalidArgumentException(HERE) << "Error: found a vertex index of " << vertexIndex << " for a total vertex number of " << nrVertices;
147       seenVertices[vertexIndex] = 1;
148     }
149   }
150   Indices pendingVertices;
151   for (UnsignedInteger i = 0; i < nrVertices; ++i)
152     if (seenVertices[i] == 0)
153       pendingVertices.add(i);
154   if (pendingVertices.getSize() != 0)
155   {
156     LOGWARN(OSS() << "There are " << pendingVertices.getSize() << " pending vertices. Check the simplices of the mesh");
157     LOGDEBUG(OSS() << "The pending vertices indices are " << pendingVertices);
158   }
159 }
160 
getMesh() const161 Mesh P1LagrangeEvaluation::getMesh() const
162 {
163   return mesh_;
164 }
165 
166 /* Values accessor */
setValues(const Sample & values)167 void P1LagrangeEvaluation::setValues(const Sample & values)
168 {
169   if (values.getSize() != mesh_.getVerticesNumber()) throw InvalidArgumentException(HERE) << "Error: expected a sample of size=" << mesh_.getVerticesNumber() << ", got size=" << values.getSize();
170   values_ = values;
171 }
172 
getValues() const173 Sample P1LagrangeEvaluation::getValues() const
174 {
175   return values_;
176 }
177 
178 /* Nearest neighbour algorithm accessor */
setNearestNeighbourAlgorithm(const NearestNeighbourAlgorithm & nearestNeighbour)179 void P1LagrangeEvaluation::setNearestNeighbourAlgorithm(const NearestNeighbourAlgorithm & nearestNeighbour)
180 {
181   NearestNeighbourAlgorithm emptyClone(nearestNeighbour.getImplementation()->emptyClone());
182   nearestNeighbour_.swap(emptyClone);
183   nearestNeighbour_.setSample(mesh_.getVertices());
184 }
185 
getNearestNeighbourAlgorithm() const186 NearestNeighbourAlgorithm P1LagrangeEvaluation::getNearestNeighbourAlgorithm() const
187 {
188   return nearestNeighbour_;
189 }
190 
191 /* EnclosingSimplexAlgorithm to speed-up point location */
setEnclosingSimplexAlgorithm(const EnclosingSimplexAlgorithm & enclosingSimplex)192 void P1LagrangeEvaluation::setEnclosingSimplexAlgorithm(const EnclosingSimplexAlgorithm & enclosingSimplex)
193 {
194   EnclosingSimplexAlgorithm emptyClone(enclosingSimplex.getImplementation()->emptyClone());
195   enclosingSimplex_.swap(emptyClone);
196   enclosingSimplex_.setVerticesAndSimplices(mesh_.getVertices(), mesh_.getSimplices());
197 }
198 
getEnclosingSimplexAlgorithm() const199 EnclosingSimplexAlgorithm P1LagrangeEvaluation::getEnclosingSimplexAlgorithm() const
200 {
201   return enclosingSimplex_;
202 }
203 
204 /* Here is the interface that all derived class must implement */
205 
206 /* Operator () */
operator ()(const Point & inP) const207 Point P1LagrangeEvaluation::operator()( const Point & inP ) const
208 {
209   const UnsignedInteger inputDimension = getInputDimension();
210   if (inP.getDimension() != inputDimension) throw InvalidArgumentException(HERE) << "Error: the given point has an invalid dimension. Expect a dimension " << inputDimension << ", got " << inP.getDimension();
211   Point result(evaluate(inP));
212   callsNumber_.increment();
213   return result;
214 }
215 
216 class P1LagrangeEvaluationComputeSamplePolicy
217 {
218   const Sample & input_;
219   Sample & output_;
220   const P1LagrangeEvaluation & lagrange_;
221 
222 public:
P1LagrangeEvaluationComputeSamplePolicy(const Sample & input,Sample & output,const P1LagrangeEvaluation & lagrange)223   P1LagrangeEvaluationComputeSamplePolicy(const Sample & input,
224                                           Sample & output,
225                                           const P1LagrangeEvaluation & lagrange)
226     : input_(input)
227     , output_(output)
228     , lagrange_(lagrange)
229   {
230     // Nothing to do
231   }
232 
operator ()(const TBB::BlockedRange<UnsignedInteger> & r) const233   inline void operator()( const TBB::BlockedRange<UnsignedInteger> & r ) const
234   {
235     for (UnsignedInteger i = r.begin(); i != r.end(); ++i)
236       output_[i] = lagrange_.evaluate(input_[i]);
237   } // operator ()
238 };  // class P1LagrangeEvaluationComputeSamplePolicy
239 
240 /* Evaluation method */
evaluate(const Point & inP) const241 Point P1LagrangeEvaluation::evaluate( const Point & inP ) const
242 {
243   const UnsignedInteger simplexIndex = enclosingSimplex_.query(inP);
244   if (simplexIndex >= mesh_.getSimplicesNumber())
245   {
246     // No enclosing simplex.   Take value at the nearest point
247     return values_[nearestNeighbour_.query(inP)];
248   }
249 
250   // Compute barycentric coordinates
251   Point coordinates(0);
252   if (!mesh_.checkPointInSimplexWithCoordinates(inP, simplexIndex, coordinates))
253   {
254     // Hmmm, should not happen
255     return values_[nearestNeighbour_.query(inP)];
256   }
257   // Here, perform the P1 interpolation
258   const Indices simplex(mesh_.getSimplex(simplexIndex));
259   Point result(values_[simplex[0]] * coordinates[0]);
260   for (UnsignedInteger j = 1; j < simplex.getSize(); ++j)
261     result += values_[simplex[j]] * coordinates[j];
262   return result;
263 }
264 
operator ()(const Sample & inS) const265 Sample P1LagrangeEvaluation::operator()( const Sample & inS ) const
266 {
267   const UnsignedInteger inputDimension = getInputDimension();
268   if (inS.getDimension() != inputDimension) throw InvalidArgumentException(HERE) << "Error: the given sample has an invalid dimension. Expect a dimension " << inputDimension << ", got " << inS.getDimension();
269   const UnsignedInteger size = inS.getSize();
270   Sample result(size, values_.getDimension());
271   if (size == 0) return result;
272   if (inS == mesh_.getVertices()) result = values_;
273   else
274   {
275     const P1LagrangeEvaluationComputeSamplePolicy policy( inS, result, *this );
276     TBB::ParallelFor( 0, size, policy );
277   } // The input sample is different from
278   callsNumber_.fetchAndAdd(size);
279   return result;
280 }
281 
282 /* Accessor for input point dimension */
getInputDimension() const283 UnsignedInteger P1LagrangeEvaluation::getInputDimension() const
284 {
285   return mesh_.getDimension();
286 }
287 
288 
289 /* Accessor for output point dimension */
getOutputDimension() const290 UnsignedInteger P1LagrangeEvaluation::getOutputDimension() const
291 {
292   return values_.getDimension();
293 }
294 
295 
296 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const297 void P1LagrangeEvaluation::save(Advocate & adv) const
298 {
299   EvaluationImplementation::save(adv);
300   adv.saveAttribute("mesh_", mesh_);
301   adv.saveAttribute("values_", values_);
302   adv.saveAttribute("nearestNeighbour_", nearestNeighbour_);
303   adv.saveAttribute("enclosingSimplex_", enclosingSimplex_);
304 }
305 
306 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)307 void P1LagrangeEvaluation::load(Advocate & adv)
308 {
309   EvaluationImplementation::load(adv);
310   adv.loadAttribute("mesh_", mesh_);
311   adv.loadAttribute("values_", values_);
312   adv.loadAttribute("nearestNeighbour_", nearestNeighbour_);
313   adv.loadAttribute("enclosingSimplex_", enclosingSimplex_);
314 }
315 
316 END_NAMESPACE_OPENTURNS
317