1#![warn(missing_docs)]
25
26use std::num::FpCategory::*;
27
28pub fn range_to_vec(start: f64, end: f64, step: f64) -> Vec<f64> {
31 let diff: f64 = end - start;
32
33 let sds_to_vec = |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).collect::<Vec<f64>>();
34
35 match (start.classify(), diff.classify(), step.classify()) {
36 (Normal | Zero | Subnormal, Normal | Subnormal, Normal | Subnormal) => {
37 if !start.is_sign_negative() && !end.is_sign_negative() &&
38 diff.is_sign_negative() == step.is_sign_negative() {
39 sds_to_vec(start, diff, step)
40 } else {
41 panic!("Both 'start' and 'end' should be non-negative, and the sign of 'step' must match the sign of 'end - start'.");
42 }
43 },
44 (_, _, Zero) => panic!("'step' cannot be zero."),
45 (_, Zero, _) => panic!("'start' and 'end' should be different."),
46 (Infinite, _, _) | (_, Infinite, _) | (_, _, Infinite) => panic!("Arguments cannot be infinite."),
47 _ => panic!("Improper arguments.")
48 }
49}
50
51pub fn sph_bessel_kind1_order0_arg_real(x: f64) -> f64 {
54 match x.classify() {
55 Normal | Subnormal => x.sin()/x,
56 Zero => 1.0,
57 Infinite => 0.0,
58 Nan => f64::NAN,
59 }
60
61}
62
63pub fn sph_bessel_kind1_order0_arg_real_ranged(start: f64, end: f64, step: f64) -> Vec<f64> {
66 let diff: f64 = end - start;
67 let default_expr = |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize)
68 .map(|n| start + (n as f64)*step).map(|x| x.sin()/x).collect::<Vec<f64>>();
69
70 match (start.classify(), diff.classify(), step.classify()) {
71 (Normal | Subnormal, Normal | Subnormal, Normal | Subnormal) => {
72 if !start.is_sign_negative() && !end.is_sign_negative() &&
73 diff.is_sign_negative() == step.is_sign_negative() {
74 default_expr(start, diff, step)
75 } else {
76 panic!("Both 'start' and 'end' should be non-negative, and the sign of 'step' must match the sign of 'end - start'.");
77 }
78 },
79 (Zero, Normal | Subnormal, Normal | Subnormal) => {
80 let mut result = Vec::with_capacity(((diff/step).ceil() as usize) + 1);
81
82 if end.is_sign_positive() && step.is_sign_positive() {
83 result.push(1.0);
84 result.extend_from_slice(&default_expr(start + step, diff, step));
85 result
86 } else {
87 panic!("Both 'end' and 'step' should be positive if 'start' is zero.");
88 }
89 },
90 (_, _, Zero) => panic!("'step' cannot be zero."),
91 (_, Zero, _) => panic!("'start' and 'end' should be different."),
92 (Infinite, _, _) | (_, Infinite, _) | (_, _, Infinite) => panic!("Arguments cannot be infinite"),
93 _ => panic!("Improper arguments.")
94 }
95
96}
97
98pub fn sph_bessel_kind1_order0_arg_real_iterable(x_list: impl IntoIterator<Item = f64>) -> Vec<f64> {
100 x_list.into_iter().map(|x: f64| match x.classify() {
101 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { x.sin()/x },
102 Zero => 1.0,
103 Infinite => 0.0,
104 _ => f64::NAN
105 }).collect::<Vec<f64>>()
106}
107
108pub fn sph_bessel_kind1_ordern_arg_real(order: usize, x: f64) -> f64 {
111 let default_expr = match order {
112 0 => |x: f64| x.sin()/x,
113 1 => |x: f64| x.sin()/x.powi(2) - x.cos()/x,
114 2 => |x: f64| x.sin()*(3.0/x.powi(3)-1.0/x) - x.cos()*(3.0/x.powi(2)),
115 _ => panic!("Only orders from 0 to 2 are currently implemented.")
116 };
117
118 match x.classify() {
119 Normal | Subnormal => default_expr(x),
120 Zero => match order {
121 0 => 1.0,
122 _ => 0.0
123 },
124 Infinite => 0.0,
125 Nan => f64::NAN,
126 }
127}
128
129pub fn sph_bessel_kind1_ordern_arg_real_ranged(order: usize, start: f64, end: f64, step: f64) -> Vec<f64> {
132 let diff: f64 = end - start;
134
135 let default_expr = match order {
136 0 => {
137 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| x.sin()/x).collect::<Vec<f64>>()
138 },
139 1 => {
140 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| x.sin()/x.powi(2) - x.cos()/x).collect::<Vec<f64>>()
141 },
142 2 => {
143 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| x.sin()*(3.0/x.powi(3)-1.0/x) - x.cos()*(3.0/x.powi(2))).collect::<Vec<f64>>()
144 },
145 _ => {
146 panic!("Only orders from 0 to 2 are currently implemented.");
147 }
148 };
149
150 match (start.classify(), diff.classify(), step.classify()) {
151 (Normal | Subnormal, Normal | Subnormal, Normal | Subnormal) => {
152 if !start.is_sign_negative() && !end.is_sign_negative() &&
153 diff.is_sign_negative() == step.is_sign_negative() {
154 default_expr(start, diff, step)
155 } else {
156 panic!("Both 'start' and 'end' should be non-negative, and the sign of 'step' must match the sign of 'end - start'.");
157 }
158 },
159 (Zero, Normal | Subnormal, Normal | Subnormal) => {
160 let mut result = Vec::with_capacity(((diff/step).ceil() as usize) + 1);
161
162 if end.is_sign_positive() && step.is_sign_positive() {
163 result.push(match order {
164 0 => 1.0,
165 1..=2 => 0.0,
166 _ => panic!("Only orders from 0 to 2 are currently implemented.")
167 });
168 result.extend_from_slice(&default_expr(start + step, diff, step));
169 result
170 } else {
171 panic!("Both 'end' and 'step' should be positive if 'start' is zero.");
172 }
173 },
174 (_, _, Zero) => panic!("'step' cannot be zero."),
175 (_, Zero, _) => panic!("'start' and 'end' should be different."),
176 (Infinite, _, _) | (_, Infinite, _) | (_, _, Infinite) => panic!("Arguments cannot be infinite."),
177 _ => panic!("Improper arguments.")
178 }
179}
180
181pub fn sph_bessel_kind1_ordern_arg_real_iterable(order: usize, x_list: impl IntoIterator<Item = f64>) -> Vec<f64> {
184 match order {
185 0 => {
186 x_list.into_iter().map(|x| match x.classify() {
187 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { x.sin()/x },
188 Zero => 1.0,
189 Infinite => 0.0,
190 _ => f64::NAN
191 }).collect::<Vec<f64>>()
192 },
193 1 => {
194 x_list.into_iter().map(|x| match x.classify() {
195 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { x.sin()/x.powi(2) - x.cos()/x },
196 Zero | Infinite => 0.0,
197 _ => f64::NAN
198 }).collect::<Vec<f64>>()
199 },
200 2 => {
201 x_list.into_iter().map(|x| match x.classify() {
202 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { x.sin()*(3.0/x.powi(3)-1.0/x) - x.cos()*(3.0/x.powi(2)) },
203 Zero | Infinite => 0.0,
204 _ => f64::NAN
205 }).collect::<Vec<f64>>()
206 },
207 _ => {
208 panic!("Only orders from 0 to 2 are currently implemented.");
209 }
210 }
211}
212
213pub fn sph_bessel_kind2_order0_arg_real(x: f64) -> f64 {
216 match x.classify() {
217 Normal | Subnormal => -x.cos()/x,
218 Zero => f64::INFINITY,
219 Infinite => 0.0,
220 Nan => f64::NAN,
221 }
222
223}
224
225pub fn sph_bessel_kind2_order0_arg_real_ranged(start: f64, end: f64, step: f64) -> Vec<f64> {
228 let diff: f64 = end - start;
229 let default_expr = |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize)
230 .map(|n| start + (n as f64)*step).map(|x| -x.cos()/x).collect::<Vec<f64>>();
231
232 match (start.classify(), diff.classify(), step.classify()) {
233 (Normal | Subnormal, Normal | Subnormal, Normal | Subnormal) => {
234 if !start.is_sign_negative() && !end.is_sign_negative() &&
235 diff.is_sign_negative() == step.is_sign_negative() {
236 default_expr(start, diff, step)
237 } else {
238 panic!("Both 'start' and 'end' should be non-negative, and the sign of 'step' must match the sign of 'end - start'.");
239 }
240 },
241 (Zero, Normal | Subnormal, Normal | Subnormal) => {
242 let mut result = Vec::with_capacity(((diff/step).ceil() as usize) + 1);
243
244 if end.is_sign_positive() && step.is_sign_positive() {
245 result.push(f64::NEG_INFINITY);
246 result.extend_from_slice(&default_expr(start + step, diff, step));
247 result
248 } else {
249 panic!("Both 'end' and 'step' should be positive if 'start' is zero.");
250 }
251 },
252 (_, _, Zero) => panic!("'step' cannot be zero."),
253 (_, Zero, _) => panic!("'start' and 'end' should be different."),
254 (Infinite, _, _) | (_, Infinite, _) | (_, _, Infinite) => panic!("Arguments cannot be infinite"),
255 _ => panic!("Improper arguments.")
256 }
257
258}
259
260pub fn sph_bessel_kind2_order0_arg_real_iterable(x_list: impl IntoIterator<Item = f64>) -> Vec<f64> {
262 x_list.into_iter().map(|x: f64| match x.classify() {
263 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { -x.cos()/x },
264 Zero => f64::NEG_INFINITY,
265 Infinite => 0.0,
266 _ => f64::NAN
267 }).collect::<Vec<f64>>()
268}
269
270pub fn sph_bessel_kind2_ordern_arg_real(order: usize, x: f64) -> f64 {
273 let default_expr = match order {
274 0 => |x: f64| -x.cos()/x,
275 1 => |x: f64| -x.cos()/x.powi(2) - x.sin()/x,
276 2 => |x: f64| x.cos()*(-3.0/x.powi(3)+1.0/x) - x.sin()*(3.0/x.powi(2)),
277 _ => panic!("Only orders from 0 to 2 are currently implemented")
278 };
279
280 match x.classify() {
281 Normal | Subnormal => default_expr(x),
282 Zero => f64::NEG_INFINITY,
283 Infinite => 0.0,
284 Nan => f64::NAN,
285 }
286
287}
288
289pub fn sph_bessel_kind2_ordern_arg_real_ranged(order: usize, start: f64, end: f64, step: f64) -> Vec<f64> {
293 let diff: f64 = end - start;
295
296 let default_expr = match order {
297 0 => {
298 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| -x.cos()/x).collect::<Vec<f64>>()
299 },
300 1 => {
301 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| -x.cos()/x.powi(2) - x.sin()/x).collect::<Vec<f64>>()
302 },
303 2 => {
304 |start: f64, diff: f64, step: f64| (0 .. (diff/step).ceil() as usize).map(|n| start + (n as f64)*step).map(|x| x.cos()*(-3.0/x.powi(3)+1.0/x) - x.sin()*(3.0/x.powi(2))).collect::<Vec<f64>>()
305 },
306 _ => {
307 panic!("Only orders from 0 to 2 are currently implemented.");
308 },
309 };
310
311 match (start.classify(), diff.classify(), step.classify()) {
312 (Normal | Subnormal, Normal | Subnormal, Normal | Subnormal) => {
313 if !start.is_sign_negative() && !end.is_sign_negative() &&
314 diff.is_sign_negative() == step.is_sign_negative() {
315 default_expr(start, diff, step)
316 } else {
317 panic!("Both 'start' and 'end' should be non-negative, and the sign of 'step' must match the sign of 'end - start'.");
318 }
319 },
320 (Zero, Normal | Subnormal, Normal | Subnormal) => {
321 let mut result = Vec::with_capacity(((diff/step).ceil() as usize) + 1);
322
323 if end.is_sign_positive() && step.is_sign_positive() {
324 result.push(match order {
325 0..=2 => f64::NEG_INFINITY,
326 _ => panic!("Only orders from 0 to 2 are currently implemented.")
327 });
328 result.extend_from_slice(&default_expr(start+step, diff, step));
329 result
330 } else {
331 panic!("Both 'end' and 'step' should be positive if 'start' is zero.");
332 }
333 },
334 (_, _, Zero) => panic!("'step' cannot be zero."),
335 (_, Zero, _) => panic!("'start' and 'end' should be different."),
336 (Infinite, _, _) | (_, Infinite, _) | (_, _, Infinite) => panic!("Arguments cannot be infinite."),
337 _ => panic!("Improper arguments.")
338 }
339}
340
341pub fn sph_bessel_kind2_ordern_arg_real_iterable(order: usize, x_list: impl IntoIterator<Item = f64>) -> Vec<f64> {
344 match order {
345 0 => {
346 x_list.into_iter().map(|x| match x.classify() {
347 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { -x.cos()/x },
348 Zero => f64::NEG_INFINITY,
349 Infinite => 0.0,
350 _ => f64::NAN
351 }).collect::<Vec<f64>>()
352 },
353 1 => {
354 x_list.into_iter().map(|x| match x.classify() {
355 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { -x.cos()/x.powi(2) - x.sin()/x },
356 Zero => f64::NEG_INFINITY,
357 Infinite => 0.0,
358 _ => f64::NAN
359 }).collect::<Vec<f64>>()
360 },
361 2 => {
362 x_list.into_iter().map(|x| match x.classify() {
363 Normal | Subnormal => if x.is_sign_negative() { f64::NAN } else { x.cos()*(-3.0/x.powi(3)+1.0/x) - x.sin()*(3.0/x.powi(2)) },
364 Zero => f64::NEG_INFINITY,
365 Infinite => 0.0,
366 _ => f64::NAN
367 }).collect::<Vec<f64>>()
368 },
369 _ => {
370 panic!("Only orders from 0 to 2 are currently implemented.");
371 }
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use std::time::Instant;
379 use plotly::{
380 common::Mode,
381 layout::{Axis, Layout},
382 Plot, Scatter, ImageFormat
383 };
384
385 #[test]
386 fn benchmark_sph_bessel_kind1_order0_1million() {
387 let start_time = Instant::now();
388
389 for n in 0..1000000 {
390 sph_bessel_kind1_order0_arg_real((n as f64)/1.0e5 + 0.1);
393 }
394
395 let elapsed_time = start_time.elapsed();
396 println!("Elapsed time: {:?}", elapsed_time);
397 }
398
399 #[test]
400 fn benchmark_sph_bessel_kind1_order0_range_10million() {
401 let start_time = Instant::now();
402
403 for n in 0..1000 {
404 sph_bessel_kind1_ordern_arg_real_ranged(2, (n as f64)/2.0e5 + 0.1, 10000.0, 1.0);
405 }
406
407 let elapsed_time = start_time.elapsed();
408 println!("Elapsed time: {:?}", elapsed_time);
409 }
410
411 #[test]
412 fn benchmark_sph_bessel_kind1_order0_iterable_10million() {
413 let start_time = Instant::now();
414
415 for n in 0..1000 {
416 sph_bessel_kind1_order0_arg_real_iterable((0..10000).map(|m| ((n + m) as f64)/3.0e3)
417 .collect::<Vec<f64>>());
418 }
419
420 let elapsed_time = start_time.elapsed();
421 println!("Elapsed time: {:?}", elapsed_time);
422 }
423
424
425 #[test]
426 fn plot_result() {
427 let x_start: f64 = 0.0;
428 let x_end: f64 = 10.0;
429 let x_step: f64 = 0.05;
430
431 let x_vec: Vec<f64> = range_to_vec(x_start, x_end, x_step);
432
433 let sph_bessel2_0: Vec<f64> = sph_bessel_kind2_ordern_arg_real_ranged(0, x_start, x_end, x_step);
434 let sph_bessel2_1: Vec<f64> = sph_bessel_kind2_ordern_arg_real_ranged(1, x_start, x_end, x_step);
435 let sph_bessel2_2: Vec<f64> = sph_bessel_kind2_ordern_arg_real_ranged(2, x_start, x_end, x_step);
436
437 let trace_sb2_0 = Scatter::new(x_vec.clone(), sph_bessel2_0)
438 .mode(Mode::Lines)
439 .name("Order 0");
440 let trace_sb2_1 = Scatter::new(x_vec.clone(), sph_bessel2_1)
441 .mode(Mode::Lines)
442 .name("Order 1");
443 let trace_sb2_2 = Scatter::new(x_vec.clone(), sph_bessel2_2)
444 .mode(Mode::Lines)
445 .name("Order 2");
446
447 let mut plot_sb = Plot::new();
448 plot_sb.add_trace(trace_sb2_0);
449 plot_sb.add_trace(trace_sb2_1);
450 plot_sb.add_trace(trace_sb2_2);
451
452 let layout_sb = Layout::new()
453 .title("Spherical Bessel functions of the second kind".into())
454 .x_axis(Axis::new().range(vec![x_start, x_end]))
455 .y_axis(Axis::new().range(vec![-2.5, 1.0]));
456 plot_sb.set_layout(layout_sb);
457
458 plot_sb.write_image("./sph_bessel_kind2.png", ImageFormat::PNG, 600, 400, 1.0);
459 }
460
461 #[test]
462 fn test_sph_bessel_kind2() {
463 println!("{:?}", sph_bessel_kind2_order0_arg_real(0.1*f64::MIN_POSITIVE));
464 println!("{:?}", sph_bessel_kind2_order0_arg_real_ranged(0.0, 2.5*f64::MIN_POSITIVE, 0.5*f64::MIN_POSITIVE));
465 println!("{:?}", sph_bessel_kind2_ordern_arg_real_ranged(2, 0.0, 2.5*f64::MIN_POSITIVE, 0.5*f64::MIN_POSITIVE));
466 }
467
468 #[test]
469 fn test_sph_bessel_kind1_ord0_arg_iterable() {
470 let x_start: f64 = 0.0;
471 let x_end: f64 = 10.0;
472 let x_step: f64 = 0.1;
473
474 let x_vec: Vec<f64> = range_to_vec(x_start, x_end, x_step);
475
476 let sb10_r: Vec<f64> = sph_bessel_kind1_order0_arg_real_ranged(x_start, x_end, x_step);
477 let sb10_i: Vec<f64> = sph_bessel_kind1_order0_arg_real_iterable(x_vec.clone());
478
479 let trace_sb10_r = Scatter::new(x_vec.clone(), sb10_r)
480 .mode(Mode::Lines)
481 .name("Ranged");
482 let trace_sb10_i = Scatter::new(x_vec.clone(), sb10_i)
483 .mode(Mode::Markers)
484 .name("Iterated");
485
486 let mut plot_sb = Plot::new();
487 plot_sb.add_trace(trace_sb10_r);
488 plot_sb.add_trace(trace_sb10_i);
489
490 let layout_sb = Layout::new()
491 .title("Spherical Bessel functions of the first kind".into())
492 .x_axis(Axis::new().range(vec![x_start, x_end]))
493 .y_axis(Axis::new().range(vec![-0.5, 1.5]));
494 plot_sb.set_layout(layout_sb);
495
496 plot_sb.write_image("./sph_bessel_kind10.png", ImageFormat::PNG, 600, 400, 1.0);
497 }
498
499 #[test]
500 fn test_sph_bessel_kind1_ordn_arg_iterable() {
501 let x_start: f64 = 0.0;
502 let x_end: f64 = 10.0;
503 let x_step: f64 = 0.1;
504
505 let x_vec: Vec<f64> = range_to_vec(x_start, x_end, x_step);
506
507 let sb11_r: Vec<f64> = sph_bessel_kind1_ordern_arg_real_ranged(1, x_start, x_end, x_step);
508 let sb11_i: Vec<f64> = sph_bessel_kind1_ordern_arg_real_iterable(1, x_vec.clone());
509
510 let trace_sb11_r = Scatter::new(x_vec.clone(), sb11_r)
511 .mode(Mode::Lines)
512 .name("Ranged");
513 let trace_sb11_i = Scatter::new(x_vec.clone(), sb11_i)
514 .mode(Mode::Markers)
515 .name("Iterated");
516
517 let mut plot_sb = Plot::new();
518 plot_sb.add_trace(trace_sb11_r);
519 plot_sb.add_trace(trace_sb11_i);
520
521 let layout_sb = Layout::new()
522 .title("Spherical Bessel functions of the first kind".into())
523 .x_axis(Axis::new().range(vec![x_start, x_end]))
524 .y_axis(Axis::new().range(vec![-0.5, 1.5]));
525 plot_sb.set_layout(layout_sb);
526
527 plot_sb.write_image("./sph_bessel_kind11.png", ImageFormat::PNG, 600, 400, 1.0);
528 }
529
530 #[test]
531 fn test_sph_bessel_kind2_ord0_arg_iterable() {
532 let x_start: f64 = 0.0;
533 let x_end: f64 = 10.0;
534 let x_step: f64 = 0.1;
535
536 let x_vec: Vec<f64> = range_to_vec(x_start, x_end, x_step);
537
538 let sb20_r: Vec<f64> = sph_bessel_kind2_order0_arg_real_ranged(x_start, x_end, x_step);
539 let sb20_i: Vec<f64> = sph_bessel_kind2_order0_arg_real_iterable(x_vec.clone());
540
541 let trace_sb20_r = Scatter::new(x_vec.clone(), sb20_r)
542 .mode(Mode::Lines)
543 .name("Ranged");
544 let trace_sb20_i = Scatter::new(x_vec.clone(), sb20_i)
545 .mode(Mode::Markers)
546 .name("Iterated");
547
548 let mut plot_sb = Plot::new();
549 plot_sb.add_trace(trace_sb20_r);
550 plot_sb.add_trace(trace_sb20_i);
551
552 let layout_sb = Layout::new()
553 .title("Spherical Bessel functions of the second kind".into())
554 .x_axis(Axis::new().range(vec![x_start, x_end]))
555 .y_axis(Axis::new().range(vec![-2.5, 1.0]));
556 plot_sb.set_layout(layout_sb);
557
558 plot_sb.write_image("./sph_bessel_kind20.png", ImageFormat::PNG, 600, 400, 1.0);
559 }
560
561 #[test]
562 fn test_sph_bessel_kind2_ordn_arg_iterable() {
563 let x_start: f64 = 0.0;
564 let x_end: f64 = 10.0;
565 let x_step: f64 = 0.1;
566
567 let x_vec: Vec<f64> = range_to_vec(x_start, x_end, x_step);
568
569 let sb21_r: Vec<f64> = sph_bessel_kind2_ordern_arg_real_ranged(1, x_start, x_end, x_step);
570 let sb21_i: Vec<f64> = sph_bessel_kind2_ordern_arg_real_iterable(1, x_vec.clone());
571
572 let trace_sb21_r = Scatter::new(x_vec.clone(), sb21_r)
573 .mode(Mode::Lines)
574 .name("Ranged");
575 let trace_sb21_i = Scatter::new(x_vec.clone(), sb21_i)
576 .mode(Mode::Markers)
577 .name("Iterated");
578
579 let mut plot_sb = Plot::new();
580 plot_sb.add_trace(trace_sb21_r);
581 plot_sb.add_trace(trace_sb21_i);
582
583 let layout_sb = Layout::new()
584 .title("Spherical Bessel functions of the second kind".into())
585 .x_axis(Axis::new().range(vec![x_start, x_end]))
586 .y_axis(Axis::new().range(vec![-2.5, 1.0]));
587 plot_sb.set_layout(layout_sb);
588
589 plot_sb.write_image("./sph_bessel_kind21.png", ImageFormat::PNG, 600, 400, 1.0);
590 }
591
592}