ENH: expand VectorSpaceOps to include copy_n/fill_n methods

- similar to what std::copy_n and std::fill_n would do, except with
  templated loops. This allows compile-time transcribing with loop
  unrolling. For example,

     vector vec1 = ..., vec2 = ...;

     FixedList<scalar, 6> values;

     VectorSpaceOps<3>::copy_n(vec1.begin(), values.begin());
     VectorSpaceOps<3>::copy_n(vec2.begin(), values.begin(3))

     // do something with all of these values

STYLE: make start index of VectorSpaceOps optional

ENH: add clamped begin(int) versions to FixedList as per UList
This commit is contained in:
Mark Olesen 2025-03-04 13:30:20 +01:00
parent cf9fa16788
commit d64682a7af
9 changed files with 197 additions and 118 deletions

View File

@ -1,3 +1,3 @@
Test-vector.C
Test-vector.cxx
EXE = $(FOAM_USER_APPBIN)/Test-vector

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2023 OpenCFD Ltd.
Copyright (C) 2018-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -32,7 +32,10 @@ Description
\*---------------------------------------------------------------------------*/
#include "vectorField.H"
#include "boolVector.H"
#include "labelVector.H"
#include "IOstreams.H"
#include "FixedList.H"
#include "Random.H"
#include <algorithm>
#include <random>
@ -125,6 +128,42 @@ void testNormalise(Field<Type>& fld)
}
// Transcribe vectorspace information into a FixedList
template<class Type>
void testTranscribe(Type& input)
{
if constexpr
(
is_vectorspace_v<Type>
&& std::is_floating_point_v<typename pTraits_cmptType<Type>::type>
)
{
constexpr auto nCmpts = pTraits_nComponents<Type>::value;
using cmpt = typename pTraits_cmptType<Type>::type;
FixedList<cmpt, nCmpts+1> values;
values.back() = 100; // some additional data
VectorSpaceOps<nCmpts>::copy_n(input.cdata(), values.data());
Info<< "Transcribed " << input << " => " << values << nl;
for (auto& val : values)
{
val *= -1;
}
VectorSpaceOps<nCmpts>::copy_n(values.cdata(), input.data());
Info<< " copied back (-1) as " << input
<< " from " << values << nl;
}
else
{
Info<< "Did not transcribe " << input << nl;
}
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
@ -240,6 +279,16 @@ int main(int argc, char *argv[])
testNormalise(vfld2);
}
Info<< nl
<< "Test transcribing components" << nl;
{
vector vec1(1.1, 2.2, 3.3);
testTranscribe(vec1);
labelVector vec2(10, 20, 30);
testTranscribe(vec2);
}
Info<< "\nEnd\n" << nl;
return 0;

View File

@ -1,3 +1,3 @@
Test-vectorTools.C
Test-vectorTools.cxx
EXE = $(FOAM_USER_APPBIN)/Test-vectorTools

View File

@ -384,6 +384,10 @@ public:
//- Return an iterator to end traversing the FixedList
inline iterator end() noexcept;
//- Return iterator at offset i from begin,
//- clamped to [0,N] range
inline iterator begin(const int i) noexcept;
// Random access iterator (const)
@ -399,6 +403,14 @@ public:
//- Return const_iterator to end traversing the constant FixedList
inline const_iterator end() const noexcept;
//- Return const_iterator at offset i from begin,
//- clamped to [0,N] range
inline const_iterator cbegin(const int i) const noexcept;
//- Return const_iterator at offset i from begin,
//- clamped to [0,N] range
inline const_iterator begin(const int i) const noexcept;
// Reverse iterator (non-const)

View File

