Peano
Loading...
Searching...
No Matches
sph_kernels.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3"""!
4SPH Kernel related constants, coefficients, and functions.
5
6All constants and kernel coefficients are taken from table 1 of
7Dehnen & Aly, MNRAS, 425, pp. 1062-1082 (2012).
8"""
9
10import numpy as np
11from typing import Union
12
13
14# List of available kernel names. Used to check whether user choice is valid.
15sph_kernel_list = [
16 "cubic_spline",
17 "quartic_spline",
18 "quintic_spline",
19 "wendland_C2",
20 "wendland_C4",
21 "wendland_C6",
22]
23
24sph_vectorized_kernel_list = [
25 "quartic_spline_vectorized",
26]
27
28
30 """!
31 Modify the default dict to raise an error if a user tries to
32 access kernels which aren't defined. Undefined kernels are
33 specified via blocked_keys kwarg during initialization.
34
35 Also limit the available kernels to those specified in the
36 sph_kernel_list list.
37 """
38
39 def __init__(self, *args, blocked_keys=(), ndim=0, **kwargs):
40 super().__init__(*args, **kwargs)
41 for key in blocked_keys:
42 if key not in sph_kernel_list and key not in sph_vectorized_kernel_list:
43 raise KeyError(
44 f"Invalid kernel key {key}. Only {sph_kernel_list} are permitted"
45 )
46 self.blocked_keys = blocked_keys
47 self.ndim = ndim
48
49 def __setitem__(self, key, value):
50 if key not in sph_kernel_list and key not in sph_vectorized_kernel_list:
51 raise KeyError(
52 f"Invalid kernel key {key}. Only {sph_kernel_list} are permitted"
53 )
54 super().__setitem__(key, value)
55
56 def __getitem__(self, key):
57 if key in self.blocked_keys:
58 raise ValueError(f"SPH kernel {key} is not defined for ndim={self.ndim}")
59 return super().__getitem__(key)
60
61
62# Translation from kernel name to kernel macro
63sph_kernel_macro_name = _forbidInvalidKernelsDict()
64
65# list for 1D, 2D, 3D values of H/h of kernel dicts
66sph_kernel_H_over_h = [
67 _forbidInvalidKernelsDict(blocked_keys=("wendland_C4", "wendland_C6"), ndim=1),
70]
71
72
73# list for 1D, 2D, 3D values of kernel normalization coefficients of kernel dicts
74sph_kernel_normalization = [
75 _forbidInvalidKernelsDict(blocked_keys=("wendland_C4", "wendland_C6"), ndim=1),
78]
79
80# dict containing functions that evaluate the kernel
81sph_kernel_eval_function = _forbidInvalidKernelsDict()
82
83# dict containing functions that evaluate the kernel derivatives
84sph_kernel_deval_function = _forbidInvalidKernelsDict()
85
86
87# Now fill up the dicts with content
88# Macro names
89sph_kernel_macro_name["cubic_spline"] = "CUBIC_SPLINE_KERNEL"
90sph_kernel_macro_name["quartic_spline"] = "QUARTIC_SPLINE_KERNEL"
91sph_kernel_macro_name["quintic_spline"] = "QUINTIC_SPLINE_KERNEL"
92sph_kernel_macro_name["wendland_C2"] = "WENDLAND_C2_KERNEL"
93sph_kernel_macro_name["wendland_C4"] = "WENDLAND_C4_KERNEL"
94sph_kernel_macro_name["wendland_C6"] = "WENDLAND_C6_KERNEL"
95sph_kernel_macro_name["quartic_spline_vectorized"] = sph_kernel_macro_name[
96 "quartic_spline"
97]
98
99# Kernel Gammas (ratio of compact support radius H to smoothing length h)
100# 3D values
101sph_kernel_H_over_h[2]["cubic_spline"] = 1.825742
102sph_kernel_H_over_h[2]["quartic_spline"] = 2.018932
103sph_kernel_H_over_h[2]["quintic_spline"] = 2.195775
104sph_kernel_H_over_h[2]["wendland_C2"] = 1.936492
105sph_kernel_H_over_h[2]["wendland_C4"] = 2.207940
106sph_kernel_H_over_h[2]["wendland_C6"] = 2.449490
107sph_kernel_H_over_h[2]["quartic_spline_vectorized"] = sph_kernel_H_over_h[2][
108 "quartic_spline"
109]
110
111# 2D values
112sph_kernel_H_over_h[1]["cubic_spline"] = 1.778002
113sph_kernel_H_over_h[1]["quartic_spline"] = 1.977173
114sph_kernel_H_over_h[1]["quintic_spline"] = 2.158131
115sph_kernel_H_over_h[1]["wendland_C2"] = 1.897367
116sph_kernel_H_over_h[1]["wendland_C4"] = 2.171239
117sph_kernel_H_over_h[1]["wendland_C6"] = 2.415230
118sph_kernel_H_over_h[1]["quartic_spline_vectorized"] = sph_kernel_H_over_h[1][
119 "quartic_spline"
120]
121
122# 1D values
123sph_kernel_H_over_h[0]["cubic_spline"] = 1.732051
124sph_kernel_H_over_h[0]["quartic_spline"] = 1.936492
125sph_kernel_H_over_h[0]["quintic_spline"] = 2.121321
126sph_kernel_H_over_h[0]["wendland_C2"] = 1.620185
127sph_kernel_H_over_h[0]["wendland_C4"] = None # not defined in 1D
128sph_kernel_H_over_h[0]["wendland_C6"] = None # not defined in 1D
129sph_kernel_H_over_h[0]["quartic_spline_vectorized"] = sph_kernel_H_over_h[0][
130 "quartic_spline"
131]
132
133
134# Kernel Normalizations
135# 3D values
136sph_kernel_normalization[2]["cubic_spline"] = 16.0 / np.pi
137sph_kernel_normalization[2]["quartic_spline"] = 15625.0 / (np.pi * 512.0)
138sph_kernel_normalization[2]["quintic_spline"] = 2187.0 / (np.pi * 40.0)
139sph_kernel_normalization[2]["wendland_C2"] = 21.0 / (np.pi * 2.0)
140sph_kernel_normalization[2]["wendland_C4"] = 495.0 / (np.pi * 32.0)
141sph_kernel_normalization[2]["wendland_C6"] = 1365.0 / (np.pi * 64.0)
142sph_kernel_normalization[2]["quartic_spline_vectorized"] = sph_kernel_normalization[2][
143 "quartic_spline"
144]
145
146# 2D values
147sph_kernel_normalization[1]["cubic_spline"] = 80.0 / (np.pi * 7.0)
148sph_kernel_normalization[1]["quartic_spline"] = 46875.0 / (np.pi * 2398.0)
149sph_kernel_normalization[1]["quintic_spline"] = 15309.0 / (np.pi * 478.0)
150sph_kernel_normalization[1]["wendland_C2"] = 7.0 / np.pi
151sph_kernel_normalization[1]["wendland_C4"] = 9.0 / np.pi
152sph_kernel_normalization[1]["wendland_C6"] = 78.0 / (np.pi * 7.0)
153sph_kernel_normalization[1]["quartic_spline_vectorized"] = sph_kernel_normalization[1][
154 "quartic_spline"
155]
156
157# 1D values
158sph_kernel_normalization[0]["cubic_spline"] = 8.0 / 3.0
159sph_kernel_normalization[0]["quartic_spline"] = 3125.0 / 768.0
160sph_kernel_normalization[0]["quintic_spline"] = 243.0 / 40.0
161sph_kernel_normalization[0]["wendland_C2"] = 5.0 / 4.0
162sph_kernel_normalization[0]["wendland_C4"] = None # not defined in 1D
163sph_kernel_normalization[0]["wendland_C6"] = None # not defined in 1D
164sph_kernel_normalization[0]["quartic_spline_vectorized"] = sph_kernel_normalization[0][
165 "quartic_spline"
166]
167
168
170 """!
171 Evaluates the cubic spline kernel
172 """
173 if q < 0.5:
174 res = 3.0 * q**2.0 * (q - 1.0) + 0.5
175 elif q < 1.0:
176 # res = q * (q * (3 - q) - 3) + 1
177 res = -(q**3.0) + 3.0 * q**2.0 - 3.0 * q + 1.0
178 else:
179 res = 0.0
180 return res
181
182
184 """!
185 Evaluates the quartic spline kernel
186 """
187 if q < 0.2:
188 res = 6 * q**4 - 2.4 * q**2 + 46 / 125
189 elif q < 0.6:
190 res = -4 * q**4 + 8 * q**3 - 4.8 * q**2 + 8 / 25 * q + 44.0 / 125
191 elif q < 1:
192 res = q**4 - 4 * q**3 + 6 * q**2 - 4 * q + 1
193 else:
194 res = 0.0
195 return res
196
197
199 """!
200 Evaluates the quartic spline kernel
201 """
202
203 res = np.zeros(q.shape)
204 mask1 = q <= 0.2
205 mask2 = np.logical_and(q > 0.2, q <= 0.6)
206 mask3 = np.logical_and(q > 0.6, q <= 1.0)
207
208 qa = q[mask1]
209 qa2 = qa**2
210 res[mask1] = 6.0 * qa2**2 - 2.4 * qa2 + 46.0 / 125.0
211
212 qb = q[mask2]
213 qb2 = qb**2
214 res[mask2] = (
215 -4.0 * qb2**2 + 8.0 * qb2 * qb - 4.8 * qb2 + 8.0 / 25.0 * qb + 44.0 / 125.0
216 )
217
218 qc = q[mask3]
219 qc2 = qc**2
220 res[mask3] = qc2**2 - 4.0 * qc2 * qc + 6.0 * qc2 - 4.0 * qc + 1.0
221
222 return res
223
224
226 """!
227 Evaluates the quintic spline kernel
228 """
229 if q < 1.0 / 3.0:
230 res = 10.0 * (q**4 * (1.0 - q) - 0.2222222222 * q**2) + 0.2716049382716049
231 elif q < 0.666666666666:
232 qsq = q**2
233 q4 = qsq**2
234 res = (
235 5
236 * (
237 q4 * (q - 3)
238 + qsq * (3.333333333333333 * q - 1.5555555555555)
239 + 0.18518518518518517 * q
240 )
241 + 0.20987654320987653
242 )
243 elif q < 1.0:
244 qsq = q**2
245 res = qsq * (qsq * (5 - q) + 10 * (1 - q)) - 5 * q + 1
246 else:
247 res = 0.0
248 return res
249
250
252 """!
253 Evaluates the quintic spline kernel
254 """
255
256 res = np.zeros(q.shape)
257 mask1 = q <= 1.0 / 3.0
258 mask2 = np.logical_and(q > 1.0 / 3.0, q <= 2.0 / 3)
259 mask3 = np.logical_and(q > 2.0 / 3, q <= 1.0)
260
261 qa = q[mask1]
262 res[mask1] = (
263 10.0 * (qa**4 * (1.0 - qa) - 0.2222222222 * qa**2) + 0.2716049382716049
264 )
265
266 qb = q[mask2]
267 qb2 = qb**2
268 qb4 = qb2**2
269
270 res[mask2] = (
271 5.0
272 * (
273 qb4 * (qb - 3.0)
274 + qb2 * (3.333333333333333 * qb - 1.5555555555555)
275 + 0.18518518518518517 * qb
276 )
277 + 0.20987654320987653
278 )
279
280 qc = q[mask3]
281 qc2 = qc**2
282 res[mask3] = qc2 * (qc2 * (5.0 - qc) + 10.0 * (1.0 - qc)) - 5.0 * qc + 1.0
283
284 return res
285
286
288 """!
289 Evaluates the Wendland C2 kernel
290 """
291 if q < 1.0:
292 qsq = q**2
293 res = qsq * (qsq * (4.0 * q - 15.0) + 10.0 * (2.0 * q - 1.0)) + 1.0
294 else:
295 res = 0.0
296 return res
297
298
300 """!
301 Evaluates the Wendland C4 kernel
302 """
303 if q < 1.0:
304 qsq = q**2
305 q4 = qsq**2
306
307 res = (
308 11.666666666666666 * q4 * q4
309 - 64.0 * q4 * qsq * q
310 + 140.0 * qsq * q4
311 - 149.3333333333333 * q4 * q
312 + 70.0 * q4
313 - 9.33333333333333 * qsq
314 + 1.0
315 )
316 else:
317 res = 0.0
318 return res
319
320
322 """!
323 Evaluates the Wendland C6 kernel
324 """
325 if q < 1.0:
326 res = (
327 32.0 * q**11
328 - 231.0 * q**10
329 + 704.0 * q**9
330 - 1155.0 * q**8
331 + 1056.0 * q**7
332 - 462.0 * q**6
333 + 66.0 * q**4
334 - 11.0 * q**2
335 + 1.0
336 )
337 else:
338 res = 0.0
339 return res
340
341
343 """!
344 Evaluates the cubic spline kernel derivative
345 """
346 if q < 0.5:
347 res = 9 * q**2 - 6 * q
348 elif q < 1:
349 res = 6 * q - 3 * q**2 - 3
350 else:
351 res = 0.0
352
353 return res
354
355
357 """!
358 Evaluates the quartic spline kernel derivative
359 """
360 if q < 0.2:
361 res = 24 * q**3 - 4.8 * q
362 elif q < 0.6:
363 res = -16 * q**3 + 24 * q**2 - 9.6 * q + 8 / 25
364 elif q < 1:
365 res = 4 * q**3 - 12 * q**2 + 12 * q - 4
366 else:
367 res = 0.0
368 return res
369
370
372 """!
373 Evaluates the quartic spline kernel derivative
374 """
375
376 res = np.zeros(q.shape)
377 mask1 = q <= 0.2
378 mask2 = np.logical_and(q > 0.2, q <= 0.6)
379 mask3 = np.logical_and(q > 0.6, q <= 1.0)
380
381 qa = q[mask1]
382 qa2 = qa**2
383 res[mask1] = 24.0 * qa**3 - 4.8 * qa
384
385 qb = q[mask2]
386 qb2 = qb**2
387 res[mask2] = -16.0 * qb2 * qb + 24.0 * qb2 - 9.6 * qb + 8.0 / 25.0
388
389 qc = q[mask3]
390 qc2 = qc**2
391 res[mask3] = 4.0 * qc2 * qc - 12.0 * qc2 + 12.0 * qc - 4.0
392
393 return res
394
395
397 """!
398 Evaluates the quintic spline kernel derivative
399 """
400 if q < 0.333333333333:
401 res = 40 * q**3 - 50 * q**4 - 4.44444444444 * q
402 elif q < 0.666666666666:
403 res = (
404 25 * q**4
405 - 60 * q**3
406 + 50 * q**2
407 - 15.555555555555 * q
408 + 0.9259259259259259
409 )
410 elif q < 1:
411 res = 20 * q**3 - 5 * q**4 + 20 * q - 30 * q**2 - 5
412 else:
413 res = 0.0
414 return res
415
416
418 """!
419 Evaluates the Wendland C2 kernel derivative
420 """
421 if q < 1.0:
422 res = 20.0 * q**4 - 60.0 * q**3 + 60.0 * q**2 - 20.0 * q
423 else:
424 res = 0.0
425 return res
426
427
429 """!
430 Evaluates the Wendland C4 kernel derivative
431 """
432 if q < 1:
433 res = (
434 93.3333333333 * q**7
435 - 448 * q**6
436 + 840 * q**5
437 - 746.6666666666 * q**4
438 + 280 * q**3
439 - 18.66666666666666 * q
440 )
441 else:
442 res = 0.0
443 return res
444
445
447 """!
448 Evaluates the Wendland C6 kernel derivative
449 """
450 if q < 1.0:
451 res = (
452 352 * q**10
453 - 2310 * q**9
454 + 6336 * q**8
455 - 9240 * q**7
456 + 7392 * q**6
457 - 2772 * q**5
458 + 264 * q**3
459 - 22 * q
460 )
461 else:
462 res = 0.0
463 return res
464
465
466sph_kernel_eval_function["cubic_spline"] = _sph_kernel_eval_cubic_spline
467sph_kernel_eval_function["quartic_spline"] = _sph_kernel_eval_quartic_spline
468sph_kernel_eval_function["quintic_spline"] = _sph_kernel_eval_quintic_spline
469sph_kernel_eval_function["wendland_C2"] = _sph_kernel_eval_wendland_C2
470sph_kernel_eval_function["wendland_C4"] = _sph_kernel_eval_wendland_C4
471sph_kernel_eval_function["wendland_C6"] = _sph_kernel_eval_wendland_C6
472sph_kernel_eval_function[
473 "quartic_spline_vectorized"
474] = _sph_kernel_eval_quartic_spline_vectorized
475
476sph_kernel_deval_function["cubic_spline"] = _sph_kernel_deval_cubic_spline
477sph_kernel_deval_function["quartic_spline"] = _sph_kernel_deval_quartic_spline
478sph_kernel_deval_function["quintic_spline"] = _sph_kernel_deval_quintic_spline
479sph_kernel_deval_function["wendland_C2"] = _sph_kernel_deval_wendland_C2
480sph_kernel_deval_function["wendland_C4"] = _sph_kernel_deval_wendland_C4
481sph_kernel_deval_function["wendland_C6"] = _sph_kernel_deval_wendland_C6
482sph_kernel_deval_function[
483 "quartic_spline_vectorized"
484] = _sph_kernel_deval_quartic_spline_vectorized
485
486
487def sph_kernel_W(
488 r: Union[float, np.ndarray], h: Union[float, np.ndarray], kernel: str, ndim: int
489):
490 """!
491 Evaluates various kernels.
492 The kernels are scaled such that W(q > 1) = 0.
493 Currently implemented:
494 cubic_spline,
495 quartic_spline,
496 quintic_spline,
497 wendland_C2,
498 wendland_C4, [not with ndim=1]
499 wendland_C6, [not with ndim=1]
500
501
502 Parameters
503 ----------
504
505 r: float or ndarray
506 particle distance(s)
507
508 h: float or ndarray
509 smoothing length(s)
510
511 kernel: str
512 which kernel to use
513
514 ndim: int
515 what dimension to use. [1-3]
516
517
518 Returns
519 -------
520
521 W: float
522 evaluated kernel
523
524 """
525
526 sigma = sph_kernel_normalization[ndim - 1][kernel]
527 H = sph_kernel_get_H(h, kernel, ndim)
528 kernel_W = sph_kernel_eval_function[kernel]
529 q = r / H
530 W = sigma * kernel_W(q) / H**ndim
531
532 return W
533
534
535def sph_kernel_dWdr(
536 r: Union[float, np.ndarray], h: Union[float, np.ndarray], kernel: str, ndim: int
537):
538 """
539 Evaluates kernel derivatives for various kernels:
540 returns dW/dr = dW/dq dq/dr = 1/H * dW/dq
541
542 The kernels are scaled such that W(q > 1) = 0.
543 Currently implemented:
544 cubic_spline,
545 quartic_spline,
546 quintic_spline,
547 wendland_C2,
548 wendland_C4, [not with ndim=1]
549 wendland_C6, [not with ndim=1]
550
551
552 Parameters
553 ----------
554
555 r: float or ndarray
556 particle distance(s)
557
558 h: float or ndarray
559 smoothing length(s)
560
561 kernel: str
562 which kernel to use
563
564 ndim: int
565 what dimension to use. [1-3]
566
567 Returns
568 -------
569
570 dWdr: float or ndarray
571 evaluated kernel derivative
572
573 """
574
575 sigma = sph_kernel_normalization[ndim - 1][kernel]
576 kernel_dWdr = sph_kernel_deval_function[kernel]
577 H = sph_kernel_get_H(h, kernel, ndim)
578 q = r / H
579 dWdr = sigma * kernel_dWdr(q) / H ** (ndim + 1)
580
581 return dWdr
582
583
585 h: Union[float, np.ndarray], kernel: str, ndim: int
586):
587 """!
588 Computes the interaction radius (compact support radius) for a given smoothing length h,
589 SPH kernel `kernel`, and kernel dimension `ndim`.
590 """
591 return sph_kernel_get_H(h, kernel, ndim)
592
593
594def sph_kernel_get_H(h: Union[float, np.ndarray], kernel: str, ndim: int):
595 """!
596 Compute the compact support radius of a given kernel and given smoothing length.
597 The kernels defined above are defined and scaled to support a region <= H.
598
599 Parameters
600 ----------
601
602 h: float or np.ndarray
603 smoothing length
604
605 kernel: str
606 which kernel to use.
607
608 ndim: int
609 how many dimensions we're working with
610
611
612 Returns
613 -------
614
615 H: float or np.dnarray
616 compact support radii.
617
618 """
619
620 return h * sph_kernel_H_over_h[ndim - 1][kernel]
621
622
624 H: Union[float, np.ndarray], kernel: str, ndim: int
625):
626 """!
627 Compute the smoothing length given the compact support length of a given kernel.
628 The kernels defined above are defined and scaled to support a region <= H.
629
630
631 Parameters
632 ----------
633
634 H: float or np.ndarray
635 compact support radii.
636
637 kernel: str
638 which kernel to use.
639
640 ndim: int
641 how many dimensions we're working with
642
643
644 Returns
645 -------
646
647 h: float or np.dnarray
648 smoothing lengt(s)
649
650 """
651
652 return H / sph_kernel_H_over_h[ndim - 1][kernel]
653
654
655def sph_kernel_root(kernel: str, ndim: int):
656 """!
657 Evaluate kernel root, i.e. W(0, h).
658 We don't actually need `h` here, because the quantity used in the kernels,
659 `q = r/h` is also zero by construction.
660
661 Parameters
662 ----------
663
664 kernel: str
665 which kernel to use
666
667 ndim: int
668 what dimension to use. [1-3]
669
670
671 Returns
672 -------
673
674 W: float
675 evaluated kernel
676
677 """
678 kernel_novector = kernel
679 if kernel.endswith("vectorized"):
680 kernel_novector = kernel[:-11]
681
682 return sph_kernel_W(0.0, 1.0, kernel_novector, ndim)
Modify the default dict to raise an error if a user tries to access kernels which aren't defined.
__init__(self, *args, blocked_keys=(), ndim=0, **kwargs)
_sph_kernel_eval_quintic_spline_vectorized(np.ndarray q)
Evaluates the quintic spline kernel.
_sph_kernel_eval_quartic_spline_vectorized(np.ndarray q)
Evaluates the quartic spline kernel.
_sph_kernel_deval_quartic_spline_vectorized(np.ndarray q)
Evaluates the quartic spline kernel derivative.
_sph_kernel_deval_wendland_C2(float q)
Evaluates the Wendland C2 kernel derivative.
sph_kernel_get_smoothing_length_from_interaction_radius(Union[float, np.ndarray] H, str kernel, int ndim)
Compute the smoothing length given the compact support length of a given kernel.
_sph_kernel_eval_quartic_spline(float q)
Evaluates the quartic spline kernel.
_sph_kernel_eval_quintic_spline(float q)
Evaluates the quintic spline kernel.
_sph_kernel_deval_wendland_C6(float q)
Evaluates the Wendland C6 kernel derivative.
_sph_kernel_deval_cubic_spline(float q)
Evaluates the cubic spline kernel derivative.
_sph_kernel_deval_quintic_spline(float q)
Evaluates the quintic spline kernel derivative.
_sph_kernel_deval_wendland_C4(float q)
Evaluates the Wendland C4 kernel derivative.
_sph_kernel_eval_cubic_spline(float q)
Evaluates the cubic spline kernel.
_sph_kernel_deval_quartic_spline(float q)
Evaluates the quartic spline kernel derivative.
_sph_kernel_eval_wendland_C4(float q)
Evaluates the Wendland C4 kernel.
sph_kernel_get_interaction_radius(Union[float, np.ndarray] h, str kernel, int ndim)
Computes the interaction radius (compact support radius) for a given smoothing length h,...
_sph_kernel_eval_wendland_C6(float q)
Evaluates the Wendland C6 kernel.
_sph_kernel_eval_wendland_C2(float q)
Evaluates the Wendland C2 kernel.