From 82b567c9cc37255d6de7a2ca87cae712034b332b Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Wed, 1 May 2024 18:42:06 +0900 Subject: [PATCH 1/5] implement array wrapper getitem --- cpp/modmesh/buffer/SimpleArray.hpp | 2 +- .../buffer/pymod/SimpleArrayCaster.hpp | 6 -- cpp/modmesh/buffer/pymod/array_common.hpp | 98 +++++++++++++++++++ cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp | 9 +- modmesh/app/mh_3dmix.py | 6 +- modmesh/app/sample_mesh.py | 36 +++---- tests/test_buffer.py | 8 ++ tests/test_gmsh.py | 6 +- tests/test_grid1d.py | 2 +- tests/test_mesh.py | 28 +++--- thirdparty/PUI | 2 +- 11 files changed, 148 insertions(+), 55 deletions(-) diff --git a/cpp/modmesh/buffer/SimpleArray.hpp b/cpp/modmesh/buffer/SimpleArray.hpp index 62e62e91..d9926d0f 100644 --- a/cpp/modmesh/buffer/SimpleArray.hpp +++ b/cpp/modmesh/buffer/SimpleArray.hpp @@ -654,7 +654,7 @@ class SimpleArray template using is_simple_array = std::is_same< - std::remove_reference_t, + std::remove_const_t>, SimpleArray::value_type>>; template diff --git a/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp b/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp index 9bb105af..587782a9 100644 --- a/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp +++ b/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp @@ -77,12 +77,6 @@ namespace detail const modmesh::SimpleArray##DATATYPE * array_from_arrayplex = reinterpret_cast(arrayplex.instance_ptr()); \ value = const_cast(array_from_arrayplex); \ return true; \ - } \ - \ - /* Conversion from C++ to Python object */ \ - static pybind11::handle cast(modmesh::SimpleArray##DATATYPE && src, pybind11::return_value_policy policy, pybind11::handle parent) \ - { \ - return base::cast(src, policy, parent); \ } \ } diff --git a/cpp/modmesh/buffer/pymod/array_common.hpp b/cpp/modmesh/buffer/pymod/array_common.hpp index 0f75f3e7..750185cc 100644 --- a/cpp/modmesh/buffer/pymod/array_common.hpp +++ b/cpp/modmesh/buffer/pymod/array_common.hpp @@ -89,6 +89,78 @@ class ArrayPropertyHelper } } + static pybind11::object getitem_parser(const SimpleArray & arr, pybind11::args const & args) + { + namespace py = pybind11; + if (args.size() != 1) + { + throw std::runtime_error("unsupported operation."); + } + + const py::object & py_key = args[0]; + + // sarr[x] + if (py::isinstance(py_key)) + { + const auto key = py_key.cast(); + return py::cast(arr.at(key)); + } + + bool is_tuple = py::isinstance(py_key); + bool is_number_tuple = false; + if (is_tuple) + { + const py::tuple tuple_in = py_key; + if (tuple_in.size() > 0) + { + is_number_tuple = py::isinstance(tuple_in[0]); + } + } + + // sarr[x, y, z] + if (is_number_tuple) + { + const auto key = py_key.cast>(); + return py::cast(arr.at(key)); + } + + // multi-dimension with slice and ellipsis + // sarr[slice, slice, ellipsis] + if (is_tuple) + { + const py::tuple tuple_in = py_key; + + auto slices = make_default_slices(arr); + process_slices(tuple_in, slices, arr.ndim()); + + SimpleArray arr_out(get_shape_from_slices(slices)); + + broadcast_array_using_slice(arr_out, slices, to_ndarray(arr)); + return py::cast(arr_out); + } + // one-dimension with slice + // sarr[slice] + if (py::isinstance(py_key)) + { + const auto slice_in = py_key.cast(); + + auto slices = make_default_slices(arr); + copy_slice(slices[0], slice_in); + + SimpleArray arr_out(get_shape_from_slices(slices)); + + broadcast_array_using_slice(arr_out, slices, to_ndarray(arr)); + return py::cast(arr_out); + } + // sarr[ellipsis] + if (py::isinstance(py_key)) + { + return py::cast(arr); + } + + throw std::runtime_error("unsupported operation."); + } + static void setitem_parser(SimpleArray & arr_out, pybind11::args const & args) { namespace py = pybind11; @@ -116,6 +188,16 @@ class ArrayPropertyHelper arr_out.at(key) = py_value.cast(); return; } + // sarr[ellipsis] = V + if (py::isinstance(py_key) && is_number) + { + const auto value = py_value.cast(); + for (ssize_t i = 0; i < arr_out.size(); i++) + { + arr_out.at(i) = value; + } + return; + } const bool is_sequence = py::isinstance(py_value) || py::isinstance(py_value) || py::isinstance(py_value); @@ -297,6 +379,22 @@ class ArrayPropertyHelper arr_out.set_nghost(nghost); } } + + static shape_type get_shape_from_slices(std::vector const & slices) + { + + shape_type shape; + for (auto const & slice : slices) + { + std::cout << slice[0] << ", " << slice[1] << ", " << slice[2] << std::endl; + + shape.push_back((slice[1] - slice[0]) / slice[2]); + + std::cout << ((slice[1] - slice[0]) / slice[2]) << std::endl; + } + std::cout << std::endl; + return shape; + } }; } /* end namespace python */ diff --git a/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp b/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp index f5b95fa1..94c59cc9 100644 --- a/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp +++ b/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp @@ -123,15 +123,8 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapSimpleArray return ret; }) .def("__len__", &wrapped_type::size) - .def( - "__getitem__", - [](wrapped_type const & self, ssize_t key) - { return self.at(key); }) - .def( - "__getitem__", - [](wrapped_type const & self, std::vector const & key) - { return self.at(key); }) .def("__setitem__", &property_helper::setitem_parser) + .def("__getitem__", &property_helper::getitem_parser) .def( "reshape", [](wrapped_type const & self, py::object const & shape) diff --git a/modmesh/app/mh_3dmix.py b/modmesh/app/mh_3dmix.py index 181d8fab..e7e0bb0b 100644 --- a/modmesh/app/mh_3dmix.py +++ b/modmesh/app/mh_3dmix.py @@ -41,16 +41,16 @@ def make_3dmix(): PYR = core.StaticMesh.PYRAMID mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd.ndarray[:, :] = [ + mh.ndcrd[:, :] = [ (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), (0.5, 1.5, 0.5), (1.5, 1, 0.5), (1.5, 0, 0.5), ] - mh.cltpn.ndarray[:] = [ + mh.cltpn[:] = [ HEX, PYR, TET, PSM, ] - mh.clnds.ndarray[:, :9] = [ + mh.clnds[:, :9] = [ (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), ] diff --git a/modmesh/app/sample_mesh.py b/modmesh/app/sample_mesh.py index df0af539..b9b39fcd 100644 --- a/modmesh/app/sample_mesh.py +++ b/modmesh/app/sample_mesh.py @@ -157,9 +157,9 @@ def make_mesh_viewer(path): def make_triangle(): mh = core.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TRIANGLE - mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) + mh.ndcrd[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) + mh.cltpn[:] = core.StaticMesh.TRIANGLE + mh.clnds[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) mh.build_interior() mh.build_boundary() mh.build_ghost() @@ -172,30 +172,30 @@ def make_2dmix(do_small=False): if do_small: mh = core.StaticMesh(ndim=2, nnode=6, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = [ + mh.ndcrd[:, :] = [ (0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (2, 1) ] - mh.cltpn.ndarray[:] = [ + mh.cltpn[:] = [ T, T, Q, ] - mh.clnds.ndarray[:, :5] = [ + mh.clnds[:, :5] = [ (3, 0, 3, 2, -1), (3, 0, 1, 3, -1), (4, 1, 4, 5, 3), ] else: mh = core.StaticMesh(ndim=2, nnode=16, nface=0, ncell=14) - mh.ndcrd.ndarray[:, :] = [ + mh.ndcrd[:, :] = [ (0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1), (0, 2), (1, 2), (2, 2), (3, 2), (0, 3), (1, 3), (2, 3), (3, 3), ] - mh.cltpn.ndarray[:] = [ + mh.cltpn[:] = [ T, T, T, T, T, T, # 0-5, Q, Q, # 6-7 T, T, T, T, # 8-11 Q, Q, # 12-13 ] - mh.clnds.ndarray[:, :5] = [ + mh.clnds[:, :5] = [ (3, 0, 5, 4, -1), (3, 0, 1, 5, -1), # 0-1 triangles (3, 1, 2, 5, -1), (3, 2, 6, 5, -1), # 2-3 triangles (3, 2, 7, 6, -1), (3, 2, 3, 7, -1), # 4-5 triangles @@ -217,16 +217,16 @@ def make_3dmix(): PYR = core.StaticMesh.PYRAMID mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd.ndarray[:, :] = [ + mh.ndcrd[:, :] = [ (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), (0.5, 1.5, 0.5), (1.5, 1, 0.5), (1.5, 0, 0.5), ] - mh.cltpn.ndarray[:] = [ + mh.cltpn[:] = [ HEX, PYR, TET, PSM, ] - mh.clnds.ndarray[:, :9] = [ + mh.clnds[:, :9] = [ (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), ] @@ -239,7 +239,7 @@ def make_3dmix(): def make_solvcon(): Q = core.StaticMesh.QUADRILATERAL mh = core.StaticMesh(ndim=2, nnode=140, nface=0, ncell=65) - mh.ndcrd.ndarray[:, :] = [ + mh.ndcrd[:, :] = [ (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), (14, 0), (15, 0), (16, 0), (18, 0), (19, 0), (20, 0), (21, 0), @@ -261,7 +261,7 @@ def make_solvcon(): (23, 4), (24, 4), (25, 4), (26, 4), (27, 4), (0, 5), (1, 5), (2, 5), (3, 5) ] - mh.cltpn.ndarray[:] = [ + mh.cltpn[:] = [ Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 0-20 Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 21-31 @@ -269,7 +269,7 @@ def make_solvcon(): Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 46-61 Q, Q, Q # 62-64 ] - mh.clnds.ndarray[:, :5] = [ + mh.clnds[:, :5] = [ (4, 0, 1, 30, 29), (4, 1, 2, 31, 30), (4, 2, 3, 32, 31), @@ -344,9 +344,9 @@ def make_solvcon(): def make_tetrahedron(): mh = core.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn.ndarray[:] = core.StaticMesh.TETRAHEDRON - mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] + mh.ndcrd[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) + mh.cltpn[:] = core.StaticMesh.TETRAHEDRON + mh.clnds[:, :5] = [(4, 0, 1, 2, 3)] mh.build_interior() mh.build_boundary() mh.build_ghost() diff --git a/tests/test_buffer.py b/tests/test_buffer.py index 92e7cca4..34a20660 100644 --- a/tests/test_buffer.py +++ b/tests/test_buffer.py @@ -434,6 +434,14 @@ def test_SimpleArray_from_ndarray_content(self): sarr.ndarray.fill(100) self.assertTrue((ndarr == 100).all()) + def test_SimpleArray_broadcast_ellipsis_value(self): + sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) + sarr[...] = 1.234 + for i in range(2): + for j in range(3): + for k in range(4): + self.assertEqual(1.234, sarr[i, j, k]) + def test_SimpleArray_broadcast_ellipsis_shape(self): sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) ndarr = np.arange(2 * 3 * 4, dtype='float64').reshape((2, 3, 4)) diff --git a/tests/test_gmsh.py b/tests/test_gmsh.py index 2e95c34a..f365e953 100644 --- a/tests/test_gmsh.py +++ b/tests/test_gmsh.py @@ -22,14 +22,14 @@ def test_gmsh_parsing(self): # Due to ghost cell and ghost node had been created, the real body # had been shifted and start with index 3 - np.testing.assert_almost_equal(blk.ndcrd.ndarray[3:, :].tolist(), + np.testing.assert_almost_equal(blk.ndcrd[3:, :].ndarray.tolist(), [[0.0, 0.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]]) # Check cells information self.assertEqual(blk.ncell, 3) - self.assertEqual(blk.cltpn.ndarray[3:].tolist(), [4, 4, 4]) - self.assertEqual(blk.clnds.ndarray[3:, :4].tolist(), [[3, 0, 1, 2], + self.assertEqual(blk.cltpn[3:].ndarray.tolist(), [4, 4, 4]) + self.assertEqual(blk.clnds[3:, :4].ndarray.tolist(), [[3, 0, 1, 2], [3, 0, 2, 3], [3, 0, 3, 1]]) diff --git a/tests/test_grid1d.py b/tests/test_grid1d.py index 2413b23d..2ba3fc1c 100644 --- a/tests/test_grid1d.py +++ b/tests/test_grid1d.py @@ -51,7 +51,7 @@ def test_coord(self): # gd.coord is a SimpleArray. self.assertEqual(np.float64, gd.coord.ndarray.dtype) - gd.coord.ndarray[:] = np.arange(11, dtype='float64') + gd.coord[:] = np.arange(11, dtype='float64') self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], list(gd)) self.assertFalse(gd.coord.is_from_python) diff --git a/tests/test_mesh.py b/tests/test_mesh.py index c1b556ee..1ce3a577 100644 --- a/tests/test_mesh.py +++ b/tests/test_mesh.py @@ -64,11 +64,11 @@ def _check_shape(self, mh, ndim, nnode, nface, ncell, self.assertEqual((mh.ngstcell + mh.ncell, mh.CLMFC+1), mh.clfcs.shape) def _check_metric_trivial(self, mh): - self.assertTrue((mh.fccnd.ndarray[:, :] == 0).all()) - self.assertTrue((mh.fcnml.ndarray[:, :] == 0).all()) - self.assertTrue((mh.fcara.ndarray[:] == 0).all()) - self.assertTrue((mh.clcnd.ndarray[:, :] == 0).all()) - self.assertTrue((mh.clvol.ndarray[:] == 0).all()) + self.assertTrue((mh.fccnd[:].ndarray == 0).all()) + self.assertTrue((mh.fcnml[:, :].ndarray == 0).all()) + self.assertTrue((mh.fcara[:].ndarray == 0).all()) + self.assertTrue((mh.clcnd[:, :].ndarray == 0).all()) + self.assertTrue((mh.clvol[:].ndarray == 0).all()) def test_construct(self): def _test(cls, ndim): @@ -82,9 +82,9 @@ def _test(cls, ndim): def test_2d_trivial_triangles(self): mh = modmesh.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn.ndarray[:] = modmesh.StaticMesh.TRIANGLE - mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) + mh.ndcrd[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) + mh.cltpn[:] = modmesh.StaticMesh.TRIANGLE + mh.clnds[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) self._check_shape(mh, ndim=2, nnode=4, nface=0, ncell=3, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, @@ -140,9 +140,9 @@ def test_2d_trivial_triangles(self): def test_3d_single_tetrahedron(self): mh = modmesh.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn.ndarray[:] = modmesh.StaticMesh.TETRAHEDRON - mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] + mh.ndcrd[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) + mh.cltpn[:] = modmesh.StaticMesh.TETRAHEDRON + mh.clnds[:, :5] = [(4, 0, 1, 2, 3)] self._check_shape(mh, ndim=3, nnode=4, nface=4, ncell=1, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, @@ -203,9 +203,9 @@ def test_3d_single_tetrahedron(self): def test_1d_single_line(self): mh = modmesh.StaticMesh(ndim=1, nnode=2, nface=0, ncell=1) - mh.ndcrd.ndarray[:] = [[0], [1]] - mh.cltpn.ndarray[:] = modmesh.StaticMesh.LINE - mh.clnds.ndarray[:, :3] = [(2, 0, 1)] + mh.ndcrd[:] = [[0], [1]] + mh.cltpn[...] = modmesh.StaticMesh.LINE + mh.clnds[:, :3] = [(2, 0, 1)] self._check_shape(mh, ndim=1, nnode=2, nface=0, ncell=1, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, diff --git a/thirdparty/PUI b/thirdparty/PUI index 1560f360..ff24318c 160000 --- a/thirdparty/PUI +++ b/thirdparty/PUI @@ -1 +1 @@ -Subproject commit 1560f360f4d5fd62f2204b7e52297338249df350 +Subproject commit ff24318c398d62c9033a75d608b962cd8173afb5 From f60c5eeb028e1e540bb5048490d9fdc270aa64d2 Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Tue, 7 May 2024 18:41:36 +0900 Subject: [PATCH 2/5] tmp --- cpp/modmesh/buffer/pymod/TypeBroadcast.hpp | 56 ++++++++++++++++++++++ cpp/modmesh/buffer/pymod/array_common.hpp | 49 +++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp b/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp index 601abbfb..65339705 100644 --- a/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp +++ b/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp @@ -151,6 +151,34 @@ struct TypeBroadcast } } + static void broadcast(SimpleArray & arr_out, std::vector const & slices, pybind11::object const & py_number) + { + const T assigned_value = py_number.cast(); + + shape_type out_shape(arr_out.ndim()); + for (size_t i = 0; i < arr_out.ndim(); i++) + { + slice_type const & slice = slices[i]; + if ((slice[1] - slice[0]) % slice[2] == 0) + { + out_shape[i] = (slice[1] - slice[0]) / slice[2]; + } + else + { + out_shape[i] = (slice[1] - slice[0]) / slice[2] + 1; + } + } + + shape_type sidx_init(arr_out.ndim()); + + for (size_t i = 0; i < arr_out.ndim(); ++i) + { + sidx_init[i] = 0; + } + + assigned_idx(arr_out, slices, assigned_value, out_shape, sidx_init, static_cast(arr_out.ndim()) - 1); + } + static void broadcast(SimpleArray & arr_out, std::vector const & slices, pybind11::array const & arr_in) { if (dtype_is_type(arr_in)) @@ -230,6 +258,34 @@ struct TypeBroadcast throw std::runtime_error(msg.str()); } + +private: + // NOLINTNEXTLINE(misc-no-recursion) + static void assigned_idx(SimpleArray & arr_out, std::vector const & slices, const T value, shape_type out_shape, shape_type sidx, int dim) + { + if (dim < 0) + { + return; + } + + for (size_t i = 0; i < out_shape[dim]; ++i) + { + sidx[dim] = i; + + size_t offset_out = 0; + for (size_t it = 0; it < arr_out.ndim(); ++it) + { + auto step = slices[it][2]; + offset_out += arr_out.stride(it) * sidx[it] * step; + } + + // NOLINTNEXTLINE(bugprone-signed-char-misuse, cert-str34-c) + arr_out.at(offset_out) = value; + + // recursion here + assigned_idx(arr_out, slices, value, out_shape, sidx, dim - 1); + } + } }; /* end struct TypeBroadCast */ } /* end namespace python */ diff --git a/cpp/modmesh/buffer/pymod/array_common.hpp b/cpp/modmesh/buffer/pymod/array_common.hpp index 750185cc..10240bb9 100644 --- a/cpp/modmesh/buffer/pymod/array_common.hpp +++ b/cpp/modmesh/buffer/pymod/array_common.hpp @@ -188,6 +188,30 @@ class ArrayPropertyHelper arr_out.at(key) = py_value.cast(); return; } + // multi-dimension with slice and ellipsis + // sarr[slice, slice, ellipsis] = v + if (py::isinstance(py_key) && is_number) + { + const py::tuple tuple_in = py_key; + + auto slices = make_default_slices(arr_out); + process_slices(tuple_in, slices, arr_out.ndim()); + + broadcast_array_using_slice(arr_out, slices, py_value); + return; + } + // one-dimension with slice + // sarr[slice] = v + if (py::isinstance(py_key) && is_number) + { + const auto slice_in = py_key.cast(); + + auto slices = make_default_slices(arr_out); + copy_slice(slices[0], slice_in); + + broadcast_array_using_slice(arr_out, slices, py_value); + return; + } // sarr[ellipsis] = V if (py::isinstance(py_key) && is_number) { @@ -380,6 +404,31 @@ class ArrayPropertyHelper } } + static void broadcast_array_using_slice(SimpleArray & arr_out, + std::vector const & slices, + const pybind11::object & py_number) + { + namespace py = pybind11; + + if (!py::isinstance(py_number) && !py::isinstance(py_number) && !py::isinstance(py_number)) + { + throw std::runtime_error("Cannot broadcast a non-number value to an array."); + } + + const size_t nghost = arr_out.nghost(); + if (0 != nghost) + { + arr_out.set_nghost(0); + } + + TypeBroadcast::broadcast(arr_out, slices, py_number); + + if (0 != nghost) + { + arr_out.set_nghost(nghost); + } + } + static shape_type get_shape_from_slices(std::vector const & slices) { From 8a6502ff196cd7b2ea9cb05c81a874b2cf588318 Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Tue, 14 May 2024 17:25:19 +0900 Subject: [PATCH 3/5] revert --- modmesh/app/mh_3dmix.py | 6 +++--- modmesh/app/sample_mesh.py | 36 ++++++++++++++++++------------------ tests/test_gmsh.py | 6 +++--- tests/test_grid1d.py | 2 +- tests/test_mesh.py | 28 ++++++++++++++-------------- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/modmesh/app/mh_3dmix.py b/modmesh/app/mh_3dmix.py index e7e0bb0b..181d8fab 100644 --- a/modmesh/app/mh_3dmix.py +++ b/modmesh/app/mh_3dmix.py @@ -41,16 +41,16 @@ def make_3dmix(): PYR = core.StaticMesh.PYRAMID mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd[:, :] = [ + mh.ndcrd.ndarray[:, :] = [ (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), (0.5, 1.5, 0.5), (1.5, 1, 0.5), (1.5, 0, 0.5), ] - mh.cltpn[:] = [ + mh.cltpn.ndarray[:] = [ HEX, PYR, TET, PSM, ] - mh.clnds[:, :9] = [ + mh.clnds.ndarray[:, :9] = [ (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), ] diff --git a/modmesh/app/sample_mesh.py b/modmesh/app/sample_mesh.py index b9b39fcd..df0af539 100644 --- a/modmesh/app/sample_mesh.py +++ b/modmesh/app/sample_mesh.py @@ -157,9 +157,9 @@ def make_mesh_viewer(path): def make_triangle(): mh = core.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn[:] = core.StaticMesh.TRIANGLE - mh.clnds[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) + mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) + mh.cltpn.ndarray[:] = core.StaticMesh.TRIANGLE + mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) mh.build_interior() mh.build_boundary() mh.build_ghost() @@ -172,30 +172,30 @@ def make_2dmix(do_small=False): if do_small: mh = core.StaticMesh(ndim=2, nnode=6, nface=0, ncell=3) - mh.ndcrd[:, :] = [ + mh.ndcrd.ndarray[:, :] = [ (0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (2, 1) ] - mh.cltpn[:] = [ + mh.cltpn.ndarray[:] = [ T, T, Q, ] - mh.clnds[:, :5] = [ + mh.clnds.ndarray[:, :5] = [ (3, 0, 3, 2, -1), (3, 0, 1, 3, -1), (4, 1, 4, 5, 3), ] else: mh = core.StaticMesh(ndim=2, nnode=16, nface=0, ncell=14) - mh.ndcrd[:, :] = [ + mh.ndcrd.ndarray[:, :] = [ (0, 0), (1, 0), (2, 0), (3, 0), (0, 1), (1, 1), (2, 1), (3, 1), (0, 2), (1, 2), (2, 2), (3, 2), (0, 3), (1, 3), (2, 3), (3, 3), ] - mh.cltpn[:] = [ + mh.cltpn.ndarray[:] = [ T, T, T, T, T, T, # 0-5, Q, Q, # 6-7 T, T, T, T, # 8-11 Q, Q, # 12-13 ] - mh.clnds[:, :5] = [ + mh.clnds.ndarray[:, :5] = [ (3, 0, 5, 4, -1), (3, 0, 1, 5, -1), # 0-1 triangles (3, 1, 2, 5, -1), (3, 2, 6, 5, -1), # 2-3 triangles (3, 2, 7, 6, -1), (3, 2, 3, 7, -1), # 4-5 triangles @@ -217,16 +217,16 @@ def make_3dmix(): PYR = core.StaticMesh.PYRAMID mh = core.StaticMesh(ndim=3, nnode=11, nface=0, ncell=4) - mh.ndcrd[:, :] = [ + mh.ndcrd.ndarray[:, :] = [ (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1), (0.5, 1.5, 0.5), (1.5, 1, 0.5), (1.5, 0, 0.5), ] - mh.cltpn[:] = [ + mh.cltpn.ndarray[:] = [ HEX, PYR, TET, PSM, ] - mh.clnds[:, :9] = [ + mh.clnds.ndarray[:, :9] = [ (8, 0, 1, 2, 3, 4, 5, 6, 7), (5, 2, 3, 7, 6, 8, -1, -1, -1), (4, 2, 6, 9, 8, -1, -1, -1, -1), (6, 2, 6, 9, 1, 5, 10, -1, -1), ] @@ -239,7 +239,7 @@ def make_3dmix(): def make_solvcon(): Q = core.StaticMesh.QUADRILATERAL mh = core.StaticMesh(ndim=2, nnode=140, nface=0, ncell=65) - mh.ndcrd[:, :] = [ + mh.ndcrd.ndarray[:, :] = [ (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (10, 0), (11, 0), (12, 0), (13, 0), (14, 0), (15, 0), (16, 0), (18, 0), (19, 0), (20, 0), (21, 0), @@ -261,7 +261,7 @@ def make_solvcon(): (23, 4), (24, 4), (25, 4), (26, 4), (27, 4), (0, 5), (1, 5), (2, 5), (3, 5) ] - mh.cltpn[:] = [ + mh.cltpn.ndarray[:] = [ Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 0-20 Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 21-31 @@ -269,7 +269,7 @@ def make_solvcon(): Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, Q, # 46-61 Q, Q, Q # 62-64 ] - mh.clnds[:, :5] = [ + mh.clnds.ndarray[:, :5] = [ (4, 0, 1, 30, 29), (4, 1, 2, 31, 30), (4, 2, 3, 32, 31), @@ -344,9 +344,9 @@ def make_solvcon(): def make_tetrahedron(): mh = core.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn[:] = core.StaticMesh.TETRAHEDRON - mh.clnds[:, :5] = [(4, 0, 1, 2, 3)] + mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) + mh.cltpn.ndarray[:] = core.StaticMesh.TETRAHEDRON + mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] mh.build_interior() mh.build_boundary() mh.build_ghost() diff --git a/tests/test_gmsh.py b/tests/test_gmsh.py index f365e953..2e95c34a 100644 --- a/tests/test_gmsh.py +++ b/tests/test_gmsh.py @@ -22,14 +22,14 @@ def test_gmsh_parsing(self): # Due to ghost cell and ghost node had been created, the real body # had been shifted and start with index 3 - np.testing.assert_almost_equal(blk.ndcrd[3:, :].ndarray.tolist(), + np.testing.assert_almost_equal(blk.ndcrd.ndarray[3:, :].tolist(), [[0.0, 0.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]]) # Check cells information self.assertEqual(blk.ncell, 3) - self.assertEqual(blk.cltpn[3:].ndarray.tolist(), [4, 4, 4]) - self.assertEqual(blk.clnds[3:, :4].ndarray.tolist(), [[3, 0, 1, 2], + self.assertEqual(blk.cltpn.ndarray[3:].tolist(), [4, 4, 4]) + self.assertEqual(blk.clnds.ndarray[3:, :4].tolist(), [[3, 0, 1, 2], [3, 0, 2, 3], [3, 0, 3, 1]]) diff --git a/tests/test_grid1d.py b/tests/test_grid1d.py index 2ba3fc1c..2413b23d 100644 --- a/tests/test_grid1d.py +++ b/tests/test_grid1d.py @@ -51,7 +51,7 @@ def test_coord(self): # gd.coord is a SimpleArray. self.assertEqual(np.float64, gd.coord.ndarray.dtype) - gd.coord[:] = np.arange(11, dtype='float64') + gd.coord.ndarray[:] = np.arange(11, dtype='float64') self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], list(gd)) self.assertFalse(gd.coord.is_from_python) diff --git a/tests/test_mesh.py b/tests/test_mesh.py index 1ce3a577..c1b556ee 100644 --- a/tests/test_mesh.py +++ b/tests/test_mesh.py @@ -64,11 +64,11 @@ def _check_shape(self, mh, ndim, nnode, nface, ncell, self.assertEqual((mh.ngstcell + mh.ncell, mh.CLMFC+1), mh.clfcs.shape) def _check_metric_trivial(self, mh): - self.assertTrue((mh.fccnd[:].ndarray == 0).all()) - self.assertTrue((mh.fcnml[:, :].ndarray == 0).all()) - self.assertTrue((mh.fcara[:].ndarray == 0).all()) - self.assertTrue((mh.clcnd[:, :].ndarray == 0).all()) - self.assertTrue((mh.clvol[:].ndarray == 0).all()) + self.assertTrue((mh.fccnd.ndarray[:, :] == 0).all()) + self.assertTrue((mh.fcnml.ndarray[:, :] == 0).all()) + self.assertTrue((mh.fcara.ndarray[:] == 0).all()) + self.assertTrue((mh.clcnd.ndarray[:, :] == 0).all()) + self.assertTrue((mh.clvol.ndarray[:] == 0).all()) def test_construct(self): def _test(cls, ndim): @@ -82,9 +82,9 @@ def _test(cls, ndim): def test_2d_trivial_triangles(self): mh = modmesh.StaticMesh(ndim=2, nnode=4, nface=0, ncell=3) - mh.ndcrd[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) - mh.cltpn[:] = modmesh.StaticMesh.TRIANGLE - mh.clnds[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) + mh.ndcrd.ndarray[:, :] = (0, 0), (-1, -1), (1, -1), (0, 1) + mh.cltpn.ndarray[:] = modmesh.StaticMesh.TRIANGLE + mh.clnds.ndarray[:, :4] = (3, 0, 1, 2), (3, 0, 2, 3), (3, 0, 3, 1) self._check_shape(mh, ndim=2, nnode=4, nface=0, ncell=3, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, @@ -140,9 +140,9 @@ def test_2d_trivial_triangles(self): def test_3d_single_tetrahedron(self): mh = modmesh.StaticMesh(ndim=3, nnode=4, nface=4, ncell=1) - mh.ndcrd[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) - mh.cltpn[:] = modmesh.StaticMesh.TETRAHEDRON - mh.clnds[:, :5] = [(4, 0, 1, 2, 3)] + mh.ndcrd.ndarray[:, :] = (0, 0, 0), (0, 1, 0), (-1, 1, 0), (0, 1, 1) + mh.cltpn.ndarray[:] = modmesh.StaticMesh.TETRAHEDRON + mh.clnds.ndarray[:, :5] = [(4, 0, 1, 2, 3)] self._check_shape(mh, ndim=3, nnode=4, nface=4, ncell=1, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, @@ -203,9 +203,9 @@ def test_3d_single_tetrahedron(self): def test_1d_single_line(self): mh = modmesh.StaticMesh(ndim=1, nnode=2, nface=0, ncell=1) - mh.ndcrd[:] = [[0], [1]] - mh.cltpn[...] = modmesh.StaticMesh.LINE - mh.clnds[:, :3] = [(2, 0, 1)] + mh.ndcrd.ndarray[:] = [[0], [1]] + mh.cltpn.ndarray[:] = modmesh.StaticMesh.LINE + mh.clnds.ndarray[:, :3] = [(2, 0, 1)] self._check_shape(mh, ndim=1, nnode=2, nface=0, ncell=1, nbound=0, ngstnode=0, ngstface=0, ngstcell=0, From cf1112463fdef59dd6d7b341d4fc8eef840d4879 Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Tue, 14 May 2024 18:58:16 +0900 Subject: [PATCH 4/5] test --- tests/test_buffer.py | 48 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/tests/test_buffer.py b/tests/test_buffer.py index 34a20660..1e3f6b27 100644 --- a/tests/test_buffer.py +++ b/tests/test_buffer.py @@ -434,7 +434,7 @@ def test_SimpleArray_from_ndarray_content(self): sarr.ndarray.fill(100) self.assertTrue((ndarr == 100).all()) - def test_SimpleArray_broadcast_ellipsis_value(self): + def test_SimpleArray_broadcast_ellipsis_number(self): sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) sarr[...] = 1.234 for i in range(2): @@ -442,6 +442,50 @@ def test_SimpleArray_broadcast_ellipsis_value(self): for k in range(4): self.assertEqual(1.234, sarr[i, j, k]) + def test_SimpleArray_broadcast_slice_ellipsis_number(self): + sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) + ndarr = np.arange(2 * 3 * 4, dtype='float64').reshape((2, 3, 4)) + + VALUE1 = 1.234 + VALUE2 = 5.678 + sarr.fill(VALUE1) + ndarr[...] = VALUE1 + + ndarr[0:1, ..., ::2] = VALUE2 + sarr[0:1, ..., ::2] = VALUE2 + + for i in range(2): + for j in range(3): + for k in range(4): + self.assertEqual(ndarr[i, j, k], sarr[i, j, k]) + + def test_SimpleArray_broadcast_slice_number(self): + TOTAL = 20 + + sarr = modmesh.SimpleArrayFloat64(TOTAL) + ndarr = np.arange(100, dtype='float64') + + VALUE1 = 1.234 + VALUE2 = 5.678 + VALUE3 = 9.123 + + sarr.fill(VALUE1) + ndarr[...] = VALUE1 + + ndarr[2:15:3] = VALUE2 + sarr[2:15:3] = VALUE2 + + for i in range(TOTAL): + print(i, ndarr[i], sarr[i]) + for i in range(TOTAL): + self.assertEqual(ndarr[i], sarr[i]) + + ndarr[5:19:2] = VALUE3 + sarr[5:19:2] = VALUE3 + + for i in range(TOTAL): + self.assertEqual(ndarr[i], sarr[i]) + def test_SimpleArray_broadcast_ellipsis_shape(self): sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) ndarr = np.arange(2 * 3 * 4, dtype='float64').reshape((2, 3, 4)) @@ -649,8 +693,6 @@ def test_SimpleArray_broadcast_slice_ghost_1d(self): sarr[::STEP] = ndarr[...] ndarr2[::STEP] = ndarr[...] - print(ndarr2.shape) - for i in range(0, N, STEP): self.assertEqual(ndarr2[i], sarr[i - G]) From fec68ad9ba940bdd25208c2309787572307c86d1 Mon Sep 17 00:00:00 2001 From: "anchi.liu" Date: Wed, 15 May 2024 17:59:32 +0900 Subject: [PATCH 5/5] add more tests --- tests/test_buffer.py | 55 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/tests/test_buffer.py b/tests/test_buffer.py index 1e3f6b27..47a3b881 100644 --- a/tests/test_buffer.py +++ b/tests/test_buffer.py @@ -434,6 +434,59 @@ def test_SimpleArray_from_ndarray_content(self): sarr.ndarray.fill(100) self.assertTrue((ndarr == 100).all()) + def test_SimpleArray_getitem_ellipsis(self): + sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) + + count = 0 + for i in range(2): + for j in range(3): + for k in range(4): + sarr[i, j, k] = count + count += 1 + + sarr2 = sarr[...] + self.assertEqual(sarr.shape, sarr2.shape) + for i in range(2): + for j in range(3): + for k in range(4): + self.assertEqual(sarr[i, j, k], sarr2[i, j, k]) + + def test_SimpleArray_getitem_slice_ellipsis(self): + sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) + ndarr = np.arange(2 * 3 * 4, dtype='float64').reshape((2, 3, 4)) + + count = 0 + for i in range(2): + for j in range(3): + for k in range(4): + sarr[i, j, k] = count + ndarr[i, j, k] = count + count += 1 + + sarr2 = sarr[::, ..., ::2] + ndarr2 = ndarr[::, ..., ::2] + self.assertEqual(ndarr2.shape, sarr2.shape) + for i in range(2): + for j in range(3): + for k in range(2): + self.assertEqual(ndarr2[i, j, k], sarr2[i, j, k]) + + def test_SimpleArray_getitem_slice(self): + sarr = modmesh.SimpleArrayFloat64(24) + ndarr = np.arange(2 * 3 * 4, dtype='float64') + + count = 0 + for i in range(24): + sarr[i] = count + ndarr[i] = count + count += 1 + + sarr2 = sarr[1:19:3] + ndarr2 = ndarr[1:19:3] + self.assertEqual(ndarr2.size, sarr2.size) + for i in range(ndarr2.size): + self.assertEqual(ndarr2[i], sarr2[i]) + def test_SimpleArray_broadcast_ellipsis_number(self): sarr = modmesh.SimpleArrayFloat64((2, 3, 4)) sarr[...] = 1.234 @@ -461,7 +514,7 @@ def test_SimpleArray_broadcast_slice_ellipsis_number(self): def test_SimpleArray_broadcast_slice_number(self): TOTAL = 20 - + sarr = modmesh.SimpleArrayFloat64(TOTAL) ndarr = np.arange(100, dtype='float64')