ppdmod 2.0.0__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.
- ppdmod-2.0.0/LICENSE +21 -0
- ppdmod-2.0.0/MANIFEST.in +1 -0
- ppdmod-2.0.0/PKG-INFO +68 -0
- ppdmod-2.0.0/README.md +14 -0
- ppdmod-2.0.0/ppdmod/__init__.py +1 -0
- ppdmod-2.0.0/ppdmod/base.py +225 -0
- ppdmod-2.0.0/ppdmod/components.py +557 -0
- ppdmod-2.0.0/ppdmod/config/standard_parameters.toml +290 -0
- ppdmod-2.0.0/ppdmod/data.py +485 -0
- ppdmod-2.0.0/ppdmod/fitting.py +546 -0
- ppdmod-2.0.0/ppdmod/options.py +164 -0
- ppdmod-2.0.0/ppdmod/parameter.py +152 -0
- ppdmod-2.0.0/ppdmod/plot.py +1241 -0
- ppdmod-2.0.0/ppdmod/utils.py +575 -0
- ppdmod-2.0.0/ppdmod.egg-info/PKG-INFO +68 -0
- ppdmod-2.0.0/ppdmod.egg-info/SOURCES.txt +22 -0
- ppdmod-2.0.0/ppdmod.egg-info/dependency_links.txt +1 -0
- ppdmod-2.0.0/ppdmod.egg-info/requires.txt +12 -0
- ppdmod-2.0.0/ppdmod.egg-info/top_level.txt +1 -0
- ppdmod-2.0.0/pyproject.toml +60 -0
- ppdmod-2.0.0/setup.cfg +4 -0
- ppdmod-2.0.0/tests/test_calculation.py +122 -0
- ppdmod-2.0.0/tests/test_components.py +149 -0
- ppdmod-2.0.0/tests/test_projection.py +76 -0
ppdmod-2.0.0/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Marten B. Scheuck
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
ppdmod-2.0.0/MANIFEST.in
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
include ppdmod/config/*.toml
|
ppdmod-2.0.0/PKG-INFO
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: ppdmod
|
3
|
+
Version: 2.0.0
|
4
|
+
Summary: A package for modelling and model-fitting protoplanetary disks
|
5
|
+
Author-email: Marten Scheuck <code@mbscheuck.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2024 Marten B. Scheuck
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
|
28
|
+
Project-URL: repository, https://codeberg.org/MBSck/ppdmod.git
|
29
|
+
Classifier: Framework :: Pytest
|
30
|
+
Classifier: Framework :: Sphinx
|
31
|
+
Classifier: License :: OSI Approved :: MIT License
|
32
|
+
Classifier: Natural Language :: English
|
33
|
+
Classifier: Operating System :: OS Independent
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
35
|
+
Classifier: Programming Language :: Python :: 3.9
|
36
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
37
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
38
|
+
Requires-Python: <3.11,>=3.10
|
39
|
+
Description-Content-Type: text/markdown
|
40
|
+
License-File: LICENSE
|
41
|
+
Requires-Dist: astropy>=6.1.4
|
42
|
+
Requires-Dist: corner>=2.2.2
|
43
|
+
Requires-Dist: dynesty>=2.1.4
|
44
|
+
Requires-Dist: emcee>=3.1.6
|
45
|
+
Requires-Dist: h5py>=3.12.1
|
46
|
+
Requires-Dist: matplotlib>=3.9.2
|
47
|
+
Requires-Dist: numpy>=2.0.2
|
48
|
+
Requires-Dist: openpyxl>=3.1.5
|
49
|
+
Requires-Dist: pandas>=2.2.3
|
50
|
+
Requires-Dist: scipy>=1.14.1
|
51
|
+
Requires-Dist: toml>=0.10.2
|
52
|
+
Requires-Dist: tqdm>=4.67.1
|
53
|
+
Dynamic: license-file
|
54
|
+
|
55
|
+
# PPDMod
|
56
|
+
## Installation
|
57
|
+
Run the following to install:
|
58
|
+
```
|
59
|
+
python pip install ppdmod
|
60
|
+
```
|
61
|
+
## Usage
|
62
|
+
## Developing PPDMod
|
63
|
+
To install ppdmod, along with the tools you need to develop and run tests,
|
64
|
+
run the following in your virtualenv:
|
65
|
+
|
66
|
+
```
|
67
|
+
$ pip install -e .
|
68
|
+
```
|
ppdmod-2.0.0/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# PPDMod
|
2
|
+
## Installation
|
3
|
+
Run the following to install:
|
4
|
+
```
|
5
|
+
python pip install ppdmod
|
6
|
+
```
|
7
|
+
## Usage
|
8
|
+
## Developing PPDMod
|
9
|
+
To install ppdmod, along with the tools you need to develop and run tests,
|
10
|
+
run the following in your virtualenv:
|
11
|
+
|
12
|
+
```
|
13
|
+
$ pip install -e .
|
14
|
+
```
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "2.0.0"
|
@@ -0,0 +1,225 @@
|
|
1
|
+
import copy
|
2
|
+
from typing import Dict, Tuple
|
3
|
+
|
4
|
+
import astropy.units as u
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
from .options import OPTIONS
|
8
|
+
from .parameter import MultiParam, Parameter
|
9
|
+
from .utils import transform_coordinates, translate_image, translate_vis
|
10
|
+
|
11
|
+
|
12
|
+
class Component:
|
13
|
+
"""The base class for the component."""
|
14
|
+
|
15
|
+
name = "GenComp"
|
16
|
+
label = None
|
17
|
+
description = "This is base component are derived."
|
18
|
+
|
19
|
+
def __init__(self, **kwargs):
|
20
|
+
"""The class's constructor."""
|
21
|
+
self.flux_lnf = Parameter(name="flux_lnf", base="lnf")
|
22
|
+
self.t3_lnf = Parameter(name="t3_lnf", base="lnf")
|
23
|
+
self.vis_lnf = Parameter(name="vis_lnf", base="lnf")
|
24
|
+
|
25
|
+
def eval(self, **kwargs) -> None:
|
26
|
+
"""Sets the parameters (values) from the keyword arguments."""
|
27
|
+
for key, val in kwargs.items():
|
28
|
+
if hasattr(self, key):
|
29
|
+
if isinstance(val, (Parameter, MultiParam)):
|
30
|
+
setattr(self, key, val.copy())
|
31
|
+
else:
|
32
|
+
if isinstance(getattr(self, key), Parameter):
|
33
|
+
getattr(self, key).value = val
|
34
|
+
else:
|
35
|
+
setattr(self, key, val)
|
36
|
+
|
37
|
+
def copy(self) -> "Component":
|
38
|
+
"""Copies the component."""
|
39
|
+
return copy.deepcopy(self)
|
40
|
+
|
41
|
+
def get_params(
|
42
|
+
self, free: bool = False, shared: bool = False, time: bool = False
|
43
|
+
) -> Dict[str, Parameter]:
|
44
|
+
"""Gets all the parameters of a component.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
component : Component
|
49
|
+
The component for which the parameters should be fetched.
|
50
|
+
free : bool, optional
|
51
|
+
If free parameters should be returned, by default False.
|
52
|
+
shared : bool, optional
|
53
|
+
If shared parameters should be returned, by default False.
|
54
|
+
time: bool, optional
|
55
|
+
If time-dependent parameters should be returned, by default False.
|
56
|
+
|
57
|
+
Returns
|
58
|
+
-------
|
59
|
+
params : dict of Parameter
|
60
|
+
"""
|
61
|
+
params = {}
|
62
|
+
for attribute in dir(self):
|
63
|
+
param = getattr(self, attribute)
|
64
|
+
if isinstance(param, Parameter):
|
65
|
+
if shared and free:
|
66
|
+
if not (param.shared and param.free):
|
67
|
+
continue
|
68
|
+
elif free:
|
69
|
+
if not param.free or param.shared:
|
70
|
+
continue
|
71
|
+
elif shared:
|
72
|
+
if not param.shared or param.free:
|
73
|
+
continue
|
74
|
+
|
75
|
+
params[attribute] = param
|
76
|
+
elif isinstance(param, MultiParam):
|
77
|
+
for p in param.params:
|
78
|
+
if shared and free:
|
79
|
+
if not (p.shared and p.free):
|
80
|
+
continue
|
81
|
+
elif free:
|
82
|
+
if not p.free or p.shared:
|
83
|
+
continue
|
84
|
+
elif shared:
|
85
|
+
if not p.shared or p.free:
|
86
|
+
continue
|
87
|
+
|
88
|
+
params[p.name] = p
|
89
|
+
return params
|
90
|
+
|
91
|
+
def flux_func(self, t: int, wl: u.um, **kwargs) -> np.ndarray:
|
92
|
+
"""Calculates the flux."""
|
93
|
+
return np.array([]).astype(OPTIONS.data.dtype.real)
|
94
|
+
|
95
|
+
def compute_flux(self, t: int, wl: u.um, **kwargs) -> np.ndarray:
|
96
|
+
"""Computes the fluxes."""
|
97
|
+
return np.abs(self.flux_func(t, wl, **kwargs)).astype(OPTIONS.data.dtype.real)
|
98
|
+
|
99
|
+
|
100
|
+
class FourierComponent(Component):
|
101
|
+
"""The base class for the Fourier (analytical) component.
|
102
|
+
|
103
|
+
Parameters
|
104
|
+
----------
|
105
|
+
xx : float
|
106
|
+
The x-coordinate of the component.
|
107
|
+
yy : float
|
108
|
+
The x-coordinate of the component.
|
109
|
+
dim : float
|
110
|
+
The dimension (px).
|
111
|
+
"""
|
112
|
+
|
113
|
+
name = "FourierComp"
|
114
|
+
description = "The component from which all analytical components are derived."
|
115
|
+
_asymmetric = False
|
116
|
+
|
117
|
+
def __init__(self, **kwargs):
|
118
|
+
"""The class's constructor."""
|
119
|
+
super().__init__(**kwargs)
|
120
|
+
self.fr = Parameter(base="fr")
|
121
|
+
self.r = Parameter(base="r")
|
122
|
+
self.phi = Parameter(base="phi")
|
123
|
+
self.pa = Parameter(base="pa")
|
124
|
+
self.cinc = Parameter(base="cinc")
|
125
|
+
self.dim = Parameter(base="dim")
|
126
|
+
self.modulation = Parameter(base="modulation")
|
127
|
+
|
128
|
+
self.eval(**kwargs)
|
129
|
+
|
130
|
+
for i in range(1, self.modulation.value + 1):
|
131
|
+
rho_str, theta_str = f"rho{i}", f"theta{i}"
|
132
|
+
rho = Parameter(name=rho_str, free=self.asymmetric, base="rho")
|
133
|
+
theta = Parameter(name=theta_str, free=self.asymmetric, base="theta")
|
134
|
+
setattr(self, rho_str, rho)
|
135
|
+
setattr(self, theta_str, theta)
|
136
|
+
|
137
|
+
def x(self, t, wl) -> u.Quantity:
|
138
|
+
r = self.r(t, wl)
|
139
|
+
if self.r(t, wl).unit == u.au:
|
140
|
+
r = (r.to(u.au) / self.dist(t, wl).to(u.pc)).value * 1e3 * u.mas
|
141
|
+
return r * np.sin(self.phi(t, wl).to(u.rad))
|
142
|
+
|
143
|
+
def y(self, t, wl) -> u.Quantity:
|
144
|
+
r = self.r(t, wl)
|
145
|
+
if self.r(t, wl).unit == u.au:
|
146
|
+
r = (r.to(u.au) / self.dist(t, wl).to(u.pc)).value * 1e3 * u.mas
|
147
|
+
return r * np.cos(self.phi(t, wl).to(u.rad))
|
148
|
+
|
149
|
+
@property
|
150
|
+
def asymmetric(self) -> bool:
|
151
|
+
"""Gets if the component is asymmetric."""
|
152
|
+
return self._asymmetric
|
153
|
+
|
154
|
+
@asymmetric.setter
|
155
|
+
def asymmetric(self, value: bool) -> None:
|
156
|
+
"""Sets the position angle and the parameters to free or false
|
157
|
+
if asymmetry is set."""
|
158
|
+
self._asymmetric = value
|
159
|
+
for i in range(1, self.modulation.value + 1):
|
160
|
+
getattr(self, f"rho{i}").free = value
|
161
|
+
getattr(self, f"theta{i}").free = value
|
162
|
+
|
163
|
+
def compute_internal_grid(self) -> Tuple[u.Quantity[u.au], u.Quantity[u.au]]:
|
164
|
+
"""Calculates the model grid.
|
165
|
+
|
166
|
+
Parameters
|
167
|
+
----------
|
168
|
+
|
169
|
+
Returns
|
170
|
+
-------
|
171
|
+
xx : astropy.units.au
|
172
|
+
The x-coordinate grid.
|
173
|
+
yy : astropy.units.au
|
174
|
+
The y-coordinate grid.
|
175
|
+
"""
|
176
|
+
return np.array([]) * u.au, np.array([]) * u.au
|
177
|
+
|
178
|
+
def vis_func(self, spf: 1 / u.rad, psi: u.rad, wl: u.um, **kwargs) -> np.ndarray:
|
179
|
+
"""Computes the correlated fluxes."""
|
180
|
+
return np.array([]).astype(OPTIONS.data.dtype.complex)
|
181
|
+
|
182
|
+
def compute_complex_vis(
|
183
|
+
self, ucoord: u.m, vcoord: u.m, t: int, wl: u.um, **kwargs
|
184
|
+
) -> np.ndarray:
|
185
|
+
"""Computes the correlated fluxes."""
|
186
|
+
ut, vt = transform_coordinates(
|
187
|
+
ucoord, vcoord, self.cinc(t, wl), self.pa(t, wl).to(u.rad)
|
188
|
+
)
|
189
|
+
wl = wl.reshape(-1, 1)
|
190
|
+
utb = (ut / wl.to(u.m)).value[..., np.newaxis] / u.rad
|
191
|
+
vtb = (vt / wl.to(u.m)).value[..., np.newaxis] / u.rad
|
192
|
+
spf, psi = np.hypot(utb, vtb), np.arctan2(utb, vtb)
|
193
|
+
|
194
|
+
shift = translate_vis(
|
195
|
+
utb.value,
|
196
|
+
vtb.value,
|
197
|
+
self.x(t, wl).to(u.rad).value,
|
198
|
+
self.y(t, wl).to(u.rad).value,
|
199
|
+
)
|
200
|
+
shift = shift.reshape(shift.shape[:-1]) if shift.shape[-1] == 1 else shift
|
201
|
+
vis = self.vis_func(spf, psi, t, wl, **kwargs)
|
202
|
+
vis = vis.reshape(vis.shape[:-1]) if vis.shape[-1] == 1 else vis
|
203
|
+
return (self.fr(t, wl).value * vis * shift).astype(OPTIONS.data.dtype.complex)
|
204
|
+
|
205
|
+
def image_func(
|
206
|
+
self, xx: u.mas, yy: u.mas, pixel_size: u.mas, t: int, wl: u.um
|
207
|
+
) -> np.ndarray:
|
208
|
+
"""Calculates the image."""
|
209
|
+
return np.array([]).astype(OPTIONS.data.dtype.real)
|
210
|
+
|
211
|
+
def compute_image(
|
212
|
+
self, dim: int, pixel_size: u.mas, t: int, wl: u.um
|
213
|
+
) -> np.ndarray:
|
214
|
+
"""Computes the image."""
|
215
|
+
wl = wl[np.newaxis, np.newaxis]
|
216
|
+
xx = np.linspace(-0.5, 0.5, dim, endpoint=False) * pixel_size * dim
|
217
|
+
xxt, yyt = transform_coordinates(
|
218
|
+
*np.meshgrid(xx, xx, sparse=True),
|
219
|
+
self.cinc(t, wl),
|
220
|
+
self.pa(t, wl).to(u.rad),
|
221
|
+
axis="x",
|
222
|
+
)
|
223
|
+
xxs, yys = translate_image(xxt, yyt, self.x(t, wl), self.y(t, wl))
|
224
|
+
image = self.image_func(xxs, yys, pixel_size, t, wl)
|
225
|
+
return (self.fr(t, wl) * image).value.astype(OPTIONS.data.dtype.real)
|