@ -499,6 +499,30 @@ Foam::FixedList<T, N>::cbegin() const noexcept
}
template<class T, unsigned N>
inline typename Foam::FixedList<T, N>::iterator
Foam::FixedList<T, N>::begin(const int i) noexcept
{
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
}
template<class T, unsigned N>
inline typename Foam::FixedList<T, N>::const_iterator
Foam::FixedList<T, N>::begin(const int i) const noexcept
{
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
}
template<class T, unsigned N>
inline typename Foam::FixedList<T, N>::const_iterator
Foam::FixedList<T, N>::cbegin(const int i) const noexcept
{
return (v_ + (i < 0 ? 0 : int(N) < i ? int(N) : i));
}
template<class T, unsigned N>
inline typename Foam::FixedList<T, N>::iterator
Foam::FixedList<T, N>::end() noexcept

View File

@ -158,7 +158,7 @@ public:
// Constructors
//- Construct initialized to zero
inline VectorSpace(const Foam::zero);
inline VectorSpace(Foam::zero);
//- Copy construct
inline VectorSpace(const VectorSpace<Form, Cmpt, Ncmpts>& vs);
@ -174,10 +174,7 @@ public:
// Member Functions
//- The number of elements in the VectorSpace = Ncmpts.
static constexpr direction size() noexcept
{
return Ncmpts;
}
static constexpr direction size() noexcept { return Ncmpts; }
inline const Cmpt& component(const direction) const;
inline Cmpt& component(const direction);
@ -186,10 +183,10 @@ public:
inline void replace(const direction, const Cmpt&);
//- Return const pointer to the first data element
inline const Cmpt* cdata() const noexcept;
const Cmpt* cdata() const noexcept { return v_; }
//- Return pointer to the first data element
inline Cmpt* data() noexcept;
Cmpt* data() noexcept { return v_; }
//- Assign all components to given value
inline void fill(const Cmpt& s);
@ -210,7 +207,7 @@ public:
inline void operator+=(const VectorSpace<Form, Cmpt, Ncmpts>&);
inline void operator-=(const VectorSpace<Form, Cmpt, Ncmpts>&);
inline void operator=(const Foam::zero);
inline void operator=(Foam::zero);
inline void operator*=(const scalar);
inline void operator/=(const scalar);
@ -224,28 +221,25 @@ public:
typedef const Cmpt* const_iterator;
// Random access iterator (non-const)
// Random access iterators (const and non-const)
//- Return an iterator to begin of VectorSpace
inline iterator begin() noexcept;
//- Return an iterator (pointer) to begin of VectorSpace
iterator begin() noexcept { return v_; }
//- Return an iterator to end of VectorSpace
inline iterator end() noexcept;
//- Return const_iterator (const pointer) to begin of VectorSpace
const_iterator begin() const noexcept { return v_; }
//- Return const_iterator (const pointer) to begin of VectorSpace
const_iterator cbegin() const noexcept { return v_; }
// Random access iterator (const)
//- Return an iterator (pointer) to end of VectorSpace
iterator end() noexcept { return (v_ + Ncmpts); }
//- Return const_iterator to begin of VectorSpace
inline const_iterator cbegin() const noexcept;
//- Return const_iterator (const pointer) to end of VectorSpace
const_iterator end() const noexcept { return (v_ + Ncmpts); }
//- Return const_iterator to end of VectorSpace
inline const_iterator cend() const noexcept;
//- Return const_iterator to begin of VectorSpace
inline const_iterator begin() const noexcept;
//- Return const_iterator to end of VectorSpace
inline const_iterator end() const noexcept;
//- Return const_iterator (const pointer) to end of VectorSpace
const_iterator cend() const noexcept { return (v_ + Ncmpts); }
// IOstream Operators

View File

