metablate 0.3.0__tar.gz → 0.4.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.
- {metablate-0.3.0 → metablate-0.4.0}/PKG-INFO +3 -1
- {metablate-0.3.0 → metablate-0.4.0}/pyproject.toml +3 -2
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/__init__.py +3 -5
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/atmosphere/atm_msise00.py +20 -13
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/atmosphere/atm_pymsis.py +36 -31
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/atmosphere/atmosphere.py +12 -13
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/material.py +32 -8
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/alpha_beta_2026.py +73 -59
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/dimant_oppenheim_2017.py +60 -39
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/kero_szasz_2008.py +16 -14
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/model.py +1 -1
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/simple_ablation_2026.py +60 -141
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/simple_motion.py +9 -8
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/stulov_mirskii_vislyi_1995.py +12 -11
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/aerodynamics.py +20 -11
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/alpha_beta.py +200 -140
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/ballistic.py +60 -30
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/luminosity.py +3 -1
- metablate-0.4.0/src/metablate/physics/plasma_production.py +281 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/spheres.py +4 -2
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/sputtering.py +8 -3
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/thermal_ablation.py +49 -50
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/types.py +30 -1
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate.egg-info/PKG-INFO +3 -1
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate.egg-info/SOURCES.txt +2 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate.egg-info/requires.txt +2 -0
- metablate-0.4.0/tests/test_ballistic.py +26 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_models.py +10 -1
- metablate-0.4.0/tests/test_plasma_production.py +25 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_sputtering.py +1 -1
- metablate-0.3.0/src/metablate/physics/plasma_production.py +0 -109
- {metablate-0.3.0 → metablate-0.4.0}/LICENSE +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/README.md +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/setup.cfg +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/atmosphere/__init__.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/models/__init__.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/physics/__init__.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/py.typed +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate/version.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate.egg-info/dependency_links.txt +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/src/metablate.egg-info/top_level.txt +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_aerodynamics.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_alpha_beta.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_atmosphere.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_dimant_oppenheim.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_dimant_oppenheim_paper.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_dimant_oppenheim_points.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_import.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_material.py +0 -0
- {metablate-0.3.0 → metablate-0.4.0}/tests/test_thermal_ablation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: metablate
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A collection of meteor ablation models
|
|
5
5
|
Author-email: Daniel Kastinen <daniel.kastinen@irf.se>, Johan Kero <kero@irf.se>
|
|
6
6
|
Maintainer-email: Daniel Kastinen <daniel.kastinen@irf.se>
|
|
@@ -702,6 +702,7 @@ Requires-Dist: xarray
|
|
|
702
702
|
Requires-Dist: spacecoords>0.2
|
|
703
703
|
Provides-Extra: develop
|
|
704
704
|
Requires-Dist: pytest; extra == "develop"
|
|
705
|
+
Requires-Dist: mypy; extra == "develop"
|
|
705
706
|
Requires-Dist: flake8; extra == "develop"
|
|
706
707
|
Requires-Dist: wheel; extra == "develop"
|
|
707
708
|
Requires-Dist: build; extra == "develop"
|
|
@@ -724,6 +725,7 @@ Requires-Dist: mkdocs-section-index; extra == "develop"
|
|
|
724
725
|
Provides-Extra: tests
|
|
725
726
|
Requires-Dist: pytest; extra == "tests"
|
|
726
727
|
Requires-Dist: pytest-cov; extra == "tests"
|
|
728
|
+
Requires-Dist: mypy; extra == "tests"
|
|
727
729
|
Provides-Extra: visualization
|
|
728
730
|
Requires-Dist: pyvista; extra == "visualization"
|
|
729
731
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "metablate"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "A collection of meteor ablation models"
|
|
9
9
|
readme = {file = "README.md", content-type = "text/markdown"}
|
|
10
10
|
authors = [
|
|
@@ -45,6 +45,7 @@ where = ["src"]
|
|
|
45
45
|
[project.optional-dependencies]
|
|
46
46
|
develop = [
|
|
47
47
|
"pytest",
|
|
48
|
+
"mypy",
|
|
48
49
|
"flake8",
|
|
49
50
|
"wheel",
|
|
50
51
|
"build",
|
|
@@ -65,7 +66,7 @@ develop = [
|
|
|
65
66
|
"mkdocs-literate-nav",
|
|
66
67
|
"mkdocs-section-index",
|
|
67
68
|
]
|
|
68
|
-
tests = ["pytest", "pytest-cov"]
|
|
69
|
+
tests = ["pytest", "pytest-cov", "mypy"]
|
|
69
70
|
visualization = ["pyvista"]
|
|
70
71
|
|
|
71
72
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
2
|
# Meteoroid Ablation Models
|
|
3
3
|
"""
|
|
4
|
+
from __future__ import annotations
|
|
4
5
|
|
|
5
6
|
import logging
|
|
6
7
|
import sys
|
|
7
8
|
|
|
8
|
-
from . import physics
|
|
9
|
-
from . import models
|
|
10
|
-
from . import material
|
|
11
|
-
from . import atmosphere
|
|
9
|
+
from . import atmosphere, material, models, physics
|
|
12
10
|
from .atmosphere import Atmosphere
|
|
13
11
|
from .version import __version__
|
|
14
12
|
|
|
15
13
|
|
|
16
|
-
def setup_logging(level=logging.INFO):
|
|
14
|
+
def setup_logging(level: int | str = logging.INFO) -> logging.Logger:
|
|
17
15
|
logger = logging.getLogger("metablate")
|
|
18
16
|
logger.setLevel(level)
|
|
19
17
|
|
|
@@ -1,37 +1,44 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
|
|
3
1
|
"""msis00
|
|
4
2
|
|
|
5
3
|
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
from types import ModuleType
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
6
11
|
import scipy.constants
|
|
7
12
|
import xarray as xr
|
|
8
|
-
from .atmosphere import Atmosphere
|
|
9
|
-
from ..types import NDArray_N
|
|
10
13
|
|
|
14
|
+
from ..types import ArrayLike
|
|
15
|
+
from .atmosphere import Atmosphere
|
|
11
16
|
|
|
17
|
+
msise00: ModuleType | None
|
|
12
18
|
try:
|
|
13
|
-
|
|
19
|
+
msise00 = import_module("msise00")
|
|
14
20
|
except ImportError:
|
|
15
21
|
msise00 = None
|
|
16
22
|
|
|
17
23
|
|
|
18
24
|
class AtmMSISE00(Atmosphere):
|
|
19
|
-
|
|
25
|
+
|
|
26
|
+
def __init__(self) -> None:
|
|
20
27
|
super().__init__(
|
|
21
28
|
supported_species=["He", "O", "N2", "O2", "Ar", "H", "N"],
|
|
22
29
|
)
|
|
23
30
|
|
|
24
31
|
def density(
|
|
25
32
|
self,
|
|
26
|
-
time:
|
|
27
|
-
lat:
|
|
28
|
-
lon:
|
|
29
|
-
alt:
|
|
33
|
+
time: ArrayLike,
|
|
34
|
+
lat: ArrayLike,
|
|
35
|
+
lon: ArrayLike,
|
|
36
|
+
alt: ArrayLike,
|
|
30
37
|
f107: float = 80.0,
|
|
31
38
|
f107s: float = 80.0,
|
|
32
39
|
Ap: float = 4.0,
|
|
33
40
|
mass_densities: bool = False,
|
|
34
|
-
**kwargs
|
|
41
|
+
**kwargs: Any,
|
|
35
42
|
) -> xr.Dataset:
|
|
36
43
|
"""TODO: Write docstring
|
|
37
44
|
|
|
@@ -43,7 +50,7 @@ class AtmMSISE00(Atmosphere):
|
|
|
43
50
|
|
|
44
51
|
result = msise00.run(
|
|
45
52
|
time=time,
|
|
46
|
-
altkm=alt * 1e-3,
|
|
53
|
+
altkm=np.asarray(alt) * 1e-3,
|
|
47
54
|
glat=lat,
|
|
48
55
|
glon=lon,
|
|
49
56
|
indices=dict(
|
|
@@ -51,7 +58,7 @@ class AtmMSISE00(Atmosphere):
|
|
|
51
58
|
f107s=f107s,
|
|
52
59
|
Ap=Ap,
|
|
53
60
|
),
|
|
54
|
-
**kwargs
|
|
61
|
+
**kwargs,
|
|
55
62
|
)
|
|
56
63
|
result["alt_km"] = result["alt_km"]*1e3
|
|
57
64
|
result = result.rename({
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
|
|
3
1
|
"""pymsis
|
|
4
2
|
|
|
5
3
|
"""
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
from
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
9
|
import numpy as np
|
|
10
|
-
import
|
|
10
|
+
import scipy.constants
|
|
11
11
|
import xarray as xr
|
|
12
12
|
|
|
13
|
+
from ..types import ArrayLike, AtmosphereData
|
|
14
|
+
from .atmosphere import Atmosphere
|
|
15
|
+
|
|
16
|
+
msis: ModuleType | None
|
|
13
17
|
try:
|
|
14
18
|
from pymsis import msis
|
|
15
19
|
except ImportError:
|
|
@@ -28,25 +32,25 @@ class AtmPymsis(Atmosphere):
|
|
|
28
32
|
Anomalous oxygen # density (m-3), | NO # density (m-3),
|
|
29
33
|
| Temperature (K)]
|
|
30
34
|
"""
|
|
31
|
-
def __init__(self):
|
|
35
|
+
def __init__(self) -> None:
|
|
32
36
|
super().__init__(
|
|
33
37
|
supported_species=["He", "O", "N2", "O2", "Ar", "H", "N"],
|
|
34
38
|
)
|
|
35
39
|
|
|
36
40
|
def density(
|
|
37
41
|
self,
|
|
38
|
-
time:
|
|
39
|
-
lat:
|
|
40
|
-
lon:
|
|
41
|
-
alt:
|
|
42
|
-
f107:
|
|
43
|
-
f107s:
|
|
44
|
-
aps:
|
|
42
|
+
time: ArrayLike,
|
|
43
|
+
lat: ArrayLike,
|
|
44
|
+
lon: ArrayLike,
|
|
45
|
+
alt: ArrayLike,
|
|
46
|
+
f107: ArrayLike | None = None,
|
|
47
|
+
f107s: ArrayLike | None = None,
|
|
48
|
+
aps: ArrayLike | None = None,
|
|
45
49
|
mass_densities: bool = False,
|
|
46
|
-
version:
|
|
50
|
+
version: float | str = 2.1,
|
|
47
51
|
as_dataset: bool = True,
|
|
48
|
-
**kwargs
|
|
49
|
-
) ->
|
|
52
|
+
**kwargs: Any,
|
|
53
|
+
) -> AtmosphereData:
|
|
50
54
|
"""Return species number densities [m^-3], or mass densities [kg/m^3].
|
|
51
55
|
|
|
52
56
|
Set ``as_dataset=False`` in an ablation model's ``atmosphere_kwargs``
|
|
@@ -66,7 +70,7 @@ class AtmPymsis(Atmosphere):
|
|
|
66
70
|
if len(inp.shape) == 0:
|
|
67
71
|
inp.shape = (1,)
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
data = msis.run(
|
|
70
74
|
dates=time,
|
|
71
75
|
lons=lon,
|
|
72
76
|
lats=lat,
|
|
@@ -77,22 +81,23 @@ class AtmPymsis(Atmosphere):
|
|
|
77
81
|
version=version,
|
|
78
82
|
**kwargs,
|
|
79
83
|
)
|
|
80
|
-
if len(
|
|
81
|
-
|
|
84
|
+
if len(data.shape) == 2:
|
|
85
|
+
data = data.reshape(data.shape[0], 1, 1, 1, data.shape[1])
|
|
82
86
|
|
|
83
87
|
variables = {
|
|
84
|
-
"Total": (["time", "lon", "lat", "alt"],
|
|
85
|
-
"N2": (["time", "lon", "lat", "alt"],
|
|
86
|
-
"O2": (["time", "lon", "lat", "alt"],
|
|
87
|
-
"O": (["time", "lon", "lat", "alt"],
|
|
88
|
-
"He": (["time", "lon", "lat", "alt"],
|
|
89
|
-
"H": (["time", "lon", "lat", "alt"],
|
|
90
|
-
"Ar": (["time", "lon", "lat", "alt"],
|
|
91
|
-
"N": (["time", "lon", "lat", "alt"],
|
|
92
|
-
"AnomalousO": (["time", "lon", "lat", "alt"],
|
|
93
|
-
"NO": (["time", "lon", "lat", "alt"],
|
|
94
|
-
"Temperature": (["time", "lon", "lat", "alt"],
|
|
88
|
+
"Total": (["time", "lon", "lat", "alt"], data[:, :, :, :, 0]),
|
|
89
|
+
"N2": (["time", "lon", "lat", "alt"], data[:, :, :, :, 1]),
|
|
90
|
+
"O2": (["time", "lon", "lat", "alt"], data[:, :, :, :, 2]),
|
|
91
|
+
"O": (["time", "lon", "lat", "alt"], data[:, :, :, :, 3]),
|
|
92
|
+
"He": (["time", "lon", "lat", "alt"], data[:, :, :, :, 4]),
|
|
93
|
+
"H": (["time", "lon", "lat", "alt"], data[:, :, :, :, 5]),
|
|
94
|
+
"Ar": (["time", "lon", "lat", "alt"], data[:, :, :, :, 6]),
|
|
95
|
+
"N": (["time", "lon", "lat", "alt"], data[:, :, :, :, 7]),
|
|
96
|
+
"AnomalousO": (["time", "lon", "lat", "alt"], data[:, :, :, :, 8]),
|
|
97
|
+
"NO": (["time", "lon", "lat", "alt"], data[:, :, :, :, 9]),
|
|
98
|
+
"Temperature": (["time", "lon", "lat", "alt"], data[:, :, :, :, 10]),
|
|
95
99
|
}
|
|
100
|
+
result: AtmosphereData
|
|
96
101
|
if as_dataset:
|
|
97
102
|
result = xr.Dataset(
|
|
98
103
|
variables,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
3
|
import logging
|
|
4
|
+
from abc import abstractmethod
|
|
4
5
|
from copy import copy
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Any
|
|
5
8
|
|
|
6
|
-
import scipy.constants as constants
|
|
7
9
|
import numpy as np
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
from scipy import constants
|
|
11
|
+
|
|
12
|
+
from ..types import ArrayLike, AtmosphereData
|
|
10
13
|
|
|
11
14
|
logger = logging.getLogger(__name__)
|
|
12
15
|
|
|
@@ -59,7 +62,8 @@ SPECIES = {
|
|
|
59
62
|
|
|
60
63
|
# TODO: get a a hold of other atm models to figure out how to generalize interface better
|
|
61
64
|
class Atmosphere:
|
|
62
|
-
|
|
65
|
+
|
|
66
|
+
def __init__(self, supported_species: list[Species] | list[str]) -> None:
|
|
63
67
|
self.species = {}
|
|
64
68
|
for s in supported_species:
|
|
65
69
|
if isinstance(s, Species):
|
|
@@ -71,11 +75,6 @@ class Atmosphere:
|
|
|
71
75
|
|
|
72
76
|
@abstractmethod
|
|
73
77
|
def density(
|
|
74
|
-
self,
|
|
75
|
-
|
|
76
|
-
lat: NDArray_N,
|
|
77
|
-
lon: NDArray_N,
|
|
78
|
-
alt: NDArray_N,
|
|
79
|
-
**kwargs
|
|
80
|
-
) -> xr.Dataset:
|
|
78
|
+
self, time: ArrayLike, lat: ArrayLike, lon: ArrayLike, alt: ArrayLike, **kwargs: Any
|
|
79
|
+
) -> AtmosphereData:
|
|
81
80
|
pass
|
|
@@ -10,16 +10,24 @@ liberated from the meteoroid by thermal ablation were released
|
|
|
10
10
|
in complete molecular form.
|
|
11
11
|
|
|
12
12
|
"""
|
|
13
|
+
from __future__ import annotations
|
|
13
14
|
|
|
14
|
-
from dataclasses import asdict
|
|
15
15
|
import logging
|
|
16
|
-
from copy import
|
|
16
|
+
from copy import deepcopy
|
|
17
|
+
from dataclasses import asdict
|
|
18
|
+
from typing import Literal, overload
|
|
19
|
+
|
|
17
20
|
from scipy import constants
|
|
18
|
-
|
|
21
|
+
|
|
22
|
+
from .types import Material, MaterialDict
|
|
19
23
|
|
|
20
24
|
logger = logging.getLogger(__name__)
|
|
21
25
|
|
|
22
|
-
MATERIALS = {}
|
|
26
|
+
MATERIALS: dict[str, Material] = {}
|
|
27
|
+
|
|
28
|
+
# Jones (1997), Table 1: abundances by atom number. As in the original
|
|
29
|
+
# ionization model
|
|
30
|
+
JONES_COMPOSITION = {"O": 0.617, "Fe": 0.059, "Mg": 0.082, "Si": 0.242}
|
|
23
31
|
|
|
24
32
|
iron = Material(
|
|
25
33
|
bulk_density=7800.0,
|
|
@@ -32,6 +40,7 @@ iron = Material(
|
|
|
32
40
|
penetration_correction_parameter_K=0.35,
|
|
33
41
|
clausius_clapeyron_coeff_A=10.6,
|
|
34
42
|
clausius_clapeyron_coeff_B=16120.0,
|
|
43
|
+
atomic_composition={"Fe": 1.0},
|
|
35
44
|
)
|
|
36
45
|
MATERIALS["iron"] = iron
|
|
37
46
|
|
|
@@ -46,6 +55,7 @@ asteroidal = Material(
|
|
|
46
55
|
penetration_correction_parameter_K=0.1,
|
|
47
56
|
clausius_clapeyron_coeff_A=10.6,
|
|
48
57
|
clausius_clapeyron_coeff_B=13500.0,
|
|
58
|
+
atomic_composition={}, # TODO: what should this be?
|
|
49
59
|
)
|
|
50
60
|
MATERIALS["asteroidal"] = asteroidal
|
|
51
61
|
|
|
@@ -60,6 +70,7 @@ cometary = Material(
|
|
|
60
70
|
penetration_correction_parameter_K=0.65,
|
|
61
71
|
clausius_clapeyron_coeff_A=10.6,
|
|
62
72
|
clausius_clapeyron_coeff_B=13500.0,
|
|
73
|
+
atomic_composition=JONES_COMPOSITION.copy(),
|
|
63
74
|
)
|
|
64
75
|
MATERIALS["cometary"] = cometary
|
|
65
76
|
|
|
@@ -74,16 +85,29 @@ porous = Material(
|
|
|
74
85
|
penetration_correction_parameter_K=0.65,
|
|
75
86
|
clausius_clapeyron_coeff_A=10.6,
|
|
76
87
|
clausius_clapeyron_coeff_B=13500.0,
|
|
88
|
+
atomic_composition=JONES_COMPOSITION.copy(),
|
|
77
89
|
)
|
|
78
90
|
MATERIALS["porous"] = porous
|
|
79
91
|
|
|
80
92
|
|
|
81
|
-
def available():
|
|
93
|
+
def available() -> list[str]:
|
|
82
94
|
"""List currently available meteoroid materials."""
|
|
83
95
|
return list(MATERIALS.keys())
|
|
84
96
|
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
@overload
|
|
99
|
+
def get(material: str, as_dict: Literal[True] = True) -> MaterialDict: ...
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@overload
|
|
103
|
+
def get(material: str, as_dict: Literal[False]) -> Material: ...
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@overload
|
|
107
|
+
def get(material: str, as_dict: bool) -> Material | MaterialDict: ...
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get(material: str, as_dict: bool = True) -> Material | MaterialDict:
|
|
87
111
|
"""Returns the physical parameters of the meteoroid based on its material.
|
|
88
112
|
|
|
89
113
|
Parameters
|
|
@@ -96,7 +120,7 @@ def get(material, as_dict=True):
|
|
|
96
120
|
Returns
|
|
97
121
|
-------
|
|
98
122
|
Material or dict
|
|
99
|
-
|
|
123
|
+
Material properties, including the optional atomic composition.
|
|
100
124
|
"""
|
|
101
125
|
|
|
102
126
|
_material = material.lower().strip()
|
|
@@ -107,4 +131,4 @@ def get(material, as_dict=True):
|
|
|
107
131
|
if as_dict:
|
|
108
132
|
return asdict(MATERIALS[_material])
|
|
109
133
|
else:
|
|
110
|
-
return
|
|
134
|
+
return deepcopy(MATERIALS[_material])
|
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import time
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
|
-
from typing import Any
|
|
9
8
|
|
|
10
9
|
import numpy as np
|
|
11
10
|
import scipy.interpolate as sci
|
|
@@ -16,10 +15,15 @@ from scipy.integrate import solve_ivp
|
|
|
16
15
|
from .. import physics
|
|
17
16
|
from ..atmosphere import Atmosphere
|
|
18
17
|
from ..types import (
|
|
18
|
+
Bounds,
|
|
19
|
+
Kwargs,
|
|
20
|
+
NDArray,
|
|
19
21
|
NDArray_2xN,
|
|
20
22
|
NDArray_N,
|
|
23
|
+
Numeric,
|
|
21
24
|
Options,
|
|
22
25
|
Results,
|
|
26
|
+
StateVector,
|
|
23
27
|
)
|
|
24
28
|
from .model import MeteorModel
|
|
25
29
|
|
|
@@ -34,9 +38,9 @@ class AlphaBetaOptions(Options):
|
|
|
34
38
|
max_time: float = 100.0 # [s]
|
|
35
39
|
target_normalized_velocity: float | None = None # [1]
|
|
36
40
|
method: str = "RK45"
|
|
37
|
-
method_kwargs:
|
|
41
|
+
method_kwargs: Kwargs = field(default_factory=dict)
|
|
38
42
|
atmosphere: Atmosphere | None = None
|
|
39
|
-
atmosphere_kwargs:
|
|
43
|
+
atmosphere_kwargs: Kwargs = field(default_factory=dict)
|
|
40
44
|
atmospheric_scale_height: float = 7610.0
|
|
41
45
|
|
|
42
46
|
|
|
@@ -62,24 +66,24 @@ class AlphaBetaResults(Results):
|
|
|
62
66
|
|
|
63
67
|
|
|
64
68
|
def dmdt(
|
|
65
|
-
v:
|
|
66
|
-
a:
|
|
67
|
-
beta:
|
|
68
|
-
mu:
|
|
69
|
-
) ->
|
|
69
|
+
v: Numeric,
|
|
70
|
+
a: Numeric,
|
|
71
|
+
beta: Numeric,
|
|
72
|
+
mu: Numeric,
|
|
73
|
+
) -> Numeric:
|
|
70
74
|
return 2 * beta * v * a / (1 - mu) * np.exp(-beta * (1 - v**2) / (1 - mu))
|
|
71
75
|
|
|
72
76
|
|
|
73
77
|
def diff_eq_rhs(
|
|
74
78
|
t: float,
|
|
75
|
-
params:
|
|
79
|
+
params: StateVector,
|
|
76
80
|
alpha: float,
|
|
77
81
|
beta: float,
|
|
78
82
|
gamma: float,
|
|
79
83
|
entry_velocity: float,
|
|
80
84
|
atmospheric_scale_height: float,
|
|
81
85
|
) -> NDArray_N | NDArray_2xN:
|
|
82
|
-
|
|
86
|
+
_, v = params
|
|
83
87
|
delta = scs.expi(beta) - scs.expi(beta * v**2)
|
|
84
88
|
coef = entry_velocity / atmospheric_scale_height
|
|
85
89
|
return np.array(
|
|
@@ -91,7 +95,8 @@ def diff_eq_rhs(
|
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
class AlphaBeta2026(MeteorModel[AlphaBetaInitialState, AlphaBetaOptions, AlphaBetaResults]):
|
|
94
|
-
|
|
98
|
+
|
|
99
|
+
def __init__(self, options: AlphaBetaOptions = AlphaBetaOptions()) -> None:
|
|
95
100
|
self.options = options
|
|
96
101
|
if options.atmosphere is not None:
|
|
97
102
|
raise NotImplementedError(
|
|
@@ -133,12 +138,12 @@ class AlphaBeta2026(MeteorModel[AlphaBetaInitialState, AlphaBetaOptions, AlphaBe
|
|
|
133
138
|
s0 = 0
|
|
134
139
|
y0 = np.array([s0, v0_norm])
|
|
135
140
|
|
|
136
|
-
def _low_velocity(t: float, y: NDArray_N, *args:
|
|
141
|
+
def _low_velocity(t: float, y: NDArray_N, *args: object) -> float:
|
|
137
142
|
res = y[1] / v_final_norm - 1
|
|
138
143
|
return res
|
|
139
144
|
|
|
140
|
-
_low_velocity.terminal = True # type: ignore
|
|
141
|
-
_low_velocity.direction = -1 # type: ignore
|
|
145
|
+
_low_velocity.terminal = True # type: ignore[attr-defined]
|
|
146
|
+
_low_velocity.direction = -1 # type: ignore[attr-defined]
|
|
142
147
|
|
|
143
148
|
events = [_low_velocity]
|
|
144
149
|
|
|
@@ -179,40 +184,46 @@ class AlphaBeta2026(MeteorModel[AlphaBetaInitialState, AlphaBetaOptions, AlphaBe
|
|
|
179
184
|
t=ivp_result.t,
|
|
180
185
|
distance=s_vals,
|
|
181
186
|
velocity=v_norm * parameters.entry_velocity,
|
|
182
|
-
height=
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
height=np.asarray(
|
|
188
|
+
physics.alpha_beta.height_direct(
|
|
189
|
+
v_norm,
|
|
190
|
+
self.options.atmospheric_scale_height,
|
|
191
|
+
parameters.alpha,
|
|
192
|
+
parameters.beta,
|
|
193
|
+
)
|
|
187
194
|
),
|
|
188
|
-
relative_mass=
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
relative_mass=np.asarray(
|
|
196
|
+
physics.alpha_beta.norm_mass_direct(
|
|
197
|
+
v_norm,
|
|
198
|
+
parameters.beta,
|
|
199
|
+
parameters.shape_change_coefficient,
|
|
200
|
+
)
|
|
192
201
|
),
|
|
193
|
-
massloss=
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
202
|
+
massloss=np.asarray(
|
|
203
|
+
dmdt(
|
|
204
|
+
v_norm,
|
|
205
|
+
diffs[1, :],
|
|
206
|
+
parameters.beta,
|
|
207
|
+
parameters.shape_change_coefficient,
|
|
208
|
+
)
|
|
198
209
|
),
|
|
199
210
|
)
|
|
200
211
|
|
|
201
212
|
|
|
202
213
|
def logposterior_alpha_beta(
|
|
203
|
-
alpha,
|
|
204
|
-
beta,
|
|
205
|
-
heights,
|
|
206
|
-
heights_std,
|
|
207
|
-
velocities,
|
|
208
|
-
velocities_std,
|
|
209
|
-
t_eval,
|
|
210
|
-
initial_height,
|
|
211
|
-
entry_velocity,
|
|
212
|
-
entry_angle,
|
|
213
|
-
shape_change_coefficient,
|
|
214
|
-
model,
|
|
215
|
-
):
|
|
214
|
+
alpha: float,
|
|
215
|
+
beta: float,
|
|
216
|
+
heights: NDArray_N,
|
|
217
|
+
heights_std: Numeric,
|
|
218
|
+
velocities: NDArray_N,
|
|
219
|
+
velocities_std: Numeric,
|
|
220
|
+
t_eval: NDArray_N,
|
|
221
|
+
initial_height: float,
|
|
222
|
+
entry_velocity: float,
|
|
223
|
+
entry_angle: float,
|
|
224
|
+
shape_change_coefficient: float,
|
|
225
|
+
model: AlphaBeta2026,
|
|
226
|
+
) -> float:
|
|
216
227
|
p = AlphaBetaInitialState(
|
|
217
228
|
epoch=None,
|
|
218
229
|
alpha=alpha,
|
|
@@ -243,19 +254,19 @@ def logposterior_alpha_beta(
|
|
|
243
254
|
|
|
244
255
|
|
|
245
256
|
def solve_alpha_beta_posterior(
|
|
246
|
-
t,
|
|
247
|
-
heights,
|
|
248
|
-
heights_std,
|
|
249
|
-
velocities,
|
|
250
|
-
velocities_std,
|
|
251
|
-
entry_angle,
|
|
252
|
-
shape_change_coefficient,
|
|
253
|
-
entry_velocity=None,
|
|
254
|
-
options=None,
|
|
255
|
-
start=None,
|
|
256
|
-
bounds=((0.01, 1e8), (0.5e-1, 200.0), (0.0, None)),
|
|
257
|
-
minimize_kwargs={},
|
|
258
|
-
):
|
|
257
|
+
t: NDArray_N,
|
|
258
|
+
heights: NDArray_N,
|
|
259
|
+
heights_std: Numeric,
|
|
260
|
+
velocities: NDArray_N,
|
|
261
|
+
velocities_std: Numeric,
|
|
262
|
+
entry_angle: float,
|
|
263
|
+
shape_change_coefficient: float,
|
|
264
|
+
entry_velocity: float | None = None,
|
|
265
|
+
options: AlphaBetaOptions | None = None,
|
|
266
|
+
start: NDArray | list[float | None] | None = None,
|
|
267
|
+
bounds: Bounds = ((0.01, 1e8), (0.5e-1, 200.0), (0.0, None)),
|
|
268
|
+
minimize_kwargs: Kwargs = {},
|
|
269
|
+
) -> sco.OptimizeResult:
|
|
259
270
|
"""Solve for alpha and beta (and optionally velocity) using minimization of the posterior [^1].
|
|
260
271
|
|
|
261
272
|
[^1]: Kastinen et al (2026) in preparation
|
|
@@ -272,10 +283,12 @@ def solve_alpha_beta_posterior(
|
|
|
272
283
|
if start is None:
|
|
273
284
|
start = [a0, b0, v0]
|
|
274
285
|
else:
|
|
275
|
-
|
|
276
|
-
|
|
286
|
+
beta_start = start[1]
|
|
287
|
+
if beta_start is None:
|
|
288
|
+
beta_start = b0
|
|
289
|
+
start[1] = beta_start
|
|
277
290
|
if start[0] is None:
|
|
278
|
-
start[0] = np.exp(Yvalues[-1]) / (2.0 *
|
|
291
|
+
start[0] = np.exp(Yvalues[-1]) / (2.0 * beta_start)
|
|
279
292
|
if start[2] is None:
|
|
280
293
|
start[2] = v0
|
|
281
294
|
|
|
@@ -286,7 +299,7 @@ def solve_alpha_beta_posterior(
|
|
|
286
299
|
start = start[:2]
|
|
287
300
|
bounds = (bounds[0], bounds[1])
|
|
288
301
|
|
|
289
|
-
def wrap_func(x):
|
|
302
|
+
def wrap_func(x: NDArray_N) -> float:
|
|
290
303
|
return -logposterior_alpha_beta(
|
|
291
304
|
x[0],
|
|
292
305
|
x[1],
|
|
@@ -301,9 +314,10 @@ def solve_alpha_beta_posterior(
|
|
|
301
314
|
shape_change_coefficient,
|
|
302
315
|
model,
|
|
303
316
|
)
|
|
317
|
+
|
|
304
318
|
else:
|
|
305
319
|
|
|
306
|
-
def wrap_func(x):
|
|
320
|
+
def wrap_func(x: NDArray_N) -> float:
|
|
307
321
|
return -logposterior_alpha_beta(
|
|
308
322
|
x[0],
|
|
309
323
|
x[1],
|