hyper-py-photometry 0.1.0__tar.gz → 0.1.2__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.
Files changed (79) hide show
  1. hyper_py_photometry-0.1.2/.github/workflows/Hyper-py_paper.yml +28 -0
  2. hyper_py_photometry-0.1.2/.github/workflows/pypi-publish.yml +80 -0
  3. hyper_py_photometry-0.1.2/.gitignore +180 -0
  4. hyper_py_photometry-0.1.2/.vscode/launch.json +18 -0
  5. hyper_py_photometry-0.1.2/IDL_comparison/Gaussian_comparison_Hyper_py_IDL_ALL.py +276 -0
  6. hyper_py_photometry-0.1.2/IDL_comparison/ellipses_map_500_Gaussians_1_centroids.reg +495 -0
  7. hyper_py_photometry-0.1.2/IDL_comparison/ellipses_map_500_Gaussians_1_ellipses.reg +987 -0
  8. hyper_py_photometry-0.1.2/IDL_comparison/ellipses_map_500_Gaussians_2_centroids.reg +486 -0
  9. hyper_py_photometry-0.1.2/IDL_comparison/ellipses_map_500_Gaussians_2_ellipses.reg +969 -0
  10. hyper_py_photometry-0.1.2/IDL_comparison/hyper_output_map_500_Gaussians_1.txt +530 -0
  11. hyper_py_photometry-0.1.2/IDL_comparison/hyper_output_map_500_Gaussians_2.txt +527 -0
  12. hyper_py_photometry-0.1.2/IDL_comparison/map_500_Gaussians_1.fits +8415 -21
  13. hyper_py_photometry-0.1.2/IDL_comparison/map_500_Gaussians_2.fits +8618 -34
  14. hyper_py_photometry-0.1.2/IDL_comparison/matched_flux_comparison_table.txt +975 -0
  15. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_Int_1.png +0 -0
  16. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_Int_2.png +0 -0
  17. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_Peak_1.png +0 -0
  18. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_Peak_2.png +0 -0
  19. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_vs_py_Int_1.png +0 -0
  20. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_vs_py_Int_2.png +0 -0
  21. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_vs_py_Peak_1.png +0 -0
  22. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_IDL_vs_py_Peak_2.png +0 -0
  23. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_py_Int_1.png +0 -0
  24. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_py_Int_2.png +0 -0
  25. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_py_Peak_1.png +0 -0
  26. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Comparison_Hyper_py_Peak_2.png +0 -0
  27. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Diff_Histogram_Int.png +0 -0
  28. hyper_py_photometry-0.1.2/IDL_comparison/output/Flux_Diff_Histogram_Peak.png +0 -0
  29. hyper_py_photometry-0.1.2/IDL_comparison/output/combined_source_counts_comparison.txt +8 -0
  30. hyper_py_photometry-0.1.2/IDL_comparison/output/matched_flux_comparison_table.txt +975 -0
  31. hyper_py_photometry-0.1.2/IDL_comparison/photometry_sources_1300_ellipses_1300_polynomial_background_4sigma_ipac_1.txt +571 -0
  32. hyper_py_photometry-0.1.2/IDL_comparison/photometry_sources_1300_ellipses_1300_polynomial_background_4sigma_ipac_2.txt +538 -0
  33. hyper_py_photometry-0.1.2/IDL_comparison/table_500_Gaussians_1.txt +504 -0
  34. hyper_py_photometry-0.1.2/IDL_comparison/table_500_Gaussians_2.txt +504 -0
  35. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/PKG-INFO +20 -13
  36. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/README.md +19 -12
  37. hyper_py_photometry-0.1.2/environment.yml +16 -0
  38. hyper_py_photometry-0.1.2/paper/Figures/Flux_Diff_Histogram_Int.png +0 -0
  39. hyper_py_photometry-0.1.2/paper/Figures/Flux_Diff_Histogram_Peak.png +0 -0
  40. hyper_py_photometry-0.1.2/paper/paper.bib +97 -0
  41. hyper_py_photometry-0.1.2/paper/paper.md +70 -0
  42. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/pyproject.toml +11 -3
  43. hyper_py_photometry-0.1.2/requirements.txt +9 -0
  44. hyper_py_photometry-0.1.2/src/hyper_py/config.yaml +136 -0
  45. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py_photometry.egg-info/PKG-INFO +20 -13
  46. hyper_py_photometry-0.1.2/src/hyper_py_photometry.egg-info/SOURCES.txt +76 -0
  47. hyper_py_photometry-0.1.2/test/maps/test_2d_map_1.fits +0 -0
  48. hyper_py_photometry-0.1.2/test/maps/test_2d_map_2.fits +0 -0
  49. hyper_py_photometry-0.1.2/test/maps/test_2dmap.txt +2 -0
  50. hyper_py_photometry-0.1.2/test/maps/test_datacube.fits +0 -0
  51. hyper_py_photometry-0.1.2/test/maps/test_datacube.txt +1 -0
  52. hyper_py_photometry-0.1.0/src/hyper_py_photometry.egg-info/SOURCES.txt +0 -30
  53. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/LICENSE +0 -0
  54. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/setup.cfg +0 -0
  55. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/__init__.py +0 -0
  56. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/bkg_multigauss.py +0 -0
  57. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/bkg_single.py +0 -0
  58. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/config.py +0 -0
  59. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/create_background_slices.py +0 -0
  60. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/data_output.py +0 -0
  61. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/detection.py +0 -0
  62. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/extract_cubes.py +0 -0
  63. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/fitting.py +0 -0
  64. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/gaussfit.py +0 -0
  65. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/groups.py +0 -0
  66. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/hyper.py +0 -0
  67. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/logger.py +0 -0
  68. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/map_io.py +0 -0
  69. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/paths_io.py +0 -0
  70. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/photometry.py +0 -0
  71. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/run_hyper.py +0 -0
  72. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/single_map.py +0 -0
  73. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/survey.py +0 -0
  74. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py/visualization.py +0 -0
  75. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py_photometry.egg-info/dependency_links.txt +0 -0
  76. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py_photometry.egg-info/entry_points.txt +0 -0
  77. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py_photometry.egg-info/requires.txt +0 -0
  78. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/src/hyper_py_photometry.egg-info/top_level.txt +0 -0
  79. {hyper_py_photometry-0.1.0 → hyper_py_photometry-0.1.2}/test/test_hyper.py +0 -0
