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/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+ import numpy as np
3
+ import sys
4
+ import os
5
+ from diffenix.Constantes import *
6
+
7
+ if __name__ == '__main__':
8
+
9
+ from diffenix.time_integration import time_integration, restart
10
+ if len(sys.argv) > 1:
11
+ import os
12
+ if sys.argv[1] == "configs":
13
+ l = os.listdir("./configs")
14
+ l.sort()
15
+ if int(sys.argv[2]) < len(l):
16
+ params = dict(np.load("./configs/" + l[int(sys.argv[2])]))
17
+ print(int(sys.argv[2]), l[int(sys.argv[2])], params["Simulation"])
18
+ if not os.path.exists(results + "/" + params["Simulation"]):
19
+ time_integration(consts_params=params)
20
+ # else:
21
+ # restart(params["Simulation"], rtol=np.double(1e-4))
22
+ else:
23
+ print("argv :", sys.argv[1])
24
+ restart(int(sys.argv[1]), -1)
25
+ else:
26
+ time_integration()
diffenix/chemains.py ADDED
@@ -0,0 +1,110 @@
1
+ import os
2
+ import numpy as np
3
+
4
+ # Defaults values
5
+
6
+ racine: str = "."
7
+ results: str = racine + "/Results"
8
+ data_leiden: str = racine + "/Leiden_database/"
9
+ data_stars: str = racine + "/data_stars/data_fus.npz"
10
+ data_radmc: str = racine + "/data_radmc"
11
+
12
+
13
+ def load_from_files(path_to_file: str = "paths.txt") -> None:
14
+ """
15
+
16
+ Load the path from a txt file
17
+
18
+ Parameters
19
+ ----------
20
+ path_to_file : str
21
+ The path to the txt file
22
+ One path per line, the delimiter between the name of path and the path is `,`
23
+ The names are racine, results, data_leiden and data_stars
24
+
25
+ Returns
26
+ -------
27
+ None
28
+
29
+ """
30
+ if not os.path.exists(path_to_file):
31
+ raise UserWarning("Constante.load_from_files : ", path_to_file, "does not exist")
32
+ global racine, results, data_leiden, data_stars, data_radmc
33
+
34
+ paths = dict(np.loadtxt("paths.txt", dtype=str, delimiter=","))
35
+ print("Paths : ", paths)
36
+ if "racine" in paths.keys():
37
+ if not os.path.exists(paths["racine"]):
38
+ raise UserWarning("Constante.load_from_files : ", paths["racine"], "does not exist")
39
+ if not os.path.isdir(paths["racine"]):
40
+ raise UserWarning("Constante.load_from_files : ", paths["racine"], "is not a directory")
41
+ racine = paths["racine"]
42
+
43
+ if "results" in paths.keys():
44
+ if not os.path.exists(paths["results"]) and not os.path.exists(racine + paths["results"]):
45
+ raise UserWarning("Constante.load_from_files : ", paths["results"], "does not exist")
46
+ if not os.path.isdir(paths["results"]) and not os.path.isdir(racine + paths["results"]):
47
+ raise UserWarning("Constante.load_from_files : ", paths["results"], "is not a directory")
48
+
49
+ if os.path.exists(paths["results"]):
50
+ results = paths["results"]
51
+ else:
52
+ results = racine + paths["results"]
53
+
54
+ if "data_leiden" in paths.keys():
55
+ if not os.path.exists(paths["data_leiden"]) and not os.path.exists(racine + paths["data_leiden"]):
56
+ raise UserWarning("Constante.load_from_files : ", paths["data_leiden"], "does not exist")
57
+ if not os.path.isdir(paths["data_leiden"]) and not os.path.isdir(racine + paths["data_leiden"]):
58
+ raise UserWarning("Constante.load_from_files : ", paths["data_leiden"], "is not a directory")
59
+
60
+ if os.path.exists(paths["data_leiden"]):
61
+ data_leiden = paths["data_leiden"]
62
+ else:
63
+ data_leiden = racine + paths["data_leiden"]
64
+
65
+ if "data_stars" in paths.keys():
66
+ if not os.path.exists(paths["data_stars"]) and not os.path.exists(racine + paths["data_stars"]):
67
+ raise UserWarning("Constante.load_from_files : ", paths["data_stars"], "does not exist")
68
+
69
+ if os.path.exists(paths["data_stars"]):
70
+ data_stars = paths["data_stars"]
71
+ else:
72
+ data_stars = racine + paths["data_stars"]
73
+
74
+ if "data_radmc" in paths.keys():
75
+ # if not os.path.exists(paths["data_radmc"]) and not os.path.exists(racine + paths["data_radmc"]):
76
+ # raise UserWarning("Constante.load_from_files : ", paths["data_radmc"], "does not exist")
77
+
78
+ if os.path.exists(paths["data_radmc"]):
79
+ data_radmc = paths["data_radmc"]
80
+ else:
81
+ data_radmc = racine + paths["data_radmc"]
82
+
83
+
84
+ if os.path.exists("./paths.txt"):
85
+ load_from_files("./paths.txt")
86
+ if not os.path.exists(racine):
87
+ raise UserWarning("Constante.load_from_files : ", racine, "does not exist")
88
+ if not os.path.isdir(racine):
89
+ raise UserWarning("Constante.load_from_files : ", racine, "is not a directory")
90
+
91
+ if not os.path.exists(results):
92
+ raise UserWarning("Constante.load_from_files : ", results, "does not exist")
93
+ if not os.path.isdir(results):
94
+ raise UserWarning("Constante.load_from_files : ", results, "is not a directory")
95
+
96
+ if not os.path.exists(data_stars):
97
+ print("User warning : no data to model the central star as been found. A constant luminosity will be assumed.")
98
+ # raise UserWarning("Constante.load_from_files : ", data_stars, "does not exist")
99
+
100
+ if not os.path.exists(data_leiden):
101
+ raise UserWarning("Constante.load_from_files : ", data_leiden, "does not exist")
102
+ if not os.path.isdir(data_leiden):
103
+ raise UserWarning("Constante.load_from_files : ", data_leiden, "is not a directory")
104
+
105
+ # if not os.path.exists(data_radmc):
106
+ # raise UserWarning("Constante.load_from_files : ", data_radmc, "does not exist")
107
+ # if not os.path.isdir(data_radmc):
108
+ # raise UserWarning("Constante.load_from_files : ", data_radmc, "is not a directory")
109
+ else:
110
+ print("No path.txt file has been found. Working directory : ", os.getcwd())
diffenix/classes.py ADDED
File without changes
@@ -0,0 +1,146 @@
1
+ import os.path
2
+
3
+ import numpy as np
4
+ from scipy.optimize import root
5
+ from wx.py.editor import directory
6
+
7
+ from diffenix.Constantes import *
8
+
9
+ dic_ref: dict = {"racine": racine, "results": results,
10
+ "C_L": C_L, "C_M": C_M, "C_t": C_t,
11
+ "Ms": Msun, "Ts": np.double(5500.), "Rs": Rsun, "Ls": Lsun,
12
+ "a_in": .05 * au, "a_out": 10 * a0, "a0": 2.65 * au,
13
+ "T0": T0, "pls_temp": np.double(-1/2), "mH_init": zero,
14
+ "alpha": np.double(1e-3), "gamma": np.double(1.),
15
+ "AB_mdot_st_init": np.double(1e-10) * Mearth / Myr,
16
+ "AB_Mbelt": np.double(0.12) * Mearth,
17
+ "AB_Abelt": np.double(0.06),
18
+ "AB_a0": np.double(2.65) * au, "AB_delta_r": np.double(2.65 / 2.) * au,
19
+ "AB_K": np.double(1e-5 * C_L * C_L / C_t), "AB_Phi": np.double(0.6), "AB_rp": np.double(1e-6 * C_L),
20
+ "AB_f_ice": np.double(0.2), "AB_rho_refr": np.double(2.5e3 * C_M / (C_L * C_L * C_L)) ,
21
+ "AB_rho_ice": np.double(1.e3 * C_M / (C_L * C_L * C_L)), "AB_sublimation_model": "thermal_full",
22
+ "AB_const_mdot": zero,
23
+ "AB_SD_amax": np.double(1.e6) * C_L, "AB_SD_abig": np.double(120e3) * C_L,
24
+ "AB_SD_amed": np.double(20.e3) * C_L, "AB_SD_amin": C_L,
25
+ "AB_SD_qh": np.double(-4.5), "AB_SD_qm": np.double(-1.2), "AB_SD_ql": np.double(-3.6),
26
+ "AB_sb": np.double(316) * C_L, "AB_e": np.double(0.075), "AB_i": np.double(0.075 / 2.),
27
+ "AB_As": np.double(5.) * C_E / C_M , "AB_bs": np.double(-0.1), "AB_bg": np.double(0.5),
28
+ "AB_t0_diss": 150 * Myr, "AB_t1_diss": Myr,
29
+ "KB_mdot_st_init": zero,
30
+ "KB_Mbelt": np.double(0.01 * Mearth), "KB_Abelt": np.double(0.06),
31
+ "KB_a0": np.double(44) * au, "KB_delta_r": np.double(8) * au,
32
+ "KB_K": np.double(1e-10 * C_L * C_L / C_t), "KB_Phi": np.double(0.6), "KB_rp": np.double(1e-6 * C_L),
33
+ "KB_f_ice": np.double(0.2), "KB_rho_refr": np.double(2.5e3 * C_M / (C_L * C_L * C_L)),
34
+ "KB_rho_ice": np.double(1.e3 * C_M / (C_L * C_L * C_L)), "KB_sublimation_model": "none",
35
+ "KB_const_mdot": zero,
36
+ "KB_SD_amax": np.double(1.e6) * C_L, "KB_SD_abig": np.double(120e3) * C_L,
37
+ "KB_SD_amed": np.double(20.e3) * C_L, "KB_SD_amin": C_L,
38
+ "KB_SD_qh": np.double(-4.5), "KB_SD_qm": np.double(-1.2), "KB_SD_ql": np.double(-3.6),
39
+ "KB_sb": np.double(316) * C_L, "KB_e": np.double(0.075), "KB_i": np.double(0.075 / 2.),
40
+ "KB_As": np.double(5.) * C_E / C_M, "KB_bs": np.double(-0.1), "KB_bg": np.double(0.5),
41
+ "KB_t0_diss": np.double(10.) * Gyr, "KB_t1_diss": np.double(10.) * Gyr,
42
+ "a_planets": np.array([au]), "m_planets": np.array([Mearth]), "f_accr": np.double(0.5),
43
+ "t0": np.double(5. * Myr),
44
+ "IT_s": 3000, "dip": 10, "rtol": np.double(1e-6), "atol": np.double(1e-250), "method_root": 'RK45',
45
+ }
46
+
47
+
48
+ def temp_ppd(r: np.ndarray[np.float64] | np.float64,
49
+ consts: dict, mdot: np.float64 = zero
50
+ ) -> np.float64 | np.ndarray[np.float64]:
51
+ """
52
+ The temperature of the protoplanetary disc
53
+ Parameters
54
+ ----------
55
+ r : np.float64 | np.ndarray[np.float64]
56
+ The distance to the central star
57
+ consts : dict
58
+ All the constants needed
59
+ mdot : np.float, optional, default=0
60
+ The accretion rate on the central star
61
+
62
+ Returns
63
+ -------
64
+ np.float64 | np.ndarray[np.float64]
65
+ The temperature of the protoplanetary disc at the dissipation time
66
+
67
+ """
68
+ C: np.float64 = 5 # Ratio between h the heigh at wich the disc became optically thick and Hz the typical scale-heigh
69
+ # asssumed to be a constant
70
+ # primordial disc protertys
71
+ M0 = 0.01 * Msun # total disc mass
72
+ r0 = 10 * au # reference radius
73
+ rout = 200 * au # outer radius
74
+ rin = 0. #inner radius
75
+ kappa_r = C_L * C_L / C_M # Rosseland opacity
76
+ sigma0 = M0 / (2. * Pi * au * (r0 - rin + 2 * np.sqrt(au) * (np.sqrt(rout) - np.sqrt(r0))))
77
+ tau = np.zeros_like(r)
78
+ if isinstance(r, np.ndarray):
79
+ tau[r < r0] = kappa_r * sigma0 * (au / r[r < r0])
80
+ tau[r >= r0] = kappa_r * sigma0 * ((r[r >= r0] / au) ** (-3 / 2))
81
+ elif r < r0:
82
+ tau[r < r0] = kappa_r * sigma0 * (au / r)
83
+ else:
84
+ tau[r < r0] = kappa_r * sigma0 * ((r / au) ** (-3 / 2))
85
+
86
+ return ((0.2 * interpRs(consts["t0"], consts["Ms"]) / r
87
+ + (C / 7) * ((Rgp * consts["Rs"] / (G * consts["Ms"] * 2.33 * 1e-3 * C_M)) ** (4 / 7))
88
+ * ((interpLbol(consts["t0"], consts["Ms"]) / (4. * Pi * sigma * interpRs(consts["t0"], consts["Ms"])
89
+ - interpRs(consts["t0"], consts["Ms"]))) ** (1 / 7))
90
+ * (r / interpRs(consts["t0"], consts["Ms"])) ** (2 / 7))
91
+ * (interpLbol(consts["t0"], consts["Ms"]) / (4. * Pi * sigma * r * r))
92
+ + ((3 / 4) * (tau + 2 / 3))
93
+ * 3 * G * consts["Ms"] * mdot * (1. - np.sqrt(interpRs(consts["t0"], consts["Ms"]) / r))
94
+ / (8. * Pi * sigma * r * r * r)) ** (1 / 4)
95
+
96
+
97
+ def ice_line(consts: dict, mdot: np.float64 = zero) -> np.float64:
98
+ """
99
+ Return the lacation of the iceline when the protoplanetary disc dissipate
100
+
101
+ Parameters
102
+ ----------
103
+ consts : dict
104
+ All the constants needed
105
+ mdot : np.float, optional, default=0
106
+ The accretion rate on the central star
107
+
108
+ Returns
109
+ -------
110
+ np.float64
111
+ the ice-line location
112
+ """
113
+ def null(r: np.float64 | np.ndarray[np.float64]) -> np.ndarray[np.float64] | np.float64:
114
+ return temp_ppd(r, consts=consts, mdot=mdot) - 170
115
+ return root(null, x0 = au).x
116
+
117
+ directory: str = "./configs"
118
+
119
+ if not os.path.exists(directory):
120
+ os.mkdir(directory)
121
+ mstars = np.linspace(wfus["M"].min(), wfus["M"].max(), 5) * Msun
122
+ mds = [0.1 * Mearth, 1e-3 * Mearth]
123
+ tinits = [Myr, np.double(5.) * Myr]
124
+ alphas = [1e-3, 1e-1]
125
+
126
+ for im in range(len(mstars)):
127
+ for imd in range(len(mds)):
128
+ for it in range(len(tinits)):
129
+ consts_ms = dic_ref.copy()
130
+ consts_ms["Ms"] = mstars[im]
131
+ consts_ms["t0"] = tinits[it]
132
+ a0: np.float64 = ice_line(consts_ms, 1e-10 * Msun / yr)
133
+ a0s = [0.7 * a0, a0, 1.3 * a0]
134
+ for ialpha in range(len(alphas)):
135
+ for ia0 in range(len(a0s)):
136
+ consts_msi = consts_ms.copy()
137
+ consts_msi["AB_Mbelt"] = mds[imd]
138
+ consts_msi["alpha"] = alphas[ialpha]
139
+ consts_msi["a0"] = a0s[ia0]
140
+ consts_msi["a_planets"] = 0.7 * a0s[ia0] * np.double(1 - 0.5)
141
+ consts_msi["a_out"] = np.double(100.) * a0s[ia0]
142
+ consts_msi["AB_a0"] = a0s[ia0]
143
+ consts_msi["AB_delta_r"] = a0s[ia0] * np.double(0.5)
144
+ consts_msi["Simulation"] = "Simulation_" + str(im + 1) + str(imd + 1) + str(it + 1) + str(ialpha + 1) + str(ia0 + 1)
145
+ print(consts_msi["Simulation"])
146
+ np.savez_compressed(directory + "/" + consts_msi["Simulation"], **consts_msi)
@@ -0,0 +1,332 @@
1
+ from __future__ import annotations
2
+ import numpy as np
3
+ from numpy.linalg import LinAlgError
4
+ from scipy.integrate import solve_ivp
5
+ from scipy.linalg import solve, LinAlgWarning
6
+ from scipy.optimize import approx_fprime
7
+ from scipy.optimize import root
8
+ import phenigraph as g
9
+
10
+
11
+ def gradient_weno5(y: np.ndarray, dx: np.float64 = 1.0, axis: int = -1) -> np.ndarray:
12
+ """
13
+ Compute the spatial derivative of y following the WENO discretization scheme, robust to discontinuity,
14
+ along a specified axis.
15
+
16
+ Parameters
17
+ ----------
18
+ y : np.ndarray
19
+ The array to be derived
20
+ dx : np.float64, optional, default: 1.0
21
+ The space step, the space domain should be evenly spaced with dx
22
+ axis : int, optional, default: -1
23
+ The axis along which to compute the derivative
24
+
25
+ Returns
26
+ -------
27
+ np.ndarray
28
+ The order 1 spatial derivative of y at each point along the specified axis
29
+ """
30
+ eps: np.float64 = np.double(1e-200)
31
+
32
+ # Move the specified axis to the last position for easier manipulation
33
+ y = np.moveaxis(y, axis, -1)
34
+
35
+ # Initialize arrays for alpha0, alpha1, alpha2
36
+ alpha0 = ((13. / 12.) * (y[..., :-4] - 2. * y[..., 1:-3] + y[..., 2:-2]) ** 2
37
+ + (1. / 4.) * (y[..., :-4] - 4. * y[..., 1:-3] + 3. * y[..., 2:-2]) ** 2)
38
+ alpha1 = ((13. / 12.) * (y[..., 2:-2] - 2. * y[..., 3:-1] + y[..., 4:]) ** 2
39
+ + (1. / 4.) * (y[..., 2:-2] - y[..., 4:]) ** 2)
40
+ alpha2 = ((13. / 12.) * (y[..., 1:-3] - 2. * y[..., 2:-2] + y[..., 3:-1]) ** 2
41
+ + (1. / 4.) * (y[..., 1:-3] - 4. * y[..., 2:-2] + 3. * y[..., 3:-1]) ** 2)
42
+ # alpha0 = ((13. / 12.) * (y[..., 2:-2] - 2. * y[..., 3:-1] + y[..., 4:]) ** 2
43
+ # + (1. / 4.) * (3. * y[..., 2:-2] - 4. * y[..., 3:-1] + y[..., 4:]) ** 2)
44
+ # alpha1 = ((13. / 12.) * (y[..., 1:-3] - 2. * y[..., 2:-2] + y[..., 3:-1]) ** 2
45
+ # + (1. / 4.) * (y[..., 1:-3] - y[..., 3:-1]) ** 2)
46
+ # alpha2 = ((13. / 12.) * (y[..., :-4] - 2. * y[..., 1:-3] + y[..., 2:-2]) ** 2
47
+ # + (1. / 4.) * (y[..., :-4] - 4. * y[..., 1:-3] + 3. * y[..., 2:-2]) ** 2)
48
+ eps = 1e-6 * np.min([abs(alpha0), abs(alpha1), abs(alpha2)]) + 1e-150
49
+ # eps = 1e200
50
+ # print(eps, np.argwhere(np.isnan(alpha0)), np.argwhere(np.isnan(alpha1)), np.argwhere(np.isnan(alpha2)))
51
+ # Compute alpha0, alpha1, alpha2 along the last axis
52
+
53
+ alpha0 = 0.1 / ((eps + alpha0) * (eps + alpha0))
54
+
55
+ alpha1 = 0.6 / ((eps + alpha1) * (eps + alpha1))
56
+
57
+ alpha2 = 0.3 / ((eps + alpha2) * (eps + alpha2))
58
+
59
+ # print(alpha0, alpha1, alpha2)
60
+
61
+ # Compute fp
62
+ fp = (alpha0 * ((2. / 6.) * y[..., :-4] - (7. / 6.) * y[..., 1:-3] + (11. / 6.) * y[..., 2:-2])
63
+ + alpha1 * (-(1. / 6.) * y[..., 1:-3] + (5. / 6.) * y[..., 2:-2] + (2. / 6.) * y[..., 3:-1])
64
+ + alpha2 * ((2. / 6.) * y[..., 2:-2] + (5. / 6.) * y[..., 3:-1] - (1. / 6.) * y[..., 4:])) / (
65
+ alpha0 + alpha1 + alpha2)
66
+ # fp = (alpha0 * ((-1. / 6.) * y[..., :-4] + (5. / 6.) * y[..., 1:-3] + (2. / 6.) * y[..., 2:-2])
67
+ # + alpha1 * ((2. / 6.) * y[..., 1:-3] + (5. / 6.) * y[..., 2:-2] - (1. / 6.) * y[..., 3:-1])
68
+ # + alpha2 * ((11. / 6.) * y[..., 2:-2] - (7. / 6.) * y[..., 3:-1] + (2. / 6.) * y[..., 4:])) / (
69
+ # alpha0 + alpha1 + alpha2)
70
+ # Initialize result array
71
+ res = np.zeros_like(y)
72
+
73
+ # Compute the derivative along the last axis
74
+ res[..., 3:-2] = (fp[..., 1:] - fp[..., :-1]) / dx
75
+ # res[..., :3] = (y[..., 1:4] - y[..., :3]) / dx
76
+ res[..., 1:3] = (res[..., 2:4] - res[..., :2]) * 2 / dx
77
+ res[..., 0] = (y[..., 1] - y[..., 0]) / dx
78
+ res[..., -2:] = (y[..., -2:] - y[..., -3:-1]) / dx
79
+
80
+ # Move the axis back to its original position
81
+ res = np.moveaxis(res, -1, axis)
82
+ # return np.gradient(y, axis=axis) / dx
83
+ # return fp
84
+ return res
85
+
86
+
87
+ def gradient_g(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1) -> np.ndarray[np.float64]:
88
+ y = np.moveaxis(y, axis, -1)
89
+ res = np.zeros_like(y)
90
+ res[..., 1:] = (y[..., 1:] - y[..., :-1]) / dx
91
+ res[..., 0] = (y[..., 1] - y[..., 0]) / dx
92
+ res = np.moveaxis(res, -1, axis)
93
+ return res
94
+
95
+
96
+ def gradient_d(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1) -> np.ndarray[np.float64]:
97
+ y = np.moveaxis(y, axis, -1)
98
+ res = np.zeros_like(y)
99
+ res[..., :-1] = (y[..., 1:] - y[..., :-1]) / dx
100
+ res[..., -1] = (y[..., -1] - y[..., -2]) / dx
101
+ res = np.moveaxis(res, -1, axis)
102
+ return res
103
+
104
+
105
+ def gradient(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1,
106
+ radial_speed: np.ndarray[np.float64] = None) -> np.ndarray[np.float64]:
107
+ y = np.moveaxis(y, axis, -1)
108
+ dg = gradient_g(y, dx, axis)
109
+ dd = gradient_d(y, dx, axis)
110
+ if radial_speed is None:
111
+ res = (dg+dd) / 2
112
+ else:
113
+ res = dd
114
+ if np.any(radial_speed < 0):
115
+ idx_negs: np.ndarray[int] = np.argwhere(radial_speed < 0)[:, 0]
116
+ res[..., idx_negs] = dg[..., idx_negs]
117
+ res = np.moveaxis(res, -1, axis)
118
+ return res
119
+
120
+
121
+ def gradient2(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1) -> np.ndarray[np.float64]:
122
+ y = np.moveaxis(y, axis, -1)
123
+ res = np.zeros_like(y)
124
+ res[..., 1:-1] = (y[..., 2:] + y[..., :-2] - 2. * y[..., 1:-1]) / (dx * dx)
125
+ # res[..., 0] = (y[..., 0] - 2 * y[..., 1] + y[..., 2]) / (dx * dx)
126
+ # res[..., -1] = (y[..., -3] - 2 * y[..., -2] + y[..., -1]) / (dx * dx)
127
+ # res[wleft * wright == 0] = 0.
128
+ res = np.moveaxis(res, -1, axis)
129
+ return res
130
+
131
+
132
+ def dleft(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1) -> np.ndarray[np.float64]:
133
+ y = np.moveaxis(y, axis, -1)
134
+ dleft: np.ndarray[np.float64] = np.zeros_like(y)
135
+ dright: np.ndarray[np.float64] = np.zeros_like(y)
136
+ wleft: np.ndarray[np.float64] = np.zeros_like(y)
137
+ wright: np.ndarray[np.float64] = np.zeros_like(y)
138
+ dleft[2:] = (1.5 * y[..., 2:] - 2. * y[..., 1:-1] + 0.5 * y[..., :-2]) / dx
139
+
140
+ dleft = np.moveaxis(dleft, -1, axis)
141
+ return dleft
142
+
143
+
144
+ def dright(y: np.ndarray[np.float64], dx: np.float64 = np.double(1), axis: int = -1) -> np.ndarray[np.float64]:
145
+ y = np.moveaxis(y, axis, -1)
146
+ dright: np.ndarray[np.float64] = np.zeros_like(y)
147
+ dright[..., :-2] = (-1.5 * y[..., :-2] + 2. * y[..., 1:-1] - 0.5 * y[..., 2:]) / dx
148
+
149
+ dright = np.moveaxis(dright, -1, axis)
150
+ return dright
151
+
152
+
153
+
154
+ def doublons(tab: np.ndarray, prec: np.float64 = np.double(1e-13)) -> np.ndarray:
155
+ res: list = []
156
+ idx_poubelle: list = []
157
+ for i in range(len(tab)):
158
+ if i not in idx_poubelle:
159
+ idx = np.argwhere(np.abs((tab - tab[i]) / tab[i]) < prec)[:, 0]
160
+ if len(idx) > 0:
161
+ res.append(idx[0])
162
+ else:
163
+ res.append(i)
164
+ idx_poubelle.extend(list(idx))
165
+ return np.array(res)
166
+
167
+
168
+ # Opérations diverses
169
+ def factorielle(n: int) -> int:
170
+ """
171
+ Calcul récursivement la factorielle de n
172
+ :param n: entier naturel dont on souhaite calculer la factorielle
173
+ :return: n!
174
+ """
175
+ if n < 0:
176
+ raise UserWarning("La factorielle d'un nombre négatif ({}) n'est pas définie".format(n))
177
+ elif n == 0 or n == 1:
178
+ return 1
179
+ else:
180
+ return n * factorielle(n - 1)
181
+
182
+ def puissance_entière(x: np.float64 | float | np.ndarray, n: int) -> np.float64 | float | np.ndarray:
183
+ """Calcul la puissance n de x"""
184
+ if type(n) is not (int or np.longlong):
185
+ raise UserWarning("Une puissance entière ne peut être calculé avec un exposant de type :" + str(type(n)))
186
+ if n == 0:
187
+ return np.ones_like(x)
188
+ elif n == -1:
189
+ return 1 / x
190
+ elif n == 1:
191
+ return x
192
+ else:
193
+ res: np.float64 | float | np.ndarray = np.copy(x)
194
+ for k in range(2, n + 1):
195
+ res *= x
196
+ return res
197
+
198
+
199
+ def calcul_coefficients_pondération(
200
+ X: np.ndarray | list[float | np.float64],
201
+ x0: float | np.float64 = np.double(0), ordre: int = 1) -> np.ndarray:
202
+ """ Cette fonction calcule les coefficients de pondération à appliquer devant les évaluations d'une fonction f aux
203
+ pas de temps dts autour du point à dériver
204
+ :param X: Valeurs des point d'évaluation de la fonction
205
+ :type X : list[double|float] | Array[double|float]
206
+
207
+ :param x0: Valeur autour de laquelle on souhaite dériver (option à choisir si x0 n'est pas présent dans x)
208
+ :type x0: float | double
209
+
210
+ :param ordre: Ordre de la dérivation. Par défaut 1. Ne peut être plus grand que le nombre de pas de temps
211
+ :type ordre : int
212
+
213
+ :return: K, tableau de la dimension de X contenant les coefficients à appliquer devant les évaluations de la
214
+ fonction f à dériver aux pas de temps correspondant
215
+ :rtype : Array[double | float]
216
+
217
+ Remarques :
218
+ - Le premier pas de temps peut être nul, et les pas de temps peuvent être positifs ou négatifs.
219
+ - La dérivation sera précise à l'ordre correspondant à la taille de X
220
+ Exemples :
221
+ - Cas le plus simple : dérivée d'ordre 1, schéma d'Euler
222
+ -- On fournit X=[0 ,dt] et la fonction retourne [-1 / dt, 1 / dt]
223
+ - Dérivation centrée d'ordre 1 :
224
+ -- On fournit X=[-dt,0,dt] et la fonction retourne [-1 / 2*dt, 0, 1 / 2*dt]
225
+ """
226
+ if ordre >= len(X):
227
+ raise UserWarning(
228
+ "Attention, pour calculer une dérivée d'ordre " + str(ordre) + ", on doit avoir au moins " + str(
229
+ ordre) + " pas de temps. Les " + str(len(X)) + " results sont insuffisantes")
230
+
231
+ Delta_X: np.ndarray = np.array(X) - x0
232
+ # mini: np.float64 = np.double(np.min(abs(Delta_X[Delta_X != 0])))
233
+ # maxi: np.float64 = np.double(np.max(abs(Delta_X[Delta_X != 0])))
234
+ # alpha: np.float64 = 10 ** ((np.log10(mini) + np.log10(maxi)) / 2)
235
+ # Delta_X /= alpha # On rééchelonne les abscisses pour ne pas avoir de trop petites results dans la matrice A
236
+
237
+ A: np.ndarray = np.array([list(puissance_entière(Delta_X, i)) for i in range(len(X))])
238
+ B: np.ndarray = np.zeros_like(X)
239
+ # B[ordre] = factorielle(ordre) / puissance_entière(alpha, ordre)
240
+ B[ordre] = factorielle(ordre)
241
+ K: np.ndarray
242
+ K = solve(A, B, overwrite_a=True, overwrite_b=True)
243
+ return K
244
+
245
+
246
+ def calcul_matrice_derivation(x: np.ndarray | list, ordre_derivation: int = 1, ordre_discrétisation: int = 2
247
+ ) -> np.ndarray:
248
+ """
249
+ Retourne une matrice qui multipliée avec tout y=f(x) retourne la dérivée d'ordre ordre_dérivation de f selon l'axe x
250
+ discrétisé à l'ordre ordre_discrétisation
251
+ :param x: tableau 1d represantant l'axe de dérivation
252
+ :param ordre_derivation: ordre de la dérivée voulue
253
+ :param ordre_discrétisation: ordre de la discrétisation de la dérivée, >= ordre_dérivation < len(x)
254
+ :return: matrice à multiplier avec y
255
+ """
256
+ if type(x) is list:
257
+ x: np.ndarray = np.array(x)
258
+ if len(x.shape) > 1:
259
+ raise UserWarning("calcul_matrice_derivation : l'axe de dérivation doit être de dimension 1."
260
+ "Ici x.shape=", x.shape)
261
+ if ordre_discrétisation < ordre_derivation:
262
+ raise UserWarning("calcul_matrice_derivation : l'ordre de discrétisation doit être supérieur "
263
+ "à l'ordre de dérivation, ici ordre_dérivation=", ordre_derivation,
264
+ "et ordre discrétisation=", ordre_discrétisation)
265
+ if ordre_discrétisation >= len(x):
266
+ raise UserWarning("calcul_matrice_derivation : l'ordre de discrétisation doit être inférieur "
267
+ "à la taille du tableau x. Ici ordre discrétisation=", ordre_discrétisation,
268
+ "len(x)=", len(x))
269
+ K: np.ndarray = np.zeros((len(x), len(x)), dtype=type(x[0]))
270
+ for i in range(len(x)):
271
+ i_inf: int = max(0, i - (ordre_discrétisation + 1) // 2)
272
+ i_sup: int = min(len(x) - 1, i + (ordre_discrétisation + 1) // 2 + (ordre_discrétisation + 1) % 2)
273
+ i_inf -= max(i + (ordre_discrétisation + 1) // 2 + (ordre_discrétisation + 1) % 2 - (len(x) - 1), 0)
274
+ i_sup += - min(i - (ordre_discrétisation + 1) // 2, 0)
275
+ K[i, i_inf:i_sup] = calcul_coefficients_pondération(X=x[i_inf:i_sup], x0=x[i], ordre=ordre_derivation)
276
+ return K
277
+
278
+ #
279
+ # def gradient2(f: np.ndarray, x: np.ndarray | None = None, axis: int = 0,
280
+ # tab_dx: np.ndarray | None = None, derivation_order: int = 1,
281
+ # discretisation_order: int = 2) -> np.ndarray:
282
+ # """
283
+ # Calculate the spatial derivative of f along x
284
+ # :param f: value to differentiate
285
+ # :param x: abscisse, if None and tab_dx None = arange(len(f))
286
+ # :param axis: axis along with the derivative is done (default 0)
287
+ # :param tab_dx: matrix used to calculate the derivative. If None tab_dx is calculated using x, derivation_order
288
+ # and discretisation_order
289
+ # :param derivation_order: int = 1 if tab_dx is None
290
+ # :param discretisation_order: int = 2 if tab_dx is None
291
+ # :return:
292
+ # """
293
+ # if tab_dx is None:
294
+ # tab_dx = calcul_matrice_derivation(x, derivation_order, discretisation_order)
295
+ # # return tab_dx @ f
296
+ # # return np.tensordot(tab_dx, f, axes=([1], [axis]))
297
+ # return np.tensordot(f, tab_dx, axes=([axis], [1]))
298
+
299
+
300
+ def linear_interpolation(x, y):
301
+ """
302
+ Interpolation linéaire vectorisée.
303
+
304
+ :param x: Points de données pour la variable indépendante.
305
+ :param y: Points de données pour les fonctions dépendantes (2D array).
306
+ :param x_new: Points pour lesquels interpoler.
307
+ :return: Valeurs interpolées pour chaque dimension.
308
+ """
309
+ def result(x_new: np.float64 | np.ndarray) -> np.float64 | np.ndarray:
310
+ if isinstance(x_new, np.ndarray):
311
+ # Calculer les indices et les poids pour l'interpolation
312
+ idx = np.searchsorted(x, x_new) - 1
313
+ idx = np.clip(idx, 0, len(x) - 2)
314
+
315
+ # Calculer les poids pour chaque intervalle
316
+ x1 = x[idx]
317
+ x2 = x[idx + 1]
318
+ alpha = (x_new - x1) / (x2 - x1)
319
+
320
+ # Interpolation linéaire vectorisée
321
+ y_new = (1 - alpha[:, np.newaxis]) * y[idx] + alpha[:, np.newaxis] * y[idx + 1]
322
+
323
+ return y_new
324
+ else:
325
+ i = np.searchsorted(x, x_new)
326
+ i = np.clip(i, 0, len(x) - 2)
327
+ x1 = x[i]
328
+ x2 = x[i + 1]
329
+ alpha = (x_new - x1) / (x2 - x1)
330
+ y_new = (1 - alpha) * y[i] + alpha*y[i + 1]
331
+ return y_new
332
+ return result