@@ -117,33 +117,30 @@ namespace
117117 podvector[index] = value;
118118 }
119119
120- /* * Bulk- copy a host (NumPy) array into an existing PODVector .
120+ /* * Create a new PODVector with a copy of a host (NumPy) array.
121121 *
122- * The source array is normalized on the host by pybind11
123- * (``c_style | forcecast``): a non-contiguous or differently-typed
124- * input is staged into a contiguous, ``T``-typed temporary before the
125- * copy. The destination is filled with a single contiguous transfer:
126- * a host copy for host-accessible allocators, or
122+ * Always copies: a ``PODVector`` owns its memory through its allocator,
123+ * so a zero-copy view is not possible. The source array is normalized on
124+ * the host by pybind11 (``c_style | forcecast``): a non-contiguous or
125+ * differently-typed input is staged into a contiguous, ``T``-typed
126+ * temporary before the copy. The destination is filled with a single
127+ * contiguous transfer: a host copy for host-accessible allocators, or
127128 * ``Gpu::copyAsync(hostToDevice, ...)`` for device memory. This keeps
128129 * the host-to-device path free of any CuPy dependency.
129130 */
130131 template <class T , class Allocator >
131- void
132- copy_from_host (
133- PODVector<T, Allocator> & podvector,
134- py::array_t <T, py::array::c_style | py::array::forcecast> const & arr
135- )
132+ PODVector<T, Allocator>
133+ from_numpy (py::array_t <T, py::array::c_style | py::array::forcecast> const & arr)
136134 {
137135 auto const buf = arr.request ();
138136 if (buf.ndim != 1 ) {
139- throw py::value_error (" copy_from_host: expected a 1-D array" );
140- }
141- if (static_cast <std::size_t >(buf.shape [0 ]) != podvector.size ()) {
142- throw py::value_error (
143- " copy_from_host: array size does not match PODVector size" );
137+ throw py::value_error (" from_numpy: expected a 1-D array" );
144138 }
145- if (podvector.empty ()) {
146- return ;
139+ auto const n = static_cast <std::size_t >(buf.shape [0 ]);
140+
141+ PODVector<T, Allocator> podvector (n);
142+ if (n == 0 ) {
143+ return podvector;
147144 }
148145
149146 auto const * src = static_cast <T const *>(buf.ptr );
@@ -152,14 +149,15 @@ namespace
152149 Gpu::copyAsync (
153150 Gpu::hostToDevice,
154151 src,
155- src + podvector. size () ,
152+ src + n ,
156153 podvector.begin ()
157154 );
158155 Gpu::streamSynchronize ();
159- return ;
156+ return podvector ;
160157 }
161158#endif
162- std::copy (src, src + podvector.size (), podvector.begin ());
159+ std::copy (src, src + n, podvector.begin ());
160+ return podvector;
163161 }
164162}
165163
@@ -271,13 +269,26 @@ void make_PODVector(py::module &m, std::string typestr, std::string allocstr)
271269 .def (" __setitem__" , &set_item<T, Allocator>)
272270 .def (" __getitem__" , &get_item<T, Allocator>)
273271
274- // bulk copy from a host (NumPy) array into this vector
275- .def ( " copy_from_host " , ©_from_host <T, Allocator>,
272+ // create a new vector with a copy of a host (NumPy) array
273+ .def_static ( " from_numpy " , &from_numpy <T, Allocator>,
276274 py::arg (" arr" ),
277- " Copy a 1-D host (NumPy) array into this vector.\n\n "
278- " The input is cast to the vector's element type and made "
279- " contiguous as needed. Device memory is filled via an AMReX "
280- " host-to-device copy." )
275+ py::return_value_policy::move,
276+ R"( Create a new PODVector from a NumPy array (or array-like).
277+
278+ Always copies the data into a newly allocated PODVector. The input is cast to
279+ the vector's element type and made contiguous as needed. The copy into
280+ device-only memory uses an AMReX host-to-device copy and does not require CuPy.
281+
282+ Parameters
283+ ----------
284+ arr : array_like
285+ Input data, convertible to a NumPy array.
286+
287+ Returns
288+ -------
289+ PODVector
290+ A new PODVector with a copy of the data.
291+ )" )
281292 ;
282293}
283294
0 commit comments