optixstuff 3.0.0__tar.gz → 4.0.1__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.
- optixstuff-4.0.1/.codespellrc +4 -0
- optixstuff-4.0.1/.editorconfig +15 -0
- optixstuff-4.0.1/.gitattributes +10 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/.gitignore +5 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/.pre-commit-config.yaml +6 -2
- {optixstuff-3.0.0 → optixstuff-4.0.1}/.zenodo.json +1 -1
- {optixstuff-3.0.0 → optixstuff-4.0.1}/CHANGELOG.md +28 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/CITATION.cff +1 -1
- {optixstuff-3.0.0 → optixstuff-4.0.1}/PKG-INFO +36 -22
- {optixstuff-3.0.0 → optixstuff-4.0.1}/README.md +9 -0
- optixstuff-4.0.1/SECURITY.md +9 -0
- optixstuff-4.0.1/pyproject.toml +110 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/_version.py +2 -2
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/coronagraph.py +38 -16
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/detector.py +2 -2
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/optical_elements.py +4 -6
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/optical_path.py +2 -1
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/primary.py +20 -20
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/speckle.py +66 -5
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/yippy_coronagraph.py +113 -5
- optixstuff-3.0.0/pyproject.toml +0 -64
- {optixstuff-3.0.0 → optixstuff-4.0.1}/.readthedocs.yaml +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/LICENSE +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/__init__.py +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/_repr.py +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/disperser.py +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/src/optixstuff/exposure.py +0 -0
- {optixstuff-3.0.0 → optixstuff-4.0.1}/tools/check_internal_refs.py +0 -0
|
@@ -15,13 +15,17 @@ repos:
|
|
|
15
15
|
args: [--pytest-test-first]
|
|
16
16
|
- id: end-of-file-fixer
|
|
17
17
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
18
|
-
rev: v0.
|
|
18
|
+
rev: v0.17.3
|
|
19
19
|
hooks:
|
|
20
20
|
- id: ruff-check
|
|
21
21
|
args: [--fix]
|
|
22
22
|
- id: ruff-format
|
|
23
|
+
- repo: https://github.com/codespell-project/codespell
|
|
24
|
+
rev: v2.4.3
|
|
25
|
+
hooks:
|
|
26
|
+
- id: codespell
|
|
23
27
|
- repo: https://github.com/compilerla/conventional-pre-commit
|
|
24
|
-
rev: v4.
|
|
28
|
+
rev: v4.4.0
|
|
25
29
|
hooks:
|
|
26
30
|
- id: conventional-pre-commit
|
|
27
31
|
stages: [commit-msg]
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.0.1](https://github.com/CoreySpohn/optixstuff/compare/v4.0.0...v4.0.1) (2026-11-06)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* route yippy stellar and sky products through their own frame ([b9a029b](https://github.com/CoreySpohn/optixstuff/commit/b9a029b2b03a96564b443ad14aacfe3fdbb9b62b))
|
|
9
|
+
|
|
10
|
+
## [4.0.0](https://github.com/CoreySpohn/optixstuff/compare/v3.0.0...v4.0.0) (2026-11-05)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ⚠ BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* **coronagraph:** AbstractCoronagraph.core_mean_intensity implementations must accept the keyword-only stellar_diam_lod argument.
|
|
16
|
+
* **coronagraph:** YippyCoronagraph.core_mean_intensity and noise_floor_ayo now return values per (lambda/D)^2, the AbstractCoronagraph contract, instead of yippy's per-pixel fraction of the star's light; both grow by 1/pixel_scale_lod**2 (16x at 0.25 lambda/D pixels). Multiply either by core_area for the fraction in the aperture, and stop dividing by pixel_scale_lod**2 downstream.
|
|
17
|
+
* American spelling throughout, rename segment_centres_m to segment_centers_m
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **coronagraph:** add stellar_diam_lod to core_mean_intensity and noise floor ([8cd9749](https://github.com/CoreySpohn/optixstuff/commit/8cd97496c3434ea7bd4effaf2b6f2f1fb095a294))
|
|
22
|
+
* **speckle:** exposure-averaged realization on the speckle-field contract ([d163bf6](https://github.com/CoreySpohn/optixstuff/commit/d163bf6a0a821b1d31bed46925ef63a4c19145de))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* American spelling throughout, rename segment_centres_m to segment_centers_m ([ccf79e1](https://github.com/CoreySpohn/optixstuff/commit/ccf79e14d8aee487d1dcabd2cf08b4a3893b2a36))
|
|
28
|
+
* **coronagraph:** report the yippy AYO noise floor per (lambda/D)^2 ([3add7c7](https://github.com/CoreySpohn/optixstuff/commit/3add7c7c79f8153cfafa0a7409b0ed497c001ee4))
|
|
29
|
+
* **coronagraph:** report yippy core mean intensity per (lambda/D)^2 ([dc55aaa](https://github.com/CoreySpohn/optixstuff/commit/dc55aaaca375bb5e45b34d93df0ff9ad5c16fbd5))
|
|
30
|
+
|
|
3
31
|
## [3.0.0](https://github.com/CoreySpohn/optixstuff/compare/v2.0.0...v3.0.0) (2026-07-20)
|
|
4
32
|
|
|
5
33
|
|
|
@@ -7,7 +7,7 @@ authors:
|
|
|
7
7
|
- family-names: Spohn
|
|
8
8
|
given-names: Corey
|
|
9
9
|
orcid: "https://orcid.org/0000-0002-3138-0240"
|
|
10
|
-
affiliation: "NASA Goddard Space Flight Center"
|
|
10
|
+
affiliation: "University of Maryland, Baltimore County; NASA Goddard Space Flight Center; CRESST II"
|
|
11
11
|
repository-code: "https://github.com/CoreySpohn/optixstuff"
|
|
12
12
|
license: MIT
|
|
13
13
|
doi: "10.5281/zenodo.20819379"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: optixstuff
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.1
|
|
4
4
|
Summary: Hardware abstractions for the HWO direct imaging simulation suite
|
|
5
5
|
Project-URL: Homepage, https://github.com/CoreySpohn/optixstuff
|
|
6
6
|
Project-URL: Issues, https://github.com/CoreySpohn/optixstuff/issues
|
|
@@ -32,33 +32,47 @@ Classifier: Intended Audience :: Science/Research
|
|
|
32
32
|
Classifier: License :: OSI Approved :: MIT License
|
|
33
33
|
Classifier: Programming Language :: Python :: 3
|
|
34
34
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
35
|
-
Requires-Python: >=3.
|
|
36
|
-
Requires-Dist: equinox>=0.
|
|
37
|
-
Requires-Dist: hwoutils
|
|
38
|
-
Requires-Dist: interpax
|
|
39
|
-
Requires-Dist: jax>=0.
|
|
40
|
-
Requires-Dist: jaxlib>=0.
|
|
41
|
-
Requires-Dist:
|
|
35
|
+
Requires-Python: >=3.12
|
|
36
|
+
Requires-Dist: equinox>=0.13.2
|
|
37
|
+
Requires-Dist: hwoutils>=1.10.0
|
|
38
|
+
Requires-Dist: interpax>=0.3.12
|
|
39
|
+
Requires-Dist: jax>=0.8.1
|
|
40
|
+
Requires-Dist: jaxlib>=0.8.1
|
|
41
|
+
Requires-Dist: jaxtyping>=0.3.3
|
|
42
|
+
Requires-Dist: yippy>=2.10.6
|
|
42
43
|
Provides-Extra: dev
|
|
43
|
-
Requires-Dist: pre-commit; extra == 'dev'
|
|
44
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
44
45
|
Provides-Extra: docs
|
|
45
|
-
Requires-Dist: ipython; extra == 'docs'
|
|
46
|
-
Requires-Dist: matplotlib; extra == 'docs'
|
|
47
|
-
Requires-Dist: myst-nb; extra == 'docs'
|
|
48
|
-
Requires-Dist: sphinx; extra == 'docs'
|
|
49
|
-
Requires-Dist: sphinx-
|
|
50
|
-
Requires-Dist: sphinx
|
|
51
|
-
Requires-Dist:
|
|
52
|
-
Requires-Dist: sphinxcontrib-mermaid; extra == 'docs'
|
|
46
|
+
Requires-Dist: ipython>=8.18; extra == 'docs'
|
|
47
|
+
Requires-Dist: matplotlib>=3.10.7; extra == 'docs'
|
|
48
|
+
Requires-Dist: myst-nb>=1.0; extra == 'docs'
|
|
49
|
+
Requires-Dist: sphinx-autoapi>=3.0; extra == 'docs'
|
|
50
|
+
Requires-Dist: sphinx-book-theme>=1.1; extra == 'docs'
|
|
51
|
+
Requires-Dist: sphinx>=7.2; extra == 'docs'
|
|
52
|
+
Requires-Dist: sphinxcontrib-mermaid>=0.9; extra == 'docs'
|
|
53
53
|
Provides-Extra: test
|
|
54
|
-
Requires-Dist: hypothesis; extra == 'test'
|
|
55
|
-
Requires-Dist:
|
|
56
|
-
Requires-Dist:
|
|
57
|
-
Requires-Dist: pytest-
|
|
54
|
+
Requires-Dist: hypothesis>=6.79.1; extra == 'test'
|
|
55
|
+
Requires-Dist: numpy>=2.0; extra == 'test'
|
|
56
|
+
Requires-Dist: psutil>=5.9.4; extra == 'test'
|
|
57
|
+
Requires-Dist: pytest-fail-slow>=0.1.0; extra == 'test'
|
|
58
|
+
Requires-Dist: pytest-markdown-docs>=0.5.0; extra == 'test'
|
|
59
|
+
Requires-Dist: pytest-testmon>=2.0.7; extra == 'test'
|
|
60
|
+
Requires-Dist: pytest-timeout>=1.3.1; extra == 'test'
|
|
61
|
+
Requires-Dist: pytest-xdist>=2.5; extra == 'test'
|
|
62
|
+
Requires-Dist: pytest>=7.3.2; extra == 'test'
|
|
58
63
|
Description-Content-Type: text/markdown
|
|
59
64
|
|
|
60
65
|
# optixstuff
|
|
61
66
|
|
|
67
|
+
<p align="center">
|
|
68
|
+
<a href="https://pypi.org/project/optixstuff/"><img src="https://img.shields.io/pypi/v/optixstuff.svg?style=flat-square&logo=pypi" alt="PyPI"/></a>
|
|
69
|
+
<a href="https://optixstuff.readthedocs.io"><img src="https://readthedocs.org/projects/optixstuff/badge/?version=latest&style=flat-square" alt="Documentation Status"/></a>
|
|
70
|
+
<a href="https://github.com/CoreySpohn/optixstuff/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/CoreySpohn/optixstuff/ci.yml?branch=main&style=flat-square&logo=github&label=tests" alt="Tests"/></a>
|
|
71
|
+
<a href="https://github.com/CoreySpohn/optixstuff/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License"/></a>
|
|
72
|
+
<a href="https://github.com/CoreySpohn/optixstuff/blob/main/pyproject.toml"><img src="https://img.shields.io/badge/python-3.12%2B-blue.svg?style=flat-square&logo=python" alt="Python 3.12+"/></a>
|
|
73
|
+
<a href="https://doi.org/10.5281/zenodo.20819379"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.20819379-blue?style=flat-square" alt="DOI"/></a>
|
|
74
|
+
</p>
|
|
75
|
+
|
|
62
76
|
Shared hardware objects — with standard values — for the HWO direct-imaging
|
|
63
77
|
simulation suite.
|
|
64
78
|
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# optixstuff
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://pypi.org/project/optixstuff/"><img src="https://img.shields.io/pypi/v/optixstuff.svg?style=flat-square&logo=pypi" alt="PyPI"/></a>
|
|
5
|
+
<a href="https://optixstuff.readthedocs.io"><img src="https://readthedocs.org/projects/optixstuff/badge/?version=latest&style=flat-square" alt="Documentation Status"/></a>
|
|
6
|
+
<a href="https://github.com/CoreySpohn/optixstuff/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/CoreySpohn/optixstuff/ci.yml?branch=main&style=flat-square&logo=github&label=tests" alt="Tests"/></a>
|
|
7
|
+
<a href="https://github.com/CoreySpohn/optixstuff/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License"/></a>
|
|
8
|
+
<a href="https://github.com/CoreySpohn/optixstuff/blob/main/pyproject.toml"><img src="https://img.shields.io/badge/python-3.12%2B-blue.svg?style=flat-square&logo=python" alt="Python 3.12+"/></a>
|
|
9
|
+
<a href="https://doi.org/10.5281/zenodo.20819379"><img src="https://img.shields.io/badge/DOI-10.5281%2Fzenodo.20819379-blue?style=flat-square" alt="DOI"/></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
3
12
|
Shared hardware objects — with standard values — for the HWO direct-imaging
|
|
4
13
|
simulation suite.
|
|
5
14
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
This is research software maintained by one person. Please report a vulnerability, for
|
|
4
|
+
example a leaked credential, a malicious dependency, or a workflow that could be abused, through
|
|
5
|
+
GitHub private vulnerability reporting: use "Report a vulnerability" on the Security tab of this
|
|
6
|
+
repository. If that is unavailable, email corey.a.spohn@nasa.gov with "SECURITY" in the subject.
|
|
7
|
+
|
|
8
|
+
Expect an acknowledgement within a week. Only the latest release receives fixes. Incorrect
|
|
9
|
+
numerical results are not security issues; please open an ordinary issue for those.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ['hatchling>=1.26.3', "hatch-fancy-pypi-readme>=24.1.0", "hatch-vcs>=0.4"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "optixstuff"
|
|
7
|
+
description = "Hardware abstractions for the HWO direct imaging simulation suite"
|
|
8
|
+
authors = [{ name = "Corey Spohn", email = "corey.a.spohn@nasa.gov" }]
|
|
9
|
+
dynamic = ['readme', 'version']
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
14
|
+
"Intended Audience :: Science/Research",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Topic :: Scientific/Engineering :: Astronomy",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"equinox>=0.13.2",
|
|
21
|
+
"hwoutils>=1.10.0",
|
|
22
|
+
"interpax>=0.3.12",
|
|
23
|
+
"jax>=0.8.1",
|
|
24
|
+
"jaxlib>=0.8.1",
|
|
25
|
+
"jaxtyping>=0.3.3",
|
|
26
|
+
"yippy>=2.10.6",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = ["pre-commit>=3.5"]
|
|
31
|
+
docs = [
|
|
32
|
+
"sphinx>=7.2",
|
|
33
|
+
"myst-nb>=1.0",
|
|
34
|
+
"sphinx-book-theme>=1.1",
|
|
35
|
+
"sphinx-autoapi>=3.0",
|
|
36
|
+
"sphinxcontrib-mermaid>=0.9",
|
|
37
|
+
"ipython>=8.18",
|
|
38
|
+
"matplotlib>=3.10.7",
|
|
39
|
+
]
|
|
40
|
+
test = [
|
|
41
|
+
"pytest>=7.3.2",
|
|
42
|
+
"hypothesis>=6.79.1",
|
|
43
|
+
"pytest-xdist>=2.5",
|
|
44
|
+
"pytest-testmon>=2.0.7",
|
|
45
|
+
"pytest-markdown-docs>=0.5.0",
|
|
46
|
+
"pytest-timeout>=1.3.1",
|
|
47
|
+
"pytest-fail-slow>=0.1.0",
|
|
48
|
+
# The optional hwoutils budget plugin uses psutil for portable RSS sampling.
|
|
49
|
+
"psutil>=5.9.4",
|
|
50
|
+
"numpy>=2.0",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://github.com/CoreySpohn/optixstuff"
|
|
55
|
+
Issues = "https://github.com/CoreySpohn/optixstuff/issues"
|
|
56
|
+
|
|
57
|
+
[tool.hatch.version]
|
|
58
|
+
source = "vcs"
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.hooks.vcs]
|
|
61
|
+
version-file = "src/optixstuff/_version.py"
|
|
62
|
+
|
|
63
|
+
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
|
64
|
+
content-type = "text/markdown"
|
|
65
|
+
|
|
66
|
+
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
|
67
|
+
path = "README.md"
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint]
|
|
70
|
+
select = ["B", "D", "E", "F", "I", "UP", "RUF"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint.pydocstyle]
|
|
73
|
+
convention = "google"
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint.per-file-ignores]
|
|
76
|
+
"tests/**" = ["D"]
|
|
77
|
+
|
|
78
|
+
[tool.hatch.build.targets.wheel]
|
|
79
|
+
packages = ["src/optixstuff"]
|
|
80
|
+
|
|
81
|
+
[tool.hatch.build.targets.sdist]
|
|
82
|
+
exclude = ["/scripts", "/docs", "/tests", "/.github"]
|
|
83
|
+
|
|
84
|
+
[tool.pytest.ini_options]
|
|
85
|
+
filterwarnings = ["error"]
|
|
86
|
+
markers = [
|
|
87
|
+
"medium: tests with a 30-second default time budget",
|
|
88
|
+
"slow: tests with a 300-second default time budget, run nightly",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[tool.testbudget]
|
|
92
|
+
library = "optixstuff"
|
|
93
|
+
mode = "report"
|
|
94
|
+
unit_s = 5
|
|
95
|
+
medium_s = 30
|
|
96
|
+
slow_s = 300
|
|
97
|
+
|
|
98
|
+
[tool.deptry]
|
|
99
|
+
known_first_party = ["optixstuff"]
|
|
100
|
+
optional_dependencies_dev_groups = ["dev", "docs", "test"]
|
|
101
|
+
|
|
102
|
+
[tool.deptry.per_rule_ignores]
|
|
103
|
+
# JAX loads its compiled backend; keep the explicit minimum version constraint.
|
|
104
|
+
DEP002 = ["jaxlib"]
|
|
105
|
+
|
|
106
|
+
[tool.ty.environment]
|
|
107
|
+
python-version = "3.11"
|
|
108
|
+
|
|
109
|
+
[tool.ty.src]
|
|
110
|
+
include = ["src/optixstuff"]
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '
|
|
22
|
-
__version_tuple__ = version_tuple = (
|
|
21
|
+
__version__ = version = '4.0.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (4, 0, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -67,7 +67,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
67
67
|
which serves the image contract from native-grid tables), physical
|
|
68
68
|
wavefront propagation, or analytical models.
|
|
69
69
|
|
|
70
|
-
All wavelength arguments are in
|
|
70
|
+
All wavelength arguments are in nanometers throughout.
|
|
71
71
|
All separations and pixel scales are in lambda/D units -- the
|
|
72
72
|
contract is dimensionless optics; angle conversions are the
|
|
73
73
|
telescope primary's business.
|
|
@@ -98,7 +98,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
98
98
|
|
|
99
99
|
Args:
|
|
100
100
|
separation_lod: Angular separation in lambda/D.
|
|
101
|
-
wavelength_nm: Wavelength in
|
|
101
|
+
wavelength_nm: Wavelength in nanometers.
|
|
102
102
|
time_s: Time since mission start in seconds.
|
|
103
103
|
|
|
104
104
|
Returns:
|
|
@@ -118,7 +118,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
118
118
|
|
|
119
119
|
Args:
|
|
120
120
|
separation_lod: Angular separation in lambda/D.
|
|
121
|
-
wavelength_nm: Wavelength in
|
|
121
|
+
wavelength_nm: Wavelength in nanometers.
|
|
122
122
|
time_s: Time since mission start in seconds.
|
|
123
123
|
|
|
124
124
|
Returns:
|
|
@@ -133,13 +133,24 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
133
133
|
wavelength_nm: ArrayLike,
|
|
134
134
|
*,
|
|
135
135
|
time_s: ArrayLike = 0.0,
|
|
136
|
+
stellar_diam_lod: ArrayLike = 0.0,
|
|
136
137
|
) -> ArrayLike:
|
|
137
138
|
"""Mean stellar intensity within the photometric aperture.
|
|
138
139
|
|
|
140
|
+
The fraction of the star's incident light per unit solid angle, in
|
|
141
|
+
(lambda/D)^-2, averaged over the aperture (azimuthally, for a
|
|
142
|
+
tabulated curve). It does not depend on any pixel grid: multiplied by
|
|
143
|
+
:meth:`core_area` it is the fraction of the star's light that leaks
|
|
144
|
+
into the photometric aperture. A backend that tabulates the fraction
|
|
145
|
+
per pixel divides by the pixel's solid angle in (lambda/D)^2.
|
|
146
|
+
|
|
139
147
|
Args:
|
|
140
148
|
separation_lod: Angular separation in lambda/D.
|
|
141
|
-
wavelength_nm: Wavelength in
|
|
149
|
+
wavelength_nm: Wavelength in nanometers.
|
|
142
150
|
time_s: Time since mission start in seconds.
|
|
151
|
+
stellar_diam_lod: Stellar angular diameter in lambda/D at the
|
|
152
|
+
queried wavelength; 0 means a point star. Implementations
|
|
153
|
+
without a diameter model accept and ignore it.
|
|
143
154
|
|
|
144
155
|
Returns:
|
|
145
156
|
Mean stellar leakage intensity in (lambda/D)^-2.
|
|
@@ -158,7 +169,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
158
169
|
|
|
159
170
|
Args:
|
|
160
171
|
separation_lod: Angular separation in lambda/D.
|
|
161
|
-
wavelength_nm: Wavelength in
|
|
172
|
+
wavelength_nm: Wavelength in nanometers.
|
|
162
173
|
time_s: Time since mission start in seconds.
|
|
163
174
|
|
|
164
175
|
Returns:
|
|
@@ -189,7 +200,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
189
200
|
the diameter axis this signature lacks.
|
|
190
201
|
|
|
191
202
|
Args:
|
|
192
|
-
wavelength_nm: Wavelength in
|
|
203
|
+
wavelength_nm: Wavelength in nanometers.
|
|
193
204
|
pixel_scale_rad: Output pixel scale in radians/pixel.
|
|
194
205
|
npixels: Output array side length in pixels. Must be a
|
|
195
206
|
Python int (not a JAX array) as it determines the
|
|
@@ -211,7 +222,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
211
222
|
"""Off-axis PSF at a given angular separation.
|
|
212
223
|
|
|
213
224
|
Args:
|
|
214
|
-
wavelength_nm: Wavelength in
|
|
225
|
+
wavelength_nm: Wavelength in nanometers.
|
|
215
226
|
separation_lod: Source separation in lambda/D.
|
|
216
227
|
pixel_scale_rad: Output pixel scale in radians/pixel.
|
|
217
228
|
npixels: Output array side length in pixels. Must be a
|
|
@@ -242,7 +253,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
242
253
|
"""On-axis stellar leakage map at the requested sampling.
|
|
243
254
|
|
|
244
255
|
Args:
|
|
245
|
-
wavelength_nm: Wavelength in
|
|
256
|
+
wavelength_nm: Wavelength in nanometers.
|
|
246
257
|
stellar_diam_lod: Stellar angular diameter in lambda/D.
|
|
247
258
|
pixel_scale_lod: Target pixel scale in lambda/D per pixel.
|
|
248
259
|
shape: Target array shape (ny, nx).
|
|
@@ -266,7 +277,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
266
277
|
"""Off-axis point-source PSFs at the requested sampling.
|
|
267
278
|
|
|
268
279
|
Args:
|
|
269
|
-
wavelength_nm: Wavelength in
|
|
280
|
+
wavelength_nm: Wavelength in nanometers.
|
|
270
281
|
x_lod: Source x-coordinates in lambda/D, shape (K,).
|
|
271
282
|
y_lod: Source y-coordinates in lambda/D, shape (K,).
|
|
272
283
|
pixel_scale_lod: Target pixel scale in lambda/D per pixel.
|
|
@@ -289,7 +300,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
289
300
|
"""Per-pixel transmission map for a spatially uniform background.
|
|
290
301
|
|
|
291
302
|
Args:
|
|
292
|
-
wavelength_nm: Wavelength in
|
|
303
|
+
wavelength_nm: Wavelength in nanometers.
|
|
293
304
|
pixel_scale_lod: Target pixel scale in lambda/D per pixel.
|
|
294
305
|
shape: Target array shape (ny, nx).
|
|
295
306
|
|
|
@@ -317,7 +328,7 @@ class AbstractCoronagraph(eqx.Module):
|
|
|
317
328
|
output carries the same units per target pixel.
|
|
318
329
|
map_pixel_scale_lod: Pixel scale of ``scene_map`` in
|
|
319
330
|
lambda/D per pixel.
|
|
320
|
-
wavelength_nm: Wavelength in
|
|
331
|
+
wavelength_nm: Wavelength in nanometers.
|
|
321
332
|
pixel_scale_lod: Target pixel scale in lambda/D per pixel.
|
|
322
333
|
shape: Target array shape (ny, nx).
|
|
323
334
|
rotation_deg: CCW rotation applied when mapping the scene
|
|
@@ -338,7 +349,10 @@ class AbstractTableCoronagraph(AbstractCoronagraph):
|
|
|
338
349
|
``psf_datacube``) -- and serves the sampling-explicit image contract
|
|
339
350
|
from it with flux-conserving resamples to the requested target grid.
|
|
340
351
|
Scalar performance curves remain the subclass's business (a table
|
|
341
|
-
backend interpolates its own curve tables).
|
|
352
|
+
backend interpolates its own curve tables). Subclasses implementing
|
|
353
|
+
``core_mean_intensity`` use ``stellar_diam_lod`` when their curve
|
|
354
|
+
tables provide a diameter axis; otherwise they accept and ignore it.
|
|
355
|
+
The image-map SPI does not prescribe a scalar curve table format.
|
|
342
356
|
|
|
343
357
|
The tables are monochromatic by construction (a YIP is a per-band
|
|
344
358
|
file), so the served maps ignore ``wavelength_nm`` beyond the
|
|
@@ -524,6 +538,9 @@ class AbstractScalarCoronagraph(AbstractCoronagraph):
|
|
|
524
538
|
model that carries native-grid tables -- subclass
|
|
525
539
|
:class:`AbstractTableCoronagraph` instead, or the image pipeline
|
|
526
540
|
will silently render zeros.
|
|
541
|
+
|
|
542
|
+
Subclasses implementing ``core_mean_intensity`` accept and ignore
|
|
543
|
+
``stellar_diam_lod`` unless they provide a stellar diameter model.
|
|
527
544
|
"""
|
|
528
545
|
|
|
529
546
|
def on_axis_psf(
|
|
@@ -626,7 +643,7 @@ class MultiBandCoronagraph(AbstractCoronagraph):
|
|
|
626
643
|
"""Build a multi-band stack.
|
|
627
644
|
|
|
628
645
|
Args:
|
|
629
|
-
band_centers_nm: Band center wavelengths in
|
|
646
|
+
band_centers_nm: Band center wavelengths in nanometers, one
|
|
630
647
|
per band.
|
|
631
648
|
bands: The per-band ``AbstractCoronagraph`` models, aligned
|
|
632
649
|
with ``band_centers_nm``.
|
|
@@ -687,10 +704,15 @@ class MultiBandCoronagraph(AbstractCoronagraph):
|
|
|
687
704
|
separation_lod, wavelength_nm, time_s=time_s
|
|
688
705
|
)
|
|
689
706
|
|
|
690
|
-
def core_mean_intensity(
|
|
691
|
-
|
|
707
|
+
def core_mean_intensity(
|
|
708
|
+
self, separation_lod, wavelength_nm, *, time_s=0.0, stellar_diam_lod=0.0
|
|
709
|
+
):
|
|
710
|
+
"""Mean stellar leakage from the nearest band at the given diameter."""
|
|
692
711
|
return self.band(wavelength_nm).core_mean_intensity(
|
|
693
|
-
separation_lod,
|
|
712
|
+
separation_lod,
|
|
713
|
+
wavelength_nm,
|
|
714
|
+
time_s=time_s,
|
|
715
|
+
stellar_diam_lod=stellar_diam_lod,
|
|
694
716
|
)
|
|
695
717
|
|
|
696
718
|
def occulter_transmission(self, separation_lod, wavelength_nm, *, time_s=0.0):
|
|
@@ -52,7 +52,7 @@ class AbstractDetector(eqx.Module):
|
|
|
52
52
|
"""Quantum efficiency at a given wavelength.
|
|
53
53
|
|
|
54
54
|
Args:
|
|
55
|
-
wavelength_nm: Wavelength in
|
|
55
|
+
wavelength_nm: Wavelength in nanometers.
|
|
56
56
|
|
|
57
57
|
Returns:
|
|
58
58
|
QE as a fraction in [0, 1].
|
|
@@ -140,7 +140,7 @@ class AbstractDetector(eqx.Module):
|
|
|
140
140
|
|
|
141
141
|
The marginal distribution of returned electrons is identical to
|
|
142
142
|
:meth:`readout_source_electrons`. The two methods produce
|
|
143
|
-
different specific
|
|
143
|
+
different specific realizations even with the same key.
|
|
144
144
|
|
|
145
145
|
Args:
|
|
146
146
|
image_rate: Incident photon rate array in ph/s/pixel.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""Optical element abstractions (throughput, filters, field stops)."""
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
import abc
|
|
5
4
|
from typing import final
|
|
6
5
|
|
|
@@ -27,7 +26,7 @@ class AbstractOpticalElement(eqx.Module):
|
|
|
27
26
|
"""Fractional throughput at a given wavelength.
|
|
28
27
|
|
|
29
28
|
Args:
|
|
30
|
-
wavelength_nm: Wavelength in
|
|
29
|
+
wavelength_nm: Wavelength in nanometers.
|
|
31
30
|
|
|
32
31
|
Returns:
|
|
33
32
|
Scalar throughput in [0, 1].
|
|
@@ -40,7 +39,7 @@ class AbstractOpticalElement(eqx.Module):
|
|
|
40
39
|
|
|
41
40
|
Args:
|
|
42
41
|
arr: Input photon rate array [ph/s/pixel].
|
|
43
|
-
wavelength_nm: Wavelength in
|
|
42
|
+
wavelength_nm: Wavelength in nanometers.
|
|
44
43
|
|
|
45
44
|
Returns:
|
|
46
45
|
Attenuated photon rate array, same shape as arr.
|
|
@@ -79,14 +78,13 @@ class ConstantThroughput(AbstractUniformElement):
|
|
|
79
78
|
def __repr__(self) -> str:
|
|
80
79
|
"""One-line summary of throughput value."""
|
|
81
80
|
return (
|
|
82
|
-
f"ConstantThroughput(name={self.name!r}, "
|
|
83
|
-
f"throughput={self.throughput:.3g})"
|
|
81
|
+
f"ConstantThroughput(name={self.name!r}, throughput={self.throughput:.3g})"
|
|
84
82
|
)
|
|
85
83
|
|
|
86
84
|
|
|
87
85
|
@final
|
|
88
86
|
class SpectralThroughput(AbstractUniformElement):
|
|
89
|
-
"""Wavelength-dependent throughput
|
|
87
|
+
"""Wavelength-dependent throughput from sampled (wavelength, throughput) pairs.
|
|
90
88
|
|
|
91
89
|
Linear interpolation between samples; throughput is zero outside
|
|
92
90
|
the defined wavelength range.
|
|
@@ -77,6 +77,7 @@ class OpticalPath(eqx.Module):
|
|
|
77
77
|
|
|
78
78
|
Args:
|
|
79
79
|
coronagraph: One of:
|
|
80
|
+
|
|
80
81
|
- an :class:`AbstractCoronagraph` instance (used as-is),
|
|
81
82
|
- a YIP path (str or :class:`pathlib.Path`, wrapped with
|
|
82
83
|
:class:`YippyCoronagraph`),
|
|
@@ -139,7 +140,7 @@ class OpticalPath(eqx.Module):
|
|
|
139
140
|
"""Total throughput of all attenuating elements.
|
|
140
141
|
|
|
141
142
|
Args:
|
|
142
|
-
wavelength_nm: Wavelength in
|
|
143
|
+
wavelength_nm: Wavelength in nanometers.
|
|
143
144
|
|
|
144
145
|
Returns:
|
|
145
146
|
Combined fractional throughput in [0, 1].
|
|
@@ -14,17 +14,17 @@ class AbstractPrimary(eqx.Module):
|
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
diameter_m: AbstractVar[float]
|
|
17
|
-
"""Primary mirror diameter in
|
|
17
|
+
"""Primary mirror diameter in meters."""
|
|
18
18
|
|
|
19
19
|
area_m2: AbstractVar[float]
|
|
20
|
-
"""Effective collecting area in square
|
|
20
|
+
"""Effective collecting area in square meters."""
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class SimplePrimary(AbstractPrimary):
|
|
24
24
|
"""A simple circular primary mirror with a central obscuration.
|
|
25
25
|
|
|
26
26
|
Args:
|
|
27
|
-
diameter_m: Primary mirror diameter in
|
|
27
|
+
diameter_m: Primary mirror diameter in meters.
|
|
28
28
|
obscuration: Linear obscuration fraction (0 = no obscuration).
|
|
29
29
|
shape_factor: Fraction of unobscured area that is collecting
|
|
30
30
|
(accounts for struts, segment gaps, etc.). Default 1.0.
|
|
@@ -47,12 +47,12 @@ class SimplePrimary(AbstractPrimary):
|
|
|
47
47
|
|
|
48
48
|
@property
|
|
49
49
|
def diameter_m(self) -> float:
|
|
50
|
-
"""Primary mirror diameter in
|
|
50
|
+
"""Primary mirror diameter in meters."""
|
|
51
51
|
return self._diameter_m
|
|
52
52
|
|
|
53
53
|
@property
|
|
54
54
|
def area_m2(self) -> float:
|
|
55
|
-
"""Effective collecting area in square
|
|
55
|
+
"""Effective collecting area in square meters."""
|
|
56
56
|
r = self._diameter_m / 2.0
|
|
57
57
|
gross_area = jnp.pi * r**2
|
|
58
58
|
return gross_area * (1.0 - self.obscuration**2) * self.shape_factor
|
|
@@ -68,9 +68,9 @@ class SimplePrimary(AbstractPrimary):
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def _hex_axial_coords(n_rings: int) -> list[tuple[int, int]]:
|
|
71
|
-
"""Axial (q, r)
|
|
71
|
+
"""Axial (q, r) centers of a hex-packed n-ring layout.
|
|
72
72
|
|
|
73
|
-
Yields ``1 + 3 n (n + 1)`` coordinates,
|
|
73
|
+
Yields ``1 + 3 n (n + 1)`` coordinates, center first.
|
|
74
74
|
"""
|
|
75
75
|
coords = [(0, 0)]
|
|
76
76
|
for q in range(-n_rings, n_rings + 1):
|
|
@@ -89,11 +89,11 @@ class SegmentedPrimary(AbstractPrimary):
|
|
|
89
89
|
DESCRIBES the geometry; it does not propagate wavefronts.
|
|
90
90
|
|
|
91
91
|
Args:
|
|
92
|
-
diameter_m: Circumscribing diameter in
|
|
93
|
-
area_m2: Effective collecting area in square
|
|
94
|
-
n_rings: Number of segment rings around the
|
|
92
|
+
diameter_m: Circumscribing diameter in meters.
|
|
93
|
+
area_m2: Effective collecting area in square meters (gap/fill corrected).
|
|
94
|
+
n_rings: Number of segment rings around the center segment.
|
|
95
95
|
n_segments: Total segment count (``1 + 3 n (n + 1)`` for a full layout).
|
|
96
|
-
segment_gap_m: Inter-segment optical gap in
|
|
96
|
+
segment_gap_m: Inter-segment optical gap in meters.
|
|
97
97
|
segment_shape: Segment shape; only ``"hexagon"`` is supported today.
|
|
98
98
|
"""
|
|
99
99
|
|
|
@@ -129,17 +129,17 @@ class SegmentedPrimary(AbstractPrimary):
|
|
|
129
129
|
|
|
130
130
|
@property
|
|
131
131
|
def diameter_m(self) -> float:
|
|
132
|
-
"""Circumscribing diameter in
|
|
132
|
+
"""Circumscribing diameter in meters."""
|
|
133
133
|
return self._diameter_m
|
|
134
134
|
|
|
135
135
|
@property
|
|
136
136
|
def area_m2(self) -> float:
|
|
137
|
-
"""Effective collecting area in square
|
|
137
|
+
"""Effective collecting area in square meters."""
|
|
138
138
|
return self._area_m2
|
|
139
139
|
|
|
140
140
|
@property
|
|
141
141
|
def segment_flat_to_flat_m(self) -> float:
|
|
142
|
-
"""Segment flat-to-flat size in
|
|
142
|
+
"""Segment flat-to-flat size in meters.
|
|
143
143
|
|
|
144
144
|
Exact (``point_to_point * sqrt(3) / 2``) when the segment size is
|
|
145
145
|
given; otherwise the legacy circumscribing approximation
|
|
@@ -151,19 +151,19 @@ class SegmentedPrimary(AbstractPrimary):
|
|
|
151
151
|
|
|
152
152
|
@property
|
|
153
153
|
def segment_pitch_m(self) -> float:
|
|
154
|
-
"""
|
|
154
|
+
"""Center-to-center distance of adjacent segments (flat-to-flat + gap)."""
|
|
155
155
|
return self.segment_flat_to_flat_m + self.segment_gap_m
|
|
156
156
|
|
|
157
157
|
@property
|
|
158
|
-
def
|
|
159
|
-
"""``(n_segments, 2)`` segment
|
|
158
|
+
def segment_centers_m(self):
|
|
159
|
+
"""``(n_segments, 2)`` segment center (x, y) positions in meters.
|
|
160
160
|
|
|
161
161
|
A flat-top hexagonal lattice (flats up, points along x), the
|
|
162
|
-
convention of the HWO EAC baseline pupils: the
|
|
163
|
-
first, and adjacent
|
|
162
|
+
convention of the HWO EAC baseline pupils: the center segment is
|
|
163
|
+
first, and adjacent centers are one ``segment_pitch_m`` apart.
|
|
164
164
|
"""
|
|
165
165
|
if self.segment_shape != "hexagon":
|
|
166
|
-
msg = f"
|
|
166
|
+
msg = f"segment_centers_m undefined for shape {self.segment_shape!r}"
|
|
167
167
|
raise NotImplementedError(msg)
|
|
168
168
|
axial = _hex_axial_coords(self.n_rings)
|
|
169
169
|
pitch = self.segment_pitch_m
|
|
@@ -20,6 +20,8 @@ intensity cubes, fitted reduced-order, or learned); they share the
|
|
|
20
20
|
import abc
|
|
21
21
|
|
|
22
22
|
import equinox as eqx
|
|
23
|
+
import jax
|
|
24
|
+
import jax.numpy as jnp
|
|
23
25
|
from equinox import AbstractVar
|
|
24
26
|
from jax.typing import ArrayLike
|
|
25
27
|
from jaxtyping import Array
|
|
@@ -33,7 +35,7 @@ class AbstractSpeckleField(eqx.Module):
|
|
|
33
35
|
or a learned generator. All produce a coronagraph-plane contrast map
|
|
34
36
|
through :meth:`realize`.
|
|
35
37
|
|
|
36
|
-
All wavelength arguments are in
|
|
38
|
+
All wavelength arguments are in nanometers; ``time_s`` is seconds since
|
|
37
39
|
mission start. The returned map is on the field's native coronagraph
|
|
38
40
|
plane at :attr:`pixel_scale_lod`; the caller resamples it to the
|
|
39
41
|
detector grid (as for the coronagraph ``stellar_intens`` map).
|
|
@@ -57,12 +59,14 @@ class AbstractSpeckleField(eqx.Module):
|
|
|
57
59
|
wavelength_nm: ArrayLike,
|
|
58
60
|
time_s: ArrayLike = 0.0,
|
|
59
61
|
) -> Array:
|
|
60
|
-
"""Speckle
|
|
62
|
+
"""Speckle flux-fraction *delta* at a given time and wavelength.
|
|
61
63
|
|
|
62
64
|
Returns the wavefront-error-induced excess over the deterministic
|
|
63
65
|
coronagraphic floor -- i.e. ``I(t) - |E_nom|^2``, expanded as
|
|
64
|
-
``2 Re(E_nom* . G eps(t)) + |G eps(t)|^2``,
|
|
65
|
-
(fraction of
|
|
66
|
+
``2 Re(E_nom* . G eps(t)) + |G eps(t)|^2``, as per-pixel flux
|
|
67
|
+
fractions (the fraction of pre-coronagraph host-star flux landing in
|
|
68
|
+
each pixel, the same units a YIP ``stellar_intens`` map carries --
|
|
69
|
+
NOT peak-referenced contrast). It must not include the
|
|
66
70
|
``|E_nom|^2`` floor itself: ``star_rate`` already applies that via
|
|
67
71
|
the YIP ``stellar_intens`` map, so re-emitting it here would double
|
|
68
72
|
count. An incoherent-halo implementation returns only the strictly
|
|
@@ -76,10 +80,67 @@ class AbstractSpeckleField(eqx.Module):
|
|
|
76
80
|
/ roll sequence.
|
|
77
81
|
|
|
78
82
|
Args:
|
|
79
|
-
wavelength_nm: Wavelength in
|
|
83
|
+
wavelength_nm: Wavelength in nanometers.
|
|
80
84
|
time_s: Time since mission start in seconds.
|
|
81
85
|
|
|
82
86
|
Returns:
|
|
83
87
|
2D float contrast-delta array on the native coronagraph plane.
|
|
84
88
|
"""
|
|
85
89
|
...
|
|
90
|
+
|
|
91
|
+
def realize_average(
|
|
92
|
+
self,
|
|
93
|
+
*,
|
|
94
|
+
wavelength_nm: ArrayLike,
|
|
95
|
+
exposure_s: ArrayLike,
|
|
96
|
+
start_time_s: ArrayLike = 0.0,
|
|
97
|
+
n_sub: int = 8,
|
|
98
|
+
) -> Array:
|
|
99
|
+
"""Speckle contrast delta averaged over an exposure.
|
|
100
|
+
|
|
101
|
+
:meth:`realize` is instantaneous, but a detector integrates. Averaging
|
|
102
|
+
matters because the map is QUADRATIC in the drifting coefficients:
|
|
103
|
+
the mean of the realized maps is not the map of the mean coefficients,
|
|
104
|
+
so an instantaneous sample is a biased stand-in for an exposure
|
|
105
|
+
whenever the field decorrelates on the exposure timescale. This
|
|
106
|
+
evaluates :meth:`realize` at ``n_sub`` sub-exposure midpoints and
|
|
107
|
+
averages, which is exact in the limit of many sub-steps and
|
|
108
|
+
second-order accurate at finite ``n_sub``.
|
|
109
|
+
|
|
110
|
+
How many sub-steps are enough, and how much the average suppresses
|
|
111
|
+
the speckle variance relative to a snapshot, are both set by how many
|
|
112
|
+
independent realizations the exposure spans. A generator that knows
|
|
113
|
+
its own temporal statistics can say so in closed form (see
|
|
114
|
+
``physicaloptix.SpeckleProcess.exposure_neff``); when the exposure is
|
|
115
|
+
short against the decorrelation time, one sub-step is already right
|
|
116
|
+
and this reduces to :meth:`realize` at the exposure midpoint.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
wavelength_nm: Wavelength in nanometers.
|
|
120
|
+
exposure_s: Exposure length in seconds.
|
|
121
|
+
start_time_s: Exposure start, in seconds since ``epoch_jd``.
|
|
122
|
+
n_sub: Number of sub-exposure samples. Must be at least 1.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
2D float contrast-delta array, averaged over the exposure.
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
ValueError: If ``n_sub`` is not a positive integer.
|
|
129
|
+
"""
|
|
130
|
+
if int(n_sub) < 1 or int(n_sub) != n_sub:
|
|
131
|
+
raise ValueError(f"n_sub must be a positive integer, got {n_sub}")
|
|
132
|
+
n_sub = int(n_sub)
|
|
133
|
+
start = jnp.asarray(start_time_s, dtype=float)
|
|
134
|
+
step = jnp.asarray(exposure_s, dtype=float) / n_sub
|
|
135
|
+
|
|
136
|
+
def sample(index):
|
|
137
|
+
return self.realize(
|
|
138
|
+
wavelength_nm=wavelength_nm, time_s=start + (index + 0.5) * step
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Accumulate rather than stacking: one map is held at a time, so a
|
|
142
|
+
# large native grid does not cost n_sub copies.
|
|
143
|
+
def accumulate(index, total):
|
|
144
|
+
return total + sample(index)
|
|
145
|
+
|
|
146
|
+
return jax.lax.fori_loop(1, n_sub, accumulate, sample(0)) / n_sub
|
|
@@ -98,9 +98,31 @@ class YippyCoronagraph(AbstractTableCoronagraph):
|
|
|
98
98
|
wavelength_nm: ArrayLike,
|
|
99
99
|
*,
|
|
100
100
|
time_s: ArrayLike = 0.0,
|
|
101
|
+
stellar_diam_lod: ArrayLike = 0.0,
|
|
101
102
|
) -> ArrayLike:
|
|
102
|
-
"""Mean stellar leakage from the YIP interpolation table.
|
|
103
|
-
|
|
103
|
+
"""Mean stellar leakage per (lambda/D)^2 from the YIP interpolation table.
|
|
104
|
+
|
|
105
|
+
yippy tabulates the fraction of the star's light per pixel, so the
|
|
106
|
+
value is divided by the stellar pixel's solid angle,
|
|
107
|
+
to meet the contract of :meth:`AbstractCoronagraph.core_mean_intensity`.
|
|
108
|
+
The backend interpolates over the stellar diameter axis when present;
|
|
109
|
+
otherwise it accepts and ignores the diameter.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
separation_lod: Angular separation in lambda/D.
|
|
113
|
+
wavelength_nm: Wavelength in nanometers (accepted and ignored).
|
|
114
|
+
time_s: Time since mission start in seconds (accepted and ignored).
|
|
115
|
+
stellar_diam_lod: Stellar angular diameter in lambda/D at the
|
|
116
|
+
queried wavelength; 0 means a point star.
|
|
117
|
+
"""
|
|
118
|
+
per_pixel = self._backend.core_mean_intensity(separation_lod, stellar_diam_lod)
|
|
119
|
+
return (
|
|
120
|
+
per_pixel
|
|
121
|
+
/ getattr(
|
|
122
|
+
self._backend, "stellar_pixel_scale_lod", self._backend.pixel_scale_lod
|
|
123
|
+
)
|
|
124
|
+
** 2
|
|
125
|
+
)
|
|
104
126
|
|
|
105
127
|
def occulter_transmission(
|
|
106
128
|
self,
|
|
@@ -123,6 +145,68 @@ class YippyCoronagraph(AbstractTableCoronagraph):
|
|
|
123
145
|
"""Stellar leakage PSF from the YIP stellar intensity model."""
|
|
124
146
|
return self._backend.stellar_intens(0.0)
|
|
125
147
|
|
|
148
|
+
def stellar_map(
|
|
149
|
+
self,
|
|
150
|
+
wavelength_nm: ArrayLike,
|
|
151
|
+
stellar_diam_lod: ArrayLike,
|
|
152
|
+
*,
|
|
153
|
+
pixel_scale_lod: ArrayLike,
|
|
154
|
+
shape: tuple[int, int],
|
|
155
|
+
) -> Array:
|
|
156
|
+
"""Resample stellar flux in the backend's stellar reference frame."""
|
|
157
|
+
method = getattr(self._backend, "stellar_map", None)
|
|
158
|
+
if method is None:
|
|
159
|
+
return super().stellar_map(
|
|
160
|
+
wavelength_nm,
|
|
161
|
+
stellar_diam_lod,
|
|
162
|
+
pixel_scale_lod=pixel_scale_lod,
|
|
163
|
+
shape=shape,
|
|
164
|
+
)
|
|
165
|
+
return method(
|
|
166
|
+
wavelength_nm,
|
|
167
|
+
stellar_diam_lod,
|
|
168
|
+
pixel_scale_lod=pixel_scale_lod,
|
|
169
|
+
shape=shape,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
def source_psfs(
|
|
173
|
+
self,
|
|
174
|
+
wavelength_nm: ArrayLike,
|
|
175
|
+
x_lod: ArrayLike,
|
|
176
|
+
y_lod: ArrayLike,
|
|
177
|
+
*,
|
|
178
|
+
pixel_scale_lod: ArrayLike,
|
|
179
|
+
shape: tuple[int, int],
|
|
180
|
+
) -> Array:
|
|
181
|
+
"""Resample source flux in the backend's off-axis reference frame."""
|
|
182
|
+
method = getattr(self._backend, "source_psfs", None)
|
|
183
|
+
if method is None:
|
|
184
|
+
return super().source_psfs(
|
|
185
|
+
wavelength_nm,
|
|
186
|
+
x_lod,
|
|
187
|
+
y_lod,
|
|
188
|
+
pixel_scale_lod=pixel_scale_lod,
|
|
189
|
+
shape=shape,
|
|
190
|
+
)
|
|
191
|
+
return method(
|
|
192
|
+
wavelength_nm, x_lod, y_lod, pixel_scale_lod=pixel_scale_lod, shape=shape
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
def background_transmission(
|
|
196
|
+
self,
|
|
197
|
+
wavelength_nm: ArrayLike,
|
|
198
|
+
*,
|
|
199
|
+
pixel_scale_lod: ArrayLike,
|
|
200
|
+
shape: tuple[int, int],
|
|
201
|
+
) -> Array:
|
|
202
|
+
"""Resample sky values in the backend's sky reference frame."""
|
|
203
|
+
method = getattr(self._backend, "background_transmission", None)
|
|
204
|
+
if method is None:
|
|
205
|
+
return super().background_transmission(
|
|
206
|
+
wavelength_nm, pixel_scale_lod=pixel_scale_lod, shape=shape
|
|
207
|
+
)
|
|
208
|
+
return method(wavelength_nm, pixel_scale_lod=pixel_scale_lod, shape=shape)
|
|
209
|
+
|
|
126
210
|
def off_axis_psf(
|
|
127
211
|
self,
|
|
128
212
|
wavelength_nm: ArrayLike,
|
|
@@ -142,14 +226,38 @@ class YippyCoronagraph(AbstractTableCoronagraph):
|
|
|
142
226
|
self,
|
|
143
227
|
separation_lod: ArrayLike,
|
|
144
228
|
ppf: float = 30.0,
|
|
229
|
+
*,
|
|
230
|
+
stellar_diam_lod: ArrayLike = 0.0,
|
|
145
231
|
) -> ArrayLike:
|
|
146
|
-
"""AYO noise floor: core_mean_intensity / ppf
|
|
232
|
+
"""AYO noise floor per (lambda/D)^2: ``core_mean_intensity / ppf``.
|
|
147
233
|
|
|
148
|
-
|
|
234
|
+
Same units as :meth:`core_mean_intensity`, so multiplying by
|
|
235
|
+
:meth:`core_area` gives the floor as a fraction of the star's light.
|
|
236
|
+
yippy's own ``noise_floor_ayo`` is per pixel and is divided by the
|
|
237
|
+
stellar pixel's solid angle. Not part of the
|
|
149
238
|
AbstractCoronagraph contract -- downstream ETCs should compute
|
|
150
239
|
noise floors as pure functions.
|
|
240
|
+
|
|
241
|
+
Division by ``ppf`` precedes the pixel-area conversion to preserve
|
|
242
|
+
point-source rounding exactly.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
separation_lod: Angular separation in lambda/D.
|
|
246
|
+
ppf: Post-processing noise suppression factor.
|
|
247
|
+
stellar_diam_lod: Stellar angular diameter in lambda/D at the
|
|
248
|
+
queried wavelength; 0 means a point star. Accepted and ignored
|
|
249
|
+
when the backend has no diameter model.
|
|
151
250
|
"""
|
|
152
|
-
|
|
251
|
+
per_pixel = (
|
|
252
|
+
self._backend.core_mean_intensity(separation_lod, stellar_diam_lod) / ppf
|
|
253
|
+
)
|
|
254
|
+
return (
|
|
255
|
+
per_pixel
|
|
256
|
+
/ getattr(
|
|
257
|
+
self._backend, "stellar_pixel_scale_lod", self._backend.pixel_scale_lod
|
|
258
|
+
)
|
|
259
|
+
** 2
|
|
260
|
+
)
|
|
153
261
|
|
|
154
262
|
def raw_contrast(self, separation_lod: ArrayLike) -> ArrayLike:
|
|
155
263
|
"""Raw contrast from the YIP interpolation table."""
|
optixstuff-3.0.0/pyproject.toml
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ['hatchling', "hatch-fancy-pypi-readme", "hatch-vcs"]
|
|
3
|
-
build-backend = "hatchling.build"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "optixstuff"
|
|
7
|
-
description = "Hardware abstractions for the HWO direct imaging simulation suite"
|
|
8
|
-
authors = [{ name = "Corey Spohn", email = "corey.a.spohn@nasa.gov" }]
|
|
9
|
-
dynamic = ['readme', 'version']
|
|
10
|
-
requires-python = ">=3.11"
|
|
11
|
-
license = { file = "LICENSE" }
|
|
12
|
-
classifiers = [
|
|
13
|
-
"Development Status :: 2 - Pre-Alpha",
|
|
14
|
-
"Intended Audience :: Science/Research",
|
|
15
|
-
"License :: OSI Approved :: MIT License",
|
|
16
|
-
"Programming Language :: Python :: 3",
|
|
17
|
-
"Topic :: Scientific/Engineering :: Astronomy",
|
|
18
|
-
]
|
|
19
|
-
dependencies = ["equinox>=0.12.0", "hwoutils", "interpax", "jax>=0.4.1", "jaxlib>=0.4.1", "yippy"]
|
|
20
|
-
|
|
21
|
-
[project.optional-dependencies]
|
|
22
|
-
dev = ["pre-commit"]
|
|
23
|
-
docs = [
|
|
24
|
-
"sphinx",
|
|
25
|
-
"myst-nb",
|
|
26
|
-
"sphinx-book-theme",
|
|
27
|
-
"sphinx-autoapi",
|
|
28
|
-
"sphinx_autodoc_typehints",
|
|
29
|
-
"sphinxcontrib-mermaid",
|
|
30
|
-
"ipython",
|
|
31
|
-
"matplotlib",
|
|
32
|
-
]
|
|
33
|
-
test = ["nox", "pytest", "hypothesis", "pytest-cov"]
|
|
34
|
-
|
|
35
|
-
[project.urls]
|
|
36
|
-
Homepage = "https://github.com/CoreySpohn/optixstuff"
|
|
37
|
-
Issues = "https://github.com/CoreySpohn/optixstuff/issues"
|
|
38
|
-
|
|
39
|
-
[tool.hatch.version]
|
|
40
|
-
source = "vcs"
|
|
41
|
-
|
|
42
|
-
[tool.hatch.build.hooks.vcs]
|
|
43
|
-
version-file = "src/optixstuff/_version.py"
|
|
44
|
-
|
|
45
|
-
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
|
46
|
-
content-type = "text/markdown"
|
|
47
|
-
|
|
48
|
-
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
|
49
|
-
path = "README.md"
|
|
50
|
-
|
|
51
|
-
[tool.ruff.lint]
|
|
52
|
-
select = ["B", "D", "E", "F", "I", "UP", "RUF"]
|
|
53
|
-
|
|
54
|
-
[tool.ruff.lint.pydocstyle]
|
|
55
|
-
convention = "google"
|
|
56
|
-
|
|
57
|
-
[tool.ruff.lint.per-file-ignores]
|
|
58
|
-
"tests/**" = ["D"]
|
|
59
|
-
|
|
60
|
-
[tool.hatch.build.targets.wheel]
|
|
61
|
-
packages = ["src/optixstuff"]
|
|
62
|
-
|
|
63
|
-
[tool.hatch.build.targets.sdist]
|
|
64
|
-
exclude = ["/scripts", "/docs", "/tests", "/.github"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|