@@ -0,0 +1,28 @@
1
+ name: JOSS PDF
2
+
3
+ on:
4
+ push:
5
+ paths:
6
+ - paper/**
7
+ - .github/workflows/Hyper-py_paper.yml
8
+ workflow_dispatch: # allow manual trigger
9
+
10
+ jobs:
11
+ paper:
12
+ runs-on: ubuntu-latest
13
+ name: Hyper-py JOSS paper
14
+ steps:
15
+ - name: Checkout repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Build Hyper JOSS paper PDF
19
+ uses: openjournals/openjournals-draft-action@master
20
+ with:
21
+ journal: joss
22
+ paper-path: paper/paper.md # adjust only if your .md file is not renamed
23
+
24
+ - name: Upload compiled PDF
25
+ uses: actions/upload-artifact@v4
26
+ with:
27
+ name: paper
28
+ path: paper/*.pdf
@@ -0,0 +1,80 @@
1
+ name: Release to PyPI (on tag)
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout (with tags)
14
+ uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+
18
+ - name: Set up Python
19
+ uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.11"
22
+
23
+ - name: Derive version from tag
24
+ run: echo "PKG_VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
25
+
26
+ - name: Build sdist + wheel (version from tag)
27
+ env:
28
+ SETUPTOOLS_SCM_PRETEND_VERSION: ${{ env.PKG_VERSION }}
29
+ run: |
30
+ python -m pip install --upgrade pip build twine
31
+ python -m build
32
+ python -m twine check dist/*
33
+
34
+ - name: Upload dist artifacts
35
+ uses: actions/upload-artifact@v4
36
+ with:
37
+ name: dist
38
+ path: dist/*
39
+
40
+ publish:
41
+ needs: build
42
+ runs-on: ubuntu-latest
43
+ environment: pypi
44
+ permissions:
45
+ id-token: write
46
+ contents: read
47
+ if: startsWith(github.ref, 'refs/tags/v') # publish only on tags
48
+ steps:
49
+ - name: Download dist artifacts
50
+ uses: actions/download-artifact@v4
51
+ with:
52
+ name: dist
53
+ path: dist
54
+
55
+ - name: Publish to PyPI via OIDC
56
+ uses: pypa/gh-action-pypi-publish@release/v1
57
+ with:
58
+ skip-existing: true
59
+
60
+ release_assets:
61
+ needs: build
62
+ runs-on: ubuntu-latest
63
+ if: startsWith(github.ref, 'refs/tags/')
64
+ permissions:
65
+ contents: write
66
+ steps:
67
+ - name: Download dist artifacts
68
+ uses: actions/download-artifact@v4
69
+ with:
70
+ name: dist
71
+ path: dist
72
+
73
+ - name: GitHub Release (attach artifacts)
74
+ uses: softprops/action-gh-release@v2
75
+ with:
76
+ tag_name: ${{ github.ref_name }}
77
+ files: dist/*
78
+ generate_release_notes: true
79
+ env:
80
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,180 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ #uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116
+ .pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121
+ __pypackages__/
122
+
123
+ # Celery stuff
124
+ celerybeat-schedule
125
+ celerybeat.pid
126
+
127
+ # SageMath parsed files
128
+ *.sage.py
129
+
130
+ # Environments
131
+ .env
132
+ .venv
133
+ env/
134
+ venv/
135
+ ENV/
136
+ env.bak/
137
+ venv.bak/
138
+
139
+ # Spyder project settings
140
+ .spyderproject
141
+ .spyproject
142
+
143
+ # Rope project settings
144
+ .ropeproject
145
+
146
+ # mkdocs documentation
147
+ /site
148
+
149
+ # mypy
150
+ .mypy_cache/
151
+ .dmypy.json
152
+ dmypy.json
153
+
154
+ # Pyre type checker
155
+ .pyre/
156
+
157
+ # pytype static type analyzer
158
+ .pytype/
159
+
160
+ # Cython debug symbols
161
+ cython_debug/
162
+
163
+ # PyCharm
164
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
167
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
168
+ #.idea/
169
+
170
+ # Ruff stuff:
171
+ .ruff_cache/
172
+
173
+ # PyPI configuration file
174
+ .pypirc
175
+
176
+ .DS_Store
177
+
178
+ /Git_Ignore
179
+ test/test_2dmap.config.yaml
180
+ test/output/
@@ -0,0 +1,18 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+
5
+ {
6
+ "name": "Python Debugger: Current File",
7
+ "type": "debugpy",
8
+ "request": "launch",
9
+ // "program": "${workspaceFolder}/src/hyper_py/run_hyper.py",
10
+ "program": "${workspaceFolder}/test/test_hyper.py",
11
+ "console": "integratedTerminal",
12
+ "args": ["${workspaceFolder}/src/hyper_py/config.yaml"],
13
+ "env": {
14
+ "MPLBACKEND": "TkAgg"
15
+ }
16
+ }
17
+ ]
18
+ }
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Comparison of Reference catalog vs Hyper-py and Hyper-IDL per map,
5
+ with direct IDL-vs-py scatter plots and combined percentage difference histograms.
6
+ @author: alessio
7
+ """
8
+
9
+ import numpy as np
10
+ import matplotlib.pyplot as plt
11
+ from matplotlib import font_manager
12
+ from astropy.table import Table
13
+ from astropy import coordinates as coords
14
+ from astropy import units as u
15
+
16
+ # ################## Parameters ##################
17
+ num_sources = 500
18
+ n_sigma = 4
19
+ tot_maps = 2
20
+ tolerance = 0.02 * u.arcsec
21
+ legend_properties = font_manager.FontProperties(weight='bold', size=12)
22
+
23
+ # Beam and pixel info for unit conversion (deg)
24
+ bmaj = 1.335631849037E-05
25
+ bmin = 1.069115267860E-05
26
+ cdelt = 3.611111108576E-06
27
+
28
+ beam_area_sr = 1.1331 * bmaj * bmin
29
+ pix_area_deg2 = cdelt**2
30
+ beam_area_pix = beam_area_sr / pix_area_deg2
31
+
32
+ # Paths
33
+ dir_in_ref = "/Users/alessio/Dropbox/Work/Python/Hyper/IDL_comparison/"
34
+ dir_in_py = "/Users/alessio/Dropbox/Work/Python/Hyper/IDL_comparison/"
35
+ dir_in_idl = "/Users/alessio/Dropbox/Work/Python/Hyper/IDL_comparison/"
36
+ dir_out = "/Users/alessio/Dropbox/Work/Python/Hyper/IDL_comparison/output/"
37
+
38
+ background = 'polynomial'
39
+ table_combined_stats = dir_out + 'combined_source_counts_comparison.txt'
40
+
41
+ # Summary table
42
+ summary_table = Table(
43
+ names=['Catalog', 'Source_Type', 'Total', 'Matched', 'False', 'False_Percentage'],
44
+ dtype=['i4', 'S10', 'i4', 'i4', 'i4', 'f4']
45
+ )
46
+
47
+ # ================== Helper Functions ==================
48
+ def match_catalogs(cat_coords, ref_coords):
49
+ sep = ref_coords[:, None].separation(cat_coords[None, :])
50
+ min_sep = sep.min(axis=1)
51
+ min_idx = sep.argmin(axis=1)
52
+ matched = min_sep < tolerance
53
+ _, unique_idx = np.unique(min_idx[matched], return_index=True)
54
+ filtered = np.zeros_like(matched, dtype=bool)
55
+ filtered[np.where(matched)[0][unique_idx]] = True
56
+ return filtered, min_idx
57
+
58
+ def scatter_comparison(ref_vals, cat_vals, flux_type, ylabel, label, color, marker, suffix):
59
+ plt.figure(figsize=(8, 6))
60
+ plt.scatter(ref_vals, cat_vals, color=color, marker=marker, label=label)
61
+ minf = min(np.min(ref_vals), np.min(cat_vals))
62
+ maxf = max(np.max(ref_vals), np.max(cat_vals))
63
+
64
+ # plt.xlim(0, 1800)
65
+ # plt.ylim(0, 1800)
66
+
67
+ plt.plot([minf, maxf], [minf, maxf], 'k--', label='1:1 Line')
68
+ plt.xlabel(f'{flux_type} ($\mu$Jy)', fontsize=14, fontweight='bold')
69
+ plt.ylabel(ylabel, fontsize=14, fontweight='bold')
70
+ plt.legend(prop=legend_properties)
71
+ plt.title(f'{flux_type} Comparison: {label}', fontsize=16, fontweight='bold')
72
+ plt.tick_params(axis='both', labelsize=12, width=2)
73
+ for tick in plt.gca().get_xticklabels() + plt.gca().get_yticklabels():
74
+ tick.set_fontweight('bold')
75
+ ax = plt.gca()
76
+ for side in ['top', 'bottom', 'left', 'right']:
77
+ ax.spines[side].set_linewidth(2)
78
+ plt.savefig(f'{dir_out}Flux_Comparison_{suffix}.png')
79
+ plt.close()
80
+
81
+ def plot_flux_difference_histogram(ref_list, py_list, idl_list, flux_type, filename):
82
+ ref_all = np.concatenate(ref_list)
83
+ py_all = np.concatenate(py_list)
84
+ idl_all = np.concatenate(idl_list)
85
+
86
+ # Percentage differences
87
+ diff_py = 100. * (py_all - ref_all) / ref_all
88
+ diff_idl = 100. * (idl_all - ref_all) / ref_all
89
+
90
+ plt.figure(figsize=(8, 6))
91
+ bins = np.linspace(
92
+ min(diff_py.min(), diff_idl.min()),
93
+ max(diff_py.max(), diff_idl.max()),
94
+ 50
95
+ )
96
+
97
+ plt.hist(diff_py, bins=bins, alpha=0.6, label='Hyper-py - Ref', color='blue', edgecolor='black')
98
+ plt.hist(diff_idl, bins=bins, alpha=0.6, label='Hyper-IDL - Ref', color='green', edgecolor='black')
99
+
100
+ for diff, label, color in [(diff_py, 'py', 'blue'), (diff_idl, 'idl', 'green')]:
101
+ mean = np.mean(diff)
102
+ median = np.median(diff)
103
+ plt.axvline(mean, color=color, linestyle='--', linewidth=2, label=f'Mean {label}: {mean:.1f}%')
104
+ plt.axvline(median, color=color, linestyle=':', linewidth=2, label=f'Median {label}: {median:.1f}%')
105
+
106
+ plt.xlabel(f'{flux_type} Difference (% vs Reference)', fontsize=14, fontweight='bold')
107
+ plt.ylabel('Counts', fontsize=14, fontweight='bold')
108
+ plt.title(f'{flux_type} Difference: Hyper-py & Hyper-IDL vs Reference', fontsize=16, fontweight='bold')
109
+ plt.legend(prop=legend_properties)
110
+ plt.tick_params(axis='both', labelsize=12, width=2)
111
+ for tick in plt.gca().get_xticklabels() + plt.gca().get_yticklabels():
112
+ tick.set_fontweight('bold')
113
+ ax = plt.gca()
114
+ for side in ['top', 'bottom', 'left', 'right']:
115
+ ax.spines[side].set_linewidth(2)
116
+ plt.savefig(f'{dir_out}{filename}')
117
+ plt.close()
118
+
119
+ # Containers for histograms
120
+ py_all_hyper_py_id, py_all_hyper_idl_id, ref_all_peak, py_all_peak, idl_all_peak = [], [], [], [], []
121
+ ref_all_int, py_all_int, idl_all_int = [], [], []
122
+
123
+ # ================== Loop Over Maps ==================
124
+ for i in range(1, tot_maps + 1):
125
+ ref = Table.read(f'{dir_in_ref}table_{num_sources}_Gaussians_{i}.txt', format='ipac')
126
+ ref['RA'] = ref['RA'] * u.deg
127
+ ref['DEC'] = ref['DEC'] * u.deg
128
+ coords_ref = coords.SkyCoord(ra=ref['RA'], dec=ref['DEC'])
129
+
130
+ hpy = Table.read(f'{dir_in_py}hyper_output_map_{num_sources}_Gaussians_{i}.txt', format='ipac')
131
+ coords_py = coords.SkyCoord(ra=hpy['RA'], dec=hpy['DEC'])
132
+
133
+ hidl = Table.read(f'{dir_in_idl}photometry_sources_1300_ellipses_1300_{background}_background_'+str(n_sigma)+f'sigma_ipac_{i}.txt', format='ipac')
134
+ coords_idl = coords.SkyCoord(ra=hidl['ra'], dec=hidl['dec'])
135
+
136
+ # Match each to reference
137
+ m_py, idx_py = match_catalogs(coords_py, coords_ref)
138
+ m_idl, idx_idl = match_catalogs(coords_idl, coords_ref)
139
+
140
+ matched_py = hpy[idx_py[m_py]]
141
+ ref_matched_py = ref[m_py]
142
+ false_py = len(hpy) - len(matched_py)
143
+ summary_table.add_row([i, 'Hyper-py', len(ref), len(matched_py), false_py, false_py / len(hpy) * 100.])
144
+
145
+ matched_idl = hidl[idx_idl[m_idl]]
146
+ ref_matched_idl = ref[m_idl]
147
+ false_idl = len(hidl) - len(matched_idl)
148
+ summary_table.add_row([i, 'Hyper-IDL', len(ref), len(matched_idl), false_idl, false_idl / len(hidl) * 100.])
149
+
150
+
151
+ # Save IDs #
152
+ hyper_py_id = matched_py['HYPER_ID']
153
+ hyper_idl_id = matched_idl['HYPER_ID']
154
+
155
+ # Peak Flux plots
156
+ flux_peak_py_mjy = matched_py['FLUX_PEAK'] * beam_area_pix
157
+ flux_peak_idl_mjy = matched_idl['FLUX_PEAK_JY']
158
+
159
+ scatter_comparison(ref_matched_py['Flux_Peak'] * 1e6, flux_peak_py_mjy * 1e3, 'Flux Peak', 'Measured Flux Peak ($\mu$Jy)', 'Hyper-py', 'blue', 'o', f'Hyper_py_Peak_{i}')
160
+ scatter_comparison(ref_matched_idl['Flux_Peak'] * 1e6, flux_peak_idl_mjy * 1e3, 'Flux Peak', 'Measured Flux Peak ($\mu$Jy)', 'Hyper-IDL', 'green', 's', f'Hyper_IDL_Peak_{i}')
161
+
162
+ # Integrated Flux plots
163
+ scatter_comparison(ref_matched_py['Flux_Integrated'] * 1e6, matched_py['FLUX'] * 1e3, 'Integrated Flux', 'Measured Integrated Flux ($\mu$Jy)', 'Hyper-py', 'blue', 'o', f'Hyper_py_Int_{i}')
164
+ scatter_comparison(ref_matched_idl['Flux_Integrated'] * 1e6, matched_idl['FLUX'] * 1e3, 'Integrated Flux', 'Measured Integrated Flux ($\mu$Jy)', 'Hyper-IDL', 'green', 's', f'Hyper_IDL_Int_{i}')
165
+
166
+ # Common matches across all three
167
+ common = m_py & m_idl
168
+ if np.any(common):
169
+ ref_common = ref[common]
170
+ py_common = hpy[idx_py[common]]
171
+ idl_common = hidl[idx_idl[common]]
172
+
173
+
174
+ id_hyper_py = py_common['HYPER_ID']
175
+ id_hyper_idl = idl_common['HYPER_ID']
176
+
177
+ py_all_hyper_py_id.append(id_hyper_py)
178
+ py_all_hyper_idl_id.append(id_hyper_idl)
179
+
180
+
181
+ peak_py = py_common['FLUX_PEAK'] * beam_area_pix *1.e3
182
+ peak_idl = idl_common['FLUX_PEAK_JY'] *1.e3
183
+ int_py = py_common['FLUX'] *1.e3
184
+ int_idl = idl_common['FLUX'] *1.e3
185
+
186
+ ref_all_peak.append(ref_common['Flux_Peak'] * 1e6) # mJy
187
+ py_all_peak.append(peak_py)
188
+ idl_all_peak.append(peak_idl)
189
+
190
+ ref_all_int.append(ref_common['Flux_Integrated'] * 1e6) # mJy
191
+ py_all_int.append(int_py)
192
+ idl_all_int.append(int_idl)
193
+
194
+ # Direct IDL vs py scatter
195
+ scatter_comparison(peak_idl * 1e3, peak_py * 1e3, 'Flux Peak', 'Hyper-py Flux Peak ($\mu$Jy)', 'Hyper-IDL vs Hyper-py', 'purple', 'd', f'Hyper_IDL_vs_py_Peak_{i}')
196
+ scatter_comparison(int_idl * 1e3, int_py * 1e3, 'Integrated Flux', 'Hyper-py Integrated Flux ($\mu$Jy)', 'Hyper-IDL vs Hyper-py', 'purple', 'd', f'Hyper_IDL_vs_py_Int_{i}')
197
+
198
+
199
+
200
+
201
+
202
+
203
+ from astropy.table import Table
204
+
205
+ # ================== Save Matched Source Table ==================
206
+
207
+ # Flatten all lists
208
+ all_ref_hyper_py_id = np.concatenate(py_all_hyper_py_id)
209
+ all_ref_hyper_idl_id = np.concatenate(py_all_hyper_idl_id)
210
+
211
+
212
+ all_ref_peak = np.concatenate(ref_all_peak)
213
+ all_py_peak = np.concatenate(py_all_peak)
214
+ all_idl_peak = np.concatenate(idl_all_peak)
215
+
216
+ all_ref_int = np.concatenate(ref_all_int)
217
+ all_py_int = np.concatenate(py_all_int)
218
+ all_idl_int = np.concatenate(idl_all_int)
219
+
220
+ # Percentage differences
221
+ diff_peak_py = 100. * (all_py_peak - all_ref_peak) / all_ref_peak
222
+ diff_peak_idl = 100. * (all_idl_peak - all_ref_peak) / all_ref_peak
223
+ diff_int_py = 100. * (all_py_int - all_ref_int) / all_ref_int
224
+ diff_int_idl = 100. * (all_idl_int - all_ref_int) / all_ref_int
225
+
226
+ # Build and write the output table
227
+ matched_table = Table(
228
+ [
229
+ np.arange(1, len(all_ref_peak) + 1), # INDEX
230
+ all_ref_hyper_py_id,
231
+ all_ref_hyper_idl_id,
232
+ all_ref_peak, all_py_peak, all_idl_peak,
233
+ all_ref_int, all_py_int, all_idl_int,
234
+ diff_peak_py, diff_peak_idl,
235
+ diff_int_py, diff_int_idl
236
+ ],
237
+ names=[
238
+ 'INDEX',
239
+ 'HYPER_ID_PY', 'HYPER_ID_IDL',
240
+ 'PEAK_REF_mJy', 'PEAK_PY_mJy', 'PEAK_IDL_mJy',
241
+ 'INT_REF_mJy', 'INT_PY_mJy', 'INT_IDL_mJy',
242
+ 'D_PEAK_PY_pct', 'D_PEAK_IDL_pct',
243
+ 'D_INT_PY_pct', 'D_INT_IDL_pct'
244
+ ],
245
+ dtype=[
246
+ 'int32', 'int32', 'int32',
247
+ 'float64', 'float64', 'float64',
248
+ 'float64', 'float64', 'float64',
249
+ 'float32', 'float32', 'float32', 'float32'
250
+ ]
251
+ )
252
+
253
+ # Format columns
254
+ for col in [
255
+ 'PEAK_REF_mJy', 'PEAK_PY_mJy', 'PEAK_IDL_mJy',
256
+ 'INT_REF_mJy', 'INT_PY_mJy', 'INT_IDL_mJy',
257
+ 'D_PEAK_PY_pct', 'D_PEAK_IDL_pct',
258
+ 'D_INT_PY_pct', 'D_INT_IDL_pct'
259
+ ]:
260
+ matched_table[col].format = '6.1f'
261
+
262
+ # Save table
263
+ matched_table.meta['COMMENT'] = 'Flux comparison for sources matched in all three catalogs'
264
+ matched_table.write(dir_out + 'matched_flux_comparison_table.txt', format='ascii.ipac', overwrite=True)
265
+
266
+
267
+
268
+
269
+
270
+
271
+ # Histogram plots over all maps
272
+ plot_flux_difference_histogram(ref_all_peak, py_all_peak, idl_all_peak, 'Flux Peak', 'Flux_Diff_Histogram_Peak.png')
273
+ plot_flux_difference_histogram(ref_all_int, py_all_int, idl_all_int, 'Integrated Flux', 'Flux_Diff_Histogram_Int.png')
274
+
275
+ # Save summary stats
276
+ summary_table.write(table_combined_stats, format='ascii.ipac', overwrite=True)