Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,56 @@ jobs:
run: |
python3 -m pytest tests/

clang_sp:
name: Clang@18.0 w/ SP
runs-on: ubuntu-24.04
env:
CXXFLAGS: "-Werror -Wshadow -Woverloaded-virtual -Wextra-semi -Wunreachable-code -fno-operator-names -Wno-pass-failed"
LDFLAGS: "-fuse-ld=lld"
CXX: "clang++"
CC: "clang"
AMReX_PRECISION: "SINGLE"
AMReX_PARTICLES_PRECISION: "SINGLE"
steps:
- uses: actions/checkout@v4
- name: Dependencies
run: |
.github/workflows/dependencies/dependencies_clang18.sh
.github/workflows/dependencies/dependencies_ccache.sh
- name: Set Up Cache
uses: actions/cache@v4
with:
path: ~/.cache/ccache
key: ccache-${{ github.workflow }}-${{ github.job }}-git-${{ github.sha }}
restore-keys: |
ccache-${{ github.workflow }}-${{ github.job }}-git-
- name: Build & Install
run: |
export CCACHE_COMPRESS=1
export CCACHE_COMPRESSLEVEL=10
export CCACHE_MAXSIZE=300M
ccache -z

export CMAKE_BUILD_PARALLEL_LEVEL=4

export CC=$(which clang-18)
export CXX=$(which clang++-18)
python3 -m pip install -U pip
python3 -m pip install -U build packaging setuptools[core] wheel
python3 -m pip install -U cmake
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__)"
python3 -c "import amrex.space3d as amr; print(amr.__version__)"

ccache -s
du -hs ~/.cache/ccache

- name: Unit tests
run: |
python3 -m pytest tests/

nvcc11:
name: CUDA@12.2 GNU@10.5
runs-on: ubuntu-24.04
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def build_extension(self, ext):
"-DAMReX_GPU_BACKEND=" + AMReX_GPU_BACKEND,
"-DAMReX_MPI:BOOL=" + AMReX_MPI,
"-DAMReX_PRECISION=" + AMReX_PRECISION,
#'-DAMReX_PARTICLES_PRECISION=' + AMReX_PARTICLES_PRECISION,
"-DAMReX_PARTICLES_PRECISION=" + AMReX_PARTICLES_PRECISION,
"-DpyAMReX_CCACHE=" + PYAMREX_CCACHE,
"-DpyAMReX_IPO=" + PYAMREX_IPO,
## dependency control (developers & package managers)
Expand Down Expand Up @@ -174,6 +174,7 @@ def build_extension(self, ext):
AMReX_GPU_BACKEND = os.environ.get("AMREX_GPU_BACKEND", "NONE")
AMReX_MPI = os.environ.get("AMREX_MPI", "OFF")
AMReX_PRECISION = os.environ.get("AMREX_PRECISION", "DOUBLE")
AMReX_PARTICLES_PRECISION = os.environ.get("AMREX_PARTICLES_PRECISION", "DOUBLE")
# single value or as a list 1;2;3
AMReX_SPACEDIM = os.environ.get("AMREX_SPACEDIM", "1;2;3")
BUILD_SHARED_LIBS = os.environ.get("AMREX_BUILD_SHARED_LIBS", "OFF")
Expand Down
18 changes: 18 additions & 0 deletions src/Base/AMReX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ void init_AMReX(py::module& m)
return "SYCL";
#else
return py::none();
#endif
})
.def_property_readonly_static(
"precision",
[](py::object){
#ifdef AMREX_SINGLE_PRECISION
return "SINGLE";
#else
return "DOUBLE";
#endif
})
.def_property_readonly_static(
"precision_particles",
[](py::object){
#ifdef AMREX_SINGLE_PRECISION_PARTICLES
return "SINGLE";
#else
return "DOUBLE";
#endif
})
;
Expand Down
23 changes: 22 additions & 1 deletion src/Particle/ArrayOfStructs.H
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,38 @@ namespace
#if (AMREX_SPACEDIM >= 3)
descr.append(py::make_tuple("z", py::format_descriptor<RealType>::format()));
#endif

/* Note AMReX_Particle.H:
* struct alignas(sizeof(double)) Particle ...
* aligns the ParticleBase member (pads the end if needed).
* in
* struct ParticleBase
* {
* T m_pos[AMREX_SPACEDIM];
* T m_rdata[NReal];
* uint64_t m_idcpu = 0;
* int m_idata[NInt];
* };
*/
if constexpr (ParticleType::NReal > 0) {
for(int ii=0; ii < ParticleType::NReal; ii++) {
descr.append(py::make_tuple("rdata_"+std::to_string(ii),py::format_descriptor<RealType>::format()));
}
}
#ifdef AMREX_SINGLE_PRECISION_PARTICLES
if constexpr ((AMREX_SPACEDIM + ParticleType::NReal) % 2 != 0) { // alignas
descr.append(py::make_tuple("", "|V4")); // empty space due to alignment
}
#endif
descr.append(py::make_tuple("idcpu", py::format_descriptor<uint64_t>::format()) );
if constexpr (ParticleType::NInt > 0) {
for(int ii=0; ii < ParticleType::NInt; ++ii) {
descr.append(py::make_tuple("idata_"+std::to_string(ii),py::format_descriptor<int>::format()));
}
}
}/*
if constexpr (ParticleType::NInt % 2 != 0) { // alignas
descr.append(py::make_tuple("", "|V4")); // empty space due to alignment
}*/
Comment on lines +74 to +77

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this end-of-particle padding after the int (32bit) array fails both the double precision tests o.0


