From 6ec4c1e8530c52790f7f1e23c7827612697a09fd Mon Sep 17 00:00:00 2001 From: James Davies Date: Wed, 24 Jul 2024 14:50:56 +0200 Subject: [PATCH 01/18] remove unneeded param structures before validation --- src/py21cmfast/wrapper.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/py21cmfast/wrapper.py b/src/py21cmfast/wrapper.py index 65057c23..d22e19ee 100644 --- a/src/py21cmfast/wrapper.py +++ b/src/py21cmfast/wrapper.py @@ -317,11 +317,14 @@ def _setup_inputs( # This turns params into a dict with all the input parameters in it. params = dict(zip(pkeys + list(input_params.keys()), list(p) + params[4:])) + # Sort the params back into input order and ignore params not in input_params. + params = dict(zip(keys, [params[k] for k in keys])) + # Perform validation between different sets of inputs. validate_all_inputs(**{k: v for k, v in params.items() if k != "random_seed"}) - # Sort the params back into input order. - params = [params[k] for k in keys] + # return as list of values + params = list(params.values()) out = params if redshift != -1: From 7e05a8bed33d8863306360463f62af41d4f32a01 Mon Sep 17 00:00:00 2001 From: James Davies Date: Thu, 1 Aug 2024 11:18:12 +0200 Subject: [PATCH 02/18] fix delta k in RSD velocity gradients for non-cubic boxes --- src/py21cmfast/src/BrightnessTemperatureBox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/py21cmfast/src/BrightnessTemperatureBox.c b/src/py21cmfast/src/BrightnessTemperatureBox.c index f2b2f7b3..c7137d31 100644 --- a/src/py21cmfast/src/BrightnessTemperatureBox.c +++ b/src/py21cmfast/src/BrightnessTemperatureBox.c @@ -25,7 +25,7 @@ void get_velocity_gradient(struct UserParams *user_params, float *v, float *vel_ k_y = n_y * DELTA_K; for (n_z=0; n_z<=HII_MIDDLE_PARA; n_z++){ - k_z = n_z * DELTA_K; + k_z = n_z * DELTA_K_PARA; // take partial deriavative along the line of sight *((fftwf_complex *) vel_gradient + HII_C_INDEX(n_x,n_y,n_z)) *= k_z*I/(float)HII_TOT_NUM_PIXELS; From 6be038eeb36bfd374e5a757a7f7790960618c643 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Thu, 1 Aug 2024 11:31:29 +0200 Subject: [PATCH 03/18] ci: always save plots --- .github/workflows/test_suite.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_suite.yaml b/.github/workflows/test_suite.yaml index 93a7f524..dec4bb01 100644 --- a/.github/workflows/test_suite.yaml +++ b/.github/workflows/test_suite.yaml @@ -65,7 +65,7 @@ jobs: if: "contains(env.commit_message, 'ci debug')" run: | echo "log_level=ULTRA_DEBUG" >> $GITHUB_ENV - echo "extra_pytest_args=-s -k "test_power_spectra_lightcone[mdz_tsfluct_nthreads]" --log-level-21=DEBUG" >> $GITHUB_ENV + echo "extra_pytest_args=-s --log-level-21=DEBUG" >> $GITHUB_ENV - name: Make it a Normal Run if: "!contains(env.commit_message, 'ci debug')" @@ -99,7 +99,7 @@ jobs: python -m pytest -n 2 -l ${{ env.extra_pytest_args }} --cov=py21cmfast --cov-config=.coveragerc -vv --cov-report xml:./coverage.xml --durations=25 --plots=testplots - name: Archive Integration Test Plots - if: always() && contains(env.commit_message, 'ci debug') + if: always() uses: actions/upload-artifact@v3 with: name: integration-test-plots-${{ matrix.os }}-${{ matrix.python-version }} From 8cb260576f60f78ca2a01b9a7ab5fd1eec58bb1c Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Thu, 1 Aug 2024 15:43:05 +0200 Subject: [PATCH 04/18] ci: use powerbox<0.8 --- ci/macos-latest-env.yml | 2 +- ci/ubuntu-latest-env.yml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/macos-latest-env.yml b/ci/macos-latest-env.yml index d7a21de8..a8aeec13 100644 --- a/ci/macos-latest-env.yml +++ b/ci/macos-latest-env.yml @@ -18,7 +18,7 @@ dependencies: - gsl - fftw - pip: - - powerbox + - powerbox<0.8 - cached_property - pre-commit - pytest-plt diff --git a/ci/ubuntu-latest-env.yml b/ci/ubuntu-latest-env.yml index 3316db80..fa4dbfcc 100644 --- a/ci/ubuntu-latest-env.yml +++ b/ci/ubuntu-latest-env.yml @@ -16,7 +16,7 @@ dependencies: - matplotlib - pip - pip: - - powerbox + - powerbox<0.8 - cached_property - pre-commit - pytest-plt diff --git a/setup.py b/setup.py index 6074d1fd..3b3e0c07 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def _read(*names, **kwargs): "pytest-cov", "tox", "pytest-remotedata>=0.3.2", - "powerbox", + "powerbox<0.8", "pytest-plt", "questionary", ] From c3226a59ff8ef07722b9249ff8e17c138f90df38 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Thu, 1 Aug 2024 17:39:38 +0200 Subject: [PATCH 05/18] ci: use powerbox<0.8.1 --- ci/macos-latest-env.yml | 2 +- ci/ubuntu-latest-env.yml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/macos-latest-env.yml b/ci/macos-latest-env.yml index a8aeec13..771fbaa3 100644 --- a/ci/macos-latest-env.yml +++ b/ci/macos-latest-env.yml @@ -18,7 +18,7 @@ dependencies: - gsl - fftw - pip: - - powerbox<0.8 + - powerbox<0.8.1 - cached_property - pre-commit - pytest-plt diff --git a/ci/ubuntu-latest-env.yml b/ci/ubuntu-latest-env.yml index fa4dbfcc..8adf58fb 100644 --- a/ci/ubuntu-latest-env.yml +++ b/ci/ubuntu-latest-env.yml @@ -16,7 +16,7 @@ dependencies: - matplotlib - pip - pip: - - powerbox<0.8 + - powerbox<0.8.1 - cached_property - pre-commit - pytest-plt diff --git a/setup.py b/setup.py index 3b3e0c07..d867a282 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def _read(*names, **kwargs): "pytest-cov", "tox", "pytest-remotedata>=0.3.2", - "powerbox<0.8", + "powerbox<0.8.1", "pytest-plt", "questionary", ] From dee2d32750a05dba0b5efaa9e3b372652034ab8b Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Fri, 2 Aug 2024 11:52:31 +0200 Subject: [PATCH 06/18] temporarily adjust testk to match previous k --- tests/produce_integration_test_data.py | 4 ++-- tests/test_integration_features.py | 32 ++++++++++++++++---------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/produce_integration_test_data.py b/tests/produce_integration_test_data.py index bd3867cd..217ee567 100644 --- a/tests/produce_integration_test_data.py +++ b/tests/produce_integration_test_data.py @@ -477,8 +477,8 @@ def produce_power_spectra_for_tests(name, redshift, force, direc, **kwargs): k_l, p_l, lc = produce_lc_power_spectra(redshift, **kwargs) with h5py.File(fname, "w") as fl: - for k, v in kwargs.items(): - fl.attrs[k] = v + for key, v in kwargs.items(): + fl.attrs[key] = v fl.attrs["HII_DIM"] = coeval.user_params.HII_DIM fl.attrs["DIM"] = coeval.user_params.DIM diff --git a/tests/test_integration_features.py b/tests/test_integration_features.py index 88e003da..d6604c40 100644 --- a/tests/test_integration_features.py +++ b/tests/test_integration_features.py @@ -56,6 +56,7 @@ def test_power_spectra_coeval(name, module_direc, plt): for key, value in fl["coeval"].items() if key.startswith("power_") } + true_k = fl["coeval"]["k"][()] # Now compute the Coeval object with config.use(direc=module_direc, regenerate=False, write=True): @@ -66,7 +67,9 @@ def test_power_spectra_coeval(name, module_direc, plt): ) if plt == mpl.pyplot: - make_coeval_comparison_plot(test_k, true_powers, test_powers, plt) + make_coeval_comparison_plot( + true_k, test_k * np.sqrt(3), true_powers, test_powers, plt + ) for key in prd.COEVAL_FIELDS: if key not in true_powers: @@ -81,10 +84,6 @@ def test_power_spectra_coeval(name, module_direc, plt): ) -# assert np.sum(~np.isclose(value, test_powers[key], atol=0, rtol=1e-2)) < 10 -# np.testing.assert_allclose(value, test_powers[key], atol=0, rtol=1e-1) - - @pytest.mark.parametrize("name", options) def test_power_spectra_lightcone(name, module_direc, plt): redshift, kwargs = prd.OPTIONS[name] @@ -111,7 +110,14 @@ def test_power_spectra_lightcone(name, module_direc, plt): if plt == mpl.pyplot: make_lightcone_comparison_plot( - test_k, lc.node_redshifts, true_powers, true_global, test_powers, lc, plt + true_k, + test_k, + lc.node_redshifts, + true_powers, + true_global, + test_powers, + lc, + plt, ) for key in prd.LIGHTCONE_FIELDS: @@ -139,7 +145,7 @@ def test_power_spectra_lightcone(name, module_direc, plt): def make_lightcone_comparison_plot( - k, z, true_powers, true_global, test_powers, lc, plt + true_k, k, z, true_powers, true_global, test_powers, lc, plt ): n = len(true_global) + len(true_powers) fig, ax = plt.subplots( @@ -148,7 +154,7 @@ def make_lightcone_comparison_plot( for i, (key, val) in enumerate(true_powers.items()): make_comparison_plot( - k, val, test_powers[key], ax[:, i], xlab="k", ylab=f"{key} Power" + true_k, k, val, test_powers[key], ax[:, i], xlab="k", ylab=f"{key} Power" ) for i, (key, val) in enumerate(true_global.items(), start=i + 1): @@ -157,7 +163,7 @@ def make_lightcone_comparison_plot( ) -def make_coeval_comparison_plot(k, true_powers, test_powers, plt): +def make_coeval_comparison_plot(true_k, k, true_powers, test_powers, plt): fig, ax = plt.subplots( 2, len(true_powers), @@ -168,12 +174,14 @@ def make_coeval_comparison_plot(k, true_powers, test_powers, plt): for i, (key, val) in enumerate(true_powers.items()): make_comparison_plot( - k, val, test_powers[key], ax[:, i], xlab="k", ylab=f"{key} Power" + true_k, k, val, test_powers[key], ax[:, i], xlab="k", ylab=f"{key} Power" ) -def make_comparison_plot(x, true, test, ax, logx=True, logy=True, xlab=None, ylab=None): - ax[0].plot(x, true, label="True") +def make_comparison_plot( + xtrue, x, true, test, ax, logx=True, logy=True, xlab=None, ylab=None +): + ax[0].plot(xtrue, true, label="True") ax[0].plot(x, test, label="Test") if logx: ax[0].set_xscale("log") From 74911234fcae2948602159f051051864ae845211 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Mon, 5 Aug 2024 08:48:44 +0200 Subject: [PATCH 07/18] plot with true k --- setup.py | 2 +- src/py21cmfast/src/subcell_rsds.c | 2 +- tests/test_integration_features.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index d867a282..6074d1fd 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def _read(*names, **kwargs): "pytest-cov", "tox", "pytest-remotedata>=0.3.2", - "powerbox<0.8.1", + "powerbox", "pytest-plt", "questionary", ] diff --git a/src/py21cmfast/src/subcell_rsds.c b/src/py21cmfast/src/subcell_rsds.c index 19115fe5..454ff56c 100644 --- a/src/py21cmfast/src/subcell_rsds.c +++ b/src/py21cmfast/src/subcell_rsds.c @@ -6,7 +6,7 @@ double apply_subcell_rsds( struct IonizedBox *ionized_box, struct BrightnessTemp *box, float redshift, - struct SpinTemperature *spin_temp, + struct TsBox *spin_temp, float T_rad, float *v, float H diff --git a/tests/test_integration_features.py b/tests/test_integration_features.py index d6604c40..a9902646 100644 --- a/tests/test_integration_features.py +++ b/tests/test_integration_features.py @@ -159,7 +159,7 @@ def make_lightcone_comparison_plot( for i, (key, val) in enumerate(true_global.items(), start=i + 1): make_comparison_plot( - z, val, getattr(lc, key), ax[:, i], xlab="z", ylab=f"{key}" + z, z, val, getattr(lc, key), ax[:, i], xlab="z", ylab=f"{key}" ) From 9ee55407682912f90a5be11e84f73ec8442ba752 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:51:27 +0000 Subject: [PATCH 08/18] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pycqa/flake8: 7.0.0 → 7.1.1](https://github.com/pycqa/flake8/compare/7.0.0...7.1.1) - [github.com/asottile/pyupgrade: v3.16.0 → v3.17.0](https://github.com/asottile/pyupgrade/compare/v3.16.0...v3.17.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c342cf7..c12855ba 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - id: mixed-line-ending args: ['--fix=no'] - repo: https://github.com/pycqa/flake8 - rev: 7.0.0 # pick a git hash / tag to point to + rev: 7.1.1 # pick a git hash / tag to point to hooks: - id: flake8 additional_dependencies: @@ -51,7 +51,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/asottile/pyupgrade - rev: v3.16.0 + rev: v3.17.0 hooks: - id: pyupgrade args: [--py36-plus] From 360028adee2d835955659bd4a1d5841755a6f2b3 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Mon, 5 Aug 2024 20:11:22 +0200 Subject: [PATCH 09/18] test: fix test k plotting --- src/py21cmfast/src/BrightnessTemperatureBox.c | 4 +- src/py21cmfast/src/PerturbField.c | 14 ++--- src/py21cmfast/src/UsefulFunctions.c | 54 ++++++++++++++++++ ...est_power_spectra_coeval[minimize_mem].pdf | Bin 0 -> 45368 bytes tests/test_integration_features.py | 4 +- 5 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf diff --git a/src/py21cmfast/src/BrightnessTemperatureBox.c b/src/py21cmfast/src/BrightnessTemperatureBox.c index f2b2f7b3..d6963216 100644 --- a/src/py21cmfast/src/BrightnessTemperatureBox.c +++ b/src/py21cmfast/src/BrightnessTemperatureBox.c @@ -5,7 +5,7 @@ void get_velocity_gradient(struct UserParams *user_params, float *v, float *vel_ { memcpy(vel_gradient, v, sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); - dft_r2c_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, vel_gradient); + dft_r2c_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, (fftwf_complex *)vel_gradient); float k_x, k_y, k_z; int n_x, n_y, n_z; @@ -34,7 +34,7 @@ void get_velocity_gradient(struct UserParams *user_params, float *v, float *vel_ } } - dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, vel_gradient); + dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, (fftwf_complex *)vel_gradient); } int ComputeBrightnessTemp(float redshift, struct UserParams *user_params, struct CosmoParams *cosmo_params, diff --git a/src/py21cmfast/src/PerturbField.c b/src/py21cmfast/src/PerturbField.c index 4a1e9071..a6f46ba8 100644 --- a/src/py21cmfast/src/PerturbField.c +++ b/src/py21cmfast/src/PerturbField.c @@ -88,7 +88,7 @@ void compute_perturbed_velocities( } LOG_SUPER_DEBUG("density_perturb after modification by dDdt: "); - debugSummarizeBox(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); if(user_params->PERTURB_ON_HIGH_RES) { @@ -507,9 +507,9 @@ int ComputePerturbField( LOG_SUPER_DEBUG("density_perturb: "); if(user_params->PERTURB_ON_HIGH_RES){ - debugSummarizeBox(HIRES_density_perturb, dimension, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(HIRES_density_perturb, dimension, user_params->NON_CUBIC_FACTOR, " "); }else{ - debugSummarizeBox(LOWRES_density_perturb, dimension, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, dimension, user_params->NON_CUBIC_FACTOR, " "); } // deallocate @@ -620,7 +620,7 @@ int ComputePerturbField( } LOG_SUPER_DEBUG("LOWRES_density_perturb: "); - debugSummarizeBox(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); // transform to k-space dft_r2c_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, LOWRES_density_perturb); @@ -631,7 +631,7 @@ int ComputePerturbField( } LOG_SUPER_DEBUG("LOWRES_density_perturb after smoothing: "); - debugSummarizeBox(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); // save a copy of the k-space density field memcpy(LOWRES_density_perturb_saved, LOWRES_density_perturb, sizeof(fftwf_complex)*HII_KSPACE_NUM_PIXELS); @@ -639,7 +639,7 @@ int ComputePerturbField( dft_c2r_cube(user_params->USE_FFTW_WISDOM, user_params->HII_DIM, HII_D_PARA, user_params->N_THREADS, LOWRES_density_perturb); LOG_SUPER_DEBUG("LOWRES_density_perturb back in real space: "); - debugSummarizeBox(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); // normalize after FFT int bad_count=0; @@ -664,7 +664,7 @@ int ComputePerturbField( } if(bad_count>=5) LOG_WARNING("Total number of bad indices for LOW_density_perturb: %d", bad_count); LOG_SUPER_DEBUG("LOWRES_density_perturb back in real space (normalized): "); - debugSummarizeBox(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); + debugSummarizeBoxComplex(LOWRES_density_perturb, user_params->HII_DIM, user_params->NON_CUBIC_FACTOR, " "); #pragma omp parallel shared(perturbed_field,LOWRES_density_perturb) private(i,j,k) num_threads(user_params->N_THREADS) diff --git a/src/py21cmfast/src/UsefulFunctions.c b/src/py21cmfast/src/UsefulFunctions.c index 8d73bdc8..ddb28694 100644 --- a/src/py21cmfast/src/UsefulFunctions.c +++ b/src/py21cmfast/src/UsefulFunctions.c @@ -635,6 +635,60 @@ void debugSummarizeBox(float *box, int size, float ncf, char *indent){ } } +void debugSummarizeBoxComplex(fftwf_complex *box, int size, float ncf, char *indent){ + if(LOG_LEVEL >= SUPER_DEBUG_LEVEL){ + + float corners_real[8]; + float corners_imag[8]; + + int i,j,k, counter; + int s = size-1; + int s_ncf = size*ncf-1; + + counter = 0; + for(i=0;i= SUPER_DEBUG_LEVEL){ diff --git a/testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf b/testplots/tests.test_integration_features.py--test_power_spectra_coeval[minimize_mem].pdf new file mode 100644 index 0000000000000000000000000000000000000000..5130a0c3e860c30d557d759b1281767974dadbe9 GIT binary patch literal 45368 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 Date: Tue, 6 Aug 2024 14:28:10 +0200 Subject: [PATCH 10/18] ci: update rtd build os --- .readthedocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 3d4ff74f..57e52775 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -10,9 +10,9 @@ sphinx: configuration: docs/conf.py build: - os: ubuntu-22.04 + os: ubuntu-24.04 tools: - python: mambaforge-22.9 + python: mambaforge-latest conda: environment: docs/environment.yaml From ee34f6bf067a1a6513b5367b18e609b3f00b1c60 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Tue, 6 Aug 2024 18:44:37 +0200 Subject: [PATCH 11/18] fix: spurious warning on config setup --- src/py21cmfast/_cfg.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/py21cmfast/_cfg.py b/src/py21cmfast/_cfg.py index 5f662524..d25bb39a 100644 --- a/src/py21cmfast/_cfg.py +++ b/src/py21cmfast/_cfg.py @@ -39,7 +39,10 @@ def __init__(self, *args, write=True, file_name=None, **kwargs): for k, v in self._defaults.items(): if k not in self: if k not in self._aliases: - warnings.warn("Your configuration file is out of date. Updating...") + if self.file_name: + warnings.warn( + "Your configuration file is out of date. Updating..." + ) do_write = True self[k] = v @@ -55,9 +58,10 @@ def __init__(self, *args, write=True, file_name=None, **kwargs): del self[alias] break else: - warnings.warn( - "Your configuration file is out of date. Updating..." - ) + if self.file_name: + warnings.warn( + "Your configuration file is out of date. Updating..." + ) do_write = True self[k] = v @@ -70,7 +74,10 @@ def __init__(self, *args, write=True, file_name=None, **kwargs): self["direc"] = Path(self["direc"]).expanduser().absolute() if do_write and write and self.file_name: - self.write() + try: + self.write() + except Exception: + pass @contextlib.contextmanager def use(self, **kwargs): From c71e3206cf75b1a9efee4049618f27525c6249ea Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Tue, 6 Aug 2024 18:53:59 +0200 Subject: [PATCH 12/18] fix: some problems in cache_tools --- src/py21cmfast/_cfg.py | 5 ++--- src/py21cmfast/cache_tools.py | 13 +++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/py21cmfast/_cfg.py b/src/py21cmfast/_cfg.py index d25bb39a..c65a5155 100644 --- a/src/py21cmfast/_cfg.py +++ b/src/py21cmfast/_cfg.py @@ -91,8 +91,7 @@ def use(self, **kwargs): def write(self, fname: str | Path | None = None): """Write current configuration to file to make it permanent.""" - fname = Path(fname or self.file_name) - if fname: + if fname := Path(fname or self.file_name): if not fname.parent.exists(): fname.parent.mkdir(parents=True) @@ -113,7 +112,7 @@ def load(cls, file_name: str | Path): cfg = yaml.load(fl) return cls(cfg, file_name=file_name) else: - return cls(write=False) + return cls(write=True) config = Config.load(Path("~/.21cmfast/config.yml")) diff --git a/src/py21cmfast/cache_tools.py b/src/py21cmfast/cache_tools.py index 6477adaf..5fcd05e9 100644 --- a/src/py21cmfast/cache_tools.py +++ b/src/py21cmfast/cache_tools.py @@ -181,10 +181,16 @@ def query_cache( for file in list_datasets( direc=direc, kind=kind, hsh=hsh, seed=seed, redshift=redshift ): - cls = readbox(direc=direc, fname=file, load_data=False) + try: + kls = readbox(direc=direc, fname=file, load_data=False) + except OSError as e: + warnings.warn(f"Failed to read {file}: {e}") + continue + if show: - print(file + ": " + str(cls)) # noqa: T - yield file, cls + print(f"{file}: {str(kls)}") # noqa: T201 + + yield file, kls def get_boxes_at_redshift( @@ -200,7 +206,6 @@ def get_boxes_at_redshift( obj = readbox(direc=direc, fname=file, load_data=False) except OSError: warnings.warn(f"Failed to read {file}") - pass if not hasattr(obj, "redshift"): logger.debug(f"{file} has no redshift") From 994b7d4d3a1a7c6c7022aa93317269dd4a273bbe Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Tue, 6 Aug 2024 18:56:58 +0200 Subject: [PATCH 13/18] feat: better query cli command --- src/py21cmfast/cli.py | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/py21cmfast/cli.py b/src/py21cmfast/cli.py index bc8cde1a..a21f3a20 100644 --- a/src/py21cmfast/cli.py +++ b/src/py21cmfast/cli.py @@ -603,22 +603,41 @@ def lightcone(ctx, redshift, config, out, regen, direc, max_z, seed, lq): print(f"Saved Lightcone to {fname}.") -def _query(direc=None, kind=None, md5=None, seed=None, clear=False): - cls = list( +def _query( + direc=None, + kind=None, + md5=None, + seed=None, + clear=False, + sort_by=("kind", "redshift"), + show=None, +): + objects = list( cache_tools.query_cache(direc=direc, kind=kind, hsh=md5, seed=seed, show=False) ) if not clear: - print("%s Data Sets Found:" % len(cls)) + print(f"{len(objects)} Data Sets Found:") print("------------------") else: - print("Removing %s data sets..." % len(cls)) + print(f"Removing {len(objects)} data sets...") - for file, c in cls: - if not clear: - print(" @ {%s}:" % file) - print(" %s" % str(c)) + for sorter in sort_by[::-1]: + if sorter == "kind": + objects = sorted(objects, key=lambda x: x[1].__class__.__name__) + elif sorter == "filename": + objects = sorted(objects, key=lambda x: x[0]) + else: + objects = sorted(objects, key=lambda x: getattr(x[1], sorter, 0.0)) + for file, c in objects: + if not clear: + print(f" @ {file}:") + if not show: + print(f" {c}") + else: + for s in show: + print(f" {s}: {getattr(c, s, None)}") print() else: @@ -642,7 +661,9 @@ def _query(direc=None, kind=None, md5=None, seed=None, clear=False): default=False, help="remove all data sets returned by this query.", ) -def query(direc, kind, md5, seed, clear): +@click.option("--fields", type=str, multiple=True, default=None) +@click.option("--sort", type=str, multiple=True, default=["kind", "redshift"]) +def query(direc, kind, md5, seed, clear, fields, sort): """Query the cache database. Parameters @@ -658,7 +679,7 @@ def query(direc, kind, md5, seed, clear): clear : bool Remove all data sets returned by the query. """ - _query(direc, kind, md5, seed, clear) + _query(direc, kind, md5, seed, clear, show=fields, sort_by=tuple(sort)) @main.command() From 8d51997fe352f7f4f3c407f5a7f88928d3902dbb Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Tue, 6 Aug 2024 19:03:57 +0200 Subject: [PATCH 14/18] perf: don't always read in data when reading cache --- src/py21cmfast/wrapper.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/py21cmfast/wrapper.py b/src/py21cmfast/wrapper.py index d22e19ee..f40a7d5a 100644 --- a/src/py21cmfast/wrapper.py +++ b/src/py21cmfast/wrapper.py @@ -963,7 +963,7 @@ def initial_conditions( # First check whether the boxes already exist. if not regenerate: try: - boxes.read(direc) + boxes.read(direc, keys=()) logger.info( f"Existing init_boxes found and read in (seed={boxes.random_seed})." ) @@ -1067,7 +1067,7 @@ def perturb_field( # Check whether the boxes already exist if not regenerate: try: - fields.read(direc) + fields.read(direc, keys=()) logger.info( f"Existing z={redshift} perturb_field boxes found and read in " f"(seed={fields.random_seed})." @@ -1186,7 +1186,7 @@ def determine_halo_list( # Check whether the boxes already exist if not regenerate: try: - fields.read(direc) + fields.read(direc, keys=()) logger.info( f"Existing z={redshift} determine_halo_list boxes found and read in " f"(seed={fields.random_seed})." @@ -1307,7 +1307,7 @@ def perturb_halo_list( # Check whether the boxes already exist if not regenerate: try: - fields.read(direc) + fields.read(direc, keys=()) logger.info( "Existing z=%s perturb_halo_list boxes found and read in (seed=%s)." % (redshift, fields.random_seed) @@ -1573,7 +1573,7 @@ def ionize_box( # Check whether the boxes already exist if not regenerate: try: - box.read(direc) + box.read(direc, keys=()) logger.info( "Existing z=%s ionized boxes found and read in (seed=%s)." % (redshift, box.random_seed) @@ -1908,7 +1908,7 @@ def spin_temperature( # Check whether the boxes already exist on disk. if not regenerate: try: - box.read(direc) + box.read(direc, keys=()) logger.info( f"Existing z={redshift} spin_temp boxes found and read in " f"(seed={box.random_seed})." @@ -2052,7 +2052,7 @@ def brightness_temperature( # Check whether the boxes already exist on disk. if not regenerate: try: - box.read(direc) + box.read(direc, keys=()) logger.info( f"Existing brightness_temp box found and read in (seed={box.random_seed})." ) From 79ec2e65814dfd0d40df9687935e475d30c882ac Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Wed, 7 Aug 2024 13:00:42 +0200 Subject: [PATCH 15/18] docs: update changelog --- CHANGELOG.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e95f066c..c0d5e767 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,11 @@ Changelog dev-version ----------- +Performance +~~~~~~~~~~~ + +* When reading in data from cache, only read the necessary fields. +* Deprecations ~~~~~~~~~~~~ @@ -13,6 +18,9 @@ Fixed ~~~~~ * Fixed small issue in ``Lightcone.lightcone_coords``. +* Bug when setting ``NON_CUBIC_FACTOR != 1``. +* Using new ``matplotlib`` versions and registering ``cmap``. +* Small bug in lightcone interpolation. Added ~~~~~ @@ -44,6 +52,10 @@ Fixed ~~~~~ * Incorrect sign on adiabatic fluctuations. +* Spurious warnings about boxes being read. +* Spurious warnings about configuration files being out of date. +* Compilation for ``clang`` and newer ``gcc`` versions. + v3.3.1 [24 May 2023] ---------------------- From 516d68db70e8a4c1b20b4a4dfd8ebf49685afd14 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Wed, 7 Aug 2024 13:00:46 +0200 Subject: [PATCH 16/18] Bump Version: 3.3.1 -> 3.4.0 --- .bumpversion.cfg | 2 +- CHANGELOG.rst | 3 +++ VERSION | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index aeb098aa..97e8d915 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 3.3.1 +current_version = 3.4.0 commit = False tag = False diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c0d5e767..039c1f40 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,9 @@ Changelog dev-version ----------- +v3.4.0 [07 Aug 2024] +---------------------- + Performance ~~~~~~~~~~~ diff --git a/VERSION b/VERSION index bea438e9..18091983 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.3.1 +3.4.0 From 3dc9953b75d978ef2a95d3e33fea631a85cb6439 Mon Sep 17 00:00:00 2001 From: Steven Murray Date: Wed, 7 Aug 2024 13:27:10 +0200 Subject: [PATCH 17/18] maint: fix description to enable pypi upload --- CHANGELOG.rst | 2 +- setup.py | 1 + tmp.h5 | Bin 880 -> 0 bytes 3 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 tmp.h5 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 039c1f40..ff049ec8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,7 +11,7 @@ Performance ~~~~~~~~~~~ * When reading in data from cache, only read the necessary fields. -* + Deprecations ~~~~~~~~~~~~ diff --git a/setup.py b/setup.py index 6074d1fd..1b589524 100644 --- a/setup.py +++ b/setup.py @@ -51,6 +51,7 @@ def _read(*names, **kwargs): ), re.sub(":[a-z]+:`~?(.*?)`", r"``\1``", _read("CHANGELOG.rst")), ), + long_description_content_type="text/x-rst", author="The 21cmFAST coredev team", author_email="21cmfast.coredev@gmail.com", url="https://github.com/21cmFAST/21cmFAST", diff --git a/tmp.h5 b/tmp.h5 deleted file mode 100644 index 69f2e3197b6bd499086058e4ff59f432f57aae2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 880 zcmeD5aB<`1lHy_j0S*oZ76t(@6Gr@pf&ylU2#gPtPk=HQp>zk7Ucm%mFfuSRfaD~g z;sQ|f6`-ISqNi&f1_Q8>j0{W+JPaHRiBJm!fDVJW%>gP8k$|}i0I6*%g8%>k From 184dd9548e93f61bc2fab70f0a421a80b79b749e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 16:45:42 +0000 Subject: [PATCH 18/18] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black-pre-commit-mirror: 24.4.2 → 24.8.0](https://github.com/psf/black-pre-commit-mirror/compare/24.4.2...24.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c12855ba..0700c160 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - flake8-print - repo: https://github.com/psf/black-pre-commit-mirror - rev: 24.4.2 + rev: 24.8.0 hooks: - id: black