From ea626f5132e904f86be76db2a45557d8fc1697cc Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Sun, 25 Feb 2024 14:49:49 -0800 Subject: [PATCH 01/11] 233 drop python 38 support (#246) --- .github/workflows/pytest.yml | 2 +- .github/workflows/test_example.yml | 2 +- pyproject.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index a68d0b33..d9e90441 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -64,7 +64,7 @@ jobs: strategy: matrix: os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] - python-version: [ "3.8", "3.9", "3.10", "3.11" ] + python-version: [ "3.9", "3.10", "3.11" ] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test_example.yml b/.github/workflows/test_example.yml index 49d637d6..c8f6c14c 100644 --- a/.github/workflows/test_example.yml +++ b/.github/workflows/test_example.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] - python-version: [ "3.8", "3.9", "3.10", "3.11"] + python-version: [ "3.9", "3.10", "3.11"] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index d6c2e5a4..31cd23eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ dynamic = ["version"] description = "Acoustics toolbox for time domain acoustic and ultrasound simulations in complex and tissue-realistic media." readme = "docs/README.md" license = { file = "LICENSE" } -requires-python = ">=3.8" +requires-python = ">=3.9" authors = [ { name = "Farid Yagubbayli", email = "farid.yagubbayli@tum.de" }, { name = "Walter Simson", email = "walter.simson@tum.de"} From 17c08e2c2e36a5cd0c0068642ddb22ea5715783d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 16:42:14 -0800 Subject: [PATCH 02/11] Bump matplotlib from 3.7.2 to 3.8.3 (#325) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 31cd23eb..4f87632e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "scipy==1.10.1", "opencv-python==4.9.0.80", "deepdiff==6.7.1", - "matplotlib==3.7.2", + "matplotlib==3.8.3", "numpy>=1.22.2,<1.27.0", "nptyping==2.5.0", "beartype==0.16.4" From ad8ce176938acda47da5d3b56a090675ac314f77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 16:43:03 -0800 Subject: [PATCH 03/11] Bump scipy from 1.10.1 to 1.12.0 (#276) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4f87632e..6e140844 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,7 @@ classifiers = [ ] dependencies = [ "h5py==3.10.0", - "scipy==1.10.1", + "scipy==1.12.0", "opencv-python==4.9.0.80", "deepdiff==6.7.1", "matplotlib==3.8.3", From e0caa917d7e07388534e394937d4de03889317bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 14:29:43 -0800 Subject: [PATCH 04/11] Bump coverage from 7.4.1 to 7.4.3 (#330) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6e140844..a8343b6e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ Bug-tracker = "https://github.com/waltsims/k-wave-python/issues" [project.optional-dependencies] test = ["pytest", - "coverage==7.4.1", + "coverage==7.4.3", "phantominator", "requests==2.31.0"] example = ["gdown==4.6.0"] From c0dfdb517985f61115b36201f123de39ef26f893 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:50:39 -0800 Subject: [PATCH 05/11] Bump pre-commit from 3.6.1 to 3.6.2 (#329) Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 3.6.1 to 3.6.2. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v3.6.1...v3.6.2) --- updated-dependencies: - dependency-name: pre-commit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Walter Simson --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a8343b6e..f66ef475 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ docs = ["m2r2==0.3.2", "sphinx-tabs==3.4.4", "sphinx-toolbox==3.5.0", "furo==2024.1.29"] -dev = ["pre-commit==3.6.1"] +dev = ["pre-commit==3.6.2"] [tool.hatch.version] path = "kwave/__init__.py" From 57414508bd1fc1a33dc2319e8c3899b6c887c206 Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Mon, 26 Feb 2024 18:14:28 -0800 Subject: [PATCH 06/11] Update local testing workflow instructions (#291) * upadate workflow to create artefacts for local testing * add artifact step * Trigger a test * Trigger artifact * bump to artifact v4 * try to print artifact URL * trigger workflow * add artifact-upload-step id * trigger collection * fix typo * test_optional_dependencies * update dev documentation * delete tests not run in the CI * update docs with how to run tests * delete space * reformatting * more formatting * fix code-blocks * update coverage command * Add instructions for generating the references * Update development_environment.rst Co-authored-by: Farid Yagubbayli * Update development_environment.rst Co-authored-by: Farid Yagubbayli * Update development_environment.rst Co-authored-by: Farid Yagubbayli * Drop python 3.8 tests * Add an example link --------- Co-authored-by: Farid Yagubbayli --- .github/workflows/pytest.yml | 12 ++- .../workflows/test_optional_dependencies.yml | 23 +++++ docs/development/development_environment.rst | 66 +++++++++++--- tests/test_binary_present.py | 91 ------------------- tests/test_executor.py | 34 ------- tests/test_readme.py | 44 --------- 6 files changed, 85 insertions(+), 185 deletions(-) create mode 100644 .github/workflows/test_optional_dependencies.yml delete mode 100644 tests/test_binary_present.py delete mode 100644 tests/test_executor.py delete mode 100644 tests/test_readme.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index d9e90441..f18dd565 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -58,7 +58,17 @@ jobs: files: | ./collectedValues outPath: collectedValues.tar.gz - + - name: upload reference values artifact + id: artifact-upload-step + if: ${{ steps.matlab-refs-cache.outputs.cache-hit != 'true' }} + uses: actions/upload-artifact@v4 + with: + name: matlab_reference_test_values + path: collectedValues.tar.gz + # overwrite: true + - name: Output artifact URL + if: ${{ steps.matlab-refs-cache.outputs.cache-hit != 'true' }} + run: echo 'Artifact URL is ${{ steps.artifact-upload-step.outputs.artifact-url }}' test: needs: collect_references strategy: diff --git a/.github/workflows/test_optional_dependencies.yml b/.github/workflows/test_optional_dependencies.yml new file mode 100644 index 00000000..22392168 --- /dev/null +++ b/.github/workflows/test_optional_dependencies.yml @@ -0,0 +1,23 @@ +name: test_optional_requirements + +on: [push, pull_request] + +jobs: + test_install: + strategy: + matrix: + os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] + python-version: [ "3.9", "3.10", "3.11" ] + extra_requirements: [ "test", "examples", "docs", "dev", "all" ] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + # Pull the cache based on the hash value + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install dependencies + run: | + pip install '.[${{ matrix.extra_requirements }}]' diff --git a/docs/development/development_environment.rst b/docs/development/development_environment.rst index ab59c788..f35d42a7 100644 --- a/docs/development/development_environment.rst +++ b/docs/development/development_environment.rst @@ -2,31 +2,67 @@ Development Environment ======================= Currently, this package serves as an interface to the cpp binaries of k-Wave. -For this reason, the k-Wave binaries are packaged with the code in this repository. -The k-Wave binaries can currently be found on the `k-Wave download page `_. +For this reason, binaries are required to run simulations with `k-Wave-python`. +The binaries are downloaded by k-Wave-python when the package is run for the first time. -In order to correctly set up your development environment for this repository, clone the repository from github, and install the project dependencies. +To correctly set up your development environment for this repository, clone the repository from github, and install the project dependencies. .. code-block:: bash git clone https://github.com/waltsims/k-wave-python cd k-wave-python - pip install -e . + pip install -e '.[test,dev]' -Next, download all k-Wave binaries from the `k-Wave download page `_. +Test References +======================= -Lastly, place the contents of the linux-binaries, and windows-executables directories in the project directory structure under ``kwave/bin/linux/``, ``kwave/bin/darwin`` and ``kwave/bin/windows`` respectively. -You will have to create the directory structure yourself. +Tests compare the outputs of the python and the matlab interfaces. +These tests are located in the ``tests`` directory. The comparison between ``matlab`` and ``python`` outputs are done in two ways: -With this, you are ready to develop k-Wave-python. -If you have any issues or questions, please post them on the `k-Wave-python discussions page `_ to discuss. We look forward to interacting with you. +- **Unit testing**: k-Wave-python functions that have a direct counterpart in original k-Wave are tested by comparing the outputs of the two functions. + The output of the original k-Wave functions are stored in ``.mat`` files. + These files can be generated by running the corresponding MATLAB scripts located in the ``tests/matlab_test_data_collectors/matlab_collectors/`` directory by running ``tests/matlab_test_data_collectors/run_all_collectors.m``. + After running the scripts, the reference files can be found in ``tests/matlab_test_data_collectors/python_testes/collectedValues/``. + +.. note:: + If you do not have MATLAB installed to generate the reference files, you can download recently generated reference file outputs from the GitHub CI and place them in the ``python_testers/collectedValues/`` directory. + The latest reference files can be found in the artifacts of the latest CI run of ``pytest.yml`` (e.g. [here](https://github.com/waltsims/k-wave-python/actions/runs/7770639710/artifacts/1217868112)). +- **Integration testing**: k-Wave-python tests output .h5 files that are passed to the k-Wave binaries and ensures that they match the output of the original k-Wave. + This testing compares the output for many of the example scripts from the original k-Wave package. + Hash values of the reference output ``.h5`` file from MATLAB examples are generated and stored in ``.json`` files in ``tests/reference_outputs/``. + These ``.json`` files are stored in the code repository and do not need to be regenerated. + Since these files are generated from the original k-Wave package, they only need to be updated when a new release of k-Wave is made. -Test References -======================= +**Matlab reference file generation** is a bit involved process. Below are the steps that describe the process. -In order to ensure that the python interface is working correctly, we have created a set of tests that compare the output of the python interface to the output of the matlab interface. -These tests are located in the ``tests`` directory. The comparison between ``matlab`` and ``python`` outputs are done in two ways: +#. Open desired example in matlab, e.g. `example_pr_2D_TR_directional_sensors.m `_ +#. Find the lines where the call to one of the `kSpaceFirstOrder-family` function is made. For example, + + .. code-block:: python + + input_args = {'PMLInside', false, 'PMLSize', PML_size, 'PlotPML', false, 'Smooth', false}; + sensor_data = kspaceFirstOrder2D(kgrid, medium, source, sensor, input_args{:}); + +#. Update the ``input_args`` field by adding two new options - ``{'SaveToDisk', true, 'SaveToDiskExit': true}``. These options will ensure that we a ``.h5`` file will be created and saved in your ``tmp`` folder, while avoiding to run the actual simulation. +#. Run the modified example. You will find created files in your ``tmp`` folder. Usually exact file name depends on how many calls are made to the `kSpaceFirstOrder-family` function in the example: + * If there is only a single call, created file name will be ``example_input.h5`` + * If there are two or more calls, created files will have names like ``example_input_1.h5``, ``example_input_2.h5``, ``example_input_3.h5`` and so on +#. Now it is time to turn the ``.h5`` files to the hashed ``.json`` files. This can be done with the ``H5Summary``. + * If you have a single ``.h5`` file, adapt the lines below and run the script: + https://github.com/waltsims/k-wave-python/blob/1f9df5d987d0b3edb1a8a43fad0885d3d6079029/tests/h5_summary.py#L92-L95 + * For multiple files, adapt the lines below: + https://github.com/waltsims/k-wave-python/blob/1f9df5d987d0b3edb1a8a43fad0885d3d6079029/tests/h5_summary.py#L97-L106 + + +To run the tests, use the following command: + +.. code-block:: bash + + pytest + +To run the tests with coverage, use the following command: + +.. code-block:: bash -- Using ``.mat`` files. The files are generated by running Matlab scripts that are located in the ``tests/matlab_test_data_collectors`` directory. -- Using ``.json`` files which are generated by hashing the Matlab variables. These are located at ``tests/reference_outputs/``. In order to regenerate the ``.json`` files, check the ``H5Summary`` class and its ``.save(...)`` method. + coverage run diff --git a/tests/test_binary_present.py b/tests/test_binary_present.py deleted file mode 100644 index 5c3f8f0e..00000000 --- a/tests/test_binary_present.py +++ /dev/null @@ -1,91 +0,0 @@ -import os -import os.path - -import pytest - - -# TODO: refactor this for new lazy install strategy - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_linux_afp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'linux', 'acousticFieldPropagator-OMP')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_linux_omp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'linux', 'kspaceFirstOrder-OMP')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_linux_cuda_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'linux', 'kspaceFirstOrder-CUDA')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_afp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'acousticFieldPropagator-OMP.exe')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_cuda_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'kspaceFirstOrder-CUDA.exe')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_omp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'kspaceFirstOrder-OMP.exe')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_hdf5_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'hdf5.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_hdf5hl_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'hdf5_hl.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_cufft64_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'cufft64_10.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_libiomp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'libiomp5md.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_libmmd_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'libmmd.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_msvcp_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'msvcp140.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_svmldispmd_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'svml_dispmd.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_szip_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'szip.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_vcruntime140_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'vcruntime140.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_zlib_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'zlib.dll')) - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_windows_cufft6410_binaries_present(): - assert os.path.exists(os.path.join(os.getcwd(), 'kwave', 'bin', 'windows', 'cufft64_10.dll')) diff --git a/tests/test_executor.py b/tests/test_executor.py deleted file mode 100644 index 0f29b395..00000000 --- a/tests/test_executor.py +++ /dev/null @@ -1,34 +0,0 @@ -import sys -import unittest.mock -import logging -from kwave.executor import Executor -import pytest -import os - -check_is_linux = pytest.mark.skipif(not sys.platform.startswith('linux'), reason="Currently only implemented for linux.") - - -@check_is_linux -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -class TestExecutor(unittest.TestCase): - - @unittest.mock.patch('os.system') - def test_system_call_correct(self, os_system): - try: - ex = Executor('cpu') - input_filename = '/tmp/input.h5' - output_filename = '/tmp/output.h5' - try: - ex.run_simulation(input_filename, output_filename, options='') - except OSError: - logging.info("Caught 'Unable to open file' exception.") - - call_str = f"{ex.binary_path} -i {input_filename} -o {output_filename} " - os_system.assert_called_once_with(call_str) - except NotImplementedError as err: - if not sys.platform.startswith('darwin'): - raise err - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_readme.py b/tests/test_readme.py deleted file mode 100644 index dbd33c2f..00000000 --- a/tests/test_readme.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import re -import subprocess -import sys -from pathlib import Path -from tempfile import mkdtemp - -import pytest -import requests - - -@pytest.mark.skipif(os.environ.get("CI") == 'true', reason="Running in GitHub Workflow.") -def test_readme(): - # Check if there is internet connectivity - try: - requests.get("https://google.com") - except requests.exceptions.ConnectionError: - pytest.skip("No internet connectivity") - - # Skip the test if the operating system is MacOS - if sys.platform.startswith('darwin'): - pytest.skip("This test cannot be run on MacOS") - - # Read the getting started section from the READMEfind a .md file - with open(Path('README.md'), 'r') as f: - readme = f.read() - - tempdir = mkdtemp() - cwd = os.getcwd() - os.chdir(tempdir) - # Use a regular expression to find code blocks in the getting started section - code_blocks = re.findall(r'```bash(.*?)```', readme, re.DOTALL) - - for block in code_blocks: - instruction = block - result = subprocess.run(['bash', '-c', instruction]) - try: - assert result.returncode == 0, f"instruction failed: {instruction}" - except AssertionError as e: - os.chdir(cwd) - raise e - - os.chdir(cwd) - pass From acf5aa48d2495e5741c3a870650228560a425c4d Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Tue, 27 Feb 2024 11:40:46 -0800 Subject: [PATCH 07/11] Fix colab link for controlling the pml (#333) * Rename example_na_controlling_the_pml.ipynb to na_controlling_the_pml.ipynb * Update README.md --- examples/na_controlling_the_pml/README.md | 2 +- ...a_controlling_the_pml.ipynb => na_controlling_the_pml.ipynb} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename examples/na_controlling_the_pml/{example_na_controlling_the_pml.ipynb => na_controlling_the_pml.ipynb} (100%) diff --git a/examples/na_controlling_the_pml/README.md b/examples/na_controlling_the_pml/README.md index b0f927b4..18fcafa7 100644 --- a/examples/na_controlling_the_pml/README.md +++ b/examples/na_controlling_the_pml/README.md @@ -1,6 +1,6 @@ # Using An Ultrasound Transducer As A Sensor Example - + Open In Colab diff --git a/examples/na_controlling_the_pml/example_na_controlling_the_pml.ipynb b/examples/na_controlling_the_pml/na_controlling_the_pml.ipynb similarity index 100% rename from examples/na_controlling_the_pml/example_na_controlling_the_pml.ipynb rename to examples/na_controlling_the_pml/na_controlling_the_pml.ipynb From 7508d005ac5f49dd2488dcd01684f0aae730656d Mon Sep 17 00:00:00 2001 From: Farid Yagubbayli Date: Tue, 27 Feb 2024 19:52:40 +0000 Subject: [PATCH 08/11] Add codecov config (#334) --- .github/codecov.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/codecov.yml diff --git a/.github/codecov.yml b/.github/codecov.yml new file mode 100644 index 00000000..236a6f1d --- /dev/null +++ b/.github/codecov.yml @@ -0,0 +1,5 @@ +coverage: + status: + project: + default: + threshold: 1% From 6d2ee982bece84fc6980da99b23600f5675d2fc5 Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Tue, 27 Feb 2024 11:53:38 -0800 Subject: [PATCH 09/11] Bump python support to 3.12 (#267) * Bump pytest coverage 3.11 * Update test_example.yml * fix typo * Bump pytest to 3.12 * Update test_example.yml * Fix typo * Update test_optional_dependencies.yml --- .github/workflows/pytest.yml | 2 +- .github/workflows/test_example.yml | 2 +- .github/workflows/test_optional_dependencies.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index f18dd565..932f1aaf 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -74,7 +74,7 @@ jobs: strategy: matrix: os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] - python-version: [ "3.9", "3.10", "3.11" ] + python-version: [ "3.9", "3.10", "3.11", "3.12" ] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test_example.yml b/.github/workflows/test_example.yml index c8f6c14c..2f940d6d 100644 --- a/.github/workflows/test_example.yml +++ b/.github/workflows/test_example.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] - python-version: [ "3.9", "3.10", "3.11"] + python-version: [ "3.9", "3.10", "3.11", "3.12"] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/test_optional_dependencies.yml b/.github/workflows/test_optional_dependencies.yml index 22392168..763ed95d 100644 --- a/.github/workflows/test_optional_dependencies.yml +++ b/.github/workflows/test_optional_dependencies.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os: [ "windows-latest", "ubuntu-latest" ] #, "macos-latest"] - python-version: [ "3.9", "3.10", "3.11" ] + python-version: [ "3.9", "3.10", "3.11", "3.12" ] extra_requirements: [ "test", "examples", "docs", "dev", "all" ] runs-on: ${{matrix.os}} steps: From 8eff587099b4a86651529eb22eca5f3c3c50ecd0 Mon Sep 17 00:00:00 2001 From: Walter Simson Date: Tue, 27 Feb 2024 12:29:52 -0800 Subject: [PATCH 10/11] bump version number and update readme (#335) --- docs/README.md | 13 +++++++++++-- kwave/__init__.py | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index caad00de..61e3390f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -33,7 +33,7 @@ After installation, run the B-mode reconstruction example in the `examples` dire ```bash git clone https://github.com/waltsims/k-wave-python cd k-wave-python -git checkout v0.3.1 +git checkout v0.3.2 pip install '.[example]' python3 examples/us_bmode_linear_transducer/us_bmode_linear_transducer.py ``` @@ -48,7 +48,7 @@ This example file steps through the process of: This example expects an NVIDIA GPU by default to simulate with k-Wave. To test the reconstruction on a machine with a GPU, -set `RUN_SIMULATION` [on line 30 of `bmode_reconstruction_example.py`](https://github.com/waltsims/k-wave-python/blob/master/examples/bmode_reconstruction_example.py#L30) +set `RUN_SIMULATION` [on line 29 of `us_bmode_linear_transducer.py`](https://github.com/waltsims/k-wave-python/blob/6d2ee982bece84fc6980da99b23600f5675d2fc5/examples/us_bmode_linear_transducer/us_bmode_linear_transducer.py#L29) to `True`, and the example will run without the pre-computed data. ## Development @@ -68,6 +68,15 @@ found [here](https://k-wave-python.readthedocs.io/en/latest/development/developm The documentation for k-wave-python can be found [here](https://k-wave-python.readthedocs.io/en/latest/). +## Citation +```bibtex +@software{k-Wave-Python, +author = {Yagubbbayli, Farid and Sinden, David and Simson, Walter}, +license = {GPL-3.0}, +title = {{k-Wave-Python}}, +url = {https://github.com/waltsims/k-wave-python} +} +``` ## Contact e-mail [wsimson@stanford.edu](mailto:wsimson@stanford.edu). diff --git a/kwave/__init__.py b/kwave/__init__.py index 6ddb3bbe..aaeda958 100644 --- a/kwave/__init__.py +++ b/kwave/__init__.py @@ -8,7 +8,7 @@ # Test installation with: # python3 -m pip install -i https://test.pypi.org/simple/ --extra-index-url=https://pypi.org/simple/ k-Wave-python==0.3.0 -VERSION = '0.3.1' +VERSION = '0.3.2' # Set environment variable to binaries to get rid of user warning # This code is a crutch and should be removed when kspaceFirstOrder # is refactored From 4c082a593ee1f27c2423e8d128c48ab57a47b670 Mon Sep 17 00:00:00 2001 From: David Sinden Date: Tue, 27 Feb 2024 21:54:54 +0100 Subject: [PATCH 11/11] Update simulation_options.py - ensure that a 2d pml can be set (#328) --- kwave/options/simulation_options.py | 23 +++---- tests/test_pmlutils.py | 97 +++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 14 deletions(-) diff --git a/kwave/options/simulation_options.py b/kwave/options/simulation_options.py index d1a7d0df..5bd6bd18 100644 --- a/kwave/options/simulation_options.py +++ b/kwave/options/simulation_options.py @@ -254,26 +254,21 @@ def option_factory(kgrid: "kWaveGrid", options: SimulationOptions): raise ValueError("Optional input ''pml_size'' must be a single numerical value.") if kgrid.dim == 1: - options.pml_x_alpha = 2 options.pml_x_size = options.pml_size if options.pml_size else 20 options.plot_scale = [-1.1, 1.1] elif kgrid.dim == 2: - options.pml_x_alpha = 2 - options.pml_y_alpha = options.pml_x_alpha - if options.pml_size is None: - options.pml_x_size = 20 - options.pml_y_size = 20 - else: - options.pml_x_size = options.pml_size[0] - options.pml_y_size = options.pml_x_size + if (options.pml_size is not None): + if (len(options.pml_size) == kgrid.dim): + options.pml_x_size, options.pml_y_size = np.asarray(options.pml_size, dtype=int).ravel() + else: + options.pml_x_size, options.pml_y_size = (options.pml_size[0], options.pml_size[0]) + else: + options.pml_x_size, options.pml_y_size = (20, 20) options.plot_scale = [-1, 1] elif kgrid.dim == 3: - if options.pml_size is not None and len(options.pml_size) == kgrid.dim: - options.pml_x_size, options.pml_y_size, options.pml_z_size = options.pml_size.ravel() + if ((options.pml_size is not None) and (len(options.pml_size) == kgrid.dim)): + options.pml_x_size, options.pml_y_size, options.pml_z_size = np.asarray(options.pml_size).ravel() else: - options.pml_x_alpha = 2 - options.pml_y_alpha = options.pml_x_alpha - options.pml_z_alpha = options.pml_x_alpha if options.pml_size is None: options.pml_x_size = 10 options.pml_y_size = 10 diff --git a/tests/test_pmlutils.py b/tests/test_pmlutils.py index 3be98619..33263bde 100644 --- a/tests/test_pmlutils.py +++ b/tests/test_pmlutils.py @@ -1,4 +1,18 @@ +import pytest + + +from kwave.data import Vector + from kwave.kgrid import kWaveGrid + +from kwave.kmedium import kWaveMedium +from kwave.ksensor import kSensor +from kwave.ksource import kSource +from kwave.kspaceFirstOrder2D import kspaceFirstOrder2DC, kspace_first_order_2d_gpu +from kwave.options.simulation_execution_options import SimulationExecutionOptions +from kwave.options.simulation_options import SimulationOptions +from kwave.utils.mapgen import make_disc, make_cart_circle + from kwave.utils.pml import get_optimal_pml_size @@ -56,3 +70,86 @@ def test_get_optimal_pml_size_3D(): kgrid.setTime(round(t_end / dt) + 1, dt) assert (32 == get_optimal_pml_size(kgrid)).all() + +def test_pml_sizes_2d(): + + nx = 128 # number of grid points in the x (row) direction + x = 128e-3 # size of the domain in the x direction [m] + dx = x / nx # grid point spacing in the x direction [m] + ny = 132 # number of grid points in the y (column) direction + y = 132e-3 # size of the domain in the y direction [m] + dy = y / ny # grid point spacing in the y direction [m] + + grid_size = Vector([nx, ny]) # [grid points] + grid_spacing = Vector([dx, dy]) # [m] + + # time array + dt = 2e-9 # [s] + t_end = 300e-9 # [s] + # create the computational grid + kgrid = kWaveGrid(grid_size, grid_spacing) + # create the time array + kgrid.setTime(round(t_end / dt) + 1, dt) + + medium = kWaveMedium(sound_speed=1500, alpha_coeff=0.75, alpha_power=1.5) + + # create initial pressure distribution using make_disc + disc_magnitude = 5 # [Pa] + disc_pos = Vector([50, 50]) # [grid points] + disc_radius = 8 # [grid points] + disc_1 = disc_magnitude * make_disc(grid_size, disc_pos, disc_radius) + + disc_magnitude = 3 # [Pa] + disc_pos = Vector([80, 60]) # [grid points] + disc_radius = 5 # [grid points] + disc_2 = disc_magnitude * make_disc(grid_size, disc_pos, disc_radius) + + source = kSource() + source.p0 = disc_1 + disc_2 + + # define a centered circular sensor + sensor_radius = 4e-3 # [m] + num_sensor_points = 50 + sensor_mask = make_cart_circle(sensor_radius, num_sensor_points) + sensor = kSensor(sensor_mask) + + # these tests passes a list with three entries for the sizes of the pml + # but to a 2d simulation, and so raises an error. + with pytest.raises(ValueError): + simulation_options = SimulationOptions(pml_size=[18,18,18]) + _ = kspace_first_order_2d_gpu(medium=medium, + kgrid=kgrid, + source=source, + sensor=sensor, + simulation_options=simulation_options, + execution_options=SimulationExecutionOptions()) + + with pytest.raises(ValueError): + simulation_options = SimulationOptions(pml_size=[18,18,18]) + _ = kspaceFirstOrder2DC(medium=medium, + kgrid=kgrid, + source=source, + sensor=sensor, + simulation_options=simulation_options, + execution_options=SimulationExecutionOptions()) + + pml_size: int = 19 + simulation_options = SimulationOptions(pml_size=pml_size) + options = SimulationOptions.option_factory(kgrid, simulation_options) + assert ((options.pml_x_size == pml_size) and (options.pml_y_size == pml_size)), \ + "pml sizes incorrect when passing int" + + pml_sizes = [21, 22] + simulation_options = SimulationOptions(pml_size=pml_sizes) + options = SimulationOptions.option_factory(kgrid, simulation_options) + assert ((options.pml_x_size == pml_sizes[0]) and (options.pml_y_size == pml_sizes[1])), \ + "pml sizes incorrect when passing list" + + pml_sizes = None + pml_default: int = 20 + simulation_options = SimulationOptions() + options = SimulationOptions.option_factory(kgrid, simulation_options) + assert ((options.pml_x_size == pml_default) and (options.pml_y_size == pml_default)), \ + "pml sizes incorrect when not defining sizes" + +