diffenix 0.0.0.1__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.
- diffenix/Constantes.py +761 -0
- diffenix/__init__.py +26 -0
- diffenix/chemains.py +110 -0
- diffenix/classes.py +0 -0
- diffenix/generation_configs.py +146 -0
- diffenix/m/303/251thodes_num/303/251riques.py +332 -0
- diffenix/numericals_methods.py +230 -0
- diffenix/solve_asteroid_belt.py +1510 -0
- diffenix/systems.py +7309 -0
- diffenix/time_integration.py +265 -0
- diffenix-0.0.0.1.dist-info/METADATA +7 -0
- diffenix-0.0.0.1.dist-info/RECORD +14 -0
- diffenix-0.0.0.1.dist-info/WHEEL +5 -0
- diffenix-0.0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1510 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.integrate import trapezoid, cumulative_trapezoid, quad, solve_ivp
|
|
3
|
+
from scipy.interpolate import Akima1DInterpolator, FloaterHormannInterpolator, PchipInterpolator
|
|
4
|
+
from scipy.optimize import root
|
|
5
|
+
from scipy.signal import savgol_filter
|
|
6
|
+
import phenigraph as g
|
|
7
|
+
|
|
8
|
+
from diffenix.Constantes import *
|
|
9
|
+
from diffenix.numericals_methods import linear_interpolation, rk4, gradient2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def linear_interp(t_new, t_old, values):
|
|
13
|
+
if isinstance(t_new, list | np.ndarray):
|
|
14
|
+
return np.array([linear_interp(t_new=t, t_old=t_old, values=values) for t in t_new])
|
|
15
|
+
|
|
16
|
+
i = np.argmin(np.abs(t_new - np.array(t_old)))
|
|
17
|
+
if i == 0 or (i < len(t_old) - 1 and t_new > t_old[i]):
|
|
18
|
+
t1 = t_old[i]
|
|
19
|
+
t2 = t_old[i + 1]
|
|
20
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
21
|
+
return (1 - alpha) * values[i] + alpha * values[i + 1]
|
|
22
|
+
else:
|
|
23
|
+
t1 = t_old[i - 1]
|
|
24
|
+
t2 = t_old[i]
|
|
25
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
26
|
+
return (1 - alpha) * values[i - 1] + alpha * values[i]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Compteur:
|
|
30
|
+
"""
|
|
31
|
+
This object can be used to estimate the number of calls for a function if given in parameter
|
|
32
|
+
and incremented in the function (mutable object)
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self):
|
|
37
|
+
self.value: int = 0
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def number_density(a, amax: np.float64, abig: np.float64, amed: np.float64, amin: np.float64,
|
|
41
|
+
qh: np.float64, qm: np.float64, ql: np.float64,
|
|
42
|
+
Mtot: np.float64 = np.double(1.), rho: np.float64 = np.double(1.)) -> np.float64 | np.ndarray:
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
Return the number density of asteroid of size a
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
a : np.float64 | np.ndarray
|
|
50
|
+
The diameter(s) of asteroids
|
|
51
|
+
amax : np.float64
|
|
52
|
+
The maximum diameter
|
|
53
|
+
abig : np.float64
|
|
54
|
+
A reference diameter
|
|
55
|
+
amed : np.float64
|
|
56
|
+
|
|
57
|
+
amin : np.float64
|
|
58
|
+
The minimum diameter
|
|
59
|
+
qh : np.float64
|
|
60
|
+
The slope of distribution for the highest radius
|
|
61
|
+
qm : np.float64
|
|
62
|
+
The slope of distribution for medium seized asteroids
|
|
63
|
+
ql : np.float64
|
|
64
|
+
The slope of distribution for the smallest asteroids
|
|
65
|
+
Mtot : np.float64, optional, default=1
|
|
66
|
+
The total mass of the belt.
|
|
67
|
+
The result is directly proportional to Mtot.
|
|
68
|
+
number_density(Mtot)=Mtot*number_density(1.).
|
|
69
|
+
It can be used to estimate the number density
|
|
70
|
+
in a belt = sigma*number_density(1.) with sigma the surface density
|
|
71
|
+
rho : np.float64
|
|
72
|
+
The mean density of asteroids
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
np.float64 | np.ndarray
|
|
77
|
+
The number density of asteroids for diameter(s) a
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Normalization of the distribution. Nmax is the value of the number density for a=amax calculated such as
|
|
82
|
+
# the integration of the number density multiplied by the mass of asteroids for a diameter a giving the total mass
|
|
83
|
+
Nmax: np.float64 = (6. * Mtot
|
|
84
|
+
/ (Pi * rho
|
|
85
|
+
* (amax * amax * amax * amax / (qh + 4.)
|
|
86
|
+
+ ((abig / amax) ** qh)
|
|
87
|
+
* ((abig * abig * abig * abig * (1. / (qm + 4.) - 1 / (qh + 4.)))
|
|
88
|
+
+ ((amed / abig) ** qm)
|
|
89
|
+
* (amed * amed * amed * amed * (1. / (ql + 4.) - 1. / (qm + 4.))
|
|
90
|
+
- ((amin / amed) ** ql) * amin * amin * amin * amin / (ql + 4.)))
|
|
91
|
+
)))
|
|
92
|
+
# Nmax: np.float64 = (6. * Mbelt
|
|
93
|
+
# / (Pi * rho_refr
|
|
94
|
+
# * (((abig / amax) ** qh) * ((amed / abig) ** qm) * ql * (1 - (amin / amed) ** (ql - 1))
|
|
95
|
+
# + ((abig / amax) ** qh) * qm * (1 - (amed / abig) ** (qm - 1))
|
|
96
|
+
# + qh * (1 - (abig / amax) ** (qh - 1)))))
|
|
97
|
+
if type(a) is np.ndarray:
|
|
98
|
+
n: np.ndarray = np.zeros_like(a)
|
|
99
|
+
idx: np.ndarray = np.intersect1d(np.argwhere(a >= amin)[:, 0], np.argwhere(a < amed)[:, 0])
|
|
100
|
+
n[idx] = Nmax * ((abig / amax) ** qh) * ((amed / abig) ** qm) * ((a[idx] / amed) ** ql)
|
|
101
|
+
idx: np.ndarray = np.intersect1d(np.argwhere(a >= amed)[:, 0], np.argwhere(a < abig)[:, 0])
|
|
102
|
+
n[idx] = Nmax * ((abig / amax) ** qh) * ((a[idx] / abig) ** qm)
|
|
103
|
+
idx: np.ndarray = np.intersect1d(np.argwhere(a >= abig)[:, 0], np.argwhere(a <= amax)[:, 0])
|
|
104
|
+
n[idx] = Nmax * ((a[idx] / amax) ** qh)
|
|
105
|
+
return n
|
|
106
|
+
elif a < amin or a > amax:
|
|
107
|
+
return zero
|
|
108
|
+
elif a < amed:
|
|
109
|
+
return Nmax * ((abig / amax) ** qh) * ((amed / abig) ** qm) * ((a / amed) ** ql)
|
|
110
|
+
elif a < abig:
|
|
111
|
+
return Nmax * ((abig / amax) ** qh) * ((a / abig) ** qm)
|
|
112
|
+
else:
|
|
113
|
+
return Nmax * ((a / amax) ** qh)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
mu_H2O: np.float64 = np.double(18. * 1e-3) * C_M
|
|
117
|
+
Tref: np.float64 = np.double(273.) # K
|
|
118
|
+
hvap: np.float64 = np.double(2.78e6) * C_E / C_M
|
|
119
|
+
Bh2o: np.float64 = np.double(mu_H2O * hvap / Rgp)
|
|
120
|
+
Ah2o: np.float64 = np.double(1e5 * C_M / (C_t * C_L)) * np.exp(Bh2o / Tref)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def temp_belt(r: np.ndarray | np.float64, t: np.float64 | np.ndarray, consts: dict
|
|
124
|
+
) -> np.ndarray | np.float64:
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
Calculate and return the temperature at radius r and time t
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
r : np.ndarray | np.float64
|
|
132
|
+
radius
|
|
133
|
+
t : np.ndarray | np.float64
|
|
134
|
+
the current time
|
|
135
|
+
consts : dict
|
|
136
|
+
the constantes
|
|
137
|
+
Returns
|
|
138
|
+
-------
|
|
139
|
+
np.ndarray | np.float64
|
|
140
|
+
|
|
141
|
+
"""
|
|
142
|
+
if "Ms" in consts.keys():
|
|
143
|
+
if interpLbol is not None:
|
|
144
|
+
temp0 = (278 * (((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4)) * (
|
|
145
|
+
(a0 / au) ** (-1 / 2)))
|
|
146
|
+
else:
|
|
147
|
+
temp0 = (278 * (((1. - consts["Abelt"]) * consts["Ls"] / Lsun) ** (1 / 4)) * (
|
|
148
|
+
(a0 / au) ** (-1 / 2)))
|
|
149
|
+
else:
|
|
150
|
+
if interpLbol is not None:
|
|
151
|
+
temp0 = (278 * (((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4)) * (
|
|
152
|
+
(a0 / au) ** (-1 / 2)))
|
|
153
|
+
else:
|
|
154
|
+
temp0 = (278 * (((1. - consts["Abelt"]) * consts["Ls"] / Lsun) ** (1 / 4)) * ((a0 / au) ** (-1 / 2)))
|
|
155
|
+
if isinstance(t, float | np.float64 | int | np.int64) and t < 0:
|
|
156
|
+
return zero
|
|
157
|
+
elif t.shape == r.shape and np.any(t < zero):
|
|
158
|
+
# temp[t < consts["t0"]] = 10
|
|
159
|
+
temp0[t < consts["t0"]] = 10
|
|
160
|
+
|
|
161
|
+
temp = temp0 * (abs(r) / a0) ** (- 1 / 2)
|
|
162
|
+
return temp
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def tau_diff(pp: np.ndarray, rr: np.ndarray, t: np.float64 | np.ndarray, consts: dict) -> np.ndarray:
|
|
166
|
+
"""
|
|
167
|
+
Calculate the diffusion timescale at time t and depth p
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
|
|
172
|
+
p: np.ndarray
|
|
173
|
+
The depth at which the gas is realised
|
|
174
|
+
|
|
175
|
+
r: np.ndarray
|
|
176
|
+
The distance from the central star at which the gas is realised
|
|
177
|
+
|
|
178
|
+
t: np.ndarray
|
|
179
|
+
The time at which the gas is realised
|
|
180
|
+
|
|
181
|
+
consts
|
|
182
|
+
All the necessary constants
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
np.ndarray
|
|
187
|
+
The time needed for the gas to go at the surface
|
|
188
|
+
|
|
189
|
+
"""
|
|
190
|
+
return (3. * pp * pp * np.sqrt(2. * Pi * mu_H2O
|
|
191
|
+
/ (Rgp * temp_belt(rr, t - pp * pp / consts["K"], consts)))
|
|
192
|
+
/ (4. * consts["Phi"] * consts["rp"]))
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def sigmap_rho(t: np.float64 | np.ndarray,
|
|
196
|
+
rr: np.ndarray, pp: np.ndarray,
|
|
197
|
+
consts: dict, comp: Compteur = Compteur()) \
|
|
198
|
+
-> np.ndarray:
|
|
199
|
+
"""
|
|
200
|
+
Calculate and return the gas generation rate at depth p, radius r and time t.
|
|
201
|
+
This doesn't take into acompte the limited amonth of material
|
|
202
|
+
|
|
203
|
+
Parameters
|
|
204
|
+
----------
|
|
205
|
+
t : np.ndarray | np.float64
|
|
206
|
+
The current time
|
|
207
|
+
r : np.ndarray | np.float64
|
|
208
|
+
The distance to the central star (semi-major axis)
|
|
209
|
+
p : np.ndarray | np.float64
|
|
210
|
+
The depth into the asteroid/KBO
|
|
211
|
+
consts : dict
|
|
212
|
+
The constantes
|
|
213
|
+
comp : Compteur, optional
|
|
214
|
+
To estimate the number of iterations
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
np.ndarray | np.float64
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
S: np.float64 = 3. * (1. - consts["Phi"]) / consts["rp"]
|
|
223
|
+
T: np.ndarray = temp_belt(rr, t - pp * pp / consts["K"], consts)
|
|
224
|
+
# The pp * pp / consts["K"] factor model the thermal diffusion
|
|
225
|
+
|
|
226
|
+
drho_sub = (S * Ah2o * np.exp(-Bh2o / T)
|
|
227
|
+
* np.sqrt(mu_H2O / (2. * Pi * Rgp * T)))
|
|
228
|
+
if comp.value % 1000 == 0:
|
|
229
|
+
print("Iteration ", comp.value, ": t=", t.max() / Myr, " Myr")
|
|
230
|
+
# print("Iteration ", comp.value, ": t=", t / Myr, " Myr")
|
|
231
|
+
|
|
232
|
+
comp.value += 1
|
|
233
|
+
return drho_sub
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def minit(r: np.ndarray, pinit: np.float64, rho_ice: np.float64, a0: np.float64, delta_a: np.float64,
|
|
237
|
+
Mbelt: np.float64, kwargsN) -> np.float64:
|
|
238
|
+
p: np.ndarray = np.geomspace(kwargsN['amin'], kwargsN['amax'], 10000)
|
|
239
|
+
rr: np.ndarray = r[:, np.newaxis] + np.zeros_like(p)
|
|
240
|
+
pp: np.ndarray = (p[:, np.newaxis] + np.zeros_like(r)).T
|
|
241
|
+
rhod_control = 4. * Pi * cumulative_trapezoid(p * p * rho_ice * np.ones_like(pp), p, axis=1, initial=zero)
|
|
242
|
+
rhod_control[pp > pinit] *= 0
|
|
243
|
+
sigma0: np.ndarray = (Mbelt * ((r / a0) ** (-3. / 2.))
|
|
244
|
+
/ (4. * Pi * a0 * a0 * (np.sqrt(1. + delta_a / a0)
|
|
245
|
+
- np.sqrt(1. - delta_a / a0)))) # initial surface density
|
|
246
|
+
ni: np.ndarray = ((np.array([number_density(2. * d_2, Mtot=np.double(1.), **kwargsN)
|
|
247
|
+
for d_2 in p])[:, np.newaxis] + np.zeros(len(r))).T
|
|
248
|
+
* (sigma0[:, np.newaxis] + np.zeros(len(p)))) # number density
|
|
249
|
+
sig_init: np.ndarray = trapezoid(ni * rhod_control, 2 * p)
|
|
250
|
+
return trapezoid(2. * Pi * r * sig_init, r)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def sigma_dot(Mbelt: np.float64, r: np.ndarray,
|
|
254
|
+
tps: np.ndarray,
|
|
255
|
+
a0: np.float64, delta_a: np.float64,
|
|
256
|
+
tinit: np.float64, tmax: np.float64,
|
|
257
|
+
f_ice: np.float64, rho_refr: np.float64, rho_ice: np.float64,
|
|
258
|
+
Abelt: np.float64, K: np.float64,
|
|
259
|
+
Its: int, Phi: np.float64,
|
|
260
|
+
dmin: np.float64, dmax: np.float64, Its_p: int,
|
|
261
|
+
rp: np.float64,
|
|
262
|
+
Itt: int, kwargsN: dict, **kwargs
|
|
263
|
+
) -> np.ndarray:
|
|
264
|
+
"""
|
|
265
|
+
Estimate the surface density mass generation rate for asteroids of diameters d at the distance r of the central star
|
|
266
|
+
and of diameter d in a belt of mass Mbelt during the time times_loc
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
Mbelt : np.float64
|
|
271
|
+
The total belt's mass
|
|
272
|
+
r : np.ndarray
|
|
273
|
+
The distance to the central star
|
|
274
|
+
p : np.ndarray
|
|
275
|
+
The asteroids/KBO diameters
|
|
276
|
+
tps : np.ndarray
|
|
277
|
+
The time
|
|
278
|
+
a0 : np.float64
|
|
279
|
+
The belt's semi-major axis
|
|
280
|
+
delta_a : np.float64
|
|
281
|
+
The (half) size pf the belt
|
|
282
|
+
tinit : np.float64
|
|
283
|
+
The initial time
|
|
284
|
+
tmax : np.float64
|
|
285
|
+
The maximum time
|
|
286
|
+
f_ice : np.float64
|
|
287
|
+
The initial ice to refractory mass ratio
|
|
288
|
+
rho_refr : np.float64
|
|
289
|
+
The asteroids/KBO density
|
|
290
|
+
rho_ice : np.float64
|
|
291
|
+
The asteroids/KBO ice density
|
|
292
|
+
Abelt : np.float64
|
|
293
|
+
The asteroids/KBO albedo
|
|
294
|
+
K : np.float64
|
|
295
|
+
The thermal diffusion coefficient
|
|
296
|
+
Its : np.float64
|
|
297
|
+
The number of spatial steps
|
|
298
|
+
Phi : np.float64
|
|
299
|
+
The porosity (dimensionless number)
|
|
300
|
+
dmin : np.float64
|
|
301
|
+
The minimum diameters
|
|
302
|
+
dmax : np.float64
|
|
303
|
+
The maximum diameters
|
|
304
|
+
Its_p : np.float64
|
|
305
|
+
The number of diameters steps
|
|
306
|
+
rp : np.float64
|
|
307
|
+
The pore's radius
|
|
308
|
+
Itt : np.float64
|
|
309
|
+
The number of time steps
|
|
310
|
+
kwargsN : dict
|
|
311
|
+
The directory with all informations to build the belt's size number distribution
|
|
312
|
+
kwargs : dict
|
|
313
|
+
|
|
314
|
+
Returns
|
|
315
|
+
-------
|
|
316
|
+
np.ndarray
|
|
317
|
+
|
|
318
|
+
"""
|
|
319
|
+
if r is None:
|
|
320
|
+
r = np.linspace(a0 - delta_a, a0 + delta_a, Its)
|
|
321
|
+
|
|
322
|
+
pmin: np.float64 = max(dmin / 2., np.sqrt(K * 0.1 * Myr))
|
|
323
|
+
# pmin: np.float64 = max(dmin / 2., np.sqrt(K * 0.1 * yr))
|
|
324
|
+
p = np.geomspace(pmin / 2., dmax / 2., Its_p)
|
|
325
|
+
print("pmin= ", pmin / C_L, "m")
|
|
326
|
+
|
|
327
|
+
m_init: np.float64 = minit(r=r, pinit=pmin, rho_ice=rho_ice,
|
|
328
|
+
a0=a0, delta_a=delta_a, Mbelt=Mbelt, kwargsN=kwargsN)
|
|
329
|
+
|
|
330
|
+
if tps is None:
|
|
331
|
+
tps = np.geomspace(max(tinit, yr), tmax, Itt)
|
|
332
|
+
else:
|
|
333
|
+
tinit = tps.min()
|
|
334
|
+
tmax = tps.max()
|
|
335
|
+
|
|
336
|
+
consts: dict = {"C_L": C_L, "C_M": C_M, "C_t": C_t,
|
|
337
|
+
"Ms": Ms, "Abelt": Abelt, "K": K,
|
|
338
|
+
"a0": a0, "delta_a": delta_a, "t0": t0,
|
|
339
|
+
"rho_refr": rho_refr,
|
|
340
|
+
"Its": Its, "Its_p": Its_p,
|
|
341
|
+
"f_ice": f_ice,
|
|
342
|
+
"Phi": Phi, "rp": rp}
|
|
343
|
+
|
|
344
|
+
# Estimation of the gas sublimation rate realised for every distance of the central star r and depth p
|
|
345
|
+
# The factor tau_diff(p, r, t, consts) model the diffusion of the gas through the pore of refractory materials
|
|
346
|
+
# The thermal diffusion is modeled in sigmap_rho
|
|
347
|
+
|
|
348
|
+
rr: np.ndarray = r[::5, np.newaxis] + np.zeros_like(p)
|
|
349
|
+
pp: np.ndarray = (p[:, np.newaxis] + np.zeros_like(r[::5])).T
|
|
350
|
+
comp: Compteur = Compteur()
|
|
351
|
+
|
|
352
|
+
if kwargsN is None: # Default size distribution
|
|
353
|
+
kwargsN = dict(amax=np.double(1.e6) * C_L,
|
|
354
|
+
abig=np.double(120e3) * C_L,
|
|
355
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
356
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
357
|
+
rho=rho_refr)
|
|
358
|
+
|
|
359
|
+
def integrate(ti: np.float64, tf: np.float64, yinit: np.ndarray, dtinit: np.float64 = None,
|
|
360
|
+
kwargsN=kwargsN) -> tuple[np.ndarray]:
|
|
361
|
+
if dtinit is None:
|
|
362
|
+
# dtinit = np.double(1e-10) * (tf - ti)
|
|
363
|
+
dtinit = np.double(1e-12) * (tf - ti)
|
|
364
|
+
|
|
365
|
+
def system(t, y):
|
|
366
|
+
yp = sigmap_rho(t=t - tau_diff(pp=pp, rr=rr, t=t, consts=consts) - pp * pp / consts["K"],
|
|
367
|
+
rr=rr, pp=pp, consts=consts, comp=comp).flatten()
|
|
368
|
+
# yp = sigmap_rho(t=t, rr=rr, pp=pp, consts=consts, comp=comp).flatten()
|
|
369
|
+
# Taking into acompte the limited amounth of ice
|
|
370
|
+
yp[y > rho_ice] *= zero
|
|
371
|
+
# yp[y > 0.9 * rho_ice] *= (y[y > 0.9 * rho_ice] / Myr) * np.exp(- np.maximum(200, rho_ice / np.maximum(abs(y[y > 0.9 * rho_ice] - rho_ice), 1e-200)))
|
|
372
|
+
# yp[pp.flatten() < pmin] *= zero
|
|
373
|
+
return yp
|
|
374
|
+
|
|
375
|
+
#
|
|
376
|
+
sol_ivp = solve_ivp(system, t_span=[ti, tf],
|
|
377
|
+
y0=yinit,
|
|
378
|
+
max_step=(tmax - ti) / Itt, method="RK45", args=[],
|
|
379
|
+
rtol=np.double(1e-4), atol=atol, first_step=dtinit)
|
|
380
|
+
print(sol_ivp.message)
|
|
381
|
+
|
|
382
|
+
sol_ivp.y = np.minimum(sol_ivp.y, rho_ice)
|
|
383
|
+
res_final = sol_ivp.y[:, -1]
|
|
384
|
+
times_loc = sol_ivp.t
|
|
385
|
+
|
|
386
|
+
rhodot = np.gradient(sol_ivp.y.T.reshape((len(sol_ivp.t), pp.shape[0], pp.shape[1])), sol_ivp.t, axis=0)
|
|
387
|
+
|
|
388
|
+
yplot = sol_ivp.y.T[-1].reshape(pp.shape).T
|
|
389
|
+
yplot[np.isnan(yplot)] = zero
|
|
390
|
+
yplot[abs(yplot) == np.inf] = zero
|
|
391
|
+
if np.any(yplot > 0):
|
|
392
|
+
yplot = np.maximum(yplot, 0.1 * min(yplot[yplot > 0]))
|
|
393
|
+
else:
|
|
394
|
+
yplot = np.maximum(yplot, rho_ice * 1e-50)
|
|
395
|
+
if yplot.min() < yplot.max():
|
|
396
|
+
print("test max : ", yplot.max() / rho_ice)
|
|
397
|
+
gtest = g.image(yplot / rho_ice, r[::5] / C_L, p, colorscale="log", show=False, cmap="inferno")
|
|
398
|
+
gtest.config_ax(yscale="log")
|
|
399
|
+
gtest.title = r"$\rho_{lost}$"
|
|
400
|
+
gtest.show()
|
|
401
|
+
else:
|
|
402
|
+
print(yplot.max() / rho_ice)
|
|
403
|
+
|
|
404
|
+
rhodot_d: np.ndarray = 4. * Pi * cumulative_trapezoid(p * p * rhodot, p, axis=2, initial=zero)
|
|
405
|
+
|
|
406
|
+
rho_d: np.ndarray = 4. * Pi * cumulative_trapezoid(
|
|
407
|
+
p * p * (sol_ivp.y.T.reshape((len(sol_ivp.t), pp.shape[0], pp.shape[1]))),
|
|
408
|
+
p, axis=2, initial=zero)
|
|
409
|
+
if kwargsN is None: # Default size distribution
|
|
410
|
+
kwargsN = dict(amax=np.double(1.e6) * C_L,
|
|
411
|
+
abig=np.double(120e3) * C_L,
|
|
412
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
413
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
414
|
+
rho=rho_refr)
|
|
415
|
+
|
|
416
|
+
## Calcul of number of asteroids for all diameters and distance to central star
|
|
417
|
+
sigma0: np.ndarray = (Mbelt * ((r[::5] / a0) ** (-3. / 2.))
|
|
418
|
+
/ (4. * Pi * a0 * a0 * (np.sqrt(1. + delta_a / a0)
|
|
419
|
+
- np.sqrt(1. - delta_a / a0)))) # initial surface density
|
|
420
|
+
ni: np.ndarray = ((np.array([number_density(2. * d_2, Mtot=np.double(1.), **kwargsN)
|
|
421
|
+
for d_2 in p])[:, np.newaxis] + np.zeros(len(r[::5]))).T
|
|
422
|
+
* (sigma0[:, np.newaxis] + np.zeros(len(p)))) #number density
|
|
423
|
+
|
|
424
|
+
# Multiplying the gas production rate for one asteroid by the number of asteroids and integrated along d axis
|
|
425
|
+
res: np.ndarray = np.zeros((len(times_loc), len(r[::5])), dtype="double")
|
|
426
|
+
sig_lost: np.ndarray = np.zeros((len(times_loc), len(r[::5])), dtype="double")
|
|
427
|
+
for i in range(1, len(times_loc)):
|
|
428
|
+
res[i] = trapezoid(ni * rhodot_d[i], 2. * p)
|
|
429
|
+
sig_lost[i] = trapezoid(ni * rho_d[i], 2 * p)
|
|
430
|
+
|
|
431
|
+
mlost = trapezoid(2. * Pi * r[::5] * sig_lost, r[::5])
|
|
432
|
+
print("tf = ", times_loc[-1] / Myr, " Myr. Total mass lost : ", mlost[-1] / Mearth,
|
|
433
|
+
"Fraction of total mass loss ", 100 * mlost[-1] / (Mbelt * f_ice), "%")
|
|
434
|
+
# g.loglog(times_loc, [mlost, minit])
|
|
435
|
+
# return res, times_loc, res_final
|
|
436
|
+
return np.gradient(sig_lost, times_loc, axis=0), times_loc, res_final
|
|
437
|
+
|
|
438
|
+
times_intermediates = np.linspace(tinit, tmax, 100)[1:]
|
|
439
|
+
# times_intermediates = np.linspace(tinit, tmax, 4)[1:]
|
|
440
|
+
res_final = np.zeros(len(r[::5]) * len(p))
|
|
441
|
+
res = []
|
|
442
|
+
times = []
|
|
443
|
+
ti: np.float64 = tinit
|
|
444
|
+
dtinit = np.double(1e-10) * (times_intermediates[1] - ti)
|
|
445
|
+
for t in times_intermediates:
|
|
446
|
+
res_loc, times_loc, res_final = integrate(ti=ti, tf=t, yinit=res_final, dtinit=dtinit)
|
|
447
|
+
dtinit = times_loc[-1] - times_loc[-2]
|
|
448
|
+
print(res_loc.shape, times_loc.shape, len(r))
|
|
449
|
+
times.extend(list(times_loc))
|
|
450
|
+
res.extend(list(res_loc))
|
|
451
|
+
print(len(res), res[-1].shape)
|
|
452
|
+
ti = times_loc[-1]
|
|
453
|
+
print("Initial gas mass : ", m_init)
|
|
454
|
+
return np.array([linear_interp(r, r[::5], sig) for sig in res]), np.array(times), m_init, p
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def mbelt_coll(t: np.float64, smin: np.float64 = np.double(1.e-8) * C_L,
|
|
458
|
+
smax: np.float64 = np.double(4.e6) * C_L,
|
|
459
|
+
sb: np.float64 = np.double(316) * C_L, qp: np.float64 = np.double(3),
|
|
460
|
+
qs: np.float64 = np.double(11 / 6), qg: np.float64 = np.double(1.67), e: np.float64 = np.double(0.075),
|
|
461
|
+
i: np.float64 = None, r: np.float64 = None, dr: np.float64 = np.double(4. * au),
|
|
462
|
+
As: np.float64 = np.double(5.) * C_E / C_M, bs: np.float64 = np.double(-0.1),
|
|
463
|
+
bg: np.float64 = np.double(0.5), M0: np.float64 = KB_Mbelt, Ms: np.float64 = Ms,
|
|
464
|
+
rho_ast: np.float64 = KB_rho_refr, method_root: str = "lm",
|
|
465
|
+
st_est: np.float64 | np.ndarray = None) -> np.float64 | np.ndarray:
|
|
466
|
+
"""
|
|
467
|
+
Estimate the masse of a collisional belt as function of time (formula 38 of Löhne et al. 2008)
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
|
|
472
|
+
t : np.float64
|
|
473
|
+
The time
|
|
474
|
+
smin : np.float64
|
|
475
|
+
Minimum size of bodys
|
|
476
|
+
|
|
477
|
+
smax : np.float64
|
|
478
|
+
Maximum size of bodys
|
|
479
|
+
|
|
480
|
+
sb : np.float64
|
|
481
|
+
Medium size
|
|
482
|
+
|
|
483
|
+
qp : np.float64
|
|
484
|
+
Primordial slope as defined in Löhne et al. 2008
|
|
485
|
+
|
|
486
|
+
qs : np.float64
|
|
487
|
+
Intermediate slope
|
|
488
|
+
|
|
489
|
+
qg : np.float64
|
|
490
|
+
Slope for bodys in collisional regim
|
|
491
|
+
|
|
492
|
+
e : np.float64
|
|
493
|
+
Eccentricity
|
|
494
|
+
|
|
495
|
+
i : np.float64
|
|
496
|
+
Inclination
|
|
497
|
+
|
|
498
|
+
r : np.float64
|
|
499
|
+
Distance of the center of the belt to the central star
|
|
500
|
+
|
|
501
|
+
dr : np.float64
|
|
502
|
+
Radial extension of the belt
|
|
503
|
+
|
|
504
|
+
As : np.float64
|
|
505
|
+
Minimal disruption energy (see formula 1 of Löhne et al. 2008)
|
|
506
|
+
|
|
507
|
+
bs : np.float64
|
|
508
|
+
Slope of the massique disruption energy (see formula 1 of Löhne et al 2008)
|
|
509
|
+
|
|
510
|
+
bg : np.float64
|
|
511
|
+
Slope of the massique disruption energy (see formula 1 of Löhne et al. 2008)
|
|
512
|
+
|
|
513
|
+
M0 : np.float64
|
|
514
|
+
Initial belt's mass
|
|
515
|
+
|
|
516
|
+
Ms : np.float64
|
|
517
|
+
Central star's mass
|
|
518
|
+
|
|
519
|
+
rho_ast : np.float64
|
|
520
|
+
Density of asteroids/KBO
|
|
521
|
+
|
|
522
|
+
method_root : str, optional, default="lm"
|
|
523
|
+
The method for the root-finding routine used to find st the size such as tau(st)=t
|
|
524
|
+
|
|
525
|
+
st_est : np.float64
|
|
526
|
+
Estimation of st
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
np.float64 | np.ndarray
|
|
531
|
+
mbelt
|
|
532
|
+
|
|
533
|
+
References
|
|
534
|
+
----------
|
|
535
|
+
Löhne et al. 2008
|
|
536
|
+
|
|
537
|
+
"""
|
|
538
|
+
# qp: np.float64 = np.double(1.9)
|
|
539
|
+
if r is None:
|
|
540
|
+
r: np.float64 = a0
|
|
541
|
+
if i is None:
|
|
542
|
+
i: np.float64 = e / np.double(2.)
|
|
543
|
+
|
|
544
|
+
# dr: np.float64 = r / np.double(4.)
|
|
545
|
+
# As: np.float64 = np.double(5e2) * C_E / C_M
|
|
546
|
+
QDb: np.float64 = As * ((sb / C_L) ** (3. * bs) + (sb / (1e3 * C_L)) ** (3. * bg))
|
|
547
|
+
f_ei: np.float64 = np.sqrt((5 / 4) * e * e + i * i)
|
|
548
|
+
# Qd_smax: np.float64 = QDb * ((smax / sb) ** (3. * bg))
|
|
549
|
+
Qd_smax: np.float64 = As * ((smax / C_L) ** (3. * bs) + (smax / (1e3 * C_L)) ** (3. * bg))
|
|
550
|
+
XC_smax: np.float64 = (2. * Qd_smax * r / (f_ei * f_ei * G * Ms)) ** (1 / 3.)
|
|
551
|
+
G_smax: np.float64 = ((XC_smax ** (5. - 3. * qg) - 1.)
|
|
552
|
+
+ (2. * (qg - 5. / 3.) / (qg - 4 / 3))
|
|
553
|
+
* (XC_smax ** (4. - 3. * qg) - 1.)
|
|
554
|
+
+ ((qg - 5. / 3.) / (qg - 1.))
|
|
555
|
+
* (XC_smax ** (3. - 3. * qg) - 1.))
|
|
556
|
+
|
|
557
|
+
def tau(s: np.float64 | np.ndarray):
|
|
558
|
+
"""
|
|
559
|
+
The collisional timescale for bodys of size s
|
|
560
|
+
|
|
561
|
+
Parameters
|
|
562
|
+
----------
|
|
563
|
+
s: np.float64 | np.ndarray
|
|
564
|
+
The size
|
|
565
|
+
Returns
|
|
566
|
+
-------
|
|
567
|
+
np.float64 | np.ndarray
|
|
568
|
+
|
|
569
|
+
"""
|
|
570
|
+
Qd_s: np.float64 = As * ((s / C_L) ** (3. * bs) + (s / (1e3 * C_L)) ** (3. * bg))
|
|
571
|
+
XC_s: np.float64 = (2. * Qd_s * r / (f_ei * f_ei * G * Ms)) ** (1 / 3.)
|
|
572
|
+
G_qp_s: np.float64 = ((XC_s ** (5. - 3. * qp) - (smax / s) ** (5. - 3. * qp))
|
|
573
|
+
+ (2. * (qp - 5. / 3.) / (qp - 4 / 3))
|
|
574
|
+
* (XC_s ** (4. - 3. * qp) - (smax / s) ** (4. - 3. * qp))
|
|
575
|
+
+ ((qp - 5. / 3.) / (qp - 1.))
|
|
576
|
+
* (XC_s ** (3. - 3. * qp) - (smax / s) ** (3. - 3. * qp)))
|
|
577
|
+
return ((16 * Pi * rho_ast / (3. * M0))
|
|
578
|
+
* ((s / smax) ** (3. * qp - 5))
|
|
579
|
+
* (smax * (r ** (5. / 2.)) * dr / np.sqrt(G * Ms))
|
|
580
|
+
* ((qp - 5. / 3.) / (2 - qp))
|
|
581
|
+
* (1. - ((smin / smax) ** (6. - 3. * qp)))
|
|
582
|
+
* i / (f_ei * G_qp_s))
|
|
583
|
+
|
|
584
|
+
tau_max: np.float64 = ((16. * Pi * rho_ast / (3. * M0)) * smax
|
|
585
|
+
* ((r ** (5 / 2)) * dr / np.sqrt(G * Ms))
|
|
586
|
+
* (((qg - 5. / 3.) / (2. - qp)) / (1. - ((smin / smax) ** (6. - 3. * qp))))
|
|
587
|
+
* i / (f_ei * G_smax)) # Maximum collisional timescale
|
|
588
|
+
|
|
589
|
+
# print("tau max=", tau_max, tau(smax))
|
|
590
|
+
|
|
591
|
+
def rech_st(s: np.float64 | np.ndarray) -> np.float64 | np.ndarray:
|
|
592
|
+
"""
|
|
593
|
+
The function is used to find the scale for which tau(s)=t
|
|
594
|
+
|
|
595
|
+
Parameters
|
|
596
|
+
----------
|
|
597
|
+
s: np.float64 | np.ndarray
|
|
598
|
+
The size to test
|
|
599
|
+
|
|
600
|
+
Returns
|
|
601
|
+
-------
|
|
602
|
+
np.float64 | np.ndarray
|
|
603
|
+
A parameter null where tau(s)=t
|
|
604
|
+
"""
|
|
605
|
+
return (t - tau(s)) / t
|
|
606
|
+
|
|
607
|
+
tau_b: np.float64 = tau(sb)
|
|
608
|
+
if st_est is None: # Estimation of st such as tau(s_t)=t
|
|
609
|
+
st_est: np.float64 | np.ndarray = sb * ((t / tau_b) ** (1 / (3. * qp - 5. + 3. * (qp - 1) * bg)))
|
|
610
|
+
st: np.float64 | np.ndarray = root(rech_st, st_est, method=method_root, tol=1e-12).x
|
|
611
|
+
st = st[0]
|
|
612
|
+
|
|
613
|
+
if isinstance(t, float | np.float64) and t > tau_max:
|
|
614
|
+
return zero
|
|
615
|
+
if t < tau_b:
|
|
616
|
+
sb: np.float64 = st
|
|
617
|
+
|
|
618
|
+
C1: np.float64 = M0 / (1. - (smin / smax) ** (6. - 3. * qp))
|
|
619
|
+
C2: np.float64 = (((sb / smax) ** (6. - 3. * qp))
|
|
620
|
+
* (1. - (2. - qp) / (2. - qg)))
|
|
621
|
+
C3: np.float64 = (((sb / smax) ** (6. - 3. * qp))
|
|
622
|
+
* ((2. - qp) / (2. - qs) - (2. - qp) / (2. - qg)))
|
|
623
|
+
C4: np.float64 = (((sb / smax) ** (3. * qs - 3. * qp))
|
|
624
|
+
* ((smin / smax) ** (6. - 3. * qs))
|
|
625
|
+
* (2. - qp) / (2. - qs))
|
|
626
|
+
alpha_st: np.float64 = np.double(1. / (3. * qp - 5 + 3. * (qp - 1.) * bg))
|
|
627
|
+
|
|
628
|
+
res = C1 * (1 - C2 * ((st / sb) ** (6. - 3. * qp)) + ((st / sb) ** (3. * qg - 3. * qp)) * (C3 - C4)) / (
|
|
629
|
+
1 + t / tau_max)
|
|
630
|
+
|
|
631
|
+
if isinstance(t, np.ndarray):
|
|
632
|
+
res[t > tau_max] *= zero
|
|
633
|
+
|
|
634
|
+
return res
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class SolrhoBelt:
|
|
638
|
+
def __init__(self,
|
|
639
|
+
sublimation_model: str,
|
|
640
|
+
Mbelt: np.float64,
|
|
641
|
+
Ms: np.float64,
|
|
642
|
+
a0: np.float64, delta_a: np.float64,
|
|
643
|
+
tinit: np.float64, tmax: np.float64,
|
|
644
|
+
rho_refr: np.float64, rho_ice: np.float64, f_ice: np.float64,
|
|
645
|
+
A_ast: np.float64, K: np.float64,
|
|
646
|
+
Its: int, Phi: np.float64,
|
|
647
|
+
dmin: np.float64, sb: np.float64, dmax: np.float64,
|
|
648
|
+
e: np.float64, i: np.float64, As: np.float64, bs: np.float64, bg: np.float64,
|
|
649
|
+
Its_p: int, rp: np.float64, Itt: int,
|
|
650
|
+
t0_diss: np.float64, t1_diss: np.float64,
|
|
651
|
+
radius: np.ndarray[np.float64] | None = None,
|
|
652
|
+
kwargs_N: dict = None, sig_dots: np.ndarray = None, tps: np.ndarray = None,
|
|
653
|
+
m_init: np.float64 = zero,
|
|
654
|
+
const_mdot: np.float64 = zero, **kwargs
|
|
655
|
+
):
|
|
656
|
+
"""
|
|
657
|
+
Initialise a SolrhoBelt object
|
|
658
|
+
This object is used to get the gas surface density generation rate thru its method_root
|
|
659
|
+
interp_sig_dot(t)
|
|
660
|
+
|
|
661
|
+
Parameters
|
|
662
|
+
----------
|
|
663
|
+
sublimation_model : str, {"none", "thermal_full", "constant_rate"}
|
|
664
|
+
The model use to estimate the gas generation rate :
|
|
665
|
+
- none : No gas is produced
|
|
666
|
+
- thermal_full : the gas is produced by sublimation : all sublimation, thermal diffusion and molecular
|
|
667
|
+
diffusion are take into acompte
|
|
668
|
+
- constant_rate : The gas is produced at a constant rate of const_mdot
|
|
669
|
+
Mbelt : np.float64
|
|
670
|
+
The total belt's mass
|
|
671
|
+
Ms : np.float64
|
|
672
|
+
The central star's mass
|
|
673
|
+
a0 : np.float64
|
|
674
|
+
The belt's semi major axis (located at the middle of the belt)
|
|
675
|
+
delta_a : np.float64
|
|
676
|
+
The belt's (half) size
|
|
677
|
+
tinit : np.float64
|
|
678
|
+
The initial time
|
|
679
|
+
tmax : np.float64
|
|
680
|
+
The final time
|
|
681
|
+
rho_refr : np.float64
|
|
682
|
+
The density of refractory materials
|
|
683
|
+
rho_ice : np.float64
|
|
684
|
+
The ice's density
|
|
685
|
+
f_ice : np.float64
|
|
686
|
+
The ice to refractory mass ratio
|
|
687
|
+
A_ast : np.float64
|
|
688
|
+
Asteroids/KBO albedo
|
|
689
|
+
K : np.float64
|
|
690
|
+
Thermal diffusion coefficient
|
|
691
|
+
Its : np.float64
|
|
692
|
+
Number of spatial iterations
|
|
693
|
+
Phi : np.float64
|
|
694
|
+
Porosity (dimensionless number between 0 to 1)
|
|
695
|
+
dmin : np.float64
|
|
696
|
+
The minimum radius of asteroids/KBO
|
|
697
|
+
sb : np.float64
|
|
698
|
+
The final transition size between collisional and primordial regim of asteroids/KBO
|
|
699
|
+
dmax : np.float64
|
|
700
|
+
The maximum radius of asteroids/KBO
|
|
701
|
+
e : np.float64
|
|
702
|
+
Eccentricity
|
|
703
|
+
i : np.float64
|
|
704
|
+
Inclinaison
|
|
705
|
+
As : np.float64
|
|
706
|
+
Minimal disruption energy (see formula 1 of Löhne et al 2008)
|
|
707
|
+
bs : np.float64
|
|
708
|
+
Slope of the massique disruption energy (see formula 1 of Löhne et al 2008)
|
|
709
|
+
bg : np.float64
|
|
710
|
+
Slope of the massique disruption energy (see formula 1 of Löhne et al 2008)
|
|
711
|
+
Its_p : np.float64
|
|
712
|
+
The number of steps in diameters space
|
|
713
|
+
rp : np.float64
|
|
714
|
+
The pore's radius to estimate the gas diffusion thru the solid
|
|
715
|
+
Itt : np.float64
|
|
716
|
+
Number of spatial iterations
|
|
717
|
+
t0_diss : np.float64
|
|
718
|
+
Belt's lifetime before dissipation
|
|
719
|
+
t1_diss : np.float64
|
|
720
|
+
Dissipation timescale
|
|
721
|
+
radius : np.ndarray
|
|
722
|
+
Table of radius
|
|
723
|
+
kwargs_N : dict
|
|
724
|
+
keywords arguments for the size distribution of asteroids/KBO
|
|
725
|
+
sig_dots : np.ndarray
|
|
726
|
+
table of sigma dot as function of time and radius (shape=(Ttt, Its)) if it has already been calculated
|
|
727
|
+
This table is interpolated with the CubicSpline function to get interp_sig_dot(t)
|
|
728
|
+
const_mdot : np.float64, optional, default=0.
|
|
729
|
+
The mass generation rate for the `constant_rate` sublimation model
|
|
730
|
+
|
|
731
|
+
"""
|
|
732
|
+
self.sublimation_model: str = sublimation_model
|
|
733
|
+
self.const_mdot: np.float64 = const_mdot
|
|
734
|
+
self.Mbelt: np.float64 = Mbelt
|
|
735
|
+
self.Ms: np.float64 = Ms
|
|
736
|
+
self.Abelt: np.float64 = A_ast
|
|
737
|
+
self.K: np.float64 = K
|
|
738
|
+
self.a0: np.float64 = a0
|
|
739
|
+
self.delta_a: np.float64 = delta_a
|
|
740
|
+
self.t0: np.float64 = tinit
|
|
741
|
+
self.tmax: np.float64 = tmax
|
|
742
|
+
self.rho_refr: np.float64 = rho_refr
|
|
743
|
+
self.rho_ice: np.float64 = rho_ice
|
|
744
|
+
self.Its: int = Its
|
|
745
|
+
self.Its_p: int = Its_p
|
|
746
|
+
self.Itt: int = Itt
|
|
747
|
+
self.f_ice: np.float64 = f_ice # ice to refractory mass ratios
|
|
748
|
+
self.Phi: np.float64 = Phi # Porosity
|
|
749
|
+
self.rp: np.float64 = rp # Pore's radius
|
|
750
|
+
self.dmin: np.float64 = dmin
|
|
751
|
+
self.sb: np.float64 = sb
|
|
752
|
+
self.dmax: np.float64 = dmax
|
|
753
|
+
self.e: np.float64 = e
|
|
754
|
+
self.i: np.float64 = i
|
|
755
|
+
self.As: np.float64 = As
|
|
756
|
+
self.bs: np.float64 = bs
|
|
757
|
+
self.bg: np.float64 = bg
|
|
758
|
+
self.m_init: np.float64 = m_init
|
|
759
|
+
if radius is None:
|
|
760
|
+
self.r = np.linspace(a0 - delta_a / 2., a0 + delta_a /2., Its)
|
|
761
|
+
else:
|
|
762
|
+
self.r = np.copy(radius)
|
|
763
|
+
self.Its = len(self.r)
|
|
764
|
+
# self.p = np.linspace(dmin / 2., dmax / 2., Its_p)
|
|
765
|
+
self.p = np.geomspace(dmin / 2., dmax / 2., Its_p)
|
|
766
|
+
if tps is None:
|
|
767
|
+
self.tps: np.ndarray = tinit + np.geomspace(yr, tmax - tinit, Itt)
|
|
768
|
+
else:
|
|
769
|
+
self.tps: np.ndarray = tps
|
|
770
|
+
|
|
771
|
+
if kwargs_N is None:
|
|
772
|
+
self.kwargs_N = dict(amax=np.double(1.e6) * C_L,
|
|
773
|
+
abig=np.double(120e3) * C_L,
|
|
774
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
775
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
776
|
+
rho=self.rho_refr)
|
|
777
|
+
else:
|
|
778
|
+
self.kwargs_N = kwargs_N.copy()
|
|
779
|
+
self.sig_dots = None
|
|
780
|
+
|
|
781
|
+
if sig_dots is not None:
|
|
782
|
+
self.sig_dots = sig_dots
|
|
783
|
+
# print("test :", len(self.tps), sig_dots.shape)
|
|
784
|
+
tps, idx = np.unique(self.tps, return_index=True)
|
|
785
|
+
self.tps = np.copy(tps)
|
|
786
|
+
if self.sig_dots.shape[0] == len(self.tps):
|
|
787
|
+
self.sig_dots = (self.sig_dots[idx]).copy()
|
|
788
|
+
else:
|
|
789
|
+
self.sig_dots = (self.sig_dots.T[idx]).copy()
|
|
790
|
+
|
|
791
|
+
def linear_interp(t_new):
|
|
792
|
+
if isinstance(t_new, list | np.ndarray):
|
|
793
|
+
return np.array([linear_interp(t) for t in t_new])
|
|
794
|
+
|
|
795
|
+
i = np.argmin(np.abs(t_new - np.array(self.tps)))
|
|
796
|
+
if i == 0 or (i < len(self.tps) - 1 and t_new > self.tps[i]):
|
|
797
|
+
t1 = self.tps[i]
|
|
798
|
+
t2 = self.tps[i + 1]
|
|
799
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
800
|
+
return (1 - alpha) * self.sig_dots[i] + alpha * self.sig_dots[i + 1]
|
|
801
|
+
else:
|
|
802
|
+
t1 = self.tps[i - 1]
|
|
803
|
+
t2 = self.tps[i]
|
|
804
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
805
|
+
return (1 - alpha) * self.sig_dots[i - 1] + alpha * self.sig_dots[i]
|
|
806
|
+
|
|
807
|
+
self.interp_sig_dot = linear_interp
|
|
808
|
+
# self.interp_sig_dot = linear_interpolation(self.tps, self.sig_dots)
|
|
809
|
+
|
|
810
|
+
# def interp_sig_dot(t: np.float64 | np.ndarray) -> np.ndarray[np.float64]:
|
|
811
|
+
# return np.interp(t, self.tps, self.sig_dots)
|
|
812
|
+
# self.interp_sig_dot = interp_sig_dot
|
|
813
|
+
# self.interp_sig_dot = linear_interpolation(self.tps, sig_dots)
|
|
814
|
+
# self.interp_sig_dot = PchipInterpolator(self.tps, sig_dots)
|
|
815
|
+
# self.interp_sig_dot = CubicSpline(self.tps - 0.1 * Myr, sig_dots)
|
|
816
|
+
# self.interp_sig_dot = FloaterHormannInterpolator(self.tps, sig_dots)
|
|
817
|
+
# self.interp_log_sig_dot = CubicSpline(self.tps, np.maximum(np.log10(self.sig_dots), 1e-200))
|
|
818
|
+
#
|
|
819
|
+
# def interp_sig_dot(t: np.float64 | np.ndarray) -> np.ndarray[np.float64]:
|
|
820
|
+
# return 10 ** self.interp_log_sig_dot(t)
|
|
821
|
+
#
|
|
822
|
+
# self.interp_sig_dot = interp_sig_dot
|
|
823
|
+
elif self.sublimation_model == "none":
|
|
824
|
+
def null(t: np.float64 | np.ndarray) -> np.float64:
|
|
825
|
+
return zero
|
|
826
|
+
|
|
827
|
+
self.interp_sig_dot = null
|
|
828
|
+
|
|
829
|
+
elif self.sublimation_model == "thermal_full":
|
|
830
|
+
# self.sig_dots, self.tps, self.m_init, self.p = sigma_dot(r=self.r, tps=self.tps, kwargsN=self.kwargs_N, **self.const())
|
|
831
|
+
# print("test shape", self.sig_dots.shape, self.r.shape, self.tps.shape)
|
|
832
|
+
# M = trapezoid(trapezoid(2. * np.pi * self.r * self.sig_dots, self.r, axis=1), self.tps)
|
|
833
|
+
# print("Test mass", M / Mearth)
|
|
834
|
+
# # while M > 1.1 * self.Mbelt * self.f_ice:
|
|
835
|
+
# # self.Itt = int(self.Itt * 1.2)
|
|
836
|
+
# # print("Integration failure, restarting", self.Itt)
|
|
837
|
+
# # self.sig_dots, self.tps = sigma_dot(r=self.r, p=self.p, tps=self.tps, kwargsN=self.kwargs_N,
|
|
838
|
+
# # **self.const())
|
|
839
|
+
# # M = trapezoid(trapezoid(2. * np.pi * self.r * self.sig_dots, self.r), self.tps)
|
|
840
|
+
# # self.sig_dots = np.maximum(savgol_filter(self.sig_dots, 7, 3, axis=1), zero)
|
|
841
|
+
# # dissipation of the belt
|
|
842
|
+
# # if np.any(self.tps - 0.1 * Myr > t0_diss):
|
|
843
|
+
# # self.sig_dots[self.tps - 0.1 * Myr > t0_diss] *= np.exp(
|
|
844
|
+
# # -(self.tps[self.tps - 0.1 * Myr > t0_diss] - t0_diss) / t1_diss)
|
|
845
|
+
# tps_u, idx = np.unique(self.tps, return_index=True)
|
|
846
|
+
# self.tps = tps_u.copy()
|
|
847
|
+
# self.Itt = len(self.tps)
|
|
848
|
+
# self.sig_dots = (self.sig_dots[idx]).copy()
|
|
849
|
+
|
|
850
|
+
self.sig_dots = sig_dot_full_diff(tps=self.tps, r=self.r, const=self.const(),
|
|
851
|
+
kwargsN=self.kwargs_N, It_d=200)
|
|
852
|
+
|
|
853
|
+
minit = 1e-1 * self.Mbelt * self.f_ice
|
|
854
|
+
if np.any(cumulative_trapezoid(trapezoid(2. * Pi * self.r * self.sig_dots, self.r),
|
|
855
|
+
self.tps, initial=0.) >= minit):
|
|
856
|
+
iinit = np.argwhere(cumulative_trapezoid(trapezoid(2. * Pi * self.r * self.sig_dots, self.r),
|
|
857
|
+
self.tps, initial=0.) >= minit)[0, 0]
|
|
858
|
+
if self.tps[iinit] - self.t0 < 0.1 * Myr:
|
|
859
|
+
self.m_init = trapezoid(trapezoid(2. * Pi * self.r * self.sig_dots[:iinit], self.r),
|
|
860
|
+
self.tps[:iinit])
|
|
861
|
+
self.tps -= self.tps[iinit] - tinit
|
|
862
|
+
else:
|
|
863
|
+
self.m_init = 0.
|
|
864
|
+
else:
|
|
865
|
+
iinit= 0
|
|
866
|
+
minit = zero
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def linear_interp(t_new):
|
|
871
|
+
if isinstance(t_new, list | np.ndarray):
|
|
872
|
+
return np.array([linear_interp(t) for t in t_new])
|
|
873
|
+
|
|
874
|
+
i = np.argmin(np.abs(t_new - np.array(self.tps)))
|
|
875
|
+
if i == 0 or (i < len(self.tps) - 1 and t_new > self.tps[i]):
|
|
876
|
+
t1 = self.tps[i]
|
|
877
|
+
t2 = self.tps[i + 1]
|
|
878
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
879
|
+
return (1 - alpha) * self.sig_dots[i] + alpha * self.sig_dots[i + 1]
|
|
880
|
+
else:
|
|
881
|
+
t1 = self.tps[i - 1]
|
|
882
|
+
t2 = self.tps[i]
|
|
883
|
+
alpha = (t_new - t1) / (t2 - t1)
|
|
884
|
+
return (1 - alpha) * self.sig_dots[i - 1] + alpha * self.sig_dots[i]
|
|
885
|
+
|
|
886
|
+
self.interp_sig_dot = linear_interp
|
|
887
|
+
|
|
888
|
+
# self.interp_sig_dot = linear_interpolation(self.tps, self.sig_dots)
|
|
889
|
+
# self.interp_sig_dot = PchipInterpolator(self.tps, self.sig_dots)
|
|
890
|
+
# self.interp_sig_dot = CubicSpline(self.tps - 0.1 * Myr, self.sig_dots)
|
|
891
|
+
# self.interp_log_sig_dot = CubicSpline(self.tps, np.maximum(np.log10(self.sig_dots), 1e-200))
|
|
892
|
+
#
|
|
893
|
+
# def interp_sig_dot(t: np.float64 | np.ndarray) -> np.ndarray[np.float64]:
|
|
894
|
+
# return 10 ** self.interp_log_sig_dot(t)
|
|
895
|
+
# self.interp_sig_dot = interp_sig_dot
|
|
896
|
+
# self.interp_sig_dot = FloaterHormannInterpolator(self.tps, self.sig_dots)
|
|
897
|
+
# selot = CubicSpline(self.tps, self.sig_dots)
|
|
898
|
+
elif self.sublimation_model == "collisions":
|
|
899
|
+
|
|
900
|
+
rho = rho_refr
|
|
901
|
+
# with f_ice = rho_ice / rho
|
|
902
|
+
|
|
903
|
+
mbelt_: np.ndarray = np.array([(mbelt_coll(t, smax=self.dmax, smin=self.dmin,
|
|
904
|
+
sb=self.sb, qp=np.double((-self.kwargs_N["qh"] + 2.) / 3.),
|
|
905
|
+
qs=np.double((-self.kwargs_N["ql"] + 2.) / 3.),
|
|
906
|
+
qg=np.double((-self.kwargs_N["qm"] + 2.) / 3.), e=self.e,
|
|
907
|
+
i=self.i, r=self.a0, dr=self.delta_a, As=self.As,
|
|
908
|
+
M0=self.Mbelt, rho_ast=rho,
|
|
909
|
+
method_root="lm", bs=self.bs, bg=self.bg)) for t in self.tps])
|
|
910
|
+
|
|
911
|
+
mdot_: np.ndarray = - self.f_ice * np.gradient(mbelt_, self.tps - 0.1 * Myr)
|
|
912
|
+
# dissipation of the belt
|
|
913
|
+
if np.any(self.tps - 0.1 * Myr > t0_diss):
|
|
914
|
+
mdot_[self.tps - 0.1 * Myr > t0_diss] *= np.exp(
|
|
915
|
+
-(self.tps[self.tps - 0.1 * Myr > t0_diss] - t0_diss) / t1_diss)
|
|
916
|
+
sigmad0: np.float64 = mdot_ / (
|
|
917
|
+
4. * Pi * a0 * a0 * (np.sqrt(1. + delta_a / a0) - np.sqrt(1. - delta_a / a0)))
|
|
918
|
+
self.sig_dots: np.ndarray = np.full((len(self.tps), len(self.r)), sigmad0)
|
|
919
|
+
self.sig_dots *= ((self.r / self.a0) ** (-3. / 2.))
|
|
920
|
+
self.interp_sig_dot = CubicSpline(self.tps - 0.5 * Myr, self.sig_dots)
|
|
921
|
+
|
|
922
|
+
elif self.sublimation_model == "thermal_diff":
|
|
923
|
+
# The sublimation timescale and molecular diffusion timescale are neglected
|
|
924
|
+
def n(diameter: np.float64):
|
|
925
|
+
return number_density(diameter, **self.kwargs_N)
|
|
926
|
+
|
|
927
|
+
rho = rho_refr
|
|
928
|
+
|
|
929
|
+
def sig_dot_therm(t: np.float64):
|
|
930
|
+
t0 = 1e3 * yr
|
|
931
|
+
|
|
932
|
+
mdot_t = (2. * Pi * rho * self.f_ice * (self.K ** (3 / 2))
|
|
933
|
+
* quad(n, max(self.kwargs_N["amin"], 2. * np.sqrt(self.K * (t + t0))),
|
|
934
|
+
self.kwargs_N["amax"])[0] * np.sqrt(t))
|
|
935
|
+
if np.isnan(mdot_t):
|
|
936
|
+
mdot_t = zero
|
|
937
|
+
elif mdot_t < 0: # the integration with quad has not worked
|
|
938
|
+
diams = np.geomspace(max(self.kwargs_N["amin"], 2. * np.sqrt(self.K * (t + t0))),
|
|
939
|
+
self.kwargs_N["amax"], 1000)
|
|
940
|
+
mdot_t = (2. * Pi * rho * f_ice * (self.K ** (3 / 2))
|
|
941
|
+
* trapezoid([n(d) for d in diams], diams) * np.sqrt(t))
|
|
942
|
+
if mdot_t < 0: # the integration with quad has not worked
|
|
943
|
+
diams = np.geomspace(max(self.kwargs_N["amin"], 2. * np.sqrt(self.K * (t + t0))),
|
|
944
|
+
self.kwargs_N["amax"], 10000)
|
|
945
|
+
mdot_t = (2. * Pi * rho * f_ice * (self.K ** (3 / 2))
|
|
946
|
+
* trapezoid([n(d) for d in diams], diams) * np.sqrt(t))
|
|
947
|
+
|
|
948
|
+
sigmad0: np.float64 = mdot_t / (
|
|
949
|
+
4. * Pi * a0 * a0 * (np.sqrt(1. + delta_a / a0) - np.sqrt(1. - delta_a / a0)))
|
|
950
|
+
if t > t0_diss:
|
|
951
|
+
sigmad0 *= np.exp(-(t - t0_diss) / t1_diss)
|
|
952
|
+
return sigmad0 * ((self.r / self.a0) ** (-3. / 2.))
|
|
953
|
+
|
|
954
|
+
self.interp_sig_dot = sig_dot_therm
|
|
955
|
+
|
|
956
|
+
elif self.sublimation_model == "constant_rate":
|
|
957
|
+
# norm_gauss = quad(lambda x: np.exp(-x * x / 2.), 0, 4.)[0]
|
|
958
|
+
# sigmar: np.float64 = np.double(self.delta_a / (2. * np.sqrt(2. * np.log(2))))
|
|
959
|
+
# sigmap_0: np.float64 = m_dot / (4. * Pi * sigmar * self.a0 * norm_gauss)
|
|
960
|
+
# sigmap: np.ndarray = sigmap_0 * np.exp(- (self.r - self.a0) * (self.r - self.a0) / (
|
|
961
|
+
# 2. * sigmar * sigmar))
|
|
962
|
+
# sigmap_0: np.float64 = m_dot / (4. * Pi * self.a0 * self.a0 * (
|
|
963
|
+
# np.sqrt(1 + self.delta_a / (2 * self.a0)) - np.sqrt(1 - self.delta_a / (2 * self.a0))))
|
|
964
|
+
sigmap_0: np.float64 = const_mdot / (4. * Pi * self.a0 * self.a0 * (
|
|
965
|
+
np.sqrt(1 + self.delta_a / self.a0) - np.sqrt(1 - self.delta_a / self.a0)))
|
|
966
|
+
sigmap: np.ndarray = sigmap_0 * ((self.r / self.a0) ** (-3 / 2))
|
|
967
|
+
|
|
968
|
+
def cons_rate(t: np.float64 | np.ndarray) -> np.ndarray:
|
|
969
|
+
if t < t0_diss:
|
|
970
|
+
return sigmap
|
|
971
|
+
else:
|
|
972
|
+
return sigmap * np.exp(-max((t - t0_diss) / t1_diss, 150))
|
|
973
|
+
|
|
974
|
+
self.interp_sig_dot = cons_rate
|
|
975
|
+
|
|
976
|
+
else:
|
|
977
|
+
raise UserWarning("The sublimation model ", self.sublimation_model, " is not implemented (yet)")
|
|
978
|
+
|
|
979
|
+
def const(self, prefix: str = "") -> dict:
|
|
980
|
+
return {prefix + "sublimation_model": self.sublimation_model,
|
|
981
|
+
prefix + "tinit": self.t0, prefix + "tmax": self.tmax,
|
|
982
|
+
prefix + "Mbelt": self.Mbelt,
|
|
983
|
+
prefix + "Ms": self.Ms, prefix + "Abelt": self.Abelt, prefix + "K": self.K,
|
|
984
|
+
prefix + "a0": self.a0, prefix + "delta_a": self.delta_a, prefix + "t0": self.t0,
|
|
985
|
+
prefix + "rho_refr": self.rho_refr, prefix + "rho_ice": self.rho_ice,
|
|
986
|
+
prefix + "Its": self.Its, prefix + "Its_p": self.Its_p,
|
|
987
|
+
prefix + "f_ice": self.f_ice,
|
|
988
|
+
prefix + "Phi": self.Phi, prefix + "rp": self.rp,
|
|
989
|
+
prefix + "Itt": self.Itt,
|
|
990
|
+
prefix + "dmin": self.dmin,
|
|
991
|
+
prefix + "dmax": self.dmax,
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
def return_kwargsN(self, prefix: str = "") -> dict:
|
|
995
|
+
res = {}
|
|
996
|
+
for k in self.kwargs_N.keys():
|
|
997
|
+
res[prefix + k] = self.kwargs_N[k]
|
|
998
|
+
return res
|
|
999
|
+
|
|
1000
|
+
def graph_image_surface_density(self,
|
|
1001
|
+
vmin: np.float64 | str = 1e-10 * Mearth / (au * au * Myr),
|
|
1002
|
+
vmax: np.float64 | str = None,
|
|
1003
|
+
size_time: int = 500, size_radius: int = 1000,
|
|
1004
|
+
cmap: str = "inferno", nb_levels: int = 10,
|
|
1005
|
+
color_min: str | tuple = None, color_max: str | tuple = None,
|
|
1006
|
+
show: bool = True, save: bool = False, directory: str = None,
|
|
1007
|
+
plot_label_levels: bool = True,
|
|
1008
|
+
**kwargs_ax
|
|
1009
|
+
) -> g.Graphique:
|
|
1010
|
+
"""
|
|
1011
|
+
Build a Graphique that represant the evolution of surface density generation rate
|
|
1012
|
+
as function of time and radius
|
|
1013
|
+
|
|
1014
|
+
Parameters
|
|
1015
|
+
----------
|
|
1016
|
+
vmin : np.float64 | str , optional, {"auto", np.loat64}, default = 1e-10 * Mearth / Myr
|
|
1017
|
+
The minimum value of surface density to represant in the result (below it will be considered as saturated)
|
|
1018
|
+
If "auto", the minimal value will be the minimal value of the surface density represanted
|
|
1019
|
+
vmax : np.float64 | str, optional, {"auto", np.loat64},
|
|
1020
|
+
The maximum value of surface density to represant in the result (above it will be considered as saturated)
|
|
1021
|
+
Default, v_max is not considered like for "auto" where the maximal value will be the maximal value of the surface density represanted
|
|
1022
|
+
|
|
1023
|
+
size_time : int, optional, default=1000
|
|
1024
|
+
The image's size for the time coordinate (x-axis)
|
|
1025
|
+
size_radius : int, optional, default=1000
|
|
1026
|
+
The image's size for the radius coordinate (y-axis)
|
|
1027
|
+
cmap : str, optional, default = "inferno"
|
|
1028
|
+
The colormap used for the image
|
|
1029
|
+
nb_levels : int, optional, default=10
|
|
1030
|
+
The number of levels in the image
|
|
1031
|
+
The number of levels to plot in addition to the image
|
|
1032
|
+
color_min : str | tuple, optional
|
|
1033
|
+
The color associated with the minimum value for a `default` cmap
|
|
1034
|
+
color_max : str | tuple, optional
|
|
1035
|
+
The color associated with the maximum value for a `default` cmap
|
|
1036
|
+
plot_label_levels : bool, optional, default=False
|
|
1037
|
+
To plot (or not) labels for the contours (if True, labels are only plot every two levels)
|
|
1038
|
+
show : bool, optional, default = True
|
|
1039
|
+
Whether to show the image
|
|
1040
|
+
save : bool, optional, default = False
|
|
1041
|
+
Whether to save the image (if True, both the Graphique and the png image will be saved)
|
|
1042
|
+
directory : str, optional
|
|
1043
|
+
The directory to save the Graphique, default=self.directory
|
|
1044
|
+
|
|
1045
|
+
Returns
|
|
1046
|
+
-------
|
|
1047
|
+
g.Graphique
|
|
1048
|
+
|
|
1049
|
+
"""
|
|
1050
|
+
|
|
1051
|
+
if isinstance(vmin, str) and vmin != "auto":
|
|
1052
|
+
raise UserWarning(f"The vmin={vmin} is not a valid value for this simulation")
|
|
1053
|
+
elif isinstance(vmin, str):
|
|
1054
|
+
vmin = -inf
|
|
1055
|
+
|
|
1056
|
+
if vmax is None:
|
|
1057
|
+
vmax = np.inf
|
|
1058
|
+
if isinstance(vmax, str) and vmax != "auto":
|
|
1059
|
+
raise UserWarning(f"The vmax={vmax} is not a valid value for this simulation")
|
|
1060
|
+
elif isinstance(vmax, str):
|
|
1061
|
+
vmax = -inf
|
|
1062
|
+
|
|
1063
|
+
gr: g.Graphique = g.image(self.sig_dots[::max(len(self.tps) // size_time, 1),
|
|
1064
|
+
::max(len(self.r) // size_radius, 1)].T,
|
|
1065
|
+
np.array(self.tps)[::max(len(self.tps) // size_time, 1)] / Myr,
|
|
1066
|
+
np.array(self.r)[::max(len(self.r) // size_radius, 1)] / au,
|
|
1067
|
+
cmap=cmap, colorscale="log", show=False, vmin=vmin, vmax=vmax,
|
|
1068
|
+
color_min=color_min, color_max=color_max)
|
|
1069
|
+
if vmin == -inf:
|
|
1070
|
+
vmin = np.min(gr.array_image)
|
|
1071
|
+
|
|
1072
|
+
if vmax < inf:
|
|
1073
|
+
vmax_levels = vmax
|
|
1074
|
+
else:
|
|
1075
|
+
vmax_levels: np.float64 = np.max(gr.array_image)
|
|
1076
|
+
|
|
1077
|
+
if nb_levels > 0:
|
|
1078
|
+
levels = np.geomspace(vmin, vmax_levels, nb_levels)
|
|
1079
|
+
gr.contours(levels, color="k")
|
|
1080
|
+
|
|
1081
|
+
gr.config_colorbar(ticks=levels,
|
|
1082
|
+
ticks_labels=["{:.2e}".format(t) for t in
|
|
1083
|
+
levels],
|
|
1084
|
+
label="Surface density production rate"
|
|
1085
|
+
" [M$_\oplus$.au$^{-2}$.Myr$^{-1}$]")
|
|
1086
|
+
else:
|
|
1087
|
+
gr.config_colorbar(ticks=np.geomspace(vmin, vmax_levels, 10),
|
|
1088
|
+
ticks_labels=["{:.2e}".format(t) for t in
|
|
1089
|
+
np.geomspace(vmin, vmax_levels, 10)],
|
|
1090
|
+
label="Surface density production rate"
|
|
1091
|
+
" [M$_\oplus$.au$^{-2}$.Myr$^{-1}$]")
|
|
1092
|
+
|
|
1093
|
+
gr.config_ax(xlabel="Time [Myr]", ylabel="Distance to the central star [au] ",
|
|
1094
|
+
xscale="log", yscale="log", xlim=[self.tps[2], self.tps[-1]], ylim=[self.r[0], self.r[-1]])
|
|
1095
|
+
if vmax < np.inf and np.any(self.sig_dots > vmax):
|
|
1096
|
+
gr.config_colorbar(0, extend="max")
|
|
1097
|
+
if np.any(self.sig_dots < vmin):
|
|
1098
|
+
if 'extend' in gr.param_colorbar[0].keys():
|
|
1099
|
+
gr.config_colorbar(0, extend="both")
|
|
1100
|
+
else:
|
|
1101
|
+
gr.config_colorbar(0, extend="min")
|
|
1102
|
+
gr.filename = "Image_total_surface_density_production"
|
|
1103
|
+
|
|
1104
|
+
gr.config_ax(**kwargs_ax)
|
|
1105
|
+
if plot_label_levels:
|
|
1106
|
+
gr.config_labels_contours(levels=gr.levels[::2], fmt="%.0e")
|
|
1107
|
+
else:
|
|
1108
|
+
gr.config_labels_contours(levels=[], fmt="%.0e")
|
|
1109
|
+
|
|
1110
|
+
if directory is not None:
|
|
1111
|
+
gr.directory = directory
|
|
1112
|
+
|
|
1113
|
+
if show:
|
|
1114
|
+
gr.show()
|
|
1115
|
+
if save:
|
|
1116
|
+
gr.ext = ".png"
|
|
1117
|
+
gr.save_figure(dpi=400)
|
|
1118
|
+
gr.save()
|
|
1119
|
+
return gr
|
|
1120
|
+
|
|
1121
|
+
def graph_mdot(self, size_time: int = 500,
|
|
1122
|
+
show: bool = True, save: bool = False, directory: str = None,
|
|
1123
|
+
**kwargs_ax) -> g.Graphique:
|
|
1124
|
+
"""
|
|
1125
|
+
Build a Graphique that represant the evolution of total gas mass generation rate
|
|
1126
|
+
as function of time
|
|
1127
|
+
|
|
1128
|
+
Parameters
|
|
1129
|
+
----------
|
|
1130
|
+
size_time : int, optional, default=1000
|
|
1131
|
+
The size for the time coordinate (x-axis)
|
|
1132
|
+
show : bool, optional, default = True
|
|
1133
|
+
Whether to show the image
|
|
1134
|
+
save : bool, optional, default = False
|
|
1135
|
+
Whether to save the image (if True, both the Graphique and the png image will be saved)
|
|
1136
|
+
directory : str, optional
|
|
1137
|
+
The directory to save the Graphique, default=self.directory
|
|
1138
|
+
|
|
1139
|
+
Returns
|
|
1140
|
+
-------
|
|
1141
|
+
g.Graphique
|
|
1142
|
+
|
|
1143
|
+
"""
|
|
1144
|
+
gr: g.Graphique = g.loglog(np.array(self.tps)[::max(len(self.tps) // size_time, 1)] / Myr,
|
|
1145
|
+
trapezoid(2. * Pi * self.r * self.sig_dots[::max(len(self.tps) // size_time, 1)],
|
|
1146
|
+
self.r, axis=1)
|
|
1147
|
+
* Myr / Mearth, show=False)
|
|
1148
|
+
gr.config_ax(xlabel="Time [Myr]", ylabel="$\dot{M}$ [M$_\oplus$.Myr$^{-1}$] ",
|
|
1149
|
+
xscale="log", yscale="log", xlim=[self.tps[2], self.tps[-1]])
|
|
1150
|
+
gr.filename = "Image_total_mass_production_rate"
|
|
1151
|
+
|
|
1152
|
+
gr.config_ax(**kwargs_ax)
|
|
1153
|
+
|
|
1154
|
+
if directory is not None:
|
|
1155
|
+
gr.directory = directory
|
|
1156
|
+
if show:
|
|
1157
|
+
gr.show()
|
|
1158
|
+
if save:
|
|
1159
|
+
gr.ext = ".png"
|
|
1160
|
+
gr.save_figure(dpi=400)
|
|
1161
|
+
gr.save()
|
|
1162
|
+
return gr
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
def thermal_diff(d: np.float64, consts: dict, It_d: int = 300, t: np.ndarray = None
|
|
1166
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
1167
|
+
"""
|
|
1168
|
+
Compute the thermal diffusion equation into a bodi of radius d as a function of time assuming the bodi to be
|
|
1169
|
+
at the distance a_0 from the central star. To get the temperatures at others location r, one must multiplie the
|
|
1170
|
+
result by (r / a0) ** (-1/2)
|
|
1171
|
+
|
|
1172
|
+
Parameters
|
|
1173
|
+
----------
|
|
1174
|
+
d : np.float64
|
|
1175
|
+
The size of the bodi
|
|
1176
|
+
consts : dict
|
|
1177
|
+
The dictionary of constant parameters
|
|
1178
|
+
It_d : int
|
|
1179
|
+
The number of spatials steps
|
|
1180
|
+
t : np.ndarray
|
|
1181
|
+
the times at which evaluate the solution
|
|
1182
|
+
|
|
1183
|
+
Returns
|
|
1184
|
+
-------
|
|
1185
|
+
np.ndarray
|
|
1186
|
+
|
|
1187
|
+
"""
|
|
1188
|
+
r = np.linspace(d / (100 * It_d), d, It_d)
|
|
1189
|
+
dr = r[1] - r[0]
|
|
1190
|
+
|
|
1191
|
+
def system(t: np.float64, T: np.ndarray, compteur: Compteur = Compteur()):
|
|
1192
|
+
T[-1] = (278 * (((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4))
|
|
1193
|
+
* ((consts["a0"] / au) ** (-1 / 2)))
|
|
1194
|
+
# T[0] = 1
|
|
1195
|
+
T[0] = T[1]
|
|
1196
|
+
# dT = consts["K"] * (gradient2(T, dr) + np.gradient(T, r) / r) - T * interp_L(t, 1) / (4 * interp_L(t))
|
|
1197
|
+
dT = consts["K"] * (gradient2(T, dr) + np.gradient(T, r) / r)
|
|
1198
|
+
dT[0] = 0
|
|
1199
|
+
dT[-1] = 0
|
|
1200
|
+
if compteur.value > 0 and compteur.value % 10000 == 0:
|
|
1201
|
+
print("Iteration ", compteur.value, "t={:.4f} Myr".format(t / Myr))
|
|
1202
|
+
compteur.value += 1
|
|
1203
|
+
return dT
|
|
1204
|
+
|
|
1205
|
+
Tinit = np.zeros(It_d) + 0.1
|
|
1206
|
+
# Tinit[0] = 1
|
|
1207
|
+
compt: Compteur = Compteur()
|
|
1208
|
+
sol_ivp = solve_ivp(system, t_span=[consts["tinit"], consts["tmax"]],
|
|
1209
|
+
y0=Tinit, t_eval=t,
|
|
1210
|
+
max_step=Myr, method="LSODA", args=[compt],
|
|
1211
|
+
rtol=np.double(1e-6), atol=atol)
|
|
1212
|
+
if sol_ivp.status == -1:
|
|
1213
|
+
print(sol_ivp.message)
|
|
1214
|
+
if t is None:
|
|
1215
|
+
t = np.geomspace(consts["tinit"], consts["tmax"], 1000)
|
|
1216
|
+
temp_lim = (278 * (((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4)) * (
|
|
1217
|
+
(a0 / au) ** (-1 / 2)))
|
|
1218
|
+
return r, t, (temp_lim[:, np.newaxis] + np.zeros_like(r)).T
|
|
1219
|
+
sol_ivp.y[-1] = (278 * (((1. - consts["Abelt"]) * interpLbol(sol_ivp.t, consts["Ms"]) / Lsun) ** (1 / 4)) * (
|
|
1220
|
+
(a0 / au) ** (-1 / 2)))
|
|
1221
|
+
sol_ivp.y[0] = sol_ivp.y[1]
|
|
1222
|
+
return r, sol_ivp.t, sol_ivp.y
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
def mdot_s_l(rd: np.ndarray, t: np.ndarray, temp: np.ndarray, consts: dict) -> np.ndarray:
|
|
1226
|
+
"""
|
|
1227
|
+
Compute the gas mass generation for a given bodie
|
|
1228
|
+
|
|
1229
|
+
Parameters
|
|
1230
|
+
----------
|
|
1231
|
+
rd : np.ndarray
|
|
1232
|
+
The radial coordinates inside the asteroid / KBO
|
|
1233
|
+
t : np.ndarray
|
|
1234
|
+
The time
|
|
1235
|
+
temp : np.ndarray
|
|
1236
|
+
The array of temperature as function of rd and t calculated by `thermal_diff`
|
|
1237
|
+
consts: dict
|
|
1238
|
+
The dictionary of constants
|
|
1239
|
+
|
|
1240
|
+
Returns
|
|
1241
|
+
-------
|
|
1242
|
+
np.ndarray
|
|
1243
|
+
the gas mass generation rate as function of time
|
|
1244
|
+
|
|
1245
|
+
"""
|
|
1246
|
+
S: np.float64 = 3. * (1. - consts["Phi"]) / consts["rp"]
|
|
1247
|
+
dr = rd[1] - rd[0]
|
|
1248
|
+
rr = rd[:, np.newaxis] + np.zeros(len(t))
|
|
1249
|
+
mdot = (4 * Pi / 3.) * (S * Ah2o * np.exp(-Bh2o / temp) * np.sqrt(mu_H2O / (2. * Pi * Rgp * temp))) * (
|
|
1250
|
+
(rr + dr) ** 3 - (rr - dr) ** 3)
|
|
1251
|
+
mlost_max = (4 * Pi / 3.) * consts["rho_ice"] * ((rr + dr) ** 3 - (rr - dr) ** 3)
|
|
1252
|
+
mdot[cumulative_trapezoid(mdot, t, initial=0., axis=1) >= mlost_max] *= zero
|
|
1253
|
+
mmax = (4 * Pi / 3.) * consts["rho_ice"] * (rd.max() ** 3 - rd.min() ** 3)
|
|
1254
|
+
if np.any(cumulative_trapezoid(mdot.sum(axis=0), t, initial=0.) >= mmax):
|
|
1255
|
+
i = np.argwhere((cumulative_trapezoid(mdot.sum(axis=0), t, initial=0.) >= mmax))[0, 0]
|
|
1256
|
+
mdot[:, max(0, i - 1):] *= zero
|
|
1257
|
+
return mdot.sum(axis=0)
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
def mdot_s(s: np.float64, t: np.ndarray, r: np.ndarray, consts: dict, It_d=300) -> np.ndarray:
|
|
1261
|
+
"""
|
|
1262
|
+
Compute the gas mass generation rate for a given bodie at differents radius
|
|
1263
|
+
|
|
1264
|
+
Parameters
|
|
1265
|
+
----------
|
|
1266
|
+
s: np.float64
|
|
1267
|
+
The size of the considered bodie
|
|
1268
|
+
t: np.ndarray
|
|
1269
|
+
The time at which evaluate the gas mass generation rate
|
|
1270
|
+
r: np.ndarray
|
|
1271
|
+
The distances from the central star at which evaluate the gas mass generation rate
|
|
1272
|
+
consts : dict
|
|
1273
|
+
The dictionary of constants
|
|
1274
|
+
It_d : int
|
|
1275
|
+
The number of iterations
|
|
1276
|
+
|
|
1277
|
+
Returns
|
|
1278
|
+
-------
|
|
1279
|
+
np.ndarray
|
|
1280
|
+
the gas mass generation rate as function of time and radius
|
|
1281
|
+
|
|
1282
|
+
"""
|
|
1283
|
+
print("s={:.4f}".format(s / C_L), "m")
|
|
1284
|
+
|
|
1285
|
+
rd, t, temp = thermal_diff(d=s / 2, consts=consts, It_d=It_d, t=t)
|
|
1286
|
+
return np.array([mdot_s_l(rd=rd, t=t, temp=temp * (abs(R) / consts["a0"]) ** (-1 / 2),
|
|
1287
|
+
consts=consts) for R in r])
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
def mdot_s_surf(s: np.float64, t: np.ndarray, r: np.ndarray, consts: dict, It_d=300) -> np.ndarray:
|
|
1291
|
+
"""
|
|
1292
|
+
Compute the gas mass generation rate for a given bodies at differents radius if
|
|
1293
|
+
the gas sublimate only at its surface
|
|
1294
|
+
|
|
1295
|
+
Parameters
|
|
1296
|
+
----------
|
|
1297
|
+
s: np.float64
|
|
1298
|
+
The size of the considered bodie
|
|
1299
|
+
t: np.ndarray
|
|
1300
|
+
The time at which evaluate the gas mass generation rate
|
|
1301
|
+
r: np.ndarray
|
|
1302
|
+
The distances from the central star at which evaluate the gas mass generation rate
|
|
1303
|
+
consts : dict
|
|
1304
|
+
The dictionary of constants
|
|
1305
|
+
It_d : int
|
|
1306
|
+
The number of iterations
|
|
1307
|
+
|
|
1308
|
+
Returns
|
|
1309
|
+
-------
|
|
1310
|
+
np.ndarray
|
|
1311
|
+
the gas mass generation rate as function of time and radius
|
|
1312
|
+
|
|
1313
|
+
"""
|
|
1314
|
+
# print("s={:.4f}".format(s / C_L), "m")
|
|
1315
|
+
a1: np.float64 = 7.08e35 / (C_L * C_L * C_t)
|
|
1316
|
+
a2: np.float64 = 6062 # K
|
|
1317
|
+
T0: np.ndarray[np.float64] = (278 * ((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4)
|
|
1318
|
+
* ((consts["a0"] / au) ** (-1 / 2)))
|
|
1319
|
+
res: np.ndarray[np.float64] = np.array([Pi * s * s * 18 * mp * a1 *
|
|
1320
|
+
np.exp(- a2 / (T0 * (abs(R) / consts["a0"]) ** (-1 / 2)))
|
|
1321
|
+
/ np.sqrt(T0 * (abs(R) / consts["a0"]) ** (-1 / 2)) for R in r])
|
|
1322
|
+
mlost_max = (Pi * s * s * s / 6.) * consts["rho_ice"]
|
|
1323
|
+
res[cumulative_trapezoid(res, t, initial=0., axis=1) >= mlost_max] *= zero
|
|
1324
|
+
return res
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
def mdot_s_vol(s: np.float64, t: np.ndarray, r: np.ndarray, consts: dict, It_d=300) -> np.ndarray:
|
|
1328
|
+
"""
|
|
1329
|
+
Compute the gas mass generation rate for a given bodies at differents radius if
|
|
1330
|
+
the gas sublimate in the whole volume neglecting thermal diffusion
|
|
1331
|
+
|
|
1332
|
+
Parameters
|
|
1333
|
+
----------
|
|
1334
|
+
s: np.float64
|
|
1335
|
+
The size of the considered bodie
|
|
1336
|
+
t: np.ndarray
|
|
1337
|
+
The time at which evaluate the gas mass generation rate
|
|
1338
|
+
r: np.ndarray
|
|
1339
|
+
The distances from the central star at which evaluate the gas mass generation rate
|
|
1340
|
+
consts : dict
|
|
1341
|
+
The dictionary of constants
|
|
1342
|
+
It_d : int
|
|
1343
|
+
The number of iterations
|
|
1344
|
+
|
|
1345
|
+
Returns
|
|
1346
|
+
-------
|
|
1347
|
+
np.ndarray
|
|
1348
|
+
the gas mass generation rate as function of time and radius
|
|
1349
|
+
|
|
1350
|
+
"""
|
|
1351
|
+
# print("s={:.4f}".format(s / C_L), "m")
|
|
1352
|
+
T0: np.ndarray[np.float64] = (278 * ((1. - consts["Abelt"]) * interpLbol(t, consts["Ms"]) / Lsun) ** (1 / 4)
|
|
1353
|
+
* ((consts["a0"] / au) ** (-1 / 2)))
|
|
1354
|
+
res: np.ndarray[np.float64] = np.array([(s * s * s / 6) * (3 * (1 - consts["Phi"]) / consts["rp"])
|
|
1355
|
+
* Ah2o * np.exp(- Bh2o / (T0 * (abs(R) / consts["a0"]) ** (-1 / 2)))
|
|
1356
|
+
* np.sqrt(
|
|
1357
|
+
18 * mp / (2. * Pi * kb * T0 * (abs(R) / consts["a0"]) ** (-1 / 2)))
|
|
1358
|
+
for R in r])
|
|
1359
|
+
mlost_max = (Pi * s * s * s / 6.) * consts["rho_ice"]
|
|
1360
|
+
res[cumulative_trapezoid(res, t, initial=0., axis=1) >= mlost_max] *= zero
|
|
1361
|
+
return res
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
def sig_dot_full_diff(tps: np.ndarray, r: np.ndarray, const: dict, kwargsN: dict = None, It_d: int = 100) -> np.ndarray:
|
|
1365
|
+
"""
|
|
1366
|
+
Compute the gas surface density generation rate for a belt discribed into the const dictionary
|
|
1367
|
+
|
|
1368
|
+
Parameters
|
|
1369
|
+
----------
|
|
1370
|
+
tps : np.ndarray
|
|
1371
|
+
The time at which evaluate the result
|
|
1372
|
+
r : np.ndarray
|
|
1373
|
+
The distance to the central star at which evaluate the solution
|
|
1374
|
+
const : dict
|
|
1375
|
+
The dictionary of constants
|
|
1376
|
+
kwargsN : dict
|
|
1377
|
+
The dictionary that defined the size distribution
|
|
1378
|
+
It_d : int
|
|
1379
|
+
The number of depth steps for thermal diffusion integration
|
|
1380
|
+
|
|
1381
|
+
Returns
|
|
1382
|
+
-------
|
|
1383
|
+
np.ndarray
|
|
1384
|
+
|
|
1385
|
+
"""
|
|
1386
|
+
|
|
1387
|
+
sizes = np.geomspace(const["dmin"], const["dmax"], const["Its_p"])
|
|
1388
|
+
|
|
1389
|
+
mdots = np.array([mdot_s(s=s, t=tps, r=r, consts=const, It_d=It_d) for s in sizes])
|
|
1390
|
+
# The gas mass generation rates of each bodies's size for each radius for each time
|
|
1391
|
+
|
|
1392
|
+
if kwargsN is None: # Default size distribution
|
|
1393
|
+
kwargsN = dict(amax=np.double(1.e6) * C_L,
|
|
1394
|
+
abig=np.double(120e3) * C_L,
|
|
1395
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
1396
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
1397
|
+
rho=np.double(3e3 * C_M / (C_L * C_L * C_L)))
|
|
1398
|
+
|
|
1399
|
+
## Calcul of number of asteroids for all diameters and distance to central star
|
|
1400
|
+
sigma0: np.ndarray = (const["Mbelt"] * ((r / const["a0"]) ** (-3. / 2.))
|
|
1401
|
+
/ (4. * Pi * const["a0"] * const["a0"] * (np.sqrt(1. + const["delta_a"] / const["a0"])
|
|
1402
|
+
- np.sqrt(
|
|
1403
|
+
1. - const["delta_a"] / const["a0"])))) # initial surface density
|
|
1404
|
+
ni: np.ndarray = ((np.array([number_density(s, Mtot=np.double(1.), **kwargsN)
|
|
1405
|
+
for s in sizes])[:, np.newaxis] + np.zeros(len(r)))
|
|
1406
|
+
* (sigma0[:, np.newaxis] + np.zeros(len(sizes))).T) # number density
|
|
1407
|
+
ni: np.ndarray = ni[..., np.newaxis] + np.zeros(mdots.shape[-1])
|
|
1408
|
+
print("ni max={:.4f} min={:.4f}".format(ni.max(), ni.min()))
|
|
1409
|
+
print("mdots max={:.4f} mdots={:.4f}".format(mdots.max(), mdots.min()))
|
|
1410
|
+
return trapezoid(ni * mdots, sizes, axis=0).T
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
def sig_dot_sub_surf(tps: np.ndarray, r: np.ndarray, const: dict, kwargsN: dict = None, It_d: int = 100) -> np.ndarray:
|
|
1414
|
+
"""
|
|
1415
|
+
Compute the gas surface density generation rate for a belt discribed into the const dictionary
|
|
1416
|
+
if the sublimation append only at the surface of asteroids
|
|
1417
|
+
|
|
1418
|
+
Parameters
|
|
1419
|
+
----------
|
|
1420
|
+
tps : np.ndarray
|
|
1421
|
+
The time at which evaluate the result
|
|
1422
|
+
r : np.ndarray
|
|
1423
|
+
The distance to the central star at which evaluate the solution
|
|
1424
|
+
const : dict
|
|
1425
|
+
The dictionary of constants
|
|
1426
|
+
kwargsN : dict
|
|
1427
|
+
The dictionary that defined the size distribution
|
|
1428
|
+
It_d : int
|
|
1429
|
+
The number of depth steps for thermal diffusion integration
|
|
1430
|
+
|
|
1431
|
+
Returns
|
|
1432
|
+
-------
|
|
1433
|
+
np.ndarray
|
|
1434
|
+
|
|
1435
|
+
"""
|
|
1436
|
+
|
|
1437
|
+
sizes = np.geomspace(const["dmin"], const["dmax"], const["Its_p"])
|
|
1438
|
+
|
|
1439
|
+
mdots = np.array([mdot_s_surf(s=s, t=tps, r=r, consts=const, It_d=It_d) for s in sizes])
|
|
1440
|
+
# The gas mass generation rates of each bodies's size for each radius for each time
|
|
1441
|
+
|
|
1442
|
+
if kwargsN is None: # Default size distribution
|
|
1443
|
+
kwargsN = dict(amax=np.double(1.e6) * C_L,
|
|
1444
|
+
abig=np.double(120e3) * C_L,
|
|
1445
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
1446
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
1447
|
+
rho=np.double(3e3 * C_M / (C_L * C_L * C_L)))
|
|
1448
|
+
|
|
1449
|
+
## Calcul of number of asteroids for all diameters and distance to central star
|
|
1450
|
+
sigma0: np.ndarray = (const["Mbelt"] * ((r / const["a0"]) ** (-3. / 2.))
|
|
1451
|
+
/ (4. * Pi * const["a0"] * const["a0"] * (np.sqrt(1. + const["delta_a"] / const["a0"])
|
|
1452
|
+
- np.sqrt(
|
|
1453
|
+
1. - const["delta_a"] / const["a0"])))) # initial surface density
|
|
1454
|
+
ni: np.ndarray = ((np.array([number_density(s, Mtot=np.double(1.), **kwargsN)
|
|
1455
|
+
for s in sizes])[:, np.newaxis] + np.zeros(len(r)))
|
|
1456
|
+
* (sigma0[:, np.newaxis] + np.zeros(len(sizes))).T) # number density
|
|
1457
|
+
ni: np.ndarray = ni[..., np.newaxis] + np.zeros(mdots.shape[-1])
|
|
1458
|
+
# print("ni max={:.4f} min={:.4f}".format(ni.max(), ni.min()))
|
|
1459
|
+
# print("mdots max={:.4f} mdots={:.4f}".format(mdots.max(), mdots.min()))
|
|
1460
|
+
return trapezoid(ni * mdots, sizes, axis=0).T
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
def sig_dot_sub_vol(tps: np.ndarray, r: np.ndarray, const: dict, kwargsN: dict = None, It_d: int = 100) -> np.ndarray:
|
|
1464
|
+
"""
|
|
1465
|
+
Compute the gas surface density generation rate for a belt discribed into the const dictionary
|
|
1466
|
+
if the sublimation append only at the surface of asteroids
|
|
1467
|
+
|
|
1468
|
+
Parameters
|
|
1469
|
+
----------
|
|
1470
|
+
tps : np.ndarray
|
|
1471
|
+
The time at which evaluate the result
|
|
1472
|
+
r : np.ndarray
|
|
1473
|
+
The distance to the central star at which evaluate the solution
|
|
1474
|
+
const : dict
|
|
1475
|
+
The dictionary of constants
|
|
1476
|
+
kwargsN : dict
|
|
1477
|
+
The dictionary that defined the size distribution
|
|
1478
|
+
It_d : int
|
|
1479
|
+
The number of depth steps for thermal diffusion integration
|
|
1480
|
+
|
|
1481
|
+
Returns
|
|
1482
|
+
-------
|
|
1483
|
+
np.ndarray
|
|
1484
|
+
|
|
1485
|
+
"""
|
|
1486
|
+
|
|
1487
|
+
sizes = np.geomspace(const["dmin"], const["dmax"], const["Its_p"])
|
|
1488
|
+
|
|
1489
|
+
mdots = np.array([mdot_s_vol(s=s, t=tps, r=r, consts=const, It_d=It_d) for s in sizes])
|
|
1490
|
+
# The gas mass generation rates of each bodies's size for each radius for each time
|
|
1491
|
+
|
|
1492
|
+
if kwargsN is None: # Default size distribution
|
|
1493
|
+
kwargsN = dict(amax=np.double(1.e6) * C_L,
|
|
1494
|
+
abig=np.double(120e3) * C_L,
|
|
1495
|
+
amed=np.double(20.e3) * C_L, amin=C_L,
|
|
1496
|
+
qh=np.double(-4.5), qm=np.double(-1.2), ql=np.double(-3.6),
|
|
1497
|
+
rho=np.double(3e3 * C_M / (C_L * C_L * C_L)))
|
|
1498
|
+
|
|
1499
|
+
## Calcul of number of asteroids for all diameters and distance to central star
|
|
1500
|
+
sigma0: np.ndarray = (const["Mbelt"] * ((r / const["a0"]) ** (-3. / 2.))
|
|
1501
|
+
/ (4. * Pi * const["a0"] * const["a0"] * (np.sqrt(1. + const["delta_a"] / const["a0"])
|
|
1502
|
+
- np.sqrt(
|
|
1503
|
+
1. - const["delta_a"] / const["a0"])))) # initial surface density
|
|
1504
|
+
ni: np.ndarray = ((np.array([number_density(s, Mtot=np.double(1.), **kwargsN)
|
|
1505
|
+
for s in sizes])[:, np.newaxis] + np.zeros(len(r)))
|
|
1506
|
+
* (sigma0[:, np.newaxis] + np.zeros(len(sizes))).T) # number density
|
|
1507
|
+
ni: np.ndarray = ni[..., np.newaxis] + np.zeros(mdots.shape[-1])
|
|
1508
|
+
# print("ni max={:.4f} min={:.4f}".format(ni.max(), ni.min()))
|
|
1509
|
+
# print("mdots max={:.4f} mdots={:.4f}".format(mdots.max(), mdots.min()))
|
|
1510
|
+
return trapezoid(ni * mdots, sizes, axis=0).T
|