From 038727f95f7c85302b1abc465ac55ecd9d4b6c01 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 6 Nov 2023 11:48:40 -0800 Subject: [PATCH] Pandas: `ParticleContainer_*.to_df()` (#220) * Pandas: `ParticleContainer_*.to_df()` Copy all particles into a `pandas.DataFrame`. Supports local and MPI-gathered results. * Update Stubs --- .github/workflows/ubuntu.yml | 5 +- .github/workflows/windows.yml | 3 +- README.md | 5 + docs/source/install/dependencies.rst | 9 +- src/Particle/ParticleContainer.H | 10 + src/amrex/ParticleContainer.py | 104 +++++ src/amrex/space1d/__init__.py | 2 + src/amrex/space1d/__init__.pyi | 2 + .../space1d/amrex_1d_pybind/__init__.pyi | 432 ++++++++++++++++++ src/amrex/space2d/__init__.py | 2 + src/amrex/space2d/__init__.pyi | 2 + .../space2d/amrex_2d_pybind/__init__.pyi | 432 ++++++++++++++++++ src/amrex/space3d/__init__.py | 2 + src/amrex/space3d/__init__.pyi | 2 + .../space3d/amrex_3d_pybind/__init__.pyi | 432 ++++++++++++++++++ tests/test_particleContainer.py | 37 ++ 16 files changed, 1474 insertions(+), 7 deletions(-) create mode 100644 src/amrex/ParticleContainer.py diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 9b9b9145..ba9118cd 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -82,7 +82,7 @@ jobs: export CXX=$(which g++-10) python3 -m pip install -U pip setuptools wheel python3 -m pip install -U cmake - python3 -m pip install -U pytest mpi4py + python3 -m pip install -U pandas pytest mpi4py cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ @@ -174,7 +174,8 @@ jobs: export CCACHE_MAXSIZE=300M ccache -z - python3 -m pip install -U pip pytest + python3 -m pip install -U pip + python3 -m pip install -U pandas pytest python3 -m pip install -v . python3 -c "import amrex.space1d as amr; print(amr.__version__)" python3 -c "import amrex.space2d as amr; print(amr.__version__)" diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6682c9dd..b4b8a474 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -18,7 +18,8 @@ jobs: python-version: '3.x' - name: Build & Install run: | - python3 -m pip install -U pip pytest + python3 -m pip install -U pip + python3 -m pip install -U pandas pytest python3 -m pip install -v . if(!$?) { Exit $LASTEXITCODE } diff --git a/README.md b/README.md index c4945f83..dc85c629 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Optional dependencies include: - [mpi4py](https://mpi4py.readthedocs.io) 2.1+: for multi-node and/or multi-GPU execution - [CCache](https://ccache.dev): to speed up rebuilds (for CUDA support, needs 3.7.9+ and 4.2+ is recommended) - further [optional dependencies of AMReX](https://github.com/AMReX-Codes/amrex/) +- [pandas](https://pandas.pydata.org/) 2+: for DataFrame support - [pytest](https://docs.pytest.org/en/stable/) 6.2+: for running unit tests Optional CUDA-capable dependencies for tests include: @@ -105,6 +106,10 @@ If you wish to run unit tests, then please install `pytest` python3 -m pip install -U pytest ``` +Some of our tests depend on optional third-party modules (e.g., `pandas`, `cupy`, `numba`, and/or `pytorch`). +If these are not installed then their tests will be skipped. + + ### Configure your compiler For example, using the Clang compiler: diff --git a/docs/source/install/dependencies.rst b/docs/source/install/dependencies.rst index dd51b0fe..d43a6f15 100644 --- a/docs/source/install/dependencies.rst +++ b/docs/source/install/dependencies.rst @@ -28,10 +28,11 @@ Optional dependencies include: - further `optional dependencies of AMReX `__ - `Python dependencies `__ - - `mpi4py `__ - - `cupy `__ 11.2+ - - `numba `__ 0.56+ - - `torch `__ 1.12+ + - `mpi4py 2.1+ `__: for multi-node and/or multi-GPU execution + - `cupy 11.2+ `__ + - `numba 0.56+ `__ + - `pandas 2+ `__: for DataFrame support + - `torch 1.12+ `__ For all other systems, we recommend to use a **package dependency manager**: Pick *one* of the installation methods below to install all dependencies for pyAMReX development in a consistent manner. diff --git a/src/Particle/ParticleContainer.H b/src/Particle/ParticleContainer.H index d283d85b..114144b8 100644 --- a/src/Particle/ParticleContainer.H +++ b/src/Particle/ParticleContainer.H @@ -68,6 +68,8 @@ void make_Base_Iterators (py::module &m, std::string allocstr) py::return_value_policy::reference_internal) .def_property_readonly_static("is_soa_particle", [](const py::object&){ return ParticleType::is_soa_particle;}) + .def_property_readonly("size", &iterator_base::numParticles, + "the number of particles on this tile") .def_property_readonly("num_particles", &iterator_base::numParticles) .def_property_readonly("num_real_particles", &iterator_base::numRealParticles) .def_property_readonly("num_neighbor_particles", &iterator_base::numNeighborParticles) @@ -382,6 +384,14 @@ void make_ParticleContainer_and_Iterators (py::module &m, std::string allocstr) make_Iterators< false, iterator, Allocator >(m, allocstr); using const_iterator = amrex::ParConstIter_impl; make_Iterators< true, const_iterator, Allocator >(m, allocstr); + + // simpler particle iterator loops: return types of this particle box + py_pc + .def_property_readonly_static("iterator", [](py::object /* pc */){ return py::type::of(); }, + "amrex iterator for particle boxes") + .def_property_readonly_static("const_iterator", [](py::object /* pc */){ return py::type::of(); }, + "amrex constant iterator for particle boxes (read-only)") + ; } /** Create ParticleContainers and Iterators diff --git a/src/amrex/ParticleContainer.py b/src/amrex/ParticleContainer.py new file mode 100644 index 00000000..266496d9 --- /dev/null +++ b/src/amrex/ParticleContainer.py @@ -0,0 +1,104 @@ +""" +This file is part of pyAMReX + +Copyright 2023 AMReX community +Authors: Axel Huebl +License: BSD-3-Clause-LBNL +""" + + +def pc_to_df(self, local=True, comm=None, root_rank=0): + """ + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + """ + import pandas as pd + + # create a DataFrame per particle box and append it to the list of + # local DataFrame(s) + dfs_local = [] + for lvl in range(self.finest_level + 1): + for pti in self.const_iterator(self, level=lvl): + if pti.size == 0: + continue + + if self.is_soa_particle: + next_df = pd.DataFrame() + else: + # AoS + aos_np = pti.aos().to_numpy(copy=True) + next_df = pd.DataFrame(aos_np) + next_df.set_index("cpuid") + next_df.index.name = "cpuid" + + # SoA + soa_view = pti.soa().to_numpy(copy=True) + soa_np_real = soa_view.real + soa_np_int = soa_view.int + + for idx, array in enumerate(soa_np_real): + next_df[f"SoA_real_{idx}"] = array + for idx, array in enumerate(soa_np_int): + next_df[f"SoA_int_{idx}"] = array + + dfs_local.append(next_df) + + # MPI Gather to root rank if requested + if local: + if len(dfs_local) == 0: + df = None + else: + df = pd.concat(dfs_local) + else: + from mpi4py import MPI + + if comm is None: + comm = MPI.COMM_WORLD + rank = comm.Get_rank() + + # a list for each rank's list of DataFrame(s) + df_list_list = comm.gather(dfs_local, root=root_rank) + + if rank == root_rank: + flattened_list = [df for sublist in df_list_list for df in sublist] + + if len(flattened_list) == 0: + df = pd.DataFrame() + else: + df = pd.concat(flattened_list, ignore_index=True) + else: + df = None + + return df + + +def register_ParticleContainer_extension(amr): + """ParticleContainer helper methods""" + import inspect + import sys + + # register member functions for every ParticleContainer_* type + for _, ParticleContainer_type in inspect.getmembers( + sys.modules[amr.__name__], + lambda member: inspect.isclass(member) + and member.__module__ == amr.__name__ + and member.__name__.startswith("ParticleContainer_"), + ): + ParticleContainer_type.to_df = pc_to_df diff --git a/src/amrex/space1d/__init__.py b/src/amrex/space1d/__init__.py index 060a451d..ab4d70f7 100644 --- a/src/amrex/space1d/__init__.py +++ b/src/amrex/space1d/__init__.py @@ -48,6 +48,7 @@ def Print(*args, **kwargs): from ..ArrayOfStructs import register_AoS_extension from ..MultiFab import register_MultiFab_extension from ..PODVector import register_PODVector_extension +from ..ParticleContainer import register_ParticleContainer_extension from ..StructOfArrays import register_SoA_extension register_Array4_extension(amrex_1d_pybind) @@ -55,3 +56,4 @@ def Print(*args, **kwargs): register_PODVector_extension(amrex_1d_pybind) register_SoA_extension(amrex_1d_pybind) register_AoS_extension(amrex_1d_pybind) +register_ParticleContainer_extension(amrex_1d_pybind) diff --git a/src/amrex/space1d/__init__.pyi b/src/amrex/space1d/__init__.pyi index ae08abb9..a70e8422 100644 --- a/src/amrex/space1d/__init__.pyi +++ b/src/amrex/space1d/__init__.pyi @@ -40,6 +40,7 @@ from amrex.Array4 import register_Array4_extension from amrex.ArrayOfStructs import register_AoS_extension from amrex.MultiFab import register_MultiFab_extension from amrex.PODVector import register_PODVector_extension +from amrex.ParticleContainer import register_ParticleContainer_extension from amrex.StructOfArrays import register_SoA_extension from amrex.space1d.amrex_1d_pybind import ( AlmostEqual, @@ -472,6 +473,7 @@ __all__ = [ "register_Array4_extension", "register_MultiFab_extension", "register_PODVector_extension", + "register_ParticleContainer_extension", "register_SoA_extension", "size", "ubound", diff --git a/src/amrex/space1d/amrex_1d_pybind/__init__.pyi b/src/amrex/space1d/amrex_1d_pybind/__init__.pyi index 05d0337b..37e481f3 100644 --- a/src/amrex/space1d/amrex_1d_pybind/__init__.pyi +++ b/src/amrex/space1d/amrex_1d_pybind/__init__.pyi @@ -5028,6 +5028,11 @@ class ParConstIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5050,6 +5055,11 @@ class ParConstIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5072,6 +5082,11 @@ class ParConstIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5094,6 +5109,11 @@ class ParConstIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5116,6 +5136,11 @@ class ParConstIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5138,6 +5163,11 @@ class ParConstIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5160,6 +5190,11 @@ class ParConstIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5182,6 +5217,11 @@ class ParConstIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5204,6 +5244,11 @@ class ParConstIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5225,6 +5270,11 @@ class ParConstIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5246,6 +5296,11 @@ class ParConstIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5267,6 +5322,11 @@ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIter_0_0_4_0_arena(ParConstIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5373,6 +5433,11 @@ class ParIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5395,6 +5460,11 @@ class ParIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5417,6 +5487,11 @@ class ParIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5439,6 +5514,11 @@ class ParIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5461,6 +5541,11 @@ class ParIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5483,6 +5568,11 @@ class ParIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5505,6 +5595,11 @@ class ParIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5527,6 +5622,11 @@ class ParIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5549,6 +5649,11 @@ class ParIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5570,6 +5675,11 @@ class ParIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5591,6 +5701,11 @@ class ParIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5612,6 +5727,11 @@ class ParIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIter_0_0_4_0_arena(ParIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5760,6 +5880,8 @@ class ParticleContainer_0_0_4_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_arena + iterator = ParIter_0_0_4_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -5852,6 +5974,30 @@ class ParticleContainer_0_0_4_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5861,6 +6007,8 @@ class ParticleContainer_0_0_4_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_default + iterator = ParIter_0_0_4_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -5953,6 +6101,30 @@ class ParticleContainer_0_0_4_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5962,6 +6134,8 @@ class ParticleContainer_0_0_4_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_pinned + iterator = ParIter_0_0_4_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6054,6 +6228,30 @@ class ParticleContainer_0_0_4_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6063,6 +6261,8 @@ class ParticleContainer_0_0_5_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_arena + iterator = ParIter_0_0_5_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6155,6 +6355,30 @@ class ParticleContainer_0_0_5_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6164,6 +6388,8 @@ class ParticleContainer_0_0_5_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_default + iterator = ParIter_0_0_5_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6256,6 +6482,30 @@ class ParticleContainer_0_0_5_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6265,6 +6515,8 @@ class ParticleContainer_0_0_5_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_pinned + iterator = ParIter_0_0_5_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6357,6 +6609,30 @@ class ParticleContainer_0_0_5_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6366,6 +6642,8 @@ class ParticleContainer_1_1_2_1_arena: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_arena + iterator = ParIter_1_1_2_1_arena def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6458,6 +6736,30 @@ class ParticleContainer_1_1_2_1_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6467,6 +6769,8 @@ class ParticleContainer_1_1_2_1_default: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_default + iterator = ParIter_1_1_2_1_default def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6559,6 +6863,30 @@ class ParticleContainer_1_1_2_1_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6568,6 +6896,8 @@ class ParticleContainer_1_1_2_1_pinned: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_pinned + iterator = ParIter_1_1_2_1_pinned def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6660,6 +6990,30 @@ class ParticleContainer_1_1_2_1_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6669,6 +7023,8 @@ class ParticleContainer_pureSoA_8_2_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_arena + iterator = ParIter_pureSoA_8_2_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6747,6 +7103,30 @@ class ParticleContainer_pureSoA_8_2_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6756,6 +7136,8 @@ class ParticleContainer_pureSoA_8_2_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_default + iterator = ParIter_pureSoA_8_2_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6834,6 +7216,30 @@ class ParticleContainer_pureSoA_8_2_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6843,6 +7249,8 @@ class ParticleContainer_pureSoA_8_2_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_pinned + iterator = ParIter_pureSoA_8_2_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6921,6 +7329,30 @@ class ParticleContainer_pureSoA_8_2_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... diff --git a/src/amrex/space2d/__init__.py b/src/amrex/space2d/__init__.py index 6c654c52..799e9c7c 100644 --- a/src/amrex/space2d/__init__.py +++ b/src/amrex/space2d/__init__.py @@ -48,6 +48,7 @@ def Print(*args, **kwargs): from ..ArrayOfStructs import register_AoS_extension from ..MultiFab import register_MultiFab_extension from ..PODVector import register_PODVector_extension +from ..ParticleContainer import register_ParticleContainer_extension from ..StructOfArrays import register_SoA_extension register_Array4_extension(amrex_2d_pybind) @@ -55,3 +56,4 @@ def Print(*args, **kwargs): register_PODVector_extension(amrex_2d_pybind) register_SoA_extension(amrex_2d_pybind) register_AoS_extension(amrex_2d_pybind) +register_ParticleContainer_extension(amrex_2d_pybind) diff --git a/src/amrex/space2d/__init__.pyi b/src/amrex/space2d/__init__.pyi index 10319d3d..a1c6bff1 100644 --- a/src/amrex/space2d/__init__.pyi +++ b/src/amrex/space2d/__init__.pyi @@ -40,6 +40,7 @@ from amrex.Array4 import register_Array4_extension from amrex.ArrayOfStructs import register_AoS_extension from amrex.MultiFab import register_MultiFab_extension from amrex.PODVector import register_PODVector_extension +from amrex.ParticleContainer import register_ParticleContainer_extension from amrex.StructOfArrays import register_SoA_extension from amrex.space2d.amrex_2d_pybind import ( AlmostEqual, @@ -472,6 +473,7 @@ __all__ = [ "register_Array4_extension", "register_MultiFab_extension", "register_PODVector_extension", + "register_ParticleContainer_extension", "register_SoA_extension", "size", "ubound", diff --git a/src/amrex/space2d/amrex_2d_pybind/__init__.pyi b/src/amrex/space2d/amrex_2d_pybind/__init__.pyi index 0a223460..507fdf44 100644 --- a/src/amrex/space2d/amrex_2d_pybind/__init__.pyi +++ b/src/amrex/space2d/amrex_2d_pybind/__init__.pyi @@ -5034,6 +5034,11 @@ class ParConstIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5056,6 +5061,11 @@ class ParConstIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5078,6 +5088,11 @@ class ParConstIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5100,6 +5115,11 @@ class ParConstIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5122,6 +5142,11 @@ class ParConstIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5144,6 +5169,11 @@ class ParConstIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5166,6 +5196,11 @@ class ParConstIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5188,6 +5223,11 @@ class ParConstIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5210,6 +5250,11 @@ class ParConstIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5231,6 +5276,11 @@ class ParConstIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5252,6 +5302,11 @@ class ParConstIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5273,6 +5328,11 @@ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIter_0_0_4_0_arena(ParConstIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5379,6 +5439,11 @@ class ParIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5401,6 +5466,11 @@ class ParIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5423,6 +5493,11 @@ class ParIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5445,6 +5520,11 @@ class ParIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5467,6 +5547,11 @@ class ParIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5489,6 +5574,11 @@ class ParIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5511,6 +5601,11 @@ class ParIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5533,6 +5628,11 @@ class ParIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5555,6 +5655,11 @@ class ParIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5576,6 +5681,11 @@ class ParIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5597,6 +5707,11 @@ class ParIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5618,6 +5733,11 @@ class ParIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIter_0_0_4_0_arena(ParIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5766,6 +5886,8 @@ class ParticleContainer_0_0_4_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_arena + iterator = ParIter_0_0_4_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -5858,6 +5980,30 @@ class ParticleContainer_0_0_4_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5867,6 +6013,8 @@ class ParticleContainer_0_0_4_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_default + iterator = ParIter_0_0_4_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -5959,6 +6107,30 @@ class ParticleContainer_0_0_4_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5968,6 +6140,8 @@ class ParticleContainer_0_0_4_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_pinned + iterator = ParIter_0_0_4_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6060,6 +6234,30 @@ class ParticleContainer_0_0_4_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6069,6 +6267,8 @@ class ParticleContainer_0_0_5_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_arena + iterator = ParIter_0_0_5_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6161,6 +6361,30 @@ class ParticleContainer_0_0_5_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6170,6 +6394,8 @@ class ParticleContainer_0_0_5_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_default + iterator = ParIter_0_0_5_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6262,6 +6488,30 @@ class ParticleContainer_0_0_5_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6271,6 +6521,8 @@ class ParticleContainer_0_0_5_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_pinned + iterator = ParIter_0_0_5_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6363,6 +6615,30 @@ class ParticleContainer_0_0_5_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6372,6 +6648,8 @@ class ParticleContainer_1_1_2_1_arena: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_arena + iterator = ParIter_1_1_2_1_arena def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6464,6 +6742,30 @@ class ParticleContainer_1_1_2_1_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6473,6 +6775,8 @@ class ParticleContainer_1_1_2_1_default: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_default + iterator = ParIter_1_1_2_1_default def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6565,6 +6869,30 @@ class ParticleContainer_1_1_2_1_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6574,6 +6902,8 @@ class ParticleContainer_1_1_2_1_pinned: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_pinned + iterator = ParIter_1_1_2_1_pinned def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6666,6 +6996,30 @@ class ParticleContainer_1_1_2_1_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6675,6 +7029,8 @@ class ParticleContainer_pureSoA_8_2_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_arena + iterator = ParIter_pureSoA_8_2_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6753,6 +7109,30 @@ class ParticleContainer_pureSoA_8_2_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6762,6 +7142,8 @@ class ParticleContainer_pureSoA_8_2_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_default + iterator = ParIter_pureSoA_8_2_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6840,6 +7222,30 @@ class ParticleContainer_pureSoA_8_2_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6849,6 +7255,8 @@ class ParticleContainer_pureSoA_8_2_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_pinned + iterator = ParIter_pureSoA_8_2_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6927,6 +7335,30 @@ class ParticleContainer_pureSoA_8_2_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... diff --git a/src/amrex/space3d/__init__.py b/src/amrex/space3d/__init__.py index baa757e7..9c1b1de9 100644 --- a/src/amrex/space3d/__init__.py +++ b/src/amrex/space3d/__init__.py @@ -48,6 +48,7 @@ def Print(*args, **kwargs): from ..ArrayOfStructs import register_AoS_extension from ..MultiFab import register_MultiFab_extension from ..PODVector import register_PODVector_extension +from ..ParticleContainer import register_ParticleContainer_extension from ..StructOfArrays import register_SoA_extension register_Array4_extension(amrex_3d_pybind) @@ -55,3 +56,4 @@ def Print(*args, **kwargs): register_PODVector_extension(amrex_3d_pybind) register_SoA_extension(amrex_3d_pybind) register_AoS_extension(amrex_3d_pybind) +register_ParticleContainer_extension(amrex_3d_pybind) diff --git a/src/amrex/space3d/__init__.pyi b/src/amrex/space3d/__init__.pyi index 4467496b..dc3e69b1 100644 --- a/src/amrex/space3d/__init__.pyi +++ b/src/amrex/space3d/__init__.pyi @@ -40,6 +40,7 @@ from amrex.Array4 import register_Array4_extension from amrex.ArrayOfStructs import register_AoS_extension from amrex.MultiFab import register_MultiFab_extension from amrex.PODVector import register_PODVector_extension +from amrex.ParticleContainer import register_ParticleContainer_extension from amrex.StructOfArrays import register_SoA_extension from amrex.space3d.amrex_3d_pybind import ( AlmostEqual, @@ -472,6 +473,7 @@ __all__ = [ "register_Array4_extension", "register_MultiFab_extension", "register_PODVector_extension", + "register_ParticleContainer_extension", "register_SoA_extension", "size", "ubound", diff --git a/src/amrex/space3d/amrex_3d_pybind/__init__.pyi b/src/amrex/space3d/amrex_3d_pybind/__init__.pyi index d2759024..743719f5 100644 --- a/src/amrex/space3d/amrex_3d_pybind/__init__.pyi +++ b/src/amrex/space3d/amrex_3d_pybind/__init__.pyi @@ -5037,6 +5037,11 @@ class ParConstIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5059,6 +5064,11 @@ class ParConstIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5081,6 +5091,11 @@ class ParConstIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5103,6 +5118,11 @@ class ParConstIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5125,6 +5145,11 @@ class ParConstIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5147,6 +5172,11 @@ class ParConstIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5169,6 +5199,11 @@ class ParConstIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5191,6 +5226,11 @@ class ParConstIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5213,6 +5253,11 @@ class ParConstIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5234,6 +5279,11 @@ class ParConstIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5255,6 +5305,11 @@ class ParConstIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5276,6 +5331,11 @@ class ParConstIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParConstIter_0_0_4_0_arena(ParConstIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5382,6 +5442,11 @@ class ParIterBase_0_0_4_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5404,6 +5469,11 @@ class ParIterBase_0_0_4_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_4_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5426,6 +5496,11 @@ class ParIterBase_0_0_4_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5448,6 +5523,11 @@ class ParIterBase_0_0_5_0_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5470,6 +5550,11 @@ class ParIterBase_0_0_5_0_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_0_0_5_0_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5492,6 +5577,11 @@ class ParIterBase_0_0_5_0_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5514,6 +5604,11 @@ class ParIterBase_1_1_2_1_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_default(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5536,6 +5631,11 @@ class ParIterBase_1_1_2_1_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_1_1_2_1_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = False @@ -5558,6 +5658,11 @@ class ParIterBase_1_1_2_1_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_arena(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5579,6 +5684,11 @@ class ParIterBase_pureSoA_8_2_arena(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_default(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5600,6 +5710,11 @@ class ParIterBase_pureSoA_8_2_default(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIterBase_pureSoA_8_2_pinned(MFIter): is_soa_particle: typing.ClassVar[bool] = True @@ -5621,6 +5736,11 @@ class ParIterBase_pureSoA_8_2_pinned(MFIter): def num_real_particles(self) -> int: ... @property def pair_index(self) -> tuple[int, int]: ... + @property + def size(self) -> int: + """ + the number of particles on this tile + """ class ParIter_0_0_4_0_arena(ParIterBase_0_0_4_0_arena): is_soa_particle: typing.ClassVar[bool] = False @@ -5769,6 +5889,8 @@ class ParticleContainer_0_0_4_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_arena + iterator = ParIter_0_0_4_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -5861,6 +5983,30 @@ class ParticleContainer_0_0_4_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5870,6 +6016,8 @@ class ParticleContainer_0_0_4_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_default + iterator = ParIter_0_0_4_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -5962,6 +6110,30 @@ class ParticleContainer_0_0_4_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -5971,6 +6143,8 @@ class ParticleContainer_0_0_4_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_4_0_pinned + iterator = ParIter_0_0_4_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_4_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6063,6 +6237,30 @@ class ParticleContainer_0_0_4_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6072,6 +6270,8 @@ class ParticleContainer_0_0_5_0_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_arena + iterator = ParIter_0_0_5_0_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6164,6 +6364,30 @@ class ParticleContainer_0_0_5_0_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6173,6 +6397,8 @@ class ParticleContainer_0_0_5_0_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_default + iterator = ParIter_0_0_5_0_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6265,6 +6491,30 @@ class ParticleContainer_0_0_5_0_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6274,6 +6524,8 @@ class ParticleContainer_0_0_5_0_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_0_0_5_0_pinned + iterator = ParIter_0_0_5_0_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_5_0_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6366,6 +6618,30 @@ class ParticleContainer_0_0_5_0_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6375,6 +6651,8 @@ class ParticleContainer_1_1_2_1_arena: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_arena + iterator = ParIter_1_1_2_1_arena def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6467,6 +6745,30 @@ class ParticleContainer_1_1_2_1_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6476,6 +6778,8 @@ class ParticleContainer_1_1_2_1_default: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_default + iterator = ParIter_1_1_2_1_default def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6568,6 +6872,30 @@ class ParticleContainer_1_1_2_1_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6577,6 +6905,8 @@ class ParticleContainer_1_1_2_1_pinned: NStructInt: typing.ClassVar[int] = 1 NStructReal: typing.ClassVar[int] = 1 is_soa_particle: typing.ClassVar[bool] = False + const_iterator = ParConstIter_1_1_2_1_pinned + iterator = ParIter_1_1_2_1_pinned def AddParticlesAtLevel( self, particles: ParticleTile_1_1_2_1_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6669,6 +6999,30 @@ class ParticleContainer_1_1_2_1_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6678,6 +7032,8 @@ class ParticleContainer_pureSoA_8_2_arena: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_arena + iterator = ParIter_pureSoA_8_2_arena def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_arena, level: int, ngrow: int = 0 ) -> None: ... @@ -6756,6 +7112,30 @@ class ParticleContainer_pureSoA_8_2_arena: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6765,6 +7145,8 @@ class ParticleContainer_pureSoA_8_2_default: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_default + iterator = ParIter_pureSoA_8_2_default def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_default, level: int, ngrow: int = 0 ) -> None: ... @@ -6843,6 +7225,30 @@ class ParticleContainer_pureSoA_8_2_default: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... @@ -6852,6 +7258,8 @@ class ParticleContainer_pureSoA_8_2_pinned: NStructInt: typing.ClassVar[int] = 0 NStructReal: typing.ClassVar[int] = 0 is_soa_particle: typing.ClassVar[bool] = True + const_iterator = ParConstIter_pureSoA_8_2_pinned + iterator = ParIter_pureSoA_8_2_pinned def AddParticlesAtLevel( self, particles: ParticleTile_0_0_8_2_pinned, level: int, ngrow: int = 0 ) -> None: ... @@ -6930,6 +7338,30 @@ class ParticleContainer_pureSoA_8_2_pinned: def numLocalTilesAtLevel(self, arg0: int) -> int: ... def reserveData(self) -> None: ... def resizeData(self) -> None: ... + def to_df(self, local=True, comm=None, root_rank=0): + """ + + Copy all particles into a pandas.DataFrame + + Parameters + ---------- + self : amrex.ParticleContainer_* + A ParticleContainer class in pyAMReX + local : bool + MPI-local particles + comm : MPI Communicator + if local is False, this defaults to mpi4py.MPI.COMM_WORLD + root_rank : MPI root rank to gather to + if local is False, this defaults to 0 + + Returns + ------- + A concatenated pandas.DataFrame with particles from all levels. + + Returns None if no particles were found. + If local=False, then all ranks but the root_rank will return None. + + """ @property def finest_level(self) -> int: ... diff --git a/tests/test_particleContainer.py b/tests/test_particleContainer.py index 668ed42c..9f1897e4 100644 --- a/tests/test_particleContainer.py +++ b/tests/test_particleContainer.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- +import importlib + import numpy as np import pytest @@ -272,3 +274,38 @@ def test_per_cell(empty_particle_container, std_geometry, std_particle): assert pc.TotalNumberOfParticles() == pc.NumberOfParticlesAtLevel(0) == ncells print("npts * real_1", ncells * std_particle.real_array_data[1]) assert ncells * std_particle.real_array_data[1] == sum_1 + + +@pytest.mark.skipif( + importlib.util.find_spec("pandas") is None, reason="pandas is not available" +) +def test_pc_df(particle_container, Npart): + pc = particle_container + print(f"pc={pc}") + df = pc.to_df() + print(df.columns) + print(df) + + +@pytest.mark.skipif( + importlib.util.find_spec("pandas") is None, reason="pandas is not available" +) +def test_pc_empty_df(empty_particle_container, Npart): + pc = empty_particle_container + print(f"pc={pc}") + df = pc.to_df() + assert df is None + + +@pytest.mark.skipif( + importlib.util.find_spec("pandas") is None, reason="pandas is not available" +) +@pytest.mark.skipif(not amr.Config.have_mpi, reason="Requires AMReX_MPI=ON") +def test_pc_df_mpi(particle_container, Npart): + pc = particle_container + print(f"pc={pc}") + df = pc.to_df(local=False) + if df is not None: + # only rank 0 + print(df.columns) + print(df)