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