nullpol 0.2.2__tar.gz → 0.2.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nullpol-0.2.4/.github/workflows/ci.yml +66 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/codeql.yml +3 -3
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/scheduled-release.yml +1 -1
- {nullpol-0.2.2 → nullpol-0.2.4}/.pre-commit-config.yaml +5 -5
- {nullpol-0.2.2 → nullpol-0.2.4}/PKG-INFO +1 -1
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/data_analysis.py +1 -1
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/input.py +13 -3
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/tgrflow.py +88 -20
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/utility.py +5 -2
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/test_data_context.py +1 -1
- nullpol-0.2.4/tests/cli/test_data_analysis_validation.py +79 -0
- nullpol-0.2.4/tests/cli/test_input.py +179 -0
- nullpol-0.2.4/tests/integrations/asimov/__init__.py +1 -0
- nullpol-0.2.4/tests/integrations/asimov/conftest.py +78 -0
- nullpol-0.2.4/tests/integrations/asimov/test_tgrflow.py +310 -0
- nullpol-0.2.4/tests/integrations/asimov/test_tgrflow_integration.py +251 -0
- nullpol-0.2.4/tests/integrations/asimov/test_utility.py +85 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/smoke_test.py +15 -3
- {nullpol-0.2.2 → nullpol-0.2.4}/uv.lock +695 -685
- nullpol-0.2.2/.github/workflows/ci.yml +0 -36
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/ISSUE_TEMPLATE/bug-report.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/pull_request_template.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/documentation.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/draft-release.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/publish.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/publish_testpypi.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/release.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.github/workflows/semantic-pr-check.yml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.gitignore +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.markdownlint.yaml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.prettierignore +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.prettierrc.yaml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/.typos.toml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/CITATION.cff +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/CODE_OF_CONDUCT.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/CONTRIBUTING.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/LICENSE +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/README.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/SECURITY.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/cliff.toml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/.gitignore +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/asimov.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/citing.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/contributing.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/examples/scalar-tensor.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/injections.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/installation.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/javascripts/mathjax.js +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/antenna_patterns/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/clustering/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/likelihood/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/null_stream/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/prior/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/analysis/tf_transforms/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/cli/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/integrations/asimov/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/integrations/htcondor/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/integrations/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/simulation/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/utils/index.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/docs/reference/nullpol/version.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/generate_injection_file.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/injections.json +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/data/scalar_tensor_injection_data0_0_generation_data_dump.pickle +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_analysis/scalar_tensor_injection_data0_0_analysis_H1L1V1.err +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_analysis/scalar_tensor_injection_data0_0_analysis_H1L1V1.out +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_analysis/scalar_tensor_injection_data0_0_analysis_H1L1V1_final_result.err +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_analysis/scalar_tensor_injection_data0_0_analysis_H1L1V1_final_result.out +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_generation/scalar_tensor_injection_data0_0_generation.err +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/log_data_generation/scalar_tensor_injection_data0_0_generation.out +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/result/scalar_tensor_injection_data0_0_analysis_H1L1V1_dynesty.pickle +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/result/scalar_tensor_injection_data0_0_analysis_H1L1V1_resume.pickle +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/results_page/overview.html +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/scalar_tensor_injection_config_complete.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/bash_scalar_tensor_injection.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit.condor.sub +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit.dagman.out +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit.lib.err +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit.lib.out +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/dag_scalar_tensor_injection.submit.metrics +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/scalar_tensor_injection_data0_0_analysis_H1L1V1.submit +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/scalar_tensor_injection_data0_0_analysis_H1L1V1_final_result.submit +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/outdir_scalar_tensor_injection/submit/scalar_tensor_injection_data0_0_generation.submit +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/prior.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/scalar_tensor_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor/scalar_tensor_injection.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_fractional_projection/generate_injection_file.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_fractional_projection/scalar_tensor_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_fractional_projection/scalar_tensor_injection.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_gaussian/generate_injection_file.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_gaussian/scalar_tensor_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_gaussian/scalar_tensor_injection.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_h_marginalized/generate_injection_file.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_h_marginalized/scalar_tensor_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_h_marginalized/scalar_tensor_injection.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_weighted_gaussian/generate_injection_file.sh +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_weighted_gaussian/scalar_tensor_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/examples/injection/scalar_tensor_weighted_gaussian/scalar_tensor_injection.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/pyproject.toml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/renovate.json +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/scripts/README.md +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/scripts/auto_generate_init.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/.gitignore +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/antenna_patterns/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/antenna_patterns/base.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/antenna_patterns/conditioning.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/antenna_patterns/encoding.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/antenna_patterns/processor.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/algorithm.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/application.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/coherent_sky_scan.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/plotting.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/clustering/threshold_filter.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/data_context.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/likelihood/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/likelihood/chi2_tf_likelihood.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/likelihood/time_frequency_likelihood.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/null_stream/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/null_stream/calculator.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/null_stream/projections.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/prior/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/prior/default.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/prior/prior_files/polarization.prior +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/result.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/inverse_wavelet_freq.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/inverse_wavelet_time.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/stft.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/utils.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/wavelet_freq.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/wavelet_time.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/analysis/tf_transforms/wavelet_transforms.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/create_injection.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/create_time_frequency_filter_from_sample.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/data_generation.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/get_asimov_yaml.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/main.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/parser.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/templates/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/templates/config.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/templates/default_config_create_injection.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/templates/default_config_create_time_frequency_filter_from_sample.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/cli/templates/example_signal_parameters_create_injection.json +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/asimov.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/pesummary.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/templates/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/templates/analysis_defaults.yaml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/templates/nullpol.ini +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/asimov/templates/nullpol_analysis.yaml +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/htcondor/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/htcondor/analysis_node.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/htcondor/generation_node.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/integrations/htcondor/nullpol_pipe_dag_creator.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/simulation/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/simulation/injection.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/simulation/source.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/error.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/filesystem.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/imports.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/json_utils.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/utils/log.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/src/nullpol/version.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/.gitignore +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/antenna_patterns/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/antenna_patterns/test_base.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/antenna_patterns/test_conditioning.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/antenna_patterns/test_encoding.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/antenna_patterns/test_processor.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/clustering/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/clustering/test_algorithm.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/clustering/test_application.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/clustering/test_plotting.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/clustering/test_threshold_filter.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/fixtures/injections.json +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/likelihood/test_chi2_tf_likelihood.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/likelihood/test_time_frequency_likelihood.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/null_stream/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/null_stream/test_calculator.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/null_stream/test_projections.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/prior/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/prior/test_default.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/test_result.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_inverse_wavelet_freq.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_inverse_wavelet_time.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_stft.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_utils.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_wavelet_freq.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_wavelet_time.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/analysis/tf_transforms/test_wavelet_transforms.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/cli/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/cli/fixtures/mock_psd.txt +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/cli/test_create_injection.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/cli/test_create_time_frequency_filter.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/integrations/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/simulation/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/simulation/test_injection.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/simulation/test_source.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/test_import.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/test_version.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/__init__.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/test_error.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/test_filesystem.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/test_imports.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/test_json_utils.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/tests/utils/test_log.py +0 -0
- {nullpol-0.2.2 → nullpol-0.2.4}/zensical.toml +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [main]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [main]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
workflow_call:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ['3.12', '3.13', '3.14']
|
|
15
|
+
os: [ubuntu-latest, macos-latest]
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
|
|
22
|
+
- name: Install uv
|
|
23
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
enable-cache: 'true'
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: uv sync --group dev --frozen
|
|
30
|
+
|
|
31
|
+
- name: Run tests with pytest
|
|
32
|
+
run: uv run --frozen pytest
|
|
33
|
+
|
|
34
|
+
- name: Upload coverage to Codecov
|
|
35
|
+
if: ${{ !cancelled() }}
|
|
36
|
+
uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5
|
|
37
|
+
with:
|
|
38
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
39
|
+
|
|
40
|
+
integration:
|
|
41
|
+
# Runs the @pytest.mark.integration suite once (not in the matrix) to
|
|
42
|
+
# keep the fast unit-test matrix cheap while still gating on the
|
|
43
|
+
# heavier integration tests. Installs the optional `asimov` extra so
|
|
44
|
+
# real asimov/cbcflow paths can be exercised now and in the future.
|
|
45
|
+
needs: test
|
|
46
|
+
runs-on: ubuntu-latest
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
49
|
+
with:
|
|
50
|
+
persist-credentials: false
|
|
51
|
+
|
|
52
|
+
- name: Install uv
|
|
53
|
+
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
54
|
+
with:
|
|
55
|
+
python-version: '3.13'
|
|
56
|
+
enable-cache: 'true'
|
|
57
|
+
|
|
58
|
+
- name: Install dependencies (with asimov extra)
|
|
59
|
+
run: uv sync --group dev --extra asimov --frozen
|
|
60
|
+
|
|
61
|
+
- name: Run integration tests
|
|
62
|
+
# Clear the default addopts (which selects `-m 'not integration'`)
|
|
63
|
+
# and explicitly select the integration marker instead.
|
|
64
|
+
run:
|
|
65
|
+
uv run --frozen pytest -o addopts="" -m integration --cov src
|
|
66
|
+
--cov-report=term-missing
|
|
@@ -18,12 +18,12 @@ jobs:
|
|
|
18
18
|
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
|
|
19
19
|
|
|
20
20
|
- name: Initialize CodeQL
|
|
21
|
-
uses: github/codeql-action/init@
|
|
21
|
+
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4
|
|
22
22
|
with:
|
|
23
23
|
languages: python
|
|
24
24
|
|
|
25
25
|
- name: Autobuild
|
|
26
|
-
uses: github/codeql-action/autobuild@
|
|
26
|
+
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4
|
|
27
27
|
|
|
28
28
|
- name: Perform CodeQL Analysis
|
|
29
|
-
uses: github/codeql-action/analyze@
|
|
29
|
+
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4
|
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
|
|
21
21
|
- name: Check for new commits
|
|
22
22
|
id: check_commits
|
|
23
|
-
uses: isaac-cf-wong/detect-git-changes-action@
|
|
23
|
+
uses: isaac-cf-wong/detect-git-changes-action@c49bec37fa3496222c70649af3a09e2c2612310e # v0.0.15
|
|
24
24
|
with:
|
|
25
25
|
use-latest-tag: true
|
|
26
26
|
|
|
@@ -17,7 +17,7 @@ repos:
|
|
|
17
17
|
- id: check-json
|
|
18
18
|
|
|
19
19
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
20
|
-
rev: v0.15.
|
|
20
|
+
rev: v0.15.21
|
|
21
21
|
hooks:
|
|
22
22
|
- id: ruff
|
|
23
23
|
args: [--fix, --config=pyproject.toml]
|
|
@@ -29,18 +29,18 @@ repos:
|
|
|
29
29
|
- id: taplo-format
|
|
30
30
|
|
|
31
31
|
- repo: https://github.com/crate-ci/typos
|
|
32
|
-
rev: v1.
|
|
32
|
+
rev: v1.48.0
|
|
33
33
|
hooks:
|
|
34
34
|
- id: typos
|
|
35
35
|
args: []
|
|
36
36
|
|
|
37
37
|
- repo: https://github.com/rbubley/mirrors-prettier
|
|
38
|
-
rev: v3.9.
|
|
38
|
+
rev: v3.9.5
|
|
39
39
|
hooks:
|
|
40
40
|
- id: prettier
|
|
41
41
|
|
|
42
42
|
- repo: https://github.com/DavidAnson/markdownlint-cli2
|
|
43
|
-
rev: v0.
|
|
43
|
+
rev: v0.23.0
|
|
44
44
|
hooks:
|
|
45
45
|
- id: markdownlint-cli2
|
|
46
46
|
args:
|
|
@@ -48,7 +48,7 @@ repos:
|
|
|
48
48
|
- .markdownlint.yaml
|
|
49
49
|
|
|
50
50
|
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
51
|
-
rev: 0.11.
|
|
51
|
+
rev: 0.11.28
|
|
52
52
|
hooks:
|
|
53
53
|
# Update the uv lockfile
|
|
54
54
|
- id: uv-lock
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nullpol
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A package to perform model-independent polarization test of gravitational-wave signals.
|
|
5
5
|
Project-URL: Documentation, https://isaac-cf-wong.github.io/nullpol/
|
|
6
6
|
Project-URL: Source, https://github.com/isaac-cf-wong/nullpol
|
|
@@ -147,7 +147,7 @@ class DataAnalysisInput(BInput, Input):
|
|
|
147
147
|
raise NullpolError(f"Unsupported polarization-basis input: {modes}")
|
|
148
148
|
|
|
149
149
|
def _validate_polarization_model_setting(self):
|
|
150
|
-
supported_modes = ["b", "c", "
|
|
150
|
+
supported_modes = ["b", "c", "p", "l", "x", "y"]
|
|
151
151
|
# Check whether the modes are supported.
|
|
152
152
|
for mode in self.polarization_modes:
|
|
153
153
|
if mode not in supported_modes:
|
|
@@ -113,22 +113,32 @@ class Input(BilbyInput):
|
|
|
113
113
|
"time_frequency_filter": self.meta_data["time_frequency_filter"], # pylint: disable=no-member
|
|
114
114
|
"priors": self.search_priors,
|
|
115
115
|
}
|
|
116
|
+
extra_likelihood_kwargs = self.extra_likelihood_kwargs
|
|
116
117
|
if self.likelihood_type == "Chi2TimeFrequencyLikelihood":
|
|
117
118
|
Likelihood = Chi2TimeFrequencyLikelihood
|
|
118
|
-
likelihood_kwargs.update(
|
|
119
|
+
likelihood_kwargs.update(extra_likelihood_kwargs)
|
|
119
120
|
elif "." in self.likelihood_type:
|
|
120
121
|
split_path = self.likelihood_type.split(".")
|
|
121
122
|
module = ".".join(split_path[:-1])
|
|
122
123
|
likelihood_class = split_path[-1]
|
|
123
124
|
Likelihood = getattr(import_module(module), likelihood_class)
|
|
124
|
-
likelihood_kwargs.update(
|
|
125
|
+
likelihood_kwargs.update(extra_likelihood_kwargs)
|
|
125
126
|
else:
|
|
126
127
|
raise ValueError(f"Unknown Likelihood class {self.likelihood_type}")
|
|
127
128
|
|
|
129
|
+
likelihood_signature = inspect.signature(Likelihood.__init__)
|
|
130
|
+
accepted_keyword_names = {
|
|
131
|
+
name
|
|
132
|
+
for name, parameter in likelihood_signature.parameters.items()
|
|
133
|
+
if parameter.kind in (inspect.Parameter.POSITIONAL_OR_KEYWORD, inspect.Parameter.KEYWORD_ONLY)
|
|
134
|
+
}
|
|
135
|
+
accepts_extra_kwargs = any(
|
|
136
|
+
parameter.kind == inspect.Parameter.VAR_KEYWORD for parameter in likelihood_signature.parameters.values()
|
|
137
|
+
)
|
|
128
138
|
likelihood_kwargs = {
|
|
129
139
|
key: value
|
|
130
140
|
for key, value in likelihood_kwargs.items()
|
|
131
|
-
if key in
|
|
141
|
+
if key in accepted_keyword_names or (accepts_extra_kwargs and key in extra_likelihood_kwargs)
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
logger.debug(f"Initialise likelihood {Likelihood} with kwargs: \n{likelihood_kwargs}")
|
|
@@ -166,12 +166,12 @@ class Collector:
|
|
|
166
166
|
analysis_output["Notes"] = []
|
|
167
167
|
|
|
168
168
|
if analysis.comment is not None and (
|
|
169
|
-
corresponding_analysis is None or analysis.comment not in corresponding_analysis
|
|
169
|
+
corresponding_analysis is None or analysis.comment not in corresponding_analysis.get("Notes", [])
|
|
170
170
|
):
|
|
171
171
|
# We only want to add the comment to the notes if it doesn't already exist
|
|
172
172
|
analysis_output["Notes"].append(analysis.comment)
|
|
173
173
|
|
|
174
|
-
if analysis.review.status:
|
|
174
|
+
if analysis.review is not None and analysis.review.status:
|
|
175
175
|
if analysis.review.status.lower() == "approved":
|
|
176
176
|
analysis_output["ReviewStatus"] = "pass"
|
|
177
177
|
elif analysis.review.status.lower() == "rejected":
|
|
@@ -181,7 +181,8 @@ class Collector:
|
|
|
181
181
|
messages = sorted(analysis.review.messages, key=lambda k: k.timestamp)
|
|
182
182
|
if len(messages) > 0 and (
|
|
183
183
|
corresponding_analysis is None
|
|
184
|
-
or f"{messages[0].timestamp:%Y-%m-%d}: {messages[0].message}"
|
|
184
|
+
or f"{messages[0].timestamp:%Y-%m-%d}: {messages[0].message}"
|
|
185
|
+
not in corresponding_analysis.get("Notes", [])
|
|
185
186
|
):
|
|
186
187
|
analysis_output["Notes"].append(f"{messages[0].timestamp:%Y-%m-%d}: {messages[0].message}")
|
|
187
188
|
|
|
@@ -285,6 +286,69 @@ class Collector:
|
|
|
285
286
|
return {}
|
|
286
287
|
|
|
287
288
|
|
|
289
|
+
def validate_gr_pe_result(result: dict) -> bool:
|
|
290
|
+
"""Validate whether a PE result is suitable as the basis for a TGR analysis.
|
|
291
|
+
|
|
292
|
+
The result must be complete, non-deprecated, and not an online or
|
|
293
|
+
exploratory run.
|
|
294
|
+
|
|
295
|
+
Parameters
|
|
296
|
+
----------
|
|
297
|
+
result : dict
|
|
298
|
+
Metadata for the result to be validated.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
-------
|
|
302
|
+
bool
|
|
303
|
+
Validity of the result.
|
|
304
|
+
"""
|
|
305
|
+
uid = result.get("UID", "").lower()
|
|
306
|
+
if not uid or uid == "online" or uid.startswith("exp") or uid.startswith("detchar"):
|
|
307
|
+
return False
|
|
308
|
+
if result.get("RunStatus") != "complete":
|
|
309
|
+
return False
|
|
310
|
+
if result.get("ReviewStatus") != "pass":
|
|
311
|
+
return False
|
|
312
|
+
if result.get("Deprecated", False):
|
|
313
|
+
return False
|
|
314
|
+
# Ensure required files are present for actual PE results
|
|
315
|
+
if "ResultFile" not in result or "ConfigFile" not in result:
|
|
316
|
+
return False
|
|
317
|
+
return "PESummaryResultFile" in result
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def identify_basis_production(metadata) -> dict:
|
|
321
|
+
"""Identify the PE result that will be used as the basis for TGR analyses.
|
|
322
|
+
|
|
323
|
+
The IllustrativeResult is tried first. If it passes full validation
|
|
324
|
+
(complete, non-deprecated, not online/exp*/detchar*, and has all required
|
|
325
|
+
files) it is returned. If it is not available or does not pass, the function
|
|
326
|
+
falls back to the first result that passes the full validation. Returns an
|
|
327
|
+
empty dict if no valid result is found.
|
|
328
|
+
|
|
329
|
+
Parameters
|
|
330
|
+
----------
|
|
331
|
+
metadata :
|
|
332
|
+
CBCFlow superevent metadata.
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
-------
|
|
336
|
+
dict
|
|
337
|
+
Result metadata for the basis production, or empty dict if none found.
|
|
338
|
+
"""
|
|
339
|
+
illustrative_result_name = metadata.data["ParameterEstimation"].get("IllustrativeResult", None)
|
|
340
|
+
if illustrative_result_name is not None:
|
|
341
|
+
for result in metadata.data["ParameterEstimation"].get("Results", []):
|
|
342
|
+
if result["UID"] == illustrative_result_name and validate_gr_pe_result(result):
|
|
343
|
+
return result
|
|
344
|
+
|
|
345
|
+
for result in metadata.data["ParameterEstimation"]["Results"]:
|
|
346
|
+
if validate_gr_pe_result(result):
|
|
347
|
+
return result
|
|
348
|
+
|
|
349
|
+
return {}
|
|
350
|
+
|
|
351
|
+
|
|
288
352
|
# Functionality for information which flows from cbcflow into Asimov
|
|
289
353
|
# pylint: disable=too-few-public-methods
|
|
290
354
|
class Applicator:
|
|
@@ -369,6 +433,8 @@ class Applicator:
|
|
|
369
433
|
ligo["preferred event"] = event["UID"]
|
|
370
434
|
ligo["false alarm rate"] = event["FAR"]
|
|
371
435
|
event_time = event["GPSTime"]
|
|
436
|
+
if event_time is None:
|
|
437
|
+
raise ValueError(f"No preferred GraceDB event found for {sid}. Cannot determine event time.")
|
|
372
438
|
ligo["sname"] = sid
|
|
373
439
|
|
|
374
440
|
# also add the sampling rate to the output
|
|
@@ -387,22 +453,22 @@ class Applicator:
|
|
|
387
453
|
"event time": event_time,
|
|
388
454
|
}
|
|
389
455
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if
|
|
405
|
-
|
|
456
|
+
basis_result = identify_basis_production(metadata)
|
|
457
|
+
if not basis_result:
|
|
458
|
+
raise ValueError(
|
|
459
|
+
"No valid GR PE result found in the library. The IllustrativeResult "
|
|
460
|
+
"(if set) and all other results either have incomplete run status, "
|
|
461
|
+
"are deprecated, or are excluded by UID."
|
|
462
|
+
)
|
|
463
|
+
gr_pe = {
|
|
464
|
+
"available": True,
|
|
465
|
+
"UID GR PE": basis_result["UID"],
|
|
466
|
+
"result file path": "/" + basis_result["ResultFile"]["Path"].split(":/")[-1],
|
|
467
|
+
"config file path": "/" + basis_result["ConfigFile"]["Path"].split(":/")[-1],
|
|
468
|
+
"pesummary result path": "/" + basis_result["PESummaryResultFile"]["Path"].split(":/")[-1],
|
|
469
|
+
}
|
|
470
|
+
if "WaveformApproximant" in basis_result:
|
|
471
|
+
quality["waveform approximant"] = basis_result["WaveformApproximant"]
|
|
406
472
|
|
|
407
473
|
# now, we need to read in the information from the config file
|
|
408
474
|
# we need the reference frequency, the psd dict and the calibration dict
|
|
@@ -410,8 +476,10 @@ class Applicator:
|
|
|
410
476
|
bilby_config = bilby_config_to_asimov(config_file)
|
|
411
477
|
|
|
412
478
|
# read in the psd information if present
|
|
413
|
-
if "psds"
|
|
479
|
+
if bilby_config.get("psds") is not None:
|
|
414
480
|
gr_pe["psds"] = bilby_config.pop("psds")
|
|
481
|
+
elif "psds" in bilby_config:
|
|
482
|
+
bilby_config.pop("psds")
|
|
415
483
|
output["gr pe info"] = gr_pe.copy()
|
|
416
484
|
output["gr pe info"]["approximant"] = bilby_config["waveform"]["approximant"]
|
|
417
485
|
# config file and cbcflow can disagree about which data to use (for example, preferred frame was updated)
|
|
@@ -37,8 +37,11 @@ def read_bilby_ini_file(file: str) -> dict:
|
|
|
37
37
|
def _convert_string_to_dict(string):
|
|
38
38
|
try:
|
|
39
39
|
return bilby_pipe.utils.convert_string_to_dict(string)
|
|
40
|
-
except (bilby_pipe.utils.BilbyPipeError, AttributeError):
|
|
41
|
-
|
|
40
|
+
except (bilby_pipe.utils.BilbyPipeError, AttributeError, TypeError):
|
|
41
|
+
try:
|
|
42
|
+
return literal_eval(string)
|
|
43
|
+
except (ValueError, SyntaxError):
|
|
44
|
+
return string
|
|
42
45
|
|
|
43
46
|
|
|
44
47
|
def fill_in_pol_specific_metadata(analysis, corresponding_analysis): # pylint: disable=unused-argument
|
|
@@ -479,7 +479,7 @@ class TestTimeFrequencyDataContext:
|
|
|
479
479
|
# Create filter with wrong shape
|
|
480
480
|
wrong_filter = np.random.rand(10, 20) # Wrong dimensions
|
|
481
481
|
|
|
482
|
-
with pytest.raises(
|
|
482
|
+
with pytest.raises(ValueError, match="does not match the time frequency filter"):
|
|
483
483
|
TimeFrequencyDataContext(
|
|
484
484
|
interferometers=ifos,
|
|
485
485
|
wavelet_frequency_resolution=2.0,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""Unit tests for :meth:`DataAnalysisInput._validate_polarization_model_setting`.
|
|
2
|
+
|
|
3
|
+
This guards the fix in commit ``74b4fc3`` where the supported-modes list
|
|
4
|
+
contained a duplicate ``"b"`` and was missing ``"p"`` (plus), causing a valid
|
|
5
|
+
``"p"`` mode to be rejected in both ``polarization-modes`` and
|
|
6
|
+
``polarization-basis`` validation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from nullpol.cli.data_analysis import DataAnalysisInput
|
|
14
|
+
from nullpol.utils import NullpolError
|
|
15
|
+
|
|
16
|
+
SUPPORTED = ["b", "c", "p", "l", "x", "y"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _make_input(modes, basis):
|
|
20
|
+
"""Build a DataAnalysisInput bypassing the heavy __init__.
|
|
21
|
+
|
|
22
|
+
The validation method only reads the ``polarization_modes`` and
|
|
23
|
+
``polarization_basis`` properties, which are backed by the private
|
|
24
|
+
``_polarization_modes`` / ``_polarization_basis`` attributes.
|
|
25
|
+
"""
|
|
26
|
+
obj = object.__new__(DataAnalysisInput)
|
|
27
|
+
obj._polarization_modes = list(modes)
|
|
28
|
+
obj._polarization_basis = list(basis)
|
|
29
|
+
return obj
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TestValidatePolarizationModelSetting:
|
|
33
|
+
"""Tests for :meth:`_validate_polarization_model_setting`."""
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize("mode", SUPPORTED)
|
|
36
|
+
def test_each_supported_mode_accepted_alone(self, mode):
|
|
37
|
+
"""Every one of the six canonical modes is accepted in polarization-modes."""
|
|
38
|
+
_make_input([mode], [mode])._validate_polarization_model_setting()
|
|
39
|
+
|
|
40
|
+
def test_all_six_modes_accepted_together(self):
|
|
41
|
+
"""All six modes (b, c, p, l, x, y) pass when used together."""
|
|
42
|
+
_make_input(SUPPORTED, SUPPORTED)._validate_polarization_model_setting()
|
|
43
|
+
|
|
44
|
+
def test_plus_mode_accepted(self):
|
|
45
|
+
"""Regression: ``"p"`` (plus) must be accepted -- it was rejected before the fix."""
|
|
46
|
+
_make_input(["p", "c"], ["p"])._validate_polarization_model_setting()
|
|
47
|
+
|
|
48
|
+
def test_plus_mode_rejected_before_fix_list(self):
|
|
49
|
+
"""Sanity check: the fixed list no longer contains a duplicate ``b``."""
|
|
50
|
+
# If the old buggy list ["b","c","b","l","x","y"] were restored, "p" would fail.
|
|
51
|
+
assert SUPPORTED == ["b", "c", "p", "l", "x", "y"]
|
|
52
|
+
assert SUPPORTED.count("b") == 1
|
|
53
|
+
assert "p" in SUPPORTED
|
|
54
|
+
|
|
55
|
+
@pytest.mark.parametrize("bad_mode", ["z", "a", "B", "pc", "", "plus"])
|
|
56
|
+
def test_invalid_mode_in_modes_rejected(self, bad_mode):
|
|
57
|
+
"""An unsupported mode in ``polarization-modes`` raises NullpolError."""
|
|
58
|
+
with pytest.raises(NullpolError, match="polarization-modes"):
|
|
59
|
+
_make_input([bad_mode], [bad_mode])._validate_polarization_model_setting()
|
|
60
|
+
|
|
61
|
+
@pytest.mark.parametrize("bad_mode", ["z", "a", "P"])
|
|
62
|
+
def test_invalid_mode_in_basis_rejected(self, bad_mode):
|
|
63
|
+
"""An unsupported mode in ``polarization-basis`` raises NullpolError."""
|
|
64
|
+
with pytest.raises(NullpolError, match="polarization-basis"):
|
|
65
|
+
_make_input(["p", "c"], [bad_mode])._validate_polarization_model_setting()
|
|
66
|
+
|
|
67
|
+
def test_basis_not_subset_of_modes_rejected(self):
|
|
68
|
+
"""A basis mode that is supported but not in ``polarization-modes`` is rejected."""
|
|
69
|
+
# "b" is a supported mode, but it is not in the modes list here.
|
|
70
|
+
with pytest.raises(NullpolError, match="Basis mode"):
|
|
71
|
+
_make_input(["p", "c"], ["b"])._validate_polarization_model_setting()
|
|
72
|
+
|
|
73
|
+
def test_empty_basis_accepted(self):
|
|
74
|
+
"""An empty basis is valid (all modes are derived)."""
|
|
75
|
+
_make_input(["p", "c"], [])._validate_polarization_model_setting()
|
|
76
|
+
|
|
77
|
+
def test_basis_subset_of_modes_accepted(self):
|
|
78
|
+
"""A basis that is a strict subset of the modes is valid."""
|
|
79
|
+
_make_input(["p", "c", "b", "l"], ["p", "c"])._validate_polarization_model_setting()
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""Tests for likelihood construction from CLI input."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import types
|
|
6
|
+
|
|
7
|
+
from bilby_pipe.main import parse_args
|
|
8
|
+
|
|
9
|
+
from nullpol.cli import input as input_module
|
|
10
|
+
from nullpol.cli.data_analysis import DataAnalysisInput
|
|
11
|
+
from nullpol.cli.parser import create_nullpol_parser
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _KeywordOnlyLikelihood:
|
|
15
|
+
"""Capture the arguments supplied by :attr:`Input.likelihood`."""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
interferometers,
|
|
20
|
+
wavelet_frequency_resolution,
|
|
21
|
+
wavelet_nx,
|
|
22
|
+
polarization_modes,
|
|
23
|
+
*args,
|
|
24
|
+
polarization_basis=None,
|
|
25
|
+
time_frequency_filter=None,
|
|
26
|
+
**kwargs,
|
|
27
|
+
):
|
|
28
|
+
self.interferometers = interferometers
|
|
29
|
+
self.wavelet_frequency_resolution = wavelet_frequency_resolution
|
|
30
|
+
self.wavelet_nx = wavelet_nx
|
|
31
|
+
self.polarization_modes = polarization_modes
|
|
32
|
+
self.polarization_basis = polarization_basis
|
|
33
|
+
self.time_frequency_filter = time_frequency_filter
|
|
34
|
+
self.extra_kwargs = kwargs
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _StrictLikelihood:
|
|
38
|
+
"""Likelihood without ``**kwargs``; unknown extras must be dropped, not forwarded."""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
interferometers,
|
|
43
|
+
wavelet_frequency_resolution,
|
|
44
|
+
wavelet_nx,
|
|
45
|
+
polarization_modes,
|
|
46
|
+
polarization_basis=None,
|
|
47
|
+
time_frequency_filter=None,
|
|
48
|
+
priors=None,
|
|
49
|
+
):
|
|
50
|
+
self.interferometers = interferometers
|
|
51
|
+
self.wavelet_frequency_resolution = wavelet_frequency_resolution
|
|
52
|
+
self.wavelet_nx = wavelet_nx
|
|
53
|
+
self.polarization_modes = polarization_modes
|
|
54
|
+
self.polarization_basis = polarization_basis
|
|
55
|
+
self.time_frequency_filter = time_frequency_filter
|
|
56
|
+
self.priors = priors
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _build_inputs(args, *, interferometers, time_frequency_filter, priors, likelihood_type=None):
|
|
60
|
+
"""Assemble a :class:`DataAnalysisInput` wired for :attr:`Input.likelihood`."""
|
|
61
|
+
inputs = object.__new__(DataAnalysisInput)
|
|
62
|
+
inputs._interferometers = interferometers
|
|
63
|
+
inputs.search_priors = priors
|
|
64
|
+
inputs.wavelet_frequency_resolution = 16.0
|
|
65
|
+
inputs.wavelet_nx = 4.0
|
|
66
|
+
inputs.polarization_modes = args.polarization_modes
|
|
67
|
+
inputs.polarization_basis = args.polarization_basis
|
|
68
|
+
inputs.meta_data = {"time_frequency_filter": time_frequency_filter}
|
|
69
|
+
inputs.likelihood_type = likelihood_type if likelihood_type is not None else args.likelihood_type
|
|
70
|
+
inputs.extra_likelihood_kwargs = args.extra_likelihood_kwargs
|
|
71
|
+
return inputs
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _parse_config(tmp_path, body):
|
|
75
|
+
config_file = tmp_path / "analysis.ini"
|
|
76
|
+
config_file.write_text(body, encoding="utf-8")
|
|
77
|
+
args, unknown_args = parse_args([str(config_file)], create_nullpol_parser(top_level=False))
|
|
78
|
+
assert unknown_args == []
|
|
79
|
+
return args
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_analysis_config_forwards_keyword_only_parameters(monkeypatch, tmp_path):
|
|
83
|
+
"""A configured analysis forwards its normalized basis to the likelihood."""
|
|
84
|
+
args = _parse_config(
|
|
85
|
+
tmp_path,
|
|
86
|
+
"polarization-modes = pc\n"
|
|
87
|
+
"polarization-basis = p\n"
|
|
88
|
+
"extra-likelihood-kwargs = {'regularization_constant': 0.99}\n",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
assert args.polarization_modes == ["pc"]
|
|
92
|
+
assert args.polarization_basis == ["p"]
|
|
93
|
+
|
|
94
|
+
interferometers = object()
|
|
95
|
+
time_frequency_filter = object()
|
|
96
|
+
priors = object()
|
|
97
|
+
inputs = _build_inputs(
|
|
98
|
+
args,
|
|
99
|
+
interferometers=interferometers,
|
|
100
|
+
time_frequency_filter=time_frequency_filter,
|
|
101
|
+
priors=priors,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
assert inputs.polarization_modes == "pc"
|
|
105
|
+
assert inputs.polarization_basis == "p"
|
|
106
|
+
|
|
107
|
+
monkeypatch.setattr(input_module, "Chi2TimeFrequencyLikelihood", _KeywordOnlyLikelihood)
|
|
108
|
+
|
|
109
|
+
likelihood = input_module.Input.likelihood.fget(inputs)
|
|
110
|
+
|
|
111
|
+
assert likelihood.interferometers is interferometers
|
|
112
|
+
assert likelihood.wavelet_frequency_resolution == 16.0
|
|
113
|
+
assert likelihood.wavelet_nx == 4.0
|
|
114
|
+
assert likelihood.polarization_modes == "pc"
|
|
115
|
+
assert likelihood.polarization_basis == "p"
|
|
116
|
+
assert likelihood.time_frequency_filter is time_frequency_filter
|
|
117
|
+
assert likelihood.extra_kwargs == {"regularization_constant": 0.99}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_extra_kwargs_dropped_when_likelihood_rejects_variadic(monkeypatch, tmp_path):
|
|
121
|
+
"""A likelihood without ``**kwargs`` must not receive unknown extras (no TypeError)."""
|
|
122
|
+
args = _parse_config(
|
|
123
|
+
tmp_path,
|
|
124
|
+
"polarization-modes = pc\n"
|
|
125
|
+
"polarization-basis = p\n"
|
|
126
|
+
"extra-likelihood-kwargs = {'regularization_constant': 0.99}\n",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
interferometers = object()
|
|
130
|
+
time_frequency_filter = object()
|
|
131
|
+
priors = object()
|
|
132
|
+
inputs = _build_inputs(
|
|
133
|
+
args,
|
|
134
|
+
interferometers=interferometers,
|
|
135
|
+
time_frequency_filter=time_frequency_filter,
|
|
136
|
+
priors=priors,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
monkeypatch.setattr(input_module, "Chi2TimeFrequencyLikelihood", _StrictLikelihood)
|
|
140
|
+
|
|
141
|
+
# Would raise ``TypeError: unexpected keyword argument 'regularization_constant'``
|
|
142
|
+
# if the unknown extra were forwarded to a signature without ``**kwargs``.
|
|
143
|
+
likelihood = input_module.Input.likelihood.fget(inputs)
|
|
144
|
+
|
|
145
|
+
assert likelihood.interferometers is interferometers
|
|
146
|
+
assert likelihood.polarization_basis == "p"
|
|
147
|
+
assert likelihood.time_frequency_filter is time_frequency_filter
|
|
148
|
+
assert likelihood.priors is priors
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_dotted_path_forwards_keyword_only_parameters(monkeypatch, tmp_path):
|
|
152
|
+
"""The ``module.Class`` likelihood_type branch also forwards keyword-only params."""
|
|
153
|
+
args = _parse_config(
|
|
154
|
+
tmp_path,
|
|
155
|
+
"polarization-modes = pc\n"
|
|
156
|
+
"polarization-basis = p\n"
|
|
157
|
+
"extra-likelihood-kwargs = {'regularization_constant': 0.99}\n",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
interferometers = object()
|
|
161
|
+
time_frequency_filter = object()
|
|
162
|
+
priors = object()
|
|
163
|
+
inputs = _build_inputs(
|
|
164
|
+
args,
|
|
165
|
+
interferometers=interferometers,
|
|
166
|
+
time_frequency_filter=time_frequency_filter,
|
|
167
|
+
priors=priors,
|
|
168
|
+
likelihood_type="my_pkg.CustomLikelihood",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
fake_module = types.ModuleType("my_pkg")
|
|
172
|
+
fake_module.CustomLikelihood = _KeywordOnlyLikelihood
|
|
173
|
+
monkeypatch.setattr(input_module, "import_module", lambda name: fake_module)
|
|
174
|
+
|
|
175
|
+
likelihood = input_module.Input.likelihood.fget(inputs)
|
|
176
|
+
|
|
177
|
+
assert likelihood.polarization_basis == "p"
|
|
178
|
+
assert likelihood.time_frequency_filter is time_frequency_filter
|
|
179
|
+
assert likelihood.extra_kwargs == {"regularization_constant": 0.99}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Asimov integration tests."""
|