Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2026 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 :
20 : #ifndef LIBMESH_DENSE_VECTOR_H
21 : #define LIBMESH_DENSE_VECTOR_H
22 :
23 : // Local Includes
24 : #include "libmesh/libmesh_common.h"
25 : #include "libmesh/compare_types.h"
26 : #include "libmesh/dense_vector_base.h"
27 : #include "libmesh/int_range.h"
28 : #include "libmesh/parallel_algorithms.h"
29 : #include "libmesh/tensor_tools.h"
30 :
31 : #ifdef LIBMESH_HAVE_EIGEN
32 : #include "libmesh/ignore_warnings.h"
33 : #include <Eigen/Core>
34 : #include "libmesh/restore_warnings.h"
35 : #endif
36 :
37 : #ifdef LIBMESH_HAVE_METAPHYSICL
38 : #include "metaphysicl/raw_type.h"
39 : #endif
40 :
41 : // C++ includes
42 : #include <algorithm>
43 : #include <initializer_list>
44 : #include <vector>
45 :
46 : namespace libMesh
47 : {
48 :
49 : /**
50 : * Defines a dense vector for use in Finite Element-type computations.
51 : * This class is to basically compliment the \p DenseMatrix class. It
52 : * has additional capabilities over the \p std::vector that make it
53 : * useful for finite elements, particularly for systems of equations.
54 : * All overridden virtual functions are documented in dense_vector_base.h.
55 : *
56 : * \author Benjamin S. Kirk
57 : * \date 2003
58 : */
59 : template<typename T>
60 6492804 : class DenseVector : public DenseVectorBase<T>
61 : {
62 : public:
63 :
64 : /**
65 : * Constructor. Creates a dense vector of dimension \p n.
66 : */
67 : explicit
68 : DenseVector(const unsigned int n=0);
69 :
70 : /**
71 : * Constructor. Creates a dense vector of dimension \p n where all
72 : * entries have value \p val.
73 : */
74 : explicit
75 : DenseVector(const unsigned int n,
76 : const T & val);
77 :
78 : /**
79 : * Copy-constructor.
80 : */
81 : template <typename T2>
82 : DenseVector (const DenseVector<T2> & other_vector);
83 :
84 : /**
85 : * Copy-constructor, from a \p std::vector.
86 : */
87 : template <typename T2>
88 : DenseVector (const std::vector<T2> & other_vector);
89 :
90 : /**
91 : * Initializer list constructor.
92 : */
93 : template <typename T2>
94 : DenseVector (std::initializer_list<T2> init_list);
95 :
96 : /**
97 : * The 5 special functions can be defaulted for this class, as it
98 : * does not manage any memory itself.
99 : */
100 168768 : DenseVector (DenseVector &&) = default;
101 26255801 : DenseVector (const DenseVector &) = default;
102 28517345 : DenseVector & operator= (const DenseVector &) = default;
103 162720 : DenseVector & operator= (DenseVector &&) = default;
104 48339026 : virtual ~DenseVector() = default;
105 :
106 5425388 : virtual unsigned int size() const override final
107 : {
108 15642769 : return cast_int<unsigned int>(_val.size());
109 : }
110 :
111 19225 : virtual bool empty() const override final
112 19225 : { return _val.empty(); }
113 :
114 : virtual void zero() override final;
115 :
116 : /**
117 : * \returns Entry \p i of the vector as a const reference.
118 : */
119 : const T & operator() (const unsigned int i) const;
120 :
121 : /**
122 : * \returns Entry \p i of the vector as a writable reference.
123 : */
124 : T & operator() (const unsigned int i);
125 :
126 : /**
127 : * \returns Entry \p i of the vector as a const reference.
128 : */
129 0 : const T & operator[] (const unsigned int i) const { return (*this)(i); }
130 :
131 : /**
132 : * \returns Entry \p i of the vector as a writable reference.
133 : */
134 6 : T & operator[] (const unsigned int i) { return (*this)(i); }
135 :
136 0 : virtual T el(const unsigned int i) const override final
137 0 : { return (*this)(i); }
138 :
139 0 : virtual T & el(const unsigned int i) override final
140 0 : { return (*this)(i); }
141 :
142 : /**
143 : * Assignment operator.
144 : *
145 : * \returns A reference to *this.
146 : */
147 : template <typename T2>
148 : DenseVector<T> & operator = (const DenseVector<T2> & other_vector);
149 :
150 : /**
151 : * STL-like swap method
152 : */
153 : void swap(DenseVector<T> & other_vector);
154 :
155 : /**
156 : * Resize the vector. Sets all elements to 0.
157 : */
158 : void resize (const unsigned int n);
159 :
160 : /**
161 : * Append additional entries to (resizing, but unchanging) the
162 : * vector.
163 : */
164 : template <typename T2>
165 : void append (const DenseVector<T2> & other_vector);
166 :
167 : /**
168 : * Multiplies every element in the vector by \p factor.
169 : */
170 : void scale (const T factor);
171 :
172 : /**
173 : * Multiplies every element in the vector by \p factor.
174 : *
175 : * \returns A reference to *this.
176 : */
177 : DenseVector<T> & operator*= (const T factor);
178 :
179 : /**
180 : * Adds \p factor times \p vec to this vector.
181 : * This should only work if T += T2 * T3 is valid C++ and
182 : * if T2 is scalar. Return type is void
183 : *
184 : * \returns A reference to *this.
185 : */
186 : template <typename T2, typename T3>
187 : typename std::enable_if<
188 : ScalarTraits<T2>::value, void >::type
189 : add (const T2 factor,
190 : const DenseVector<T3> & vec);
191 :
192 : /**
193 : * \returns The dot product of *this with \p vec.
194 : *
195 : * In the complex-valued case, uses the complex conjugate of \p vec.
196 : */
197 : template <typename T2>
198 : typename CompareTypes<T, T2>::supertype dot (const DenseVector<T2> & vec) const;
199 :
200 : /**
201 : * \returns The dot product of *this with \p vec.
202 : *
203 : * In the complex-valued case, does not use the complex conjugate of
204 : * \p vec.
205 : */
206 : template <typename T2>
207 : typename CompareTypes<T, T2>::supertype indefinite_dot (const DenseVector<T2> & vec) const;
208 :
209 : /**
210 : * \returns \p true if \p vec is exactly equal to this vector, false otherwise.
211 : */
212 : template <typename T2>
213 : bool operator== (const DenseVector<T2> & vec) const;
214 :
215 : /**
216 : * \returns \p true if \p vec is not exactly equal to this vector, false otherwise.
217 : */
218 : template <typename T2>
219 : bool operator!= (const DenseVector<T2> & vec) const;
220 :
221 : /**
222 : * Adds \p vec to this vector.
223 : *
224 : * \returns A reference to *this.
225 : */
226 : template <typename T2>
227 : DenseVector<T> & operator+= (const DenseVector<T2> & vec);
228 :
229 : /**
230 : * Subtracts \p vec from this vector.
231 : *
232 : * \returns A reference to *this.
233 : */
234 : template <typename T2>
235 : DenseVector<T> & operator-= (const DenseVector<T2> & vec);
236 :
237 : /**
238 : * \returns The minimum entry of the vector, or the minimum real
239 : * part in the case of complex numbers.
240 : */
241 : Real min () const;
242 :
243 : /**
244 : * \returns The maximum entry of the vector, or the maximum real
245 : * part in the case of complex numbers.
246 : */
247 : Real max () const;
248 :
249 : /**
250 : * \returns The \f$l_1\f$-norm of the vector, i.e. the sum of the
251 : * absolute values of the entries.
252 : */
253 : Real l1_norm () const;
254 :
255 : /**
256 : * \returns The \f$l_2\f$-norm of the vector, i.e. the square root
257 : * of the sum of the squares of the entries.
258 : */
259 : Real l2_norm () const;
260 :
261 : /**
262 : * \returns The \f$l_\infty\f$-norm of the vector, i.e. the maximum
263 : * absolute value of the entries.
264 : */
265 : Real linfty_norm () const;
266 :
267 : /**
268 : * Puts the principal subvector of size \p sub_n (i.e. first sub_n
269 : * entries) into \p dest.
270 : */
271 : void get_principal_subvector (unsigned int sub_n, DenseVector<T> & dest) const;
272 :
273 : /**
274 : * \returns A reference to the underlying data storage vector.
275 : *
276 : * This should be used with caution (i.e. one should not change the
277 : * size of the vector, etc.) but is useful for interoperating with
278 : * low level BLAS routines which expect a simple array.
279 : */
280 77641308 : std::vector<T> & get_values() { return _val; }
281 :
282 : /**
283 : * \returns A constant reference to the underlying data storage vector.
284 : */
285 1164024 : const std::vector<T> & get_values() const { return _val; }
286 :
287 : /**
288 : * \returns An iterator pointing to the beginning of the vector
289 : */
290 0 : typename std::vector<T>::const_iterator begin() const { return _val.begin(); }
291 0 : typename std::vector<T>::iterator begin() { return _val.begin(); }
292 :
293 : /**
294 : * \returns An iterator pointing to the end of the vector
295 : */
296 0 : typename std::vector<T>::const_iterator end() const { return _val.end(); }
297 0 : typename std::vector<T>::iterator end() { return _val.end(); }
298 :
299 : /**
300 : * Returns true iff every entry is finite.
301 : */
302 : friend bool isfinite (const DenseVector<T> & var)
303 : {
304 : using std::isfinite;
305 : using libMesh::isfinite; // for T==complex
306 : for (const T & v : var._val)
307 : if (!isfinite(v))
308 : return false;
309 : return true;
310 : }
311 :
312 : /**
313 : * Returns true iff no entry is NaN and any entry is infinite.
314 : *
315 : * This is arguably inconsistent with our std::complex overload (and
316 : * the C99 Annex G recommendations for _Complex, and C++
317 : * std::complex arithmetic), which treats mixed (inf,NaN) pairs as
318 : * infinite, but this is probably safer for users.
319 : */
320 : friend bool isinf (const DenseVector<T> & var)
321 : {
322 : using std::isinf;
323 : using libMesh::isinf; // for T==complex
324 : using std::isnan;
325 : using libMesh::isnan;
326 : bool has_inf = false;
327 : for (const T & v : var._val)
328 : {
329 : // NaN anywhere makes us NaN, not inf
330 : if (isnan(v))
331 : return false;
332 : has_inf = has_inf || isinf(v);
333 : }
334 : return has_inf;
335 : }
336 :
337 : /**
338 : * Returns true iff any entry is NaN.
339 : *
340 : * This is arguably inconsistent with our std::complex overload (and
341 : * the C99 Annex G recommendations for _Complex, and C++
342 : * std::complex arithmetic), which treats mixed (inf,NaN) pairs as
343 : * infinite, but this is probably safer for users.
344 : */
345 : friend bool isnan (const DenseVector<T> & var)
346 : {
347 : using std::isnan;
348 : using libMesh::isnan; // for T==complex
349 : for (const T & v : var._val)
350 : if (isnan(v))
351 : return true;
352 : return false;
353 : }
354 :
355 : private:
356 :
357 : /**
358 : * The actual data values, stored as a 1D array.
359 : */
360 : std::vector<T> _val;
361 : };
362 :
363 :
364 :
365 : // ------------------------------------------------------------
366 : // DenseVector member functions
367 : template<typename T>
368 : inline
369 42035546 : DenseVector<T>::DenseVector(const unsigned int n) :
370 41154328 : _val (n, T{})
371 : {
372 37540202 : }
373 :
374 :
375 : template<typename T>
376 : inline
377 0 : DenseVector<T>::DenseVector(const unsigned int n,
378 : const T & val) :
379 0 : _val (n, val)
380 : {
381 0 : }
382 :
383 :
384 :
385 : template<typename T>
386 : template<typename T2>
387 : inline
388 : DenseVector<T>::DenseVector (const DenseVector<T2> & other_vector) :
389 : DenseVectorBase<T>()
390 : {
391 : const std::vector<T2> & other_vals = other_vector.get_values();
392 :
393 : _val.assign(other_vals.begin(), other_vals.end());
394 : }
395 :
396 :
397 :
398 : template<typename T>
399 : template<typename T2>
400 : inline
401 3065766 : DenseVector<T>::DenseVector (const std::vector<T2> & other_vector) :
402 3065766 : _val(other_vector)
403 : {
404 2734653 : }
405 :
406 :
407 : template<typename T>
408 : template <typename T2>
409 : inline
410 : DenseVector<T>::DenseVector (std::initializer_list<T2> init_list) :
411 : _val(init_list.begin(), init_list.end())
412 : {
413 : }
414 :
415 :
416 :
417 : template<typename T>
418 : template<typename T2>
419 : inline
420 : DenseVector<T> & DenseVector<T>::operator = (const DenseVector<T2> & other_vector)
421 : {
422 : const std::vector<T2> & other_vals = other_vector.get_values();
423 : _val.assign(other_vals.begin(), other_vals.end());
424 : return *this;
425 : }
426 :
427 :
428 :
429 : template<typename T>
430 : inline
431 3906688 : void DenseVector<T>::swap(DenseVector<T> & other_vector)
432 : {
433 3906688 : _val.swap(other_vector._val);
434 3906688 : }
435 :
436 :
437 :
438 : template<typename T>
439 : inline
440 146572291 : void DenseVector<T>::resize(const unsigned int n)
441 : {
442 160177316 : _val.resize(n);
443 :
444 11572420 : zero();
445 146572291 : }
446 :
447 :
448 :
449 : template<typename T>
450 : template<typename T2>
451 : inline
452 0 : void DenseVector<T>::append (const DenseVector<T2> & other_vector)
453 : {
454 0 : const std::vector<T2> & other_vals = other_vector.get_values();
455 :
456 0 : _val.reserve(this->size() + other_vals.size());
457 0 : _val.insert(_val.end(), other_vals.begin(), other_vals.end());
458 0 : }
459 :
460 :
461 :
462 : template<typename T>
463 : inline
464 1531137 : void DenseVector<T>::zero()
465 : {
466 1531137 : std::fill (_val.begin(),
467 : _val.end(),
468 : T{});
469 1531137 : }
470 :
471 :
472 :
473 : template<typename T>
474 : inline
475 3222565 : const T & DenseVector<T>::operator () (const unsigned int i) const
476 : {
477 3222565 : libmesh_assert_less (i, _val.size());
478 :
479 502577023 : return _val[i];
480 : }
481 :
482 :
483 :
484 : template<typename T>
485 : inline
486 2700 : T & DenseVector<T>::operator () (const unsigned int i)
487 : {
488 2700 : libmesh_assert_less (i, _val.size());
489 :
490 6118900425 : return _val[i];
491 : }
492 :
493 :
494 :
495 : template<typename T>
496 : inline
497 3997375 : void DenseVector<T>::scale (const T factor)
498 : {
499 442730123 : for (auto & v : _val)
500 382236164 : v *= factor;
501 3997375 : }
502 :
503 :
504 :
505 : template<typename T>
506 : inline
507 3997375 : DenseVector<T> & DenseVector<T>::operator*= (const T factor)
508 : {
509 3997375 : this->scale(factor);
510 3997375 : return *this;
511 : }
512 :
513 :
514 :
515 : template<typename T>
516 : template<typename T2, typename T3>
517 : inline
518 : typename std::enable_if<
519 : ScalarTraits<T2>::value, void >::type
520 24084850 : DenseVector<T>::add (const T2 factor,
521 : const DenseVector<T3> & vec)
522 : {
523 2355460 : libmesh_assert_equal_to (this->size(), vec.size());
524 :
525 2739688 : const int N = cast_int<int>(_val.size());
526 237529944 : for (int i=0; i<N; i++)
527 247786298 : (*this)(i) += static_cast<T>(factor)*vec(i);
528 24084850 : }
529 :
530 :
531 :
532 : template<typename T>
533 : template<typename T2>
534 : inline
535 13517648 : typename CompareTypes<T, T2>::supertype DenseVector<T>::dot (const DenseVector<T2> & vec) const
536 : {
537 14742872 : if (!_val.size())
538 61600 : return 0.;
539 :
540 1164024 : libmesh_assert_equal_to (this->size(), vec.size());
541 :
542 : #ifdef LIBMESH_HAVE_EIGEN
543 : // We reverse the order of the arguments to dot() here since
544 : // the convention in Eigen is to take the complex conjugate of the
545 : // *first* argument, while ours is to take the complex conjugate of
546 : // the second.
547 11679624 : return Eigen::Map<const typename Eigen::Matrix<T2, Eigen::Dynamic, 1>>(vec.get_values().data(), vec.size())
548 12843648 : .dot(Eigen::Map<const typename Eigen::Matrix<T, Eigen::Dynamic, 1>>(_val.data(), _val.size()));
549 : #else
550 : typename CompareTypes<T, T2>::supertype val = 0.;
551 :
552 : const int N = cast_int<int>(_val.size());
553 : // The following pragma tells clang's vectorizer that it is safe to
554 : // reorder floating point operations for this loop.
555 : #ifdef __clang__
556 : #pragma clang loop vectorize(enable)
557 : #endif
558 : for (int i=0; i<N; i++)
559 : val += (*this)(i)*libmesh_conj(vec(i));
560 :
561 : return val;
562 : #endif
563 : }
564 :
565 : template<typename T>
566 : template<typename T2>
567 : inline
568 : typename CompareTypes<T, T2>::supertype DenseVector<T>::indefinite_dot (const DenseVector<T2> & vec) const
569 : {
570 : libmesh_assert_equal_to (this->size(), vec.size());
571 :
572 : typename CompareTypes<T, T2>::supertype val = 0.;
573 :
574 : const int N = cast_int<int>(_val.size());
575 : for (int i=0; i<N; i++)
576 : val += (*this)(i)*(vec(i));
577 :
578 : return val;
579 : }
580 :
581 : template<typename T>
582 : template<typename T2>
583 : inline
584 : bool DenseVector<T>::operator== (const DenseVector<T2> & vec) const
585 : {
586 : libmesh_assert_equal_to (this->size(), vec.size());
587 :
588 : const int N = cast_int<int>(_val.size());
589 : for (int i=0; i<N; i++)
590 : if ((*this)(i) != vec(i))
591 : return false;
592 :
593 : return true;
594 : }
595 :
596 :
597 :
598 : template<typename T>
599 : template<typename T2>
600 : inline
601 : bool DenseVector<T>::operator!= (const DenseVector<T2> & vec) const
602 : {
603 : libmesh_assert_equal_to (this->size(), vec.size());
604 :
605 : const int N = cast_int<int>(_val.size());
606 : for (int i=0; i<N; i++)
607 : if ((*this)(i) != vec(i))
608 : return true;
609 :
610 : return false;
611 : }
612 :
613 :
614 :
615 : template<typename T>
616 : template<typename T2>
617 : inline
618 933728 : DenseVector<T> & DenseVector<T>::operator+= (const DenseVector<T2> & vec)
619 : {
620 92736 : libmesh_assert_equal_to (this->size(), vec.size());
621 :
622 139104 : const int N = cast_int<int>(_val.size());
623 43113344 : for (int i=0; i<N; i++)
624 47488616 : (*this)(i) += vec(i);
625 :
626 933728 : return *this;
627 : }
628 :
629 :
630 :
631 : template<typename T>
632 : template<typename T2>
633 : inline
634 20540894 : DenseVector<T> & DenseVector<T>::operator-= (const DenseVector<T2> & vec)
635 : {
636 2023299 : libmesh_assert_equal_to (this->size(), vec.size());
637 :
638 4048385 : const int N = cast_int<int>(_val.size());
639 229289967 : for (int i=0; i<N; i++)
640 234344471 : (*this)(i) -= vec(i);
641 :
642 20540894 : return *this;
643 : }
644 :
645 :
646 :
647 : template<typename T>
648 : inline
649 0 : Real DenseVector<T>::min () const
650 : {
651 0 : libmesh_assert (this->size());
652 : typedef decltype(libmesh_real(T(0))) realfromT;
653 : return libmesh_transform_reduce
654 0 : (_val.begin(), _val.end(), std::numeric_limits<realfromT>::max(),
655 0 : [](const auto & a, const auto & b){using std::min; return min(a,b);},
656 0 : [](const T & v){return libmesh_real(v);});
657 : }
658 :
659 :
660 :
661 : template<typename T>
662 : inline
663 0 : Real DenseVector<T>::max () const
664 : {
665 0 : libmesh_assert (this->size());
666 : typedef decltype(libmesh_real(T(0))) realfromT;
667 : return libmesh_transform_reduce
668 0 : (_val.begin(), _val.end(), std::numeric_limits<realfromT>::lowest(),
669 0 : [](const auto & a, const auto & b){using std::max; return max(a,b);},
670 0 : [](const T & v){return libmesh_real(v);});
671 : }
672 :
673 :
674 :
675 : template<typename T>
676 : inline
677 0 : Real DenseVector<T>::l1_norm () const
678 : {
679 0 : if (!_val.size())
680 0 : return 0.;
681 :
682 : #ifdef LIBMESH_HAVE_EIGEN
683 0 : return Eigen::Map<const typename Eigen::Matrix<T, Eigen::Dynamic, 1>>(_val.data(), _val.size()).template lpNorm<1>();
684 : #else
685 : return libmesh_transform_reduce
686 : (_val.begin(), _val.end(), Real(0), std::plus<>(),
687 : [](const T & v){using std::abs; return abs(v);});
688 : #endif
689 : }
690 :
691 :
692 :
693 : template<typename T>
694 : inline
695 0 : Real DenseVector<T>::l2_norm () const
696 : {
697 0 : if (!_val.size())
698 0 : return 0.;
699 :
700 : #ifdef LIBMESH_HAVE_EIGEN
701 0 : return Eigen::Map<const typename Eigen::Matrix<T, Eigen::Dynamic, 1>>(_val.data(), _val.size()).norm();
702 : #else
703 : using std::sqrt;
704 : return sqrt(libmesh_transform_reduce
705 : (_val.begin(), _val.end(), Real(0), std::plus<>(),
706 : [](const T & v){return TensorTools::norm_sq(v);}));
707 : #endif
708 : }
709 :
710 :
711 :
712 : template<typename T>
713 : inline
714 0 : Real DenseVector<T>::linfty_norm () const
715 : {
716 0 : if (!_val.size())
717 0 : return 0.;
718 :
719 : #ifdef LIBMESH_HAVE_EIGEN
720 0 : return Eigen::Map<const typename Eigen::Matrix<T, Eigen::Dynamic, 1>>(_val.data(), _val.size()).template lpNorm<Eigen::Infinity>();
721 : #else
722 : return libmesh_transform_reduce
723 : (_val.begin(), _val.end(), Real(0),
724 : [](auto a, auto b){using std::max; return max(a,b);},
725 : [](const T & v){using std::abs; return abs(v);});
726 : #endif
727 : }
728 :
729 :
730 :
731 : template<typename T>
732 : inline
733 11312742 : void DenseVector<T>::get_principal_subvector (unsigned int sub_n,
734 : DenseVector<T> & dest) const
735 : {
736 1008892 : libmesh_assert_less_equal ( sub_n, this->size() );
737 :
738 10303850 : dest.resize(sub_n);
739 1008892 : const int N = cast_int<int>(sub_n);
740 113671452 : for (int i=0; i<N; i++)
741 120645390 : dest(i) = _val[i];
742 11312742 : }
743 :
744 :
745 : } // namespace libMesh
746 :
747 : #ifdef LIBMESH_HAVE_METAPHYSICL
748 : namespace MetaPhysicL
749 : {
750 : template <typename T>
751 : struct RawType<libMesh::DenseVector<T>>
752 : {
753 : typedef libMesh::DenseVector<typename RawType<T>::value_type> value_type;
754 :
755 : static value_type value (const libMesh::DenseVector<T> & in)
756 : {
757 : const auto s = in.size();
758 : value_type ret(s);
759 : for (unsigned int i = 0; i < s; ++i)
760 : ret(i) = raw_value(in(i));
761 :
762 : return ret;
763 : }
764 : };
765 : }
766 : #endif
767 :
768 : #endif // LIBMESH_DENSE_VECTOR_H
|