diff --git a/.github/workflows/testing_OSX.yml b/.github/workflows/testing_OSX.yml index 5542289a2..c86e2398e 100644 --- a/.github/workflows/testing_OSX.yml +++ b/.github/workflows/testing_OSX.yml @@ -13,14 +13,18 @@ jobs: cc: "clang" fc: "gfortran-14" swig_builtin: "On" #uses swig 4.0.2 - py: "/usr/bin/python3" + py: "/usr/bin/python3" # python 3.14 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Preinstall run: | - brew install hdf5 fftw cfitsio muparser libomp swig python3 - pip install numpy==1.26.4 cmake + brew install hdf5 fftw cfitsio muparser libomp swig + python3 -m venv venv + source venv/bin/activate + python -m pip install --upgrade pip + pip install numpy cmake + pip show numpy # cross-check numpy version for Python_NumPy_INCLUDE_DIR - name: Set up the build env: CXX: ${{ matrix.config.cxx }} @@ -30,7 +34,17 @@ jobs: run: | mkdir build cd build - cmake .. -DENABLE_PYTHON=True -DENABLE_TESTING=On -DENABLE_SWIG_BUILTIN=${{ matrix.config.swig_builtin }} -DSIMD_EXTENSIONS="none" + + cmake .. \ + -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/venv \ + -DENABLE_PYTHON=True \ + -DPython_EXECUTABLE=$GITHUB_WORKSPACE/venv/bin/python \ + -DCMAKE_CXX_STANDARD=17 \ + -DCMAKE_CXX_FLAGS="-std=c++17 -stdlib=libc++" \ + -DENABLE_TESTING=On \ + -DENABLE_SWIG_BUILTIN=${{ matrix.config.swig_builtin }} \ + -DPython_INSTALL_PACKAGE_DIR=$GITHUB_WORKSPACE/venv/lib/python3.14/site-packages + - name: Build CRPropa run: | cd build diff --git a/.github/workflows/testing_ubuntu20.yml b/.github/workflows/testing_ubuntu20.yml deleted file mode 100644 index 54e0f834e..000000000 --- a/.github/workflows/testing_ubuntu20.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: crpropa-testing_ubuntu20 -on: [push, pull_request] - -jobs: - linux: - runs-on: ${{ matrix.config.os }} - strategy: - fail-fast: false - matrix: - config: - - name: "ubuntu-20" - os: ubuntu-20.04 - cxx: "g++-9" - cc: "gcc-9" - fc: "gfortran-9" - swig_builtin: "Off" #uses swig 4.0.1 - py: "/usr/bin/python3" #python 3.8 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Preinstall - run: | - sudo apt-get update - sudo apt-get install libmuparser-dev python3-dev python-dev python3-numpy python-numpy python3-setuptools python-setuptools libhdf5-serial-dev libomp5 libomp-dev libfftw3-dev libcfitsio-dev lcov - - name: Set up the build - env: - CXX: ${{ matrix.config.cxx }} - CC: ${{ matrix.config.cc }} - FC: ${{ matrix.config.fc }} - run: | - mkdir build - cd build - cmake .. -DENABLE_PYTHON=True -DENABLE_TESTING=On -DENABLE_SWIG_BUILTIN=${{ matrix.config.swig_builtin }} -DSIMD_EXTENSIONS=native - - name: Build CRPropa - run: | - cd build - make - - name: Run tests - run: | - cd build - make test - - name: Archive test results - if: always() - uses: actions/upload-artifact@v4 - with: - name: "test-report_${{matrix.config.name}}" - path: build/Testing/Temporary/LastTest.log \ No newline at end of file diff --git a/.github/workflows/testing_ubuntu22.yml b/.github/workflows/testing_ubuntu22.yml index 775a88d25..49b60a65b 100644 --- a/.github/workflows/testing_ubuntu22.yml +++ b/.github/workflows/testing_ubuntu22.yml @@ -21,7 +21,10 @@ jobs: - name: Preinstall run: | sudo apt-get update - sudo apt-get install libmuparser-dev python3 python3-numpy python3-setuptools libhdf5-serial-dev libomp5 libomp-dev libfftw3-dev libcfitsio-dev lcov + sudo apt-get install libmuparser-dev python3 python3-setuptools libhdf5-serial-dev libomp5 libomp-dev libfftw3-dev libcfitsio-dev lcov + pip install --upgrade pip + pip install numpy + pip show numpy - name: Set up the build env: CXX: ${{ matrix.config.cxx }} diff --git a/.github/workflows/testing_ubuntu24.yml b/.github/workflows/testing_ubuntu24.yml new file mode 100644 index 000000000..8ae440e19 --- /dev/null +++ b/.github/workflows/testing_ubuntu24.yml @@ -0,0 +1,93 @@ +name: crpropa-testing_ubuntu24 +on: [push, pull_request] + +jobs: + x64: + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - name: "ubuntu-24" + os: ubuntu-24.04 + cxx: "g++-14" + cc: "gcc-14" + fc: "gfortran-14" + swig_builtin: "On" + py: "/usr/bin/python3" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Preinstall + run: | + sudo apt-get update + sudo apt-get install libmuparser-dev python-dev-is-python3 libhdf5-serial-dev libomp5 libomp-dev libfftw3-dev libcfitsio-dev lcov + pip install numpy + - name: Set up the build + env: + CXX: ${{ matrix.config.cxx }} + CC: ${{ matrix.config.cc }} + FC: ${{ matrix.config.fc }} + run: | + mkdir build + cd build + cmake .. -DENABLE_PYTHON=True -DENABLE_TESTING=On -DENABLE_SWIG_BUILTIN=${{ matrix.config.swig_builtin }} -DSIMD_EXTENSIONS="none" + - name: Build CRPropa + run: | + cd build + make + - name: Run tests + run: | + cd build + make test + - name: Archive test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: "test-report_${{matrix.config.os}}" + path: build/Testing/Temporary/LastTest.log + arm: + runs-on: ${{ matrix.config.os }} + strategy: + fail-fast: false + matrix: + config: + - name: "ubuntu-24" + os: ubuntu-24.04-arm + cxx: "g++-14" + cc: "gcc-14" + fc: "gfortran-14" + swig_builtin: "On" + py: "/usr/bin/python3" + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Preinstall + run: | + sudo apt-get update + sudo apt-get install libmuparser-dev python-dev-is-python3 libhdf5-serial-dev libomp5 libomp-dev libfftw3-dev libcfitsio-dev lcov + pip install numpy + pip show numpy + - name: Set up the build + env: + CXX: ${{ matrix.config.cxx }} + CC: ${{ matrix.config.cc }} + FC: ${{ matrix.config.fc }} + run: | + mkdir build + cd build + cmake .. -DENABLE_PYTHON=True -DENABLE_TESTING=On -DENABLE_SWIG_BUILTIN=${{ matrix.config.swig_builtin }} -DSIMD_EXTENSIONS="none" + - name: Build CRPropa + run: | + cd build + make + - name: Run tests + run: | + cd build + make test + - name: Archive test results + if: always() + uses: actions/upload-artifact@v4 + with: + name: "test-report_${{matrix.config.os}}" + path: build/Testing/Temporary/LastTest.log \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 20df39c9a..d5424f4db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,7 +157,7 @@ list(APPEND CRPROPA_EXTRA_LIBRARIES sophia gfortran) list(APPEND CRPROPA_EXTRA_INCLUDES libs/sophia) # Galactic magnetic lenses -option(ENABLE_GALACTICMAGNETICLENS "Galactic Magnetic Lens" ON) +option(ENABLE_GALACTICMAGNETICLENS "Galactic Magnetic Lens" OFF) option(INSTALL_EIGEN "Install provided EIGEN headers" OFF) SET(EIGEN_PATH "" CACHE STRING "Use EIGEN from this path instead of the version shipped with CRPropa") SET(WITH_GALACTIC_LENSES FALSE) @@ -453,6 +453,8 @@ find_package(Python 3.0 REQUIRED COMPONENTS Interpreter Development NumPy) if(ENABLE_PYTHON AND Python_FOUND) find_package(SWIG 3.0 REQUIRED) include_directories(${Python_INCLUDE_DIRS}) + add_definitions(-DCRPROPA_HAVE_PYTHON) + list(APPEND CRPROPA_SWIG_DEFINES -DCRPROPA_HAVE_PYTHON) # print Python info in detail message(STATUS "Python: Found!") @@ -467,23 +469,29 @@ if(ENABLE_PYTHON AND Python_FOUND) message(STATUS " development libraries: NOT found!") endif(Python_Development_FOUND) - + # use Python_INSTALL_PACKAGE_DIR if provided; otherwise, install in Python_SITELIB set(Python_INSTALL_PACKAGE_DIR "${Python_SITELIB}" CACHE PATH "folder in which the python package is installed") message(STATUS " package install directory: ${Python_INSTALL_PACKAGE_DIR}") - - + # look for NumPy if(Python_NumPy_FOUND) - set(CMAKE_SWIG_FLAGS -DWITHNUMPY ${CRP}) - list(APPEND CRPROPA_SWIG_DEFINES -DWITHNUMPY) - include_directories(${Python_NumPy_INCLUDE_DIRS}) - message(STATUS "NumPy: Found!") - message(STATUS " headers: ${Python_NumPy_INCLUDE_DIRS} (version ${Python_NumPy_VERSION})") + set(CMAKE_SWIG_FLAGS -DWITHNUMPY ${CRP}) + list(APPEND CRPROPA_SWIG_DEFINES -DWITHNUMPY) + include_directories(${Python_NumPy_INCLUDE_DIRS}) + message(STATUS "NumPy: Found!") + message(STATUS " headers: ${Python_NumPy_INCLUDE_DIRS} (version ${Python_NumPy_VERSION})") elseif(Python_NumPy_FOUND) - message(STATUS "NumPy: NOT found!") - message(STATUS " CRPropa might work just fine with Python, but features like Galactic lenses will not be available.") + message(STATUS "NumPy: NOT found!") + message(STATUS " CRPropa might work just fine with Python, but features like Galactic lenses will not be available.") endif(Python_NumPy_FOUND) + + # deactivate lenses if NumPy version is >= 2.0 + if(Python_NumPy_VERSION VERSION_GREATER_EQUAL 2.0 AND WITH_GALACTIC_LENSES) + set(WITH_GALACTIC_LENSES FALSE CACHE BOOL "" FORCE) + set(ENABLE_GALACTICMAGNETICLENS OFF CACHE BOOL "" FORCE) + message(WARNING "NumPy version >= 2.0 detected, disabling Galactic lenses support") + endif() if(SWIG_VERSION VERSION_GREATER 4.0) # Use swig 4 builtin doxygen instead of external program diff --git a/doc/pages/example_notebooks/targeting/Targeting.ipynb b/doc/pages/example_notebooks/targeting/Targeting.ipynb index 0ce0a8813..a71b9e166 100644 --- a/doc/pages/example_notebooks/targeting/Targeting.ipynb +++ b/doc/pages/example_notebooks/targeting/Targeting.ipynb @@ -91,43 +91,46 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import healpy as hp\n", "import matplotlib.pylab as plt\n", + "import crpropa as crp\n", "\n", - "crdata = np.genfromtxt('TargetedEmission.txt')\n", - "Id = crdata[:,3]\n", - "E = crdata[:,4] * EeV\n", - "px = crdata[:,8]\n", - "py = crdata[:,9]\n", - "pz = crdata[:,10]\n", - "w = crdata[:,29]\n", - "lons = np.arctan2(-1. * py, -1. *px)\n", - "lats = np.pi / 2 - np.arccos( -pz / np.sqrt(px*px + py*py+ pz*pz) )\n", + "# If CRPropa was compiled without the GalacticMagneticLens the ParticleMapsContainer does not exist\n", + "if hasattr(crp, 'ParticleMapsContainer'):\n", + " crdata = np.genfromtxt('TargetedEmission.txt')\n", + " Id = crdata[:,3]\n", + " E = crdata[:,4] * EeV\n", + " px = crdata[:,8]\n", + " py = crdata[:,9]\n", + " pz = crdata[:,10]\n", + " w = crdata[:,29]\n", + " lons = np.arctan2(-1. * py, -1. *px)\n", + " lats = np.pi / 2 - np.arccos( -pz / np.sqrt(px*px + py*py+ pz*pz) )\n", "\n", - "M = ParticleMapsContainer()\n", - "for i in range(len(E)):\n", - " M.addParticle(int(Id[i]), E[i], lons[i], lats[i], w[i])\n", + " M = ParticleMapsContainer()\n", + " for i in range(len(E)):\n", + " M.addParticle(int(Id[i]), E[i], lons[i], lats[i], w[i])\n", "\n", - "###################################################################\n", - "# WARNING\n", - "# The calls M.getEnergies()/getParticleIds()/getMap() all segfault.\n", - "################################################################### \n", + " ###################################################################\n", + " # WARNING\n", + " # The calls M.getEnergies()/getParticleIds()/getMap() all segfault.\n", + " ################################################################### \n", "\n", - "#stack all maps\n", - "#crMap = np.zeros(49152)\n", - "#for pid in M.getParticleIds():\n", - " #energies = M.getEnergies(int(pid))\n", - " #for i, energy in enumerate(energies):\n", - " #continue\n", - " #crMap += M.getMap(int(pid), energy * eV )\n", - "#\n", - "##plot maps using healpy\n", - "#hp.mollview(map=crMap, title='Targeted emission')\n", - "#plt.show()" + " #stack all maps\n", + " #crMap = np.zeros(49152)\n", + " #for pid in M.getParticleIds():\n", + " #energies = M.getEnergies(int(pid))\n", + " #for i, energy in enumerate(energies):\n", + " #continue\n", + " #crMap += M.getMap(int(pid), energy * eV )\n", + " #\n", + " ##plot maps using healpy\n", + " #hp.mollview(map=crMap, title='Targeted emission')\n", + " #plt.show()" ] }, { diff --git a/include/crpropa/Vector3.h b/include/crpropa/Vector3.h index 9d67f2073..f26ed986e 100644 --- a/include/crpropa/Vector3.h +++ b/include/crpropa/Vector3.h @@ -6,6 +6,13 @@ #include #include #include +#ifdef CRPROPA_HAVE_PYTHON +#include +#include +#endif // CRPROPA_HAVE_PYTHON +#include + +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION namespace crpropa { @@ -410,6 +417,85 @@ class Vector3 { data[2] = f; return *this; } + + const std::string getDescription() { + char buffer[256]; + sprintf(buffer, "Vector(%.6G, %.6G, %.6G)", data[0], data[1], data[2]); + return buffer; + } + + // ---------------------------- + // Python numpy interface + // ---------------------------- +#ifdef CRPROPA_HAVE_PYTHON + PyObject* __array__() { + npy_intp dims[1] = {3}; + PyObject *array; + // type handling + if (typeid(T) == typeid(double)) + array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, (void *)data); + else if (typeid(T) == typeid(float)) + array = PyArray_SimpleNewFromData(1, dims, NPY_FLOAT, (void *)data); + else { + PyErr_SetString(PyExc_TypeError, "Unsupported type for Vector3"); + return NULL; + } + return array; + } + + size_t __len__() { + return 3; + } + + // initialize the vector from a numpy array + Vector3(PyObject* inputObject) { + // check if input is a numpy array + if (!PyArray_Check(inputObject)) { + throw std::runtime_error("Expected a numpy array as input"); + } + + // convert to PyArrayObject + PyArrayObject *array = (PyArrayObject*)PyArray_FROM_O(inputObject); + + // chek dimensions + if (PyArray_NDIM(array) != 1 || PyArray_DIM(array, 0) != 3) { + throw std::runtime_error("Expected a 1D array of length 3"); + } + + // handle different numpy dtypes + if (PyArray_TYPE(array) == NPY_DOUBLE) { + // convert pyarray data + double *dataPtr = (double *)PyArray_DATA(array); + data[0] = dataPtr[0]; + data[1] = dataPtr[1]; + data[2] = dataPtr[2]; + } else if (PyArray_TYPE(array) == NPY_FLOAT) { + // convert pyarray data + float *dataPtr = (float *)PyArray_DATA(array); + data[0] = dataPtr[0]; + data[1] = dataPtr[1]; + data[2] = dataPtr[2]; + } else if (PyArray_TYPE(array) == NPY_INT32) { + // convert pyarray data + int *dataPtr = (int *)PyArray_DATA(array); + data[0] = static_cast(dataPtr[0]); + data[1] = static_cast(dataPtr[1]); + data[2] = static_cast(dataPtr[2]); + } else if (PyArray_TYPE(array) == NPY_INT64) { + // convert pyarray data + long long *dataPtr = (long long *)PyArray_DATA(array); + data[0] = static_cast(dataPtr[0]); + data[1] = static_cast(dataPtr[1]); + data[2] = static_cast(dataPtr[2]); + } else { + throw std::runtime_error("Unsupported numpy array dtype"); + } + + // free the reference to the numpy array + Py_DECREF(array); + } +#endif // CRPROPA_HAVE_PYTHON + }; #ifndef SWIG @@ -438,3 +524,5 @@ typedef Vector3 Vector3f; } // namespace crpropa #endif // CRPROPA_VECTOR3_H + + diff --git a/python/2_headers.i b/python/2_headers.i index 2621afb50..bc06d7d85 100644 --- a/python/2_headers.i +++ b/python/2_headers.i @@ -97,25 +97,6 @@ } %extend crpropa::Vector3 { - size_t __len__() { - return 3; - } - - PyObject* __array__() { - npy_intp shape[1]; - shape[0] = 3; - PyObject *ro; - if (sizeof($self->data[0]) == NPY_SIZEOF_FLOAT) { - ro = PyArray_SimpleNewFromData(1, shape, NPY_FLOAT, $self->data); - } else if (sizeof($self->data[0]) == NPY_SIZEOF_DOUBLE) { - ro = PyArray_SimpleNewFromData(1, shape, NPY_DOUBLE, $self->data); - } else { - KISS_LOG_ERROR << "crpropa::Vector3 has fixed size of 3 elements!"; - } - - return ro; - } - double __getitem__(size_t i) { if(i > 2) { throw RangeError(); @@ -132,14 +113,9 @@ $self->data[i] = value; return 0; } - - const std::string getDescription() { - char buffer[256]; - sprintf( buffer, "Vector(%.6G, %.6G, %.6G)", $self->x, $self->y, $self->z ); - return buffer; - } } + %feature("python:slot", "tp_str", functype="reprfunc") crpropa::Vector3::getDescription(); %feature("python:slot", "tp_repr", functype="reprfunc") crpropa::Vector3::getDescription(); diff --git a/src/Common.cpp b/src/Common.cpp index a8bf997fd..591ededc8 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -13,15 +13,23 @@ namespace crpropa { +std::string removeNullCharacter(std::string path) { + // check for null character in data path and remove it + if (path.find('\x00') != std::string::npos) + path.erase(std::remove(path.begin(), path.end(), '\x00'), path.end()); + return path; +} + std::string getDataPath(std::string filename) { static std::string dataPath; + if (dataPath.size()) - return concat_path(dataPath, filename); + return removeNullCharacter(concat_path(dataPath, filename)); const char *env_path = getenv("CRPROPA_DATA_PATH"); if (env_path) { if (is_directory(env_path)) { - dataPath = env_path; + dataPath = removeNullCharacter(env_path); KISS_LOG_INFO << "getDataPath: use environment variable, " << dataPath << std::endl; return concat_path(dataPath, filename); @@ -32,7 +40,7 @@ std::string getDataPath(std::string filename) { { std::string _path = CRPROPA_INSTALL_PREFIX "/share/crpropa"; if (is_directory(_path)) { - dataPath = _path; + dataPath = removeNullCharacter(_path); KISS_LOG_INFO << "getDataPath: use install prefix, " << dataPath << std::endl; return concat_path(dataPath, filename); @@ -41,7 +49,7 @@ std::string getDataPath(std::string filename) { #endif { - std::string _path = executable_path() + "../data"; + std::string _path = removeNullCharacter(executable_path() + "../data"); if (is_directory(_path)) { dataPath = _path; KISS_LOG_INFO << "getDataPath: use executable path, " << dataPath @@ -52,7 +60,7 @@ std::string getDataPath(std::string filename) { dataPath = "data"; KISS_LOG_INFO << "getDataPath: use default, " << dataPath << std::endl; - return concat_path(dataPath, filename); + return removeNullCharacter(concat_path(dataPath, filename)); } diff --git a/src/module/EMPairProduction.cpp b/src/module/EMPairProduction.cpp index 2b5eed623..717c62cc4 100644 --- a/src/module/EMPairProduction.cpp +++ b/src/module/EMPairProduction.cpp @@ -1,6 +1,7 @@ #include "crpropa/module/EMPairProduction.h" #include "crpropa/Units.h" #include "crpropa/Random.h" +#include "kiss/logger.h" #include #include @@ -186,13 +187,23 @@ void EMPairProduction::performInteraction(Candidate *candidate) const { double E = candidate->current.getEnergy() * (1 + z); // check if in tabulated energy range - if (E < tabE.front() or (E > tabE.back())) + if (E < tabE.front() or (E > tabE.back())) { + KISS_LOG_WARNING + << "EMPairProduction: Energy " + << E / eV << " eV is not in tabulated range"; return; + } // sample the value of s Random &random = Random::instance(); size_t i = closestIndex(E, tabE); // find closest tabulation point size_t j = random.randBin(tabCDF[i]); + if (j <= 0) { + KISS_LOG_WARNING + << "EMPaiProduction: Sampled s value is the lowest tabulated value, which is not physical." + << " The index j will be set to 1 to avoid division by zero."; + j = 1; // ensure j is at least 1 to avoid division by + } double lo = std::max(4 * mec2 * mec2, tabs[j-1]); // first s-tabulation point below min(s_kin) = (2 me c^2)^2; ensure physical value double hi = tabs[j]; double s = lo + random.rand() * (hi - lo); @@ -204,8 +215,13 @@ void EMPairProduction::performInteraction(Candidate *candidate) const { double f = Ep / E; // for some backgrounds Ee=nan due to precision limitations. - if (not std::isfinite(Ee) || not std::isfinite(Ep)) + if (not std::isfinite(Ee) || not std::isfinite(Ep)) { + KISS_LOG_WARNING + << "EMPairProduction: Sampled energies are not finite for primary energy " + << E / eV << " eV and s = " << s / (eV * eV) << " eV^2 (maximum tabulated s = " + << tabs.back() / (eV * eV) << " eV^2)."; return; + } // sample random position along current step Vector3d pos = random.randomInterpolatedPosition(candidate->previous.getPosition(), candidate->current.getPosition()); diff --git a/test/testDiffusionSDE.py b/test/testDiffusionSDE.py index 9b9598580..54874af12 100644 --- a/test/testDiffusionSDE.py +++ b/test/testDiffusionSDE.py @@ -1,5 +1,5 @@ # coding=utf-8 -import sys +import sys, math try: import unittest @@ -103,10 +103,10 @@ def CDF_norm(x, mu=0., sigma=1.): x = np.array(x) x_tilde = (x-mu)/np.sqrt(2.)/sigma try: - cdf = [1/2.*(1.+np.math.erf(x)) for x in x_tilde] + cdf = [1/2.*(1.+math.erf(x)) for x in x_tilde] return np.array(cdf) except TypeError: - cdf = 1/2.*(1.+np.math.erf(x_tilde)) + cdf = 1/2.*(1.+math.erf(x_tilde)) return cdf diff --git a/test/testInteraction.cpp b/test/testInteraction.cpp index 0da78c795..86758bdba 100644 --- a/test/testInteraction.cpp +++ b/test/testInteraction.cpp @@ -769,7 +769,7 @@ TEST(EMPairProduction, secondaries) { // pass if no interaction has ocurred (no tabulated rates) if (c.isActive()) continue; - + // expect 2 secondaries EXPECT_EQ(c.secondaries.size(), 2); diff --git a/test/testPythonExtension.py b/test/testPythonExtension.py index 2c4045d35..21c2f8d55 100644 --- a/test/testPythonExtension.py +++ b/test/testPythonExtension.py @@ -21,7 +21,6 @@ class testCrossLanguagePolymorphism(unittest.TestCase): - def test_module(self): class CountingModule(crp.Module): def __init__(self): @@ -249,13 +248,13 @@ def testPublicReferenceAccess(self): v.x = 23. self.assertEqual(v.x, 23.) - ## this test fails in some systems - # def testArrayInterface(self): - # # this test fails for some combinations of Python version and system - # v = crp.Vector3d(1., 2., 3.) - # self.assertEqual(2., np.mean(v) ) - # x = np.ones(3) - # self.assertEqual(6., sum(v * x) ) + # this test fails in some systems + def testArrayInterface(self): + # this test fails for some combinations of Python version and system + v = crp.Vector3d(1., 2., 3.) + self.assertEqual(2., np.mean(v) ) + x = np.ones(3) + self.assertEqual(6., sum(v * x) ) def testRepr(self): v = crp.Vector3d(1., 2., 3.) @@ -277,6 +276,41 @@ def testOutOfBound(self): self.assertRaises(IndexError, v.__getitem__, 3) self.assertRaises(IndexError, v.__setitem__, 3, 10) + """ + # This test is currently disabled because it fails on some systems. + def testVector3dToArray(self): + v = crp.Vector3d(1., 2., 3.) + a = np.array([v]) + self.assertEqual(a.shape, (1, 3)) + self.assertEqual(a.dtype, float) + self.assertEqual(a[0, 0], 1.) + self.assertEqual(a[0, 1], 2.) + self.assertEqual(a[0, 2], 3.) + + def testVector3fToArray(self): + v = crp.Vector3f(1., 2., 3.) + a = np.array([v]) + self.assertEqual(a.shape, (1, 3)) + self.assertEqual(a.dtype, np.float32) + self.assertEqual(a[0, 0], 1.) + self.assertEqual(a[0, 1], 2.) + self.assertEqual(a[0, 2], 3.) + """ + + def testVector3dConstructorDouble(self): + + for dtype in [float, np.float32, int, np.int32]: + a = np.arange(3, dtype=dtype) + 1 + v = crp.Vector3d(a) + self.assertEqual(v.x, 1.) + self.assertEqual(v.y, 2.) + self.assertEqual(v.z, 3.) + self.assertEqual(v[0], 1.) + self.assertEqual(v[1], 2.) + self.assertEqual(v[2], 3.) + + + class testParticleCollector(unittest.TestCase): def testParticleCollectorIterator(self): collector = crp.ParticleCollector()