diff options
Diffstat (limited to 'Source')
-rw-r--r-- | Source/Particles/Gather/FieldGather.H | 48 | ||||
-rw-r--r-- | Source/Particles/Gather/GetExternalFields.H | 114 | ||||
-rw-r--r-- | Source/Particles/PhysicalParticleContainer.H | 17 | ||||
-rw-r--r-- | Source/Particles/PhysicalParticleContainer.cpp | 86 | ||||
-rw-r--r-- | Source/Particles/Pusher/GetAndSetPosition.H | 3 |
5 files changed, 155 insertions, 113 deletions
diff --git a/Source/Particles/Gather/FieldGather.H b/Source/Particles/Gather/FieldGather.H index 3ca71680c..fa92a7ae9 100644 --- a/Source/Particles/Gather/FieldGather.H +++ b/Source/Particles/Gather/FieldGather.H @@ -9,6 +9,7 @@ #define FIELDGATHER_H_ #include "Particles/Pusher/GetAndSetPosition.H" +#include "Particles/Gather/GetExternalFields.H" #include "Particles/ShapeFactors.H" #include "Utils/WarpX_Complex.H" @@ -196,15 +197,15 @@ void doGatherShapeN (const amrex::ParticleReal xp, // order can differ for each component of each field // when lower_in_v is set to 1 #if (AMREX_SPACEDIM == 2) - // Gather field on particle Eyp[i] from field on grid ey_arr + // Gather field on particle Eyp from field on grid ey_arr for (int iz=0; iz<=depos_order; iz++){ for (int ix=0; ix<=depos_order; ix++){ Eyp += sx_ey[ix]*sz_ey[iz]* ey_arr(lo.x+j_ey+ix, lo.y+l_ey+iz, 0, 0); } } - // Gather field on particle Exp[i] from field on grid ex_arr - // Gather field on particle Bzp[i] from field on grid bz_arr + // Gather field on particle Exp from field on grid ex_arr + // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ Exp += sx_ex[ix]*sz_ex[iz]* @@ -213,8 +214,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, bz_arr(lo.x+j_bz+ix, lo.y+l_bz+iz, 0, 0); } } - // Gather field on particle Ezp[i] from field on grid ez_arr - // Gather field on particle Bxp[i] from field on grid bx_arr + // Gather field on particle Ezp from field on grid ez_arr + // Gather field on particle Bxp from field on grid bx_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int ix=0; ix<=depos_order; ix++){ Ezp += sx_ez[ix]*sz_ez[iz]* @@ -223,7 +224,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, bx_arr(lo.x+j_bx+ix, lo.y+l_bx+iz, 0, 0); } } - // Gather field on particle Byp[i] from field on grid by_arr + // Gather field on particle Byp from field on grid by_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ Byp += sx_by[ix]*sz_by[iz]* @@ -247,7 +248,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, for (int imode=1 ; imode < n_rz_azimuthal_modes ; imode++) { - // Gather field on particle Eyp[i] from field on grid ey_arr + // Gather field on particle Eyp from field on grid ey_arr for (int iz=0; iz<=depos_order; iz++){ for (int ix=0; ix<=depos_order; ix++){ const amrex::Real dEy = (+ ey_arr(lo.x+j_ey+ix, lo.y+l_ey+iz, 0, 2*imode-1)*xy.real() @@ -255,8 +256,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, Eyp += sx_ey[ix]*sz_ey[iz]*dEy; } } - // Gather field on particle Exp[i] from field on grid ex_arr - // Gather field on particle Bzp[i] from field on grid bz_arr + // Gather field on particle Exp from field on grid ex_arr + // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ const amrex::Real dEx = (+ ex_arr(lo.x+j_ex+ix, lo.y+l_ex+iz, 0, 2*imode-1)*xy.real() @@ -267,8 +268,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, Bzp += sx_bz[ix]*sz_bz[iz]*dBz; } } - // Gather field on particle Ezp[i] from field on grid ez_arr - // Gather field on particle Bxp[i] from field on grid bx_arr + // Gather field on particle Ezp from field on grid ez_arr + // Gather field on particle Bxp from field on grid bx_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int ix=0; ix<=depos_order; ix++){ const amrex::Real dEz = (+ ez_arr(lo.x+j_ez+ix, lo.y+l_ez+iz, 0, 2*imode-1)*xy.real() @@ -279,7 +280,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, Bxp += sx_bx[ix]*sz_bx[iz]*dBx; } } - // Gather field on particle Byp[i] from field on grid by_arr + // Gather field on particle Byp from field on grid by_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ const amrex::Real dBy = (+ by_arr(lo.x+j_by+ix, lo.y+l_by+iz, 0, 2*imode-1)*xy.real() @@ -300,7 +301,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, #endif #else // (AMREX_SPACEDIM == 3) - // Gather field on particle Exp[i] from field on grid ex_arr + // Gather field on particle Exp from field on grid ex_arr for (int iz=0; iz<=depos_order; iz++){ for (int iy=0; iy<=depos_order; iy++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ @@ -309,7 +310,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } } - // Gather field on particle Eyp[i] from field on grid ey_arr + // Gather field on particle Eyp from field on grid ey_arr for (int iz=0; iz<=depos_order; iz++){ for (int iy=0; iy<=depos_order-lower_in_v; iy++){ for (int ix=0; ix<=depos_order; ix++){ @@ -318,7 +319,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } } - // Gather field on particle Ezp[i] from field on grid ez_arr + // Gather field on particle Ezp from field on grid ez_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int iy=0; iy<=depos_order; iy++){ for (int ix=0; ix<=depos_order; ix++){ @@ -327,7 +328,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } } - // Gather field on particle Bzp[i] from field on grid bz_arr + // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ for (int iy=0; iy<=depos_order-lower_in_v; iy++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ @@ -336,7 +337,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } } - // Gather field on particle Byp[i] from field on grid by_arr + // Gather field on particle Byp from field on grid by_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int iy=0; iy<=depos_order; iy++){ for (int ix=0; ix<=depos_order-lower_in_v; ix++){ @@ -345,7 +346,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } } - // Gather field on particle Bxp[i] from field on grid bx_arr + // Gather field on particle Bxp from field on grid bx_arr for (int iz=0; iz<=depos_order-lower_in_v; iz++){ for (int iy=0; iy<=depos_order-lower_in_v; iy++){ for (int ix=0; ix<=depos_order; ix++){ @@ -360,7 +361,9 @@ void doGatherShapeN (const amrex::ParticleReal xp, /** * \brief Field gather for particles * - * /param GetPosition : A functor for returning the particle position. + * /param getPosition : A functor for returning the particle position. + * /param getExternalEField : A functor for assigning the external E field. + * /param getExternalBField : A functor for assigning the external B field. * \param Exp, Eyp, Ezp : Pointer to array of electric field on particles. * \param Bxp, Byp, Bzp : Pointer to array of magnetic field on particles. * \param exfab eyfab ezfab : Array4 of the electric field, either full array or tile. @@ -372,7 +375,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, * \param n_rz_azimuthal_modes : Number of azimuthal modes when using RZ geometry */ template <int depos_order, int lower_in_v> -void doGatherShapeN(const GetParticlePosition& GetPosition, +void doGatherShapeN(const GetParticlePosition& getPosition, + const GetExternalEField& getExternalE, const GetExternalBField& getExternalB, amrex::ParticleReal * const Exp, amrex::ParticleReal * const Eyp, amrex::ParticleReal * const Ezp, amrex::ParticleReal * const Bxp, amrex::ParticleReal * const Byp, amrex::ParticleReal * const Bzp, @@ -413,7 +417,9 @@ void doGatherShapeN(const GetParticlePosition& GetPosition, [=] AMREX_GPU_DEVICE (long ip) { amrex::ParticleReal xp, yp, zp; - GetPosition(ip, xp, yp, zp); + getPosition(ip, xp, yp, zp); + getExternalE(ip, Exp[ip], Eyp[ip], Ezp[ip]); + getExternalB(ip, Bxp[ip], Byp[ip], Bzp[ip]); doGatherShapeN<depos_order, lower_in_v>( xp, yp, zp, Exp[ip], Eyp[ip], Ezp[ip], Bxp[ip], Byp[ip], Bzp[ip], diff --git a/Source/Particles/Gather/GetExternalFields.H b/Source/Particles/Gather/GetExternalFields.H new file mode 100644 index 000000000..8e08fc101 --- /dev/null +++ b/Source/Particles/Gather/GetExternalFields.H @@ -0,0 +1,114 @@ +#ifndef WARPX_PARTICLES_GATHER_GETEXTERNALFIELDS_H_ +#define WARPX_PARTICLES_GATHER_GETEXTERNALFIELDS_H_ + +#include "Particles/WarpXParticleContainer.H" + +#include <AMReX_REAL.H> + +#include <limits> + +enum ExternalFieldInitType { Constant, Parser }; + +/** \brief Base class for functors that assign external + * field values (E or B) to particles. +*/ +struct GetExternalField +{ + ExternalFieldInitType m_type; + + amrex::GpuArray<amrex::ParticleReal, 3> m_field_value; + + ParserWrapper<4>* m_xfield_partparser = nullptr; + ParserWrapper<4>* m_yfield_partparser = nullptr; + ParserWrapper<4>* m_zfield_partparser = nullptr; + GetParticlePosition m_get_position; + amrex::Real m_time; + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + void operator () (long i, + amrex::ParticleReal& field_x, + amrex::ParticleReal& field_y, + amrex::ParticleReal& field_z) const noexcept + { + if (m_type == Constant) + { + field_x = m_field_value[0]; + field_y = m_field_value[1]; + field_z = m_field_value[2]; + } + else if (m_type == Parser) + { + AMREX_ASSERT(m_xfield_partparser != nullptr); + AMREX_ASSERT(m_yfield_partparser != nullptr); + AMREX_ASSERT(m_zfield_partparser != nullptr); + + amrex::ParticleReal x, y, z; + m_get_position(i, x, y, z); + field_x = (*m_xfield_partparser)(x, y, z, m_time); + field_y = (*m_yfield_partparser)(x, y, z, m_time); + field_z = (*m_zfield_partparser)(x, y, z, m_time); + } + else + { + amrex::Abort("ExternalFieldInitType not known!!! \n"); + } + } +}; + +/** \brief Functor that can be used to assign the external + * E field to a particle inside a ParallelFor kernel +*/ +struct GetExternalEField : GetExternalField +{ + GetExternalEField (const WarpXParIter& a_pti, int a_offset = 0) noexcept + { + auto& warpx = WarpX::GetInstance(); + auto& mypc = warpx.GetPartContainer(); + if (mypc.m_E_ext_particle_s=="constant" || mypc.m_E_ext_particle_s=="default") + { + m_type = Constant; + m_field_value[0] = mypc.m_E_external_particle[0]; + m_field_value[1] = mypc.m_E_external_particle[1]; + m_field_value[2] = mypc.m_E_external_particle[2]; + } + else if (mypc.m_E_ext_particle_s=="parse_e_ext_particle_function") + { + m_type = Parser; + m_time = warpx.gett_new(a_pti.GetLevel()); + m_get_position = GetParticlePosition(a_pti, a_offset); + m_xfield_partparser = mypc.m_Ex_particle_parser.get(); + m_yfield_partparser = mypc.m_Ey_particle_parser.get(); + m_zfield_partparser = mypc.m_Ez_particle_parser.get(); + } + } +}; + +/** \brief Functor that can be used to assign the external + * B field to a particle inside a ParallelFor kernel +*/ +struct GetExternalBField : GetExternalField +{ + GetExternalBField (const WarpXParIter& a_pti, int a_offset = 0) noexcept + { + auto& warpx = WarpX::GetInstance(); + auto& mypc = warpx.GetPartContainer(); + if (mypc.m_B_ext_particle_s=="constant" || mypc.m_B_ext_particle_s=="default") + { + m_type = Constant; + m_field_value[0] = mypc.m_B_external_particle[0]; + m_field_value[1] = mypc.m_B_external_particle[1]; + m_field_value[2] = mypc.m_B_external_particle[2]; + } + else if (mypc.m_B_ext_particle_s=="parse_e_ext_particle_function") + { + m_type = Parser; + m_time = warpx.gett_new(a_pti.GetLevel()); + m_get_position = GetParticlePosition(a_pti, a_offset); + m_xfield_partparser = mypc.m_Bx_particle_parser.get(); + m_yfield_partparser = mypc.m_By_particle_parser.get(); + m_zfield_partparser = mypc.m_Bz_particle_parser.get(); + } + } +}; + +#endif diff --git a/Source/Particles/PhysicalParticleContainer.H b/Source/Particles/PhysicalParticleContainer.H index e806f9880..e1426a482 100644 --- a/Source/Particles/PhysicalParticleContainer.H +++ b/Source/Particles/PhysicalParticleContainer.H @@ -63,23 +63,6 @@ public: void InitIonizationModule (); - /** - * \brief Apply external E and B fields on the particles. The E and B - * fields could be defined as a constant or using a parser for reading - * in a mathematical expression. The default value for the E- and B-fields - * is (0.0,0.0,0.0). - * - * \param[in,out] Exp-Bzp pointer to fields on particles modified based - * on external E and B - * \param[in] xp,yp,zp arrays of particle positions required to compute - * mathematical expression for the external fields - * using parser. - */ - void AssignExternalFieldOnParticles ( WarpXParIter& pti, - RealVector& Exp, RealVector& Eyp, - RealVector& Ezp, RealVector& Bxp, - RealVector& Byp, RealVector& Bzp, int lev); - virtual void FieldGather (int lev, const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, diff --git a/Source/Particles/PhysicalParticleContainer.cpp b/Source/Particles/PhysicalParticleContainer.cpp index 5fd6f8d4f..2eee6aa79 100644 --- a/Source/Particles/PhysicalParticleContainer.cpp +++ b/Source/Particles/PhysicalParticleContainer.cpp @@ -18,6 +18,7 @@ #include "Utils/IonizationEnergiesTable.H" #include "Particles/Gather/FieldGather.H" #include "Particles/Pusher/GetAndSetPosition.H" +#include "Particles/Gather/GetExternalFields.H" #include "Utils/WarpXAlgorithmSelection.H" @@ -902,69 +903,6 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) } void -PhysicalParticleContainer::AssignExternalFieldOnParticles ( - WarpXParIter& pti, - RealVector& Exp, RealVector& Eyp, RealVector& Ezp, - RealVector& Bxp, RealVector& Byp, RealVector& Bzp, int lev) -{ - const long np = pti.numParticles(); - /// get WarpX class object - auto & warpx = WarpX::GetInstance(); - /// get MultiParticleContainer class object - auto & mypc = warpx.GetPartContainer(); - if (mypc.m_E_ext_particle_s=="constant" || - mypc.m_E_ext_particle_s=="default") { - Exp.assign(np,mypc.m_E_external_particle[0]); - Eyp.assign(np,mypc.m_E_external_particle[1]); - Ezp.assign(np,mypc.m_E_external_particle[2]); - } - if (mypc.m_B_ext_particle_s=="constant" || - mypc.m_B_ext_particle_s=="default") { - Bxp.assign(np,mypc.m_B_external_particle[0]); - Byp.assign(np,mypc.m_B_external_particle[1]); - Bzp.assign(np,mypc.m_B_external_particle[2]); - } - if (mypc.m_E_ext_particle_s=="parse_e_ext_particle_function") { - const auto GetPosition = GetParticlePosition(pti); - Real* const AMREX_RESTRICT Exp_data = Exp.dataPtr(); - Real* const AMREX_RESTRICT Eyp_data = Eyp.dataPtr(); - Real* const AMREX_RESTRICT Ezp_data = Ezp.dataPtr(); - ParserWrapper<4> *xfield_partparser = mypc.m_Ex_particle_parser.get(); - ParserWrapper<4> *yfield_partparser = mypc.m_Ey_particle_parser.get(); - ParserWrapper<4> *zfield_partparser = mypc.m_Ez_particle_parser.get(); - Real time = warpx.gett_new(lev); - amrex::ParallelFor(pti.numParticles(), - [=] AMREX_GPU_DEVICE (long i) { - ParticleReal x, y, z; - GetPosition(i, x, y, z); - Exp_data[i] = (*xfield_partparser)(x, y, z, time); - Eyp_data[i] = (*yfield_partparser)(x, y, z, time); - Ezp_data[i] = (*zfield_partparser)(x, y, z, time); - }); - } - if (mypc.m_B_ext_particle_s=="parse_b_ext_particle_function") { - const auto GetPosition = GetParticlePosition(pti); - Real* const AMREX_RESTRICT Bxp_data = Bxp.dataPtr(); - Real* const AMREX_RESTRICT Byp_data = Byp.dataPtr(); - Real* const AMREX_RESTRICT Bzp_data = Bzp.dataPtr(); - ParserWrapper<4> *xfield_partparser = mypc.m_Bx_particle_parser.get(); - ParserWrapper<4> *yfield_partparser = mypc.m_By_particle_parser.get(); - ParserWrapper<4> *zfield_partparser = mypc.m_Bz_particle_parser.get(); - Real time = warpx.gett_new(lev); - amrex::ParallelFor(pti.numParticles(), - [=] AMREX_GPU_DEVICE (long i) { - ParticleReal x, y, z; - GetPosition(i, x, y, z); - Bxp_data[i] = (*xfield_partparser)(x, y, z, time); - Byp_data[i] = (*yfield_partparser)(x, y, z, time); - Bzp_data[i] = (*zfield_partparser)(x, y, z, time); - }); - } -} - - - -void PhysicalParticleContainer::FieldGather (int lev, const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, @@ -2114,11 +2052,6 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, // If do_not_gather = 1 by user, do not do anything if (np_to_gather == 0 || do_not_gather) return; - // initializing the field value to the externally applied field before - // gathering fields from the grid to the particles. - AssignExternalFieldOnParticles(pti, Exp, Eyp, Ezp, Bxp, Byp, Bzp, lev); - - // Get cell size on gather_lev const std::array<Real,3>& dx = WarpX::CellSize(std::max(gather_lev,0)); @@ -2135,7 +2068,10 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, // Add guard cells to the box. box.grow(ngE); - const auto GetPosition = GetParticlePosition(pti, offset); + const auto getPosition = GetParticlePosition(pti, offset); + + const auto getExternalE = GetExternalEField(pti, offset); + const auto getExternalB = GetExternalBField(pti, offset); // Lower corner of tile box physical domain (take into account Galilean shift) Real cur_time = WarpX::GetInstance().gett_new(lev); @@ -2150,7 +2086,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, // different versions of template function doGatherShapeN if (WarpX::l_lower_order_in_v){ if (WarpX::nox == 1){ - doGatherShapeN<1,1>(GetPosition, + doGatherShapeN<1,1>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, @@ -2158,7 +2094,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, np_to_gather, dx, xyzmin, lo, WarpX::n_rz_azimuthal_modes); } else if (WarpX::nox == 2){ - doGatherShapeN<2,1>(GetPosition, + doGatherShapeN<2,1>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, @@ -2166,7 +2102,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, np_to_gather, dx, xyzmin, lo, WarpX::n_rz_azimuthal_modes); } else if (WarpX::nox == 3){ - doGatherShapeN<3,1>(GetPosition, + doGatherShapeN<3,1>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, @@ -2176,7 +2112,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, } } else { if (WarpX::nox == 1){ - doGatherShapeN<1,0>(GetPosition, + doGatherShapeN<1,0>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, @@ -2184,7 +2120,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, np_to_gather, dx, xyzmin, lo, WarpX::n_rz_azimuthal_modes); } else if (WarpX::nox == 2){ - doGatherShapeN<2,0>(GetPosition, + doGatherShapeN<2,0>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, @@ -2192,7 +2128,7 @@ PhysicalParticleContainer::FieldGather (WarpXParIter& pti, np_to_gather, dx, xyzmin, lo, WarpX::n_rz_azimuthal_modes); } else if (WarpX::nox == 3){ - doGatherShapeN<3,0>(GetPosition, + doGatherShapeN<3,0>(getPosition, getExternalE, getExternalB, Exp.dataPtr() + offset, Eyp.dataPtr() + offset, Ezp.dataPtr() + offset, Bxp.dataPtr() + offset, Byp.dataPtr() + offset, Bzp.dataPtr() + offset, diff --git a/Source/Particles/Pusher/GetAndSetPosition.H b/Source/Particles/Pusher/GetAndSetPosition.H index e575bab16..7f9886d79 100644 --- a/Source/Particles/Pusher/GetAndSetPosition.H +++ b/Source/Particles/Pusher/GetAndSetPosition.H @@ -32,6 +32,9 @@ struct GetParticlePosition #elif (AMREX_SPACEDIM == 2) static constexpr RType m_snan = std::numeric_limits<RType>::quiet_NaN(); #endif + + GetParticlePosition () = default; + GetParticlePosition (const WarpXParIter& a_pti, int a_offset = 0) noexcept { const auto& aos = a_pti.GetArrayOfStructs(); |