@ -35,9 +35,9 @@ License
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace(const Foam::zero)
inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace(Foam::zero)
{
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, Zero, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), Cmpt(Foam::zero{}));
}
@ -47,7 +47,7 @@ inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace
const VectorSpace<Form, Cmpt, Ncmpts>& vs
)
{
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
}
@ -58,7 +58,7 @@ inline Foam::VectorSpace<Form, Cmpt, Ncmpts>::VectorSpace
const VectorSpace<Form2, Cmpt2, Ncmpts>& vs
)
{
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
}
@ -163,7 +163,7 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::replace
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::fill(const Cmpt& s)
{
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, s, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), s);
}
@ -171,7 +171,7 @@ template<class Form, class Cmpt, Foam::direction Ncmpts>
inline Form Foam::VectorSpace<Form, Cmpt, Ncmpts>::uniform(const Cmpt& s)
{
Form v;
VectorSpaceOps<Ncmpts,0>::eqOpS(v, s, eqOp<Cmpt>());
v.fill(s);
return v;
}
@ -186,68 +186,6 @@ Foam::VectorSpace<Form, Cmpt, Ncmpts>::block() const
}
// * * * * * * * * * * * * * * * * Iterator * * * * * * * * * * * * * * * * //
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::data() noexcept
{
return v_;
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cdata() const noexcept
{
return v_;
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::begin() noexcept
{
return v_;
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::end() noexcept
{
return (v_ + Ncmpts);
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cbegin()
const noexcept
{
return v_;
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::cend()
const noexcept
{
return (v_ + Ncmpts);
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::begin()
const noexcept
{
return v_;
}
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline const Cmpt* Foam::VectorSpace<Form, Cmpt, Ncmpts>::end()
const noexcept
{
return (v_ + Ncmpts);
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
template<class Form, class Cmpt, Foam::direction Ncmpts>
@ -346,7 +284,7 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=
const VectorSpace<Form, Cmpt, Ncmpts>& vs
)
{
VectorSpaceOps<Ncmpts,0>::eqOp(*this, vs, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::copy_n(vs.cbegin(), this->begin());
}
@ -371,9 +309,9 @@ inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator-=
template<class Form, class Cmpt, Foam::direction Ncmpts>
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=(const Foam::zero)
inline void Foam::VectorSpace<Form, Cmpt, Ncmpts>::operator=(Foam::zero)
{
VectorSpaceOps<Ncmpts,0>::eqOpS(*this, 0, eqOp<Cmpt>());
VectorSpaceOps<Ncmpts>::fill_n(this->begin(), Cmpt(Foam::zero{}));
}

View File

@ -43,9 +43,40 @@ namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Recursive execution. Terminating at \<N\>, starting at index \<I\>
template<direction N, direction I>
template<direction N, direction I=0>
struct VectorSpaceOps
{
//- Somewhat equivalent to std::copy_n() but with templated loops.
// \param [in] input indexable input data
// \param [out] result indexable output data
template<class Input, class Output>
static inline void copy_n(Input input, Output result)
{
// if constexpr (I < N)
{
result[I] = input[I];
VectorSpaceOps<N, I+1>::copy_n(input, result);
}
}
//- Somewhat equivalent to std::fill_n() but with templated loops
// \param [out] result indexable output data
// \param val the value to assign for each entry
template<class Output, class T>
static inline void fill_n(Output result, const T& val)
{
// if constexpr (I < N)
{
result[I] = val;
VectorSpaceOps<N, I+1>::fill_n(result, val);
}
}
//- Apply the binary assignment operation to each vector-space
//- component.
// \param [in,out] vs vector-space (indexed) data
// \param s scalar/component data (non-indexed)
// \param eo binary combine/assign operation
template<class V, class S, class EqOp>
static inline void eqOpS(V& vs, const S& s, EqOp eo)
{
@ -56,6 +87,10 @@ struct VectorSpaceOps
}
}
//- Apply the inplace binary reduction operation.
// \param [in,out] s scalar or component data (non-indexed)
// \param [in] vs input vector-space (indexed) data
// \param eo binary combine/assign operation
template<class S, class V, class EqOp>
static inline void SeqOp(S& s, const V& vs, EqOp eo)
{
@ -66,6 +101,10 @@ struct VectorSpaceOps
}
}
//- Apply the inplace binary assignment operation to the components.
// \param [in,out] vs1 vector-space (indexed) data
// \param [in] vs2 second vector-space (indexed) data
// \param eo binary combine/assign operation
template<class V1, class V2, class EqOp>
static inline void eqOp(V1& vs1, const V2& vs2, EqOp eo)
{
@ -76,34 +115,51 @@ struct VectorSpaceOps
}
}
template<class V, class V1, class S, class Op>
static inline void opVS(V& vs, const V1& vs1, const S& s, Op o)
//- Apply the binary operation between vector-space and scalar data
//- and assign the result.
// \param [out] vs vector-space (indexed) data
// \param [in] vs1 vector-space (indexed) data operand
// \param [in] s scalar operand
// \param bop binary operation
template<class V, class V1, class S, class BinaryOp>
static inline void opVS(V& vs, const V1& vs1, const S& s, BinaryOp bop)
{
// if constexpr (I < N)
{
vs.v_[I] = o(vs1.v_[I], s);
VectorSpaceOps<N, I+1>::opVS(vs, vs1, s, o);
vs.v_[I] = bop(vs1.v_[I], s);
VectorSpaceOps<N, I+1>::opVS(vs, vs1, s, bop);
}
}
template<class V, class S, class V1, class Op>
static inline void opSV(V& vs, const S& s, const V1& vs1, Op o)
//- Apply the binary operation between scalar and vector-space data
//- and assign the result.
// \param [out] vs vector-space (indexed) data
// \param [in] s scalar operand
// \param [in] vs1 vector-space (indexed) data operand
// \param bop binary operation
template<class V, class S, class V1, class BinaryOp>
static inline void opSV(V& vs, const S& s, const V1& vs1, BinaryOp bop)
{
// if constexpr (I < N)
{
vs.v_[I] = o(s, vs1.v_[I]);
VectorSpaceOps<N, I+1>::opSV(vs, s, vs1, o);
vs.v_[I] = bop(s, vs1.v_[I]);
VectorSpaceOps<N, I+1>::opSV(vs, s, vs1, bop);
}
}
template<class V, class V1, class Op>
static inline void op(V& vs, const V1& vs1, const V1& vs2, Op o)
//- Apply the binary operation between two vector-space data
//- and assign the result.
// \param [out] vs vector-space (indexed) data
// \param [in] vs1 vector-space (indexed) data operand
// \param [in] vs2 vector-space (indexed) data operand
// \param bop binary operation
template<class V, class V1, class BinaryOp>
static inline void op(V& vs, const V1& vs1, const V1& vs2, BinaryOp bop)
{
// if constexpr (I < N)
{
vs.v_[I] = o(vs1.v_[I], vs2.v_[I]);
VectorSpaceOps<N, I+1>::op(vs, vs1, vs2, o);
vs.v_[I] = bop(vs1.v_[I], vs2.v_[I]);
VectorSpaceOps<N, I+1>::op(vs, vs1, vs2, bop);
}
}
};
@ -115,6 +171,12 @@ struct VectorSpaceOps
template<direction N>
struct VectorSpaceOps<N, N>
{
template<class Input, class Output>
static inline void copy_n(Input, Output) {}
template<class Output, class T>
static inline void fill_n(Output, const T&) {}
template<class V, class S, class EqOp>
static inline void eqOpS(V&, const S&, EqOp) {}
@ -124,14 +186,14 @@ struct VectorSpaceOps<N, N>
template<class V1, class V2, class EqOp>
static inline void eqOp(V1&, const V2&, EqOp) {}
template<class V, class V1, class S, class Op>
static inline void opVS(V& vs, const V1&, const S&, Op) {}
template<class V, class V1, class S, class BinaryOp>
static inline void opVS(V&, const V1&, const S&, BinaryOp) {}
template<class V, class S, class V1, class Op>
static inline void opSV(V& vs, const S&, const V1&, Op) {}
template<class V, class S, class V1, class BinaryOp>
static inline void opSV(V&, const S&, const V1&, BinaryOp) {}
template<class V, class V1, class Op>
static inline void op(V& vs, const V1&, const V1&, Op) {}
template<class V, class V1, class BinaryOp>
static inline void op(V&, const V1&, const V1&, BinaryOp) {}
};