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.
@@ -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