d["descr"] = descr;
d["version"] = 3;
Expand Down
14 changes: 8 additions & 6 deletions tests/test_aos.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,16 @@ def test_array_interface():
# print('particle 2 from aos:\n',aos[1])
# print('array interface\n', aos.__array_interface__)
arr = aos.to_numpy()
int_arg = 7 if arr[0][0].dtype == "float32" else 6 # padding

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that users will have to know, that there might be this extra padding element so the indices for the integers might be shifted?

@ax3l ax3l Aug 1, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fear so... I have not found a way to kick out numpy struct elements (in the SP case: the paddings) without doing a copy yet.

That said, nobody should use AoS anymore in AMReX. I am inclined to drop all support for it in pyAMReX to stop wasting time :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solicited feedback now on #459 and on Slack

assert (
np.isclose(arr[0][0], 1.0)
and np.isclose(arr[0][4], 5.2)
and np.isclose(arr[0][6], 6)
and np.isclose(arr[0][int_arg], 6) # fixme in SP: reads int32(0)
)
assert (
np.isclose(arr[1][2], 10)
and np.isclose(arr[1][3], 11.1)
and np.isclose(arr[1][6], 13)
and np.isclose(arr[1][int_arg], 13)
)

p3 = amr.Particle_2_1(x=-3)
Expand All @@ -102,10 +103,11 @@ def test_array_interface():
assert aos[0].y == arr[0][1] == 0
assert aos[0].z == arr[0][2] == 0

shape = amr.Config.spacedim + amr.Particle_2_1.NReal + amr.Particle_2_1.NInt + 1
for ii in range(shape):
arr[1][ii] = 0
arr[1][1] = -5 # np.array([0, -5, 0,0,0,0,0])
arr[1][0] = 0
arr[1][1] = -5
arr[1][2] = 0
arr[1][3] = 0
arr[1][4] = 0 # np.array([0, -5, 0,0,0,0,0, ...])
print("array:", arr)
print("aos[0]:", aos[0], "aos[1]:", aos[1])
assert aos[1].y == arr[1][1] == -5
Expand Down
4 changes: 3 additions & 1 deletion tests/test_parmparse.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import os

import numpy as np

import amrex.space3d as amr


Expand All @@ -14,7 +16,7 @@ def test_parmparse():
dopml = pp_param.get_bool("do_pml")

assert dopml
assert dt == 1.0e-5
assert np.isclose(dt, 1.0e-5)
assert ncell == 100

pp.pretty_print_table()
24 changes: 18 additions & 6 deletions tests/test_particle.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,31 @@ def test_particle_init():
def test_particle_set():
p1 = amr.Particle_8_0()
p1.setPos(1, 1.5)
assert p1.pos(0) == 0 and p1.pos(1) == 1.5 and p1.pos(2) == 0
assert (
np.isclose(p1.pos(0), 0)
and np.isclose(p1.pos(1), 1.5)
and np.isclose(p1.pos(2), 0)
)
p1.setPos([1.0, 1, 2])
assert p1.pos(0) == 1 and p1.pos(1) == 1 and p1.pos(2) == 2
assert (
np.isclose(p1.pos(0), 1)
and np.isclose(p1.pos(1), 1)
and np.isclose(p1.pos(2), 2)
)
p1.setPos(amr.RealVect(2, 3.3, 4.2))
assert p1.pos(0) == 2 and p1.pos(1) == 3.3 and p1.pos(2) == 4.2
assert (
np.isclose(p1.pos(0), 2)
and np.isclose(p1.pos(1), 3.3)
and np.isclose(p1.pos(2), 4.2)
)

print(p1.x, p1.y, p1.z)
p1.x = 2.1
assert p1.x == 2.1
assert np.isclose(p1.x, 2.1)
p1.y = 3.2
assert p1.y == 3.2
assert np.isclose(p1.y, 3.2)
p1.z = 5.1
assert p1.z == 5.1
assert np.isclose(p1.z, 5.1)


def test_rdata():
Expand Down
24 changes: 17 additions & 7 deletions tests/test_particleContainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,10 @@ def test_pc_init():

aos = pti.aos()
aos_arr = aos.to_numpy()
assert aos[0].x == 0.30
assert aos[0].y == 0.35
assert aos[0].z == 0.40
assert aos_arr[0]["z"] == 0.40
assert np.isclose(aos[0].x, 0.30)
assert np.isclose(aos[0].y, 0.35)
assert np.isclose(aos[0].z, 0.40)
assert np.isclose(aos_arr[0]["z"], 0.40)

soa = pti.soa()
real_arrays = soa.get_real_data()
Expand Down Expand Up @@ -294,7 +294,9 @@ def test_particle_init(Npart, particle_container):
assert int_arrays[0][0] == 1
assert isinstance(aos_arr[0]["rdata_0"], np.floating)
assert isinstance(aos_arr[0]["idata_0"], np.integer)
assert np.isclose(aos_arr[0]["rdata_0"], 0.5) and aos_arr[0]["idata_0"] == 5
assert (
np.isclose(aos_arr[0]["rdata_0"], 0.5) and aos_arr[0]["idata_0"] == 5
) # fixme in SP: random value np.int32(-2147483648) == 5

aos_arr[0]["idata_0"] = 2
aos1 = pt.get_array_of_structs()
Expand Down Expand Up @@ -337,7 +339,7 @@ def test_particle_init(Npart, particle_container):
aos = pt.get_array_of_structs()
print(aos[0])
assert aos[0].get_idata(0) == 2
assert real_arrays[1][0] == -1.2
assert np.isclose(real_arrays[1][0], -1.2)
assert int_arrays[0][0] == -3


Expand All @@ -362,7 +364,7 @@ def test_per_cell(empty_particle_container, std_geometry, std_particle):
pc.total_number_of_particles() == pc.number_of_particles_at_level(0) == ncells
)
print("npts * real_1", ncells * std_particle.real_array_data[1])
assert ncells * std_particle.real_array_data[1] == sum_1
assert np.isclose(ncells * std_particle.real_array_data[1], sum_1)


def test_soa_pc_numpy(soa_particle_container, Npart):
Expand Down Expand Up @@ -462,6 +464,10 @@ class Config:
@pytest.mark.skipif(
importlib.util.find_spec("pandas") is None, reason="pandas is not available"
)
@pytest.mark.skipif(
amr.Config.precision_particles == "SINGLE",
reason="Requires DOUBLE precision particles",
)
def test_pc_df(particle_container, Npart):
pc = particle_container
print(f"pc={pc}")
Expand Down Expand Up @@ -550,6 +556,10 @@ def test_pc_empty_df(empty_particle_container, Npart):
@pytest.mark.skipif(
importlib.util.find_spec("pandas") is None, reason="pandas is not available"
)
@pytest.mark.skipif(
amr.Config.precision_particles == "SINGLE",
reason="Requires DOUBLE precision particles",
)
@pytest.mark.skipif(not amr.Config.have_mpi, reason="Requires AMReX_MPI=ON")
def test_pc_df_mpi(particle_container, Npart):
pc = particle_container
Expand Down
2 changes: 1 addition & 1 deletion tests/test_particleTile.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def test_ptile_soa():
ar1 = np.array(rdata[1], copy=False)
ir0 = np.array(idata[0], copy=False)
print(ar0.dtype)
assert ar0.dtype == "float"
assert ar0.dtype == "float" or ar0.dtype == "float32"
assert ir0.dtype == "int32"
print("---------")
ir0[0] = -55
Expand Down
6 changes: 4 additions & 2 deletions tests/test_realbox.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-

import numpy as np

import amrex.space3d as amr
from amrex.space3d import RealVect as RV
from amrex.space3d import XDim3
Expand All @@ -14,9 +16,9 @@ def test_realbox_empty():

rb.setLo([-1, -2, -3])
rb.setHi([0, 0, 0])
assert rb.volume() == 6
assert np.isclose(rb.volume(), 6)
rb.setHi(0, 1.2)
assert rb.hi(0) == 1.2
assert np.isclose(rb.hi(0), 1.2)


def test_realbox_frombox(std_box):
Expand Down
16 changes: 10 additions & 6 deletions tests/test_realvect.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ def test_realvect_init():

for ii in range(amr.Config.spacedim):
assert rv[ii] == 0
assert rv2[ii] == ii + 0.1
assert rv3[ii] == 0.3
assert np.isclose(rv2[ii], ii + 0.1)
assert np.isclose(rv3[ii], 0.3)


@pytest.mark.skipif(amr.Config.spacedim != 1, reason="Requires AMREX_SPACEDIM = 1")
Expand Down Expand Up @@ -49,8 +49,8 @@ def test_rv_3d1():
# Check indexing
assert obj[0] == 1
assert obj[1] == 2
assert obj[2] == 3.14
assert obj[-1] == 3.14
assert np.isclose(obj[2], 3.14)
assert np.isclose(obj[-1], 3.14)
assert obj[-2] == 2
assert obj[-3] == 1
with pytest.raises(IndexError):
Expand Down Expand Up @@ -137,9 +137,13 @@ def test_subtraction():
assert v4[ii] == v5[ii]

# v - v
assert v5 - v3 == uv
res1 = v5 - v3
for ii in range(amr.Config.spacedim):
assert np.isclose(res1[ii], uv[ii])
# r - v
assert 1.0 - v6 == amr.RealVect(2.0)
res2 = 1.0 - v6
for ii in range(amr.Config.spacedim):
assert np.isclose(res2[ii], 2.0)


def test_multiplication():
Expand Down
Loading