diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a744bd2..b4b2f79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,13 @@ name: FAST-PT CI on: push: branches: - - main - - code-upgrades - - structural-changes + - beta-fpt + - master pull_request: branches: - - main + - beta-fpt + - master + workflow_dispatch: jobs: test: @@ -16,7 +17,7 @@ jobs: strategy: matrix: - python-version: ["3.13", "3.11"] + python-version: ["3.13", "3.11", "3.9"] steps: - name: Checkout repository @@ -31,7 +32,8 @@ jobs: run: | python -m pip install --upgrade pip pip install -e . + pip install numpy==1.26.4 pip install pytest - name: Run unit/benchmark tests - run: pytest tests/ --disable-warnings + run: pytest tests/ diff --git a/.gitignore b/.gitignore index 940ff1f..8872c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ examples/*.pdf examples/*.npy examples/output/* +examples/outputs/* *.pyc build/ plot_test_outputs/ diff --git a/examples/v4_example.py b/examples/v4_example.py index 0638c08..92e6222 100644 --- a/examples/v4_example.py +++ b/examples/v4_example.py @@ -214,23 +214,26 @@ # 7. SAVE/LOAD FUNCTIONALITY # =============================================================================== print('\n7. === SAVE/LOAD DEMONSTRATION ===') -handler.output_dir = 'output' # Set output directory -# Save some results -handler.save_output(P_1loop_result, 'one_loop_example', type='csv') -handler.save_params('example_params', P=P_linear, C_window=0.75) +# Update the default parameters before saving the handler instance +handler.update_default_params( + P=P_linear, + C_window=0.75, + P_window=np.array([0.2, 0.2]), +) +handler.save_instance('example_handler') +# ^^ This will also save the parameters used to make the handler's fastpt instance -# Load them back -loaded_result = handler.load('one_loop_example_output.csv') -loaded_params = handler.load_params('example_params') +# Load them back in a new handler instance +loaded_handler = FPTHandler.load_instance('outputs/example_handler') -# And to use the loaded parameters: -handler.run('one_loop_dd', **loaded_params) -# or using Fast-PT directly: -fpt.one_loop_dd(**loaded_params) +# Now use the loaded handler with stored parameters to run calculations: +handler.run('one_loop_dd') +# or using the loaded Fast-PT instance directly: +loaded_handler.fastpt.one_loop_dd(**loaded_handler.default_params) print('✓ Saved and loaded results and parameters') -print(f' → Loaded parameters: {list(loaded_params.keys())}') +print(f' → Loaded parameters: {list(loaded_handler.default_params.keys())}') # =============================================================================== # 8. CACHE INFORMATION diff --git a/tests/test_benchmarks.py b/tests/test_benchmarks.py index d9a735d..b5a91ad 100644 --- a/tests/test_benchmarks.py +++ b/tests/test_benchmarks.py @@ -3,6 +3,8 @@ from fastpt import FASTPT import os import warnings +import sys +import platform data_path = os.path.join(os.path.dirname(__file__), 'benchmarking', 'Pk_test.dat') d = np.loadtxt(data_path) @@ -35,6 +37,10 @@ def calc_and_show(bmark, stored, func): plt.legend() plt.show() +@pytest.mark.skipif( + sys.version_info >= (3, 13) or platform.machine() == "arm64", + reason="Strict benchmark comparison is not reliable on Python 3.13+ or ARM64 runners" + ) def test_one_loop_dd(fpt): bmark = fpt.one_loop_dd(P, C_window=C_window)[0] stored = np.loadtxt('tests/benchmarking/P_dd_benchmark.txt') @@ -44,6 +50,10 @@ def test_one_loop_dd(fpt): " We can guarantee a precision of 9e-5 up until a k value of 10.") assert np.allclose(bmark, stored) +@pytest.mark.skipif( + sys.version_info >= (3, 13) or platform.machine() == "arm64", + reason="Strict benchmark comparison is not reliable on Python 3.13+ or ARM64 runners" + ) def test_one_loop_dd_bias(fpt): bmark = list(fpt.one_loop_dd_bias(P, C_window=C_window)) new_array = np.zeros(3000) @@ -57,6 +67,10 @@ def test_one_loop_dd_bias(fpt): " We can guarantee a precision of 9e-5 up until a k value of 10.") assert np.allclose(bmark, stored) +@pytest.mark.skipif( + sys.version_info >= (3, 13) or platform.machine() == "arm64", + reason="Strict benchmark comparison is not reliable on Python 3.13+ or ARM64 runners" + ) def test_one_loop_dd_bias_b3nl(fpt): bmark = list(fpt.one_loop_dd_bias_b3nl(P, C_window=C_window)) new_array = np.zeros(3000) @@ -70,6 +84,10 @@ def test_one_loop_dd_bias_b3nl(fpt): " We can guarantee a precision of 6e-4 up until a k value of 10.") assert np.allclose(bmark, stored) +@pytest.mark.skipif( + sys.version_info >= (3, 13) or platform.machine() == "arm64", + reason="Strict benchmark comparison is not reliable on Python 3.13+ or ARM64 runners" + ) def test_one_loop_dd_bias_lpt_NL(fpt): bmark = list(fpt.one_loop_dd_bias_lpt_NL(P, C_window=C_window)) new_array = np.zeros(3000) @@ -136,6 +154,10 @@ def test_RSD_ABsum_mu(fpt): bmark = np.transpose(fpt.RSD_ABsum_mu(P, 1.0, 1.0, C_window=C_window)) assert np.allclose(bmark, np.loadtxt('tests/benchmarking/P_RSD_ABsum_mu_benchmark.txt')) +@pytest.mark.skipif( + sys.version_info >= (3, 13) or platform.machine() == "arm64", + reason="Strict benchmark comparison is not reliable on Python 3.13+ or ARM64 runners" + ) def test_IRres(fpt): bmark = fpt.IRres(P, C_window=C_window) stored = np.transpose(np.loadtxt('tests/benchmarking/P_IRres_benchmark.txt')) diff --git a/tests/test_handler.py b/tests/test_handler.py index 798f61b..f20bd08 100644 --- a/tests/test_handler.py +++ b/tests/test_handler.py @@ -1067,6 +1067,8 @@ def test_save_and_use_in_run(fpt, temp_output_dir): ################# POWER SPECTRA GENERATOR TESTS ################# def test_generate_power_spectra_basic(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test basic single-mode power spectra generation with default parameters""" # Test class with default parameters class_result = handler.generate_power_spectra(method='class') @@ -1081,6 +1083,8 @@ def test_generate_power_spectra_basic(handler): assert np.all(camb_result > 0) def test_generate_power_spectra_methods(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test power spectra generation with different methods""" # Generate power spectra with different methods but same params class_result = handler.generate_power_spectra(method='class', omega_cdm=0.12, h=0.7, omega_b=0.022, z=0.5) @@ -1107,6 +1111,8 @@ def test_generate_power_spectra_single_mode_array_error(handler): handler.generate_power_spectra(omega_cdm=[0.1, 0.2]) def test_generate_power_spectra_params(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test power spectra generation with different parameter values""" # Generate with different parameter values base_result = handler.generate_power_spectra(method='class') @@ -1118,6 +1124,8 @@ def test_generate_power_spectra_params(handler): assert not np.allclose(base_result, high_cdm_result) def test_bulk_power_spectra(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test bulk mode power spectra generation""" # Test with arrays of different lengths bulk_results = handler.generate_power_spectra( @@ -1139,6 +1147,8 @@ def test_bulk_power_spectra(handler): assert np.all(result > 0) def test_bulk_power_spectra_single_entry(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test bulk mode with single entry arrays""" # When all parameters are length 1, should return a single result single_bulk_result = handler.generate_power_spectra( @@ -1154,6 +1164,8 @@ def test_bulk_power_spectra_single_entry(handler): assert len(single_bulk_result) == len(handler.fastpt.k_original) def test_diff_power_spectra_basic(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test diff mode power spectra generation""" # Test with basic parameters diff_results = handler.generate_power_spectra( @@ -1178,6 +1190,8 @@ def test_diff_power_spectra_basic(handler): assert len(value) == len(handler.fastpt.k_original) def test_diff_power_spectra_multi_param(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test diff mode with multiple variable parameters""" diff_results = handler.generate_power_spectra( mode='diff', @@ -1196,6 +1210,8 @@ def test_diff_power_spectra_multi_param(handler): assert (0.0, 2) in diff_results.keys(), "h high variation not found" def test_diff_power_spectra_requires_length_3(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test that diff mode requires at least one parameter with length 3""" with pytest.raises(ValueError, match="must have length 3"): handler.generate_power_spectra( @@ -1207,6 +1223,8 @@ def test_diff_power_spectra_requires_length_3(handler): ) def test_diff_power_spectra_with_multiple_z(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test diff mode with multiple redshifts""" diff_results = handler.generate_power_spectra( mode='diff', @@ -1227,6 +1245,8 @@ def test_diff_power_spectra_with_multiple_z(handler): assert len(z05_keys) == 3 def test_camb_specific_params(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test CAMB-specific parameters""" # With nonlinear=True result_nl = handler.generate_power_spectra( @@ -1253,6 +1273,8 @@ def test_camb_specific_params(handler): assert not np.allclose(result_nl, result_halofit) def test_class_camb_parameter_consistency(handler): + pytest.importorskip("classy", reason="classy is not installed") + pytest.importorskip("camb", reason="camb is not installed") """Test consistency in parameter handling between CLASS and CAMB""" # Generate spectra with same parameters params = {