metablate 0.1.0__py3-none-any.whl
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/__init__.py +16 -0
- metablate/ablation_model.py +40 -0
- metablate/atmosphere/__init__.py +8 -0
- metablate/atmosphere/atm_msise00.py +67 -0
- metablate/atmosphere/atm_pymsis.py +114 -0
- metablate/atmosphere/atmosphere.py +80 -0
- metablate/coordinates.py +149 -0
- metablate/material.py +155 -0
- metablate/models/__init__.py +5 -0
- metablate/models/dimant_oppenheim_2017.py +293 -0
- metablate/models/kero_szasz_2008.py +348 -0
- metablate/ode.py +82 -0
- metablate/physics/__init__.py +5 -0
- metablate/physics/alpha_beta.py +679 -0
- metablate/physics/ballistic.py +104 -0
- metablate/physics/luminosity.py +78 -0
- metablate/physics/sputtering.py +219 -0
- metablate/physics/thermal_ablation.py +616 -0
- metablate/py.typed +0 -0
- metablate/version.py +3 -0
- metablate-0.1.0.dist-info/METADATA +766 -0
- metablate-0.1.0.dist-info/RECORD +25 -0
- metablate-0.1.0.dist-info/WHEEL +5 -0
- metablate-0.1.0.dist-info/licenses/LICENSE +674 -0
- metablate-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import scipy.integrate as sci
|
|
3
|
+
import scipy.optimize as sco
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def velocity(
|
|
7
|
+
heights,
|
|
8
|
+
atm_mass_density,
|
|
9
|
+
ballistic_coefficient,
|
|
10
|
+
radiant_local_elevation,
|
|
11
|
+
initial_velocity=None,
|
|
12
|
+
degrees=False,
|
|
13
|
+
):
|
|
14
|
+
"""Velocity function for ballistic coefficient solution of trajectory
|
|
15
|
+
|
|
16
|
+
$$
|
|
17
|
+
c_b = \\frac{M_e}{c_d S_e}
|
|
18
|
+
$$
|
|
19
|
+
|
|
20
|
+
where $M_e$ is entry mass, $S_e$ is cross-sectional area and $c_d$ is
|
|
21
|
+
aerodynamic drag coefficient.
|
|
22
|
+
|
|
23
|
+
Notes
|
|
24
|
+
-----
|
|
25
|
+
Assumes atmospheric mass densities are sorted according to time
|
|
26
|
+
"""
|
|
27
|
+
el = np.radians(radiant_local_elevation) if degrees else radiant_local_elevation
|
|
28
|
+
alpha = 0.5 / (ballistic_coefficient * np.sin(el))
|
|
29
|
+
|
|
30
|
+
int_atm_density = sci.cumulative_trapezoid(atm_mass_density, heights)
|
|
31
|
+
v = np.exp(alpha * int_atm_density)
|
|
32
|
+
v = np.insert(v, 0, 1.0)
|
|
33
|
+
|
|
34
|
+
if initial_velocity is not None:
|
|
35
|
+
v *= initial_velocity
|
|
36
|
+
return v
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _LSQ_fit_func(cB, heights, velocities, initial_velocity, weights, atm_dens, el, degrees):
|
|
40
|
+
pred_v = velocity(
|
|
41
|
+
heights,
|
|
42
|
+
atm_dens,
|
|
43
|
+
cB,
|
|
44
|
+
el,
|
|
45
|
+
initial_velocity=initial_velocity,
|
|
46
|
+
degrees=degrees,
|
|
47
|
+
)
|
|
48
|
+
w = weights[1:] if weights.size - pred_v.size == 1 else weights
|
|
49
|
+
lsq = np.sum(((pred_v - velocities)/w) ** 2)
|
|
50
|
+
return lsq
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _LSQ_fit_func_two_vars(x, heights, velocities, weights, atm_dens, el, degrees):
|
|
54
|
+
cB, initial_velocity = x
|
|
55
|
+
pred_v = velocity(
|
|
56
|
+
heights,
|
|
57
|
+
atm_dens,
|
|
58
|
+
cB,
|
|
59
|
+
el,
|
|
60
|
+
initial_velocity=initial_velocity,
|
|
61
|
+
degrees=degrees,
|
|
62
|
+
)
|
|
63
|
+
w = weights[1:] if weights.size - pred_v.size == 1 else weights
|
|
64
|
+
lsq = np.sum(((pred_v - velocities)/w) ** 2)
|
|
65
|
+
return lsq
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def fit_velocity(
|
|
69
|
+
heights,
|
|
70
|
+
velocities,
|
|
71
|
+
atm_mass_density,
|
|
72
|
+
radiant_local_elevation,
|
|
73
|
+
initial_velocity=None,
|
|
74
|
+
start=None,
|
|
75
|
+
weights=None,
|
|
76
|
+
degrees=False,
|
|
77
|
+
minimize_kwargs={},
|
|
78
|
+
):
|
|
79
|
+
if weights is None:
|
|
80
|
+
weights = np.ones_like(velocities)
|
|
81
|
+
if initial_velocity is None:
|
|
82
|
+
x0 = (1.0, velocities[0])
|
|
83
|
+
_obj_func = _LSQ_fit_func_two_vars
|
|
84
|
+
else:
|
|
85
|
+
x0 = (1.0, )
|
|
86
|
+
_obj_func = _LSQ_fit_func
|
|
87
|
+
|
|
88
|
+
if start is None:
|
|
89
|
+
start = x0
|
|
90
|
+
|
|
91
|
+
result = sco.minimize(
|
|
92
|
+
_obj_func,
|
|
93
|
+
x0=start,
|
|
94
|
+
args=(
|
|
95
|
+
heights,
|
|
96
|
+
velocities,
|
|
97
|
+
weights,
|
|
98
|
+
atm_mass_density,
|
|
99
|
+
radiant_local_elevation,
|
|
100
|
+
degrees,
|
|
101
|
+
),
|
|
102
|
+
**minimize_kwargs
|
|
103
|
+
)
|
|
104
|
+
return result.x
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def luminosity_hill_2005(velocity, thermal_ablation):
|
|
5
|
+
"""Luminosity during thermal ablation.
|
|
6
|
+
|
|
7
|
+
Luminosity occurs in meteors as a result of decay of excited atomic (and
|
|
8
|
+
a few molecular) states following collisions between ablated meteor atoms
|
|
9
|
+
and atmospheric constituents. This specific model is described in [^1] and [^2].
|
|
10
|
+
|
|
11
|
+
[^1]: Friichtenicht and Becker: Determination of meteor paramters using laboratory
|
|
12
|
+
simulations techniques, 'Evolutionary and physical properties of meteoroids',
|
|
13
|
+
National Astronautics and Space Administration, Chapter 6, p. 53-81 (1973)
|
|
14
|
+
|
|
15
|
+
[^2]: Hill et al.: High geocentric velocity meteor ablation, A&A 444 p. 615-624 (2005)
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
velocity : float or numpy.ndarray
|
|
20
|
+
Meteoroid velocity [m/s]
|
|
21
|
+
thermal_ablation : float or numpy.ndarray
|
|
22
|
+
Mass loss due to thermal ablation [kg/s]
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
float or numpy.ndarray
|
|
27
|
+
Luminosity [W]
|
|
28
|
+
|
|
29
|
+
Notes
|
|
30
|
+
-----
|
|
31
|
+
Symbol definitions:
|
|
32
|
+
|
|
33
|
+
- `I` = [W] light intensity of a meteoroid, i.e. luminous intensity; radiant intensity
|
|
34
|
+
- `tau_I` = luminous efficiency factor
|
|
35
|
+
- `mu` = [kg] mean molecular mass of ablated material
|
|
36
|
+
- `v` = [m/s] meteoroid velocity
|
|
37
|
+
- `epsilon` = emissivity
|
|
38
|
+
- `zeta` = excitation coeff
|
|
39
|
+
- `rho_m` = [kg/m3] meteoroid density
|
|
40
|
+
- `abla` = mass loss due to thermal ablation
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# -- Universal constants
|
|
45
|
+
|
|
46
|
+
# -- Variables
|
|
47
|
+
v = velocity # [m/s] velocity
|
|
48
|
+
vkm = v / 1e3 # [km/s] velocity
|
|
49
|
+
abla = thermal_ablation
|
|
50
|
+
|
|
51
|
+
# -- for visible meteors, the energy is in lines, it is believed that they
|
|
52
|
+
# are composed of iron (Cepleca p. 355, table 3)
|
|
53
|
+
epsilon_mu = 7.668e6 # [J/kg] mean excitation energy = epsilon/mu; mu = mean molecular mass
|
|
54
|
+
|
|
55
|
+
# -- The excitation coeff for different velocity intervals:
|
|
56
|
+
# -- See Hill et al. and references therein
|
|
57
|
+
zeta = np.zeros(v.shape, dtype=v.dtype)
|
|
58
|
+
ind = v < 20000
|
|
59
|
+
|
|
60
|
+
zeta[ind] = -2.1887e-9 * v[ind] ** 2 + 4.2903e-13 * v[ind] ** 3 - 1.2447e-17 * v[ind] ** 4
|
|
61
|
+
|
|
62
|
+
ind = np.logical_and(v >= 20000, v < 60000)
|
|
63
|
+
zeta[ind] = 0.01333 * vkm[ind] ** 1.25
|
|
64
|
+
|
|
65
|
+
ind = np.logical_and(v >= 60000, v < 100000)
|
|
66
|
+
zeta[ind] = -12.835 + 6.7672e-4 * v[ind] - 1.163076e-8 * v[ind] ** 2
|
|
67
|
+
zeta[ind] += 9.191681e-14 * v[ind] ** 3 - 2.7465805e-19 * v[ind] ** 4
|
|
68
|
+
|
|
69
|
+
ind = v >= 100000
|
|
70
|
+
zeta[ind] = 1.615 + 1.3725e-5 * v[ind]
|
|
71
|
+
|
|
72
|
+
# -- Luminous efficiency factor
|
|
73
|
+
tau_I = 2 * epsilon_mu * zeta / v**2
|
|
74
|
+
|
|
75
|
+
# -- The normal lumonous equation
|
|
76
|
+
intensity = -0.5 * tau_I * abla * v**2
|
|
77
|
+
|
|
78
|
+
return intensity
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
# Sputtering physics
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
from scipy import constants
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def sputtering_kero_szasz_2008(mass, velocity, material_data, density):
|
|
16
|
+
"""Calculates the meteoroid mass loss due to sputtering [^1][^2].
|
|
17
|
+
|
|
18
|
+
All units need to be given in the cgs system to remain
|
|
19
|
+
consistent with the units in which the sputtering yield
|
|
20
|
+
equation was derived i.e., variables given in SI system
|
|
21
|
+
are converted into cgs in this function when needed.
|
|
22
|
+
|
|
23
|
+
Changes pre-python port: See [matlab-version](https://gitlab.irf.se/kero/ablation_matlab)
|
|
24
|
+
|
|
25
|
+
[^1]: Rogers et al.: Mass loss due to sputtering and
|
|
26
|
+
thermal processes in meteoroid ablation.
|
|
27
|
+
Planetary and Space Science 53 p. 1341-1354 (2005)
|
|
28
|
+
[^2]: Tielens et al.: The physics of grain-grain
|
|
29
|
+
collisions and gas-grain sputtering in interstellar shocks.
|
|
30
|
+
The Astrophisicsl Journal 431, p. 321-340 (1994)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
mass : float/numpy.ndarray
|
|
36
|
+
Meteoroid mass in [kg]
|
|
37
|
+
velocity : float/numpy.ndarray
|
|
38
|
+
Meteoroid velocity in [m/s]
|
|
39
|
+
material_data : dict
|
|
40
|
+
Meteoroid material data, see [`material_data`][ablate.functions.material.material_data].
|
|
41
|
+
density : xarray.Dataset
|
|
42
|
+
Dataset of atmospheric constituent densities. Variables should be
|
|
43
|
+
constituents and coordinates, and then indexed on the mass(es) and
|
|
44
|
+
velocity(ies) of the input meteoroid(s).
|
|
45
|
+
See [`Atmosphere`][ablate.atmosphere.atmosphere.Atmosphere].
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
float or numpy.ndarray
|
|
50
|
+
Mass loss due to sputtering for each input meteoroid
|
|
51
|
+
|
|
52
|
+
Notes
|
|
53
|
+
-----
|
|
54
|
+
Variables explanation:
|
|
55
|
+
|
|
56
|
+
- `E_th`: threshold energy: the minimum projectile kinetic energy needed for
|
|
57
|
+
a given projectile and target to induce sputtering
|
|
58
|
+
- `a`: screening length
|
|
59
|
+
- `beta`: maximum fractional energy transfer in head-on elastic collision
|
|
60
|
+
- `alpha`: dimensionless function of the mass ratio
|
|
61
|
+
- `Rp`: mean projected range
|
|
62
|
+
- `R`: mean penetrated path
|
|
63
|
+
- `Y`: sputtering yield at normal incidence: the ratio of the mean number
|
|
64
|
+
of sputtered particles per projectile
|
|
65
|
+
- `U0`: surface binding energy (eV), use the sublimation energy of the material
|
|
66
|
+
- `M1`: projectile mass
|
|
67
|
+
- `M2`: mean molecular mass per atom of the target
|
|
68
|
+
- `Z1`: projectile atomic number
|
|
69
|
+
- `Z2`: target atomic number
|
|
70
|
+
- `sn`: universal function
|
|
71
|
+
- `E`: incident projectile energy
|
|
72
|
+
- `Gamma`: reduced energy
|
|
73
|
+
- `M2`: mean molecular mass per atom of the target
|
|
74
|
+
- `A`: shape factor, sphere = 1.21
|
|
75
|
+
- `v`: meteoroid velocity
|
|
76
|
+
- `m`: meteoroid mass
|
|
77
|
+
- `rho_m`: meteoroid density
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
v = copy.copy(velocity)
|
|
81
|
+
m = copy.copy(mass)
|
|
82
|
+
|
|
83
|
+
shape = None
|
|
84
|
+
if isinstance(v, np.ndarray):
|
|
85
|
+
if isinstance(m, np.ndarray):
|
|
86
|
+
assert v.shape == m.shape
|
|
87
|
+
shape = v.shape
|
|
88
|
+
elif isinstance(m, np.ndarray):
|
|
89
|
+
shape = m.shape
|
|
90
|
+
|
|
91
|
+
# Universal constants
|
|
92
|
+
u = constants.u
|
|
93
|
+
elem = constants.value("elementary charge")
|
|
94
|
+
a0 = constants.value("Bohr radius") * 1e2 # Bohr radius in cm (cgs system)
|
|
95
|
+
|
|
96
|
+
# Atmospheric molecules
|
|
97
|
+
# M1 = projectile mass in kg [O N2 O2 He Ar H N], for molecules:
|
|
98
|
+
# average of its constitutents (see table 3 on p. 333 in Tielens et al)
|
|
99
|
+
# Z for a molecule: average of its consitutents
|
|
100
|
+
# (see table 3 on p. 333 in Tielens et al, not given in the table, but I use the same method)
|
|
101
|
+
avalible_species = ["O", "N2", "O2", "He", "Ar", "H", "N"]
|
|
102
|
+
m1 = np.array([15.9994, 14.007, 15.999, 4.0026, 39.948, 1.0079, 14.007]) * u
|
|
103
|
+
z1 = np.array([8, 7, 8, 2, 18, 1, 7])
|
|
104
|
+
use_species = np.full(m1.shape, False, dtype=bool)
|
|
105
|
+
|
|
106
|
+
if shape is not None:
|
|
107
|
+
# This needs to happen first
|
|
108
|
+
v = np.outer(np.ones(m1.shape), v)
|
|
109
|
+
m = np.outer(np.ones(m1.shape), m)
|
|
110
|
+
# then this
|
|
111
|
+
m1 = np.outer(m1, np.ones(shape))
|
|
112
|
+
z1 = np.outer(z1, np.ones(shape))
|
|
113
|
+
|
|
114
|
+
_density = np.empty(m1.shape, dtype=np.float64)
|
|
115
|
+
for ind, key in enumerate(avalible_species):
|
|
116
|
+
if key in density:
|
|
117
|
+
_density[ind, ...] = density[key].values.squeeze()
|
|
118
|
+
use_species[ind] = np.logical_not(np.any(np.isnan(_density[ind, ...])))
|
|
119
|
+
|
|
120
|
+
m1 = m1[use_species, ...]
|
|
121
|
+
z1 = z1[use_species, ...]
|
|
122
|
+
_density = _density[use_species, ...]
|
|
123
|
+
|
|
124
|
+
if shape is not None:
|
|
125
|
+
m = m[use_species, ...]
|
|
126
|
+
v = v[use_species, ...]
|
|
127
|
+
|
|
128
|
+
m2 = material_data["m2"]
|
|
129
|
+
u0 = material_data["u0"]
|
|
130
|
+
k = material_data["k"]
|
|
131
|
+
z2 = material_data["z2"]
|
|
132
|
+
rho_m = material_data["rho_m"]
|
|
133
|
+
|
|
134
|
+
beta = 4 * m1 * m2 / (m1 + m2) ** 2
|
|
135
|
+
|
|
136
|
+
m1_m2 = m1 / m2
|
|
137
|
+
E_th = 8 * u0 * m1_m2 ** (1.0 / 3.0)
|
|
138
|
+
|
|
139
|
+
# when M1 ./ M2 > 0.3; E_th is in eV
|
|
140
|
+
less03 = m1_m2 <= 0.3
|
|
141
|
+
E_th[less03] = u0 / (beta[less03] * (1 - beta[less03]))
|
|
142
|
+
|
|
143
|
+
alpha = 0.3 * (m2 / m1) ** (2.0 / 3.0)
|
|
144
|
+
|
|
145
|
+
less05 = (m2 / m1) < 0.5
|
|
146
|
+
alpha[less05] = 0.2
|
|
147
|
+
|
|
148
|
+
# balances 'alpha' if m1/m2 grows too big;
|
|
149
|
+
# 'alpha' should be between 1/2 and 2/3 see page 324 in Tielens
|
|
150
|
+
Rp_R = (k * m2 / m1 + 1.0) ** (-1.0)
|
|
151
|
+
a = 0.885 * a0 / np.sqrt(z1 ** (2.0 / 3.0) + z2 ** (2.0 / 3.0))
|
|
152
|
+
|
|
153
|
+
# ---------------------------------
|
|
154
|
+
# Stepping through the atmosphere --
|
|
155
|
+
# ---------------------------------
|
|
156
|
+
|
|
157
|
+
# ---------------------------------------
|
|
158
|
+
# Sputtering yield at normal incidence: --
|
|
159
|
+
# ---------------------------------------
|
|
160
|
+
E = (m1 * v**2.0 / 2.0) / elem # joule -> eV by dividing by 'elem'
|
|
161
|
+
# Sputtering only occurs if E > E_th
|
|
162
|
+
yes = E > E_th
|
|
163
|
+
|
|
164
|
+
Y = np.zeros(yes.shape, dtype=np.float64)
|
|
165
|
+
Gamma = np.zeros(yes.shape, dtype=np.float64)
|
|
166
|
+
|
|
167
|
+
m1 = m1 * 1e3 # [g] projectile mean molecular mass
|
|
168
|
+
m2 = m2 * 1e3 # [g] target mean molecular mass
|
|
169
|
+
m = m * 1e3 # [g] meteoroid mass
|
|
170
|
+
rho_m = rho_m / 1e3 # [g/cm3] meteoroid density
|
|
171
|
+
|
|
172
|
+
# Calculating the yield for all atmospheric constituents for the given
|
|
173
|
+
# surface material (meteoroid density)
|
|
174
|
+
# taking the energy in ergs so the unit of sn is ergs cm2; elementary charge in cgs: in esu
|
|
175
|
+
Gamma[yes] = (
|
|
176
|
+
m2
|
|
177
|
+
/ (m1[yes] + m2)
|
|
178
|
+
* a[yes]
|
|
179
|
+
/ (z1[yes] * z2 * (elem / 3.33564e-10) ** 2.0)
|
|
180
|
+
* E[yes]
|
|
181
|
+
* elem
|
|
182
|
+
* 1e7
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
sn_1 = 3.441 * np.sqrt(Gamma) * np.log(Gamma + 2.781)
|
|
186
|
+
sn_2 = 1.0 + 6.35 * np.sqrt(Gamma) + Gamma * (-1.708 + 6.882 * np.sqrt(Gamma))
|
|
187
|
+
sn = sn_1 / sn_2 # [ergs cm2]
|
|
188
|
+
|
|
189
|
+
# Valid for E > E_th:
|
|
190
|
+
Y[yes] = (
|
|
191
|
+
3.56
|
|
192
|
+
/ u0
|
|
193
|
+
* m1[yes]
|
|
194
|
+
/ (m1[yes] + m2)
|
|
195
|
+
* z1[yes]
|
|
196
|
+
* z2
|
|
197
|
+
/ np.sqrt(z1[yes] ** (2.0 / 3.0) + z2 ** (2.0 / 3.0))
|
|
198
|
+
)
|
|
199
|
+
Y[yes] *= (
|
|
200
|
+
alpha[yes]
|
|
201
|
+
* Rp_R[yes]
|
|
202
|
+
* sn[yes]
|
|
203
|
+
* (1.0 - (E_th[yes] / E[yes]) ** (2.0 / 3.0))
|
|
204
|
+
* (1.0 - E_th[yes] / E[yes]) ** 2.0
|
|
205
|
+
)
|
|
206
|
+
# we have tried to put in E_th in eV and E in cgs-units (ergs) but the results were convincingly wrong...
|
|
207
|
+
|
|
208
|
+
# the total yield is the sum of all individual yields x the atmospheric density
|
|
209
|
+
Y_tot = np.sum(_density * 1e-6 * Y, axis=0) # density in cm^-3?
|
|
210
|
+
|
|
211
|
+
# ------------------------------
|
|
212
|
+
# Mass loss due to sputtering: --
|
|
213
|
+
# ------------------------------
|
|
214
|
+
A = 1.21 # Sphere
|
|
215
|
+
|
|
216
|
+
dmdt = -2.0 * m2 * A * velocity * 1e2 * (mass / rho_m) ** (2.0 / 3.0) * Y_tot # [g/s]
|
|
217
|
+
dmdt = dmdt / 1e3 # [kg/s]
|
|
218
|
+
|
|
219
|
+
return dmdt
|