diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index c223d28f..372d1c58 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -104,12 +104,11 @@ Releasing To release a new Mesh2HRTF version do the following - Write the new version to the file VERSION and mesh2input.py -- Update HISTORY.rst (also include new contributors) - Commit all changes to develop -- merge develop into main +- Update HISTORY.rst (also include new contributors) - check if the tests pass - check if the documentation is building - check if installing the python package works via ``pip install -e .`` -- add a tag with the version number ``git tag ``, e.g. ``git tag v1.0.0`` -- push the tag using ``git push origin --follow-tags`` -- add a release on github +- merge develop into main +- add a tagg and release on github +- merge main into develop diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..8a09a0bf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +# This Dockerfile can be used to run Mesh2HRTF inside a Docker container. +# Some information on how the container can be generated are given below. For +# more information refer to docker.com +# +# Inside the container you can +# - Run the `hrtf_mesh_grading` +# - Run numerical simualtions with the `NumCalc` command +# - Access the Mesh2HRTF Python API, e.g. by running `python3` and then +# `import mesh2hrtf` +# +# You can build the container in the command line by executing +# `docker build --tag ubuntu:mesh2hrtf .` (without the ` signs) +# +# Start the container with out an external volume with: +# `docker run -dit --name mesh2hrtf ubuntu:mesh2hrtf /bin/bash` +# +# Start the container with an external volume mounted at /home/data: +# `docker run -dit --name mesh2hrtf -v '/local/folder:/home/data' ubuntu:mesh2hrtf /bin/bash` + +FROM ubuntu:22.04 + +# All commands are run from this path +WORKDIR /home + +# Configure tzdata and timezone if problems appear during `apt-get install cmake` +#ENV TZ=Europe/Berlin +#RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# install required dependencies (remove specific versions if desired) +RUN apt-get update +RUN DEBIAN_FRONTEND=noninteractive apt-get upgrade -y +RUN DEBIAN_FRONTEND=noninteractive apt-get install -y cmake \ + make \ + build-essential\ + libx11-dev \ + libxrandr-dev \ + libxinerama-dev \ + libxcursor-dev \ + libxi-dev \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + libeigen3-dev \ + python3 \ + python3-pip + +# copy Mesh2HRTF git repo to docker container +COPY . /home/Mesh2HRTF + +# install Mesh2HRTF Python API +RUN pip install -e /home/Mesh2HRTF + +# build NumCalc +RUN cd /home/Mesh2HRTF/mesh2hrtf/NumCalc/src && make + +# add symbolic linc for NumCalc +RUN ln -s /home/Mesh2HRTF/mesh2hrtf/NumCalc/bin/NumCalc /usr/local/bin + +# build the pmp-library +RUN cd /home/Mesh2HRTF/mesh2hrtf/Mesh2Input/Meshes/GradingHybrid/pmp-library \ + && mkdir build && cd build && cmake .. && make && make install + +# add libpmp to the search path +RUN echo /usr/local/lib > /etc/ld.so.conf.d/local.conf +RUN ldconfig + +# add symbolic link for hrtf-mesh-grading +RUN ln -s /home/Mesh2HRTF/mesh2hrtf/Mesh2Input/Meshes/GradingHybrid/pmp-library/build/hrtf_mesh_grading /usr/local/bin diff --git a/HISTORY.rst b/HISTORY.rst index cd26ffe7..f050c670 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,9 +1,12 @@ History ======= -v1.0.1 (7 Mai 2023) +v1.1.0 (11 Mai 2023) ------------------- +* Add dockerfile running the Mesh2HRTF Python API, `NumCalc`, and `hrtf_mesh_grading` * Fixed a bug in installing the Mesh2HRTF Python API +* Fixed a bug in `manage_numcalc` if running multiple Mesh2HRTF projects +* Fix order of HRIRs/HRTFs plotted by `inspect_sofa_files` when choosing the median or frontal plane v1.0.0 (28 April 2023) ---------------------- diff --git a/VERSION b/VERSION index 7dea76ed..9084fa2f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.1 +1.1.0 diff --git a/docs/conf.py b/docs/conf.py index 106c0f1f..985c1388 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -71,7 +71,7 @@ # # The short X.Y version. with open(os.path.join('..', 'VERSION')) as version_file: - version = version_file.read() + version = version_file.read().strip() # The full version, including alpha/beta/rc tags. release = version diff --git a/mesh2hrtf/Mesh2Input/mesh2input.py b/mesh2hrtf/Mesh2Input/mesh2input.py index 8f459d66..602aba68 100644 --- a/mesh2hrtf/Mesh2Input/mesh2input.py +++ b/mesh2hrtf/Mesh2Input/mesh2input.py @@ -302,7 +302,7 @@ def save(operator, # get Mesh2HRTF version with open(os.path.join(programPath, "..", "VERSION")) as read_version: - version = read_version.readline() + version = read_version.readline().strip() # Export path and export directory handling if not filepath.endswith(os.path.sep): diff --git a/mesh2hrtf/NumCalc/manage_numcalc.py b/mesh2hrtf/NumCalc/manage_numcalc.py index cf39f8fd..96dc88ef 100644 --- a/mesh2hrtf/NumCalc/manage_numcalc.py +++ b/mesh2hrtf/NumCalc/manage_numcalc.py @@ -97,8 +97,12 @@ def manage_numcalc(project_path=os.getcwd(), numcalc_path=None, numcalc_path = "NumCalc" if numcalc_path is None else numcalc_path ram_info = psutil.virtual_memory() - max_ram_load = ram_info.total / 1073741824 \ - if max_ram_load is None else max_ram_load + if max_ram_load is None: + max_ram_load = ram_info.total / 1073741824 + elif max_ram_load > ram_info.total / 1073741824: + raise ValueError(( + f"The maximum RAM load of {max_ram_load} GB must be smaller than " + f"the total RAM, which is {ram_info.total / 1073741824} GB.")) # helping variables ------------------------------------------------------- @@ -268,13 +272,12 @@ def manage_numcalc(project_path=os.getcwd(), numcalc_path=None, # check if available memory is enough for running the instance with the # highest memory consumption without ever exceeding 100% of RAM. - ram_available, ram_used = _get_current_ram(ram_offset) - if ram_available < instances_to_run[-1, 3] * ram_safety_factor: + if max_ram_load < instances_to_run[-1, 3] * ram_safety_factor: # note: it IS possible to run simulations that use even more than # 100% of available system RAM - only the performance will be poor. _raise_error(( f"Stop - not sufficient free RAM for this simulation project: " - f"Available RAM is {round(ram_available, 2)} GB, but frequency" + f"Available RAM is {round(max_ram_load, 2)} GB, but frequency" f" step {int(instances_to_run[-1, 1])} of source " f"{int(instances_to_run[-1, 0])} requires " f"{round(instances_to_run[-1, 3] * ram_safety_factor, 2)} " diff --git a/mesh2hrtf/Output2HRTF/inspect_sofa_files.py b/mesh2hrtf/Output2HRTF/inspect_sofa_files.py index aac7eca8..8b84e470 100644 --- a/mesh2hrtf/Output2HRTF/inspect_sofa_files.py +++ b/mesh2hrtf/Output2HRTF/inspect_sofa_files.py @@ -204,7 +204,7 @@ def _inspect_sofa_files(file, savedir, atol, plot, plane, if not np.any(mask): warnings.warn(( - "Did not find and sources on the horizontal plane for " + f"Did not find and sources on the {plane} plane for " f"within +/-{atol} deg. for {file}")) return @@ -218,7 +218,8 @@ def _inspect_sofa_files(file, savedir, atol, plot, plane, # plot time data if mode == "hrir": _, qm, _ = pf.plot.time_2d( - signal[mask, cc], indices=angles, dB=dB_time, + signal[mask, cc][np.argsort(angles)], + indices=np.sort(angles), dB=dB_time, ax=ax_time[cc], cmap="coolwarm") ax_time[cc].set_title(name) @@ -230,7 +231,8 @@ def _inspect_sofa_files(file, savedir, atol, plot, plane, # plot frequency data _, qm, _ = pf.plot.freq_2d( - signal[mask, cc], ax=ax_freq[cc], indices=angles, + signal[mask, cc][np.argsort(angles)], + indices=np.sort(angles), ax=ax_freq[cc], dB=dB_freq, freq_scale=freq_scale, cmap="Reds") if mode == "hrir": ax_freq[cc].set_xlabel(f"{angle} in degree") @@ -245,5 +247,6 @@ def _inspect_sofa_files(file, savedir, atol, plot, plane, # save plt.tight_layout() - plt.savefig(os.path.join(savedir, tail[:-5] + "_3D.jpeg"), - dpi=300, bbox_inches="tight") + plt.savefig( + os.path.join(savedir, f"{tail[:-5]}_3D_{plane}_plane.jpeg"), + dpi=300, bbox_inches="tight") diff --git a/setup.py b/setup.py index 33ffcff6..f77b87fd 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ history = history_file.read() with open('VERSION') as version_file: - version = version_file.read() + version = version_file.read().strip() requirements = [ 'numpy>=1.14.0', diff --git a/tests/resources/test_numcalc/analytical_references/comparisonplot_rigid_bothears_2_ml-fmm-bem.jpg b/tests/resources/test_numcalc/analytical_references/comparisonplot_rigid_bothears_2_ml-fmm-bem.jpg index 2d2fedf1..fdc00135 100644 Binary files a/tests/resources/test_numcalc/analytical_references/comparisonplot_rigid_bothears_2_ml-fmm-bem.jpg and b/tests/resources/test_numcalc/analytical_references/comparisonplot_rigid_bothears_2_ml-fmm-bem.jpg differ diff --git a/tests/test_output.py b/tests/test_output.py index c409afff..60a67356 100644 --- a/tests/test_output.py +++ b/tests/test_output.py @@ -1,7 +1,8 @@ +import matplotlib as mpl +import matplotlib.pyplot as plt import pytest import numpy as np import numpy.testing as npt -import matplotlib.pyplot as plt from tempfile import TemporaryDirectory import shutil import os @@ -11,6 +12,8 @@ import sofar as sf import mesh2hrtf as m2h +mpl.use('Agg') + cwd = os.path.dirname(__file__) data_shtf = os.path.join(cwd, 'resources', 'SHTF') data_nc = os.path.join(cwd, 'resources', 'nc.out') @@ -104,8 +107,9 @@ def test_output_two_hrtf_and_Output2HRTF(num_sources): ["HRIR", "2D", ["HRIR_*_2D"], ["HRIR_*_3D", "HRTF_*_2D", "HRTF_*_3D"]], ["HRIR", "3D", ["HRIR_*_3D"], ["HRIR_*_2D", "HRTF_*_2D", "HRTF_*_3D"]] )) +@pytest.mark.parametrize("plane", ('horizontal', 'median', 'frontal')) def test_inspect_sofa_files_single_project( - pattern, plot, created, not_created): + pattern, plot, created, not_created, plane): """ Test if inspect_sofa_files creates the correct plots for a single project. Note: Not all options for reading from and saving to different directories @@ -123,20 +127,28 @@ def test_inspect_sofa_files_single_project( os.remove(file) # create plots - m2h.inspect_sofa_files(tmp_shtf, pattern, plot=plot) + m2h.inspect_sofa_files(tmp_shtf, pattern, plot=plot, plane=plane) grid = "FourPointHorPlane_r100cm" # check if the correct files exist and are missing for file in created: file = os.path.join(tmp_shtf, "Output2HRTF", file.replace("*", grid)) - extension = ".pdf" if "2D" in file else ".jpeg" - assert os.path.isfile(file + extension) + if "2D" in file: + file_name = f"{file}.pdf" + else: + file_name = f"{file}_{plane}_plane.jpeg" + assert os.path.isfile(file_name) for file in not_created: file = os.path.join(tmp_shtf, "Output2HRTF", file.replace("*", grid)) - extension = ".pdf" if "2D" in file else ".jpeg" - assert not os.path.isfile(file + extension) + if "2D" in file: + file_name = f"{file}.pdf" + else: + file_name = f"{file}_{plane}_plane.jpeg" + assert not os.path.isfile(file_name) + + plt.close('all') def test_compute_hrir_custom_sampling_rate(): diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 20962043..fde49ffd 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -5,6 +5,9 @@ from glob import glob from tempfile import TemporaryDirectory import mesh2hrtf as m2h +import matplotlib as mpl + +mpl.use("Agg") cwd = os.path.dirname(__file__) data_shtf = join(cwd, 'resources', 'SHTF') @@ -109,10 +112,10 @@ def test_outputs_to_hrtfs_full(): output = [os.path.basename(o) for o in output] files = ["HRTF_HRIR_FourPointHorPlane_r100cm.sofa", "HRTF_HRIR_FourPointHorPlane_r100cm_2D.pdf", - "HRTF_HRIR_FourPointHorPlane_r100cm_3D.jpeg", + "HRTF_HRIR_FourPointHorPlane_r100cm_3D_horizontal_plane.jpeg", "SHTF_HRIR_FourPointHorPlane_r100cm.sofa", "SHTF_HRIR_FourPointHorPlane_r100cm_2D.pdf", - "SHTF_HRIR_FourPointHorPlane_r100cm_3D.jpeg"] + "SHTF_HRIR_FourPointHorPlane_r100cm_3D_horizontal_plane.jpeg"] assert len(output) == len(files) for file in files: assert file in output diff --git a/tests/test_tutorials.py b/tests/test_tutorials.py index 4deb6f36..d741cb51 100644 --- a/tests/test_tutorials.py +++ b/tests/test_tutorials.py @@ -80,11 +80,10 @@ def test_tutorials(tutorial): cwd=tmp.name, check=True, capture_output=True) if run_numcalc: - # run manage_numcalc + # run manage_numcalc and process output print("running NumCalc") m2h.manage_numcalc(os.path.join(tmp.name, tutorial[:-3]), numcalc) - # run manage_numcalc print("running output2hrtf") m2h.output2hrtf(os.path.join(tmp.name, tutorial[:-3])) plt.